diff --git a/.bettercodehub.yml b/.bettercodehub.yml index cce850d71..d7be51695 100644 --- a/.bettercodehub.yml +++ b/.bettercodehub.yml @@ -1,5 +1,3 @@ -exclude: -- /app/src/main/java/io/github/wulkanowy/data/db/dao/entities/.* -component_depth: 1 +component_depth: 10 languages: -- java +- kotlin diff --git a/.circleci/config.yml b/.circleci/config.yml index cd6d17745..ce2922ba3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,11 +7,11 @@ references: container_config: &container_config docker: - - image: circleci/android:api-27-alpha + - image: circleci/android@sha256:5cdc8626cc6f13efe5ed982cdcdb432b0472f8740fed8743a6461e025ad6cdfc working_directory: *workspace_root environment: environment: - JVM_OPTS: -Xmx3200m + _JAVA_OPTS: -Xmx3072m attach_workspace: &attach_workspace attach_workspace: @@ -25,6 +25,8 @@ jobs: build: <<: *container_config steps: + - run: | + curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | bash - checkout - restore_cache: <<: *general_cache_key @@ -33,10 +35,10 @@ jobs: command: ./gradlew dependencies --no-daemon --stacktrace --console=plain -PdisablePreDex || true - run: name: Initial build - command: ./gradlew build assembleDebug -x test -x lint -x fabricGenerateResourcesRelease --no-daemon --stacktrace --console=plain -PdisablePreDex - - store_artifacts: - path: ./app/build/outputs/apk/ - destination: apks/ + command: ./gradlew build -x test -x lint -x fabricGenerateResourcesFdroidRelease -x fabricGenerateResourcesPlayRelease -x packageRelease --no-daemon --stacktrace --console=plain -PdisablePreDex + - run: + name: Run FOSSA + command: fossa --no-ansi || true - persist_to_workspace: root: *workspace_root paths: @@ -54,7 +56,7 @@ jobs: <<: *general_cache_key - run: name: Run lint - command: ./gradlew lint -x fabricGenerateResourcesRelease --no-daemon --stacktrace --console=plain -PdisablePreDex + command: ./gradlew lint -x fabricGenerateResourcesFdroidRelease -x fabricGenerateResourcesPlayRelease --no-daemon --stacktrace --console=plain -PdisablePreDex - store_artifacts: path: ./app/build/reports/ destination: lint_reports/app/ @@ -73,16 +75,13 @@ jobs: <<: *general_cache_key - run: name: Run app tests - command: ./gradlew :app:test :app:jacocoTestReport -x fabricGenerateResourcesRelease --no-daemon --stacktrace --console=plain -PdisablePreDex + command: ./gradlew :app:test :app:jacocoTestReport -x fabricGenerateResourcesFdroidRelease -x fabricGenerateResourcesPlayRelease --no-daemon --stacktrace --console=plain -PdisablePreDex - run: 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: @@ -90,38 +89,16 @@ jobs: paths: - "./app/build/jacoco" - api-test: - <<: *container_config - steps: - - *attach_workspace - - restore_cache: - <<: *general_cache_key - - run: - name: Run api tests - command: ./gradlew :api:test :api:jacocoTestReport --no-daemon --stacktrace --console=plain -PdisablePreDex - - run: - 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/ - - store_test_results: - path: ./api/build/test-results - - persist_to_workspace: - root: *workspace_root - paths: - - "./api/build/jacoco" - instrumented: <<: *container_config steps: - *attach_workspace + - run: + name: Accept licenses + command: yes | sdkmanager --licenses && yes | sdkmanager --update - 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-22;default;armeabi-v7a" && echo "no" | avdmanager create avd -n test -k "system-images;android-22;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 @@ -137,15 +114,13 @@ jobs: # unlock the emulator screen sleep 30 adb shell input keyevent 82 - - run: - name: Clean project - command: ./gradlew clean --no-daemon --stacktrace --console=plain -PdisablePreDex - run: name: Run instrumented tests - command: ./gradlew createDebugCoverageReport --no-daemon --stacktrace --console=plain -PdisablePreDex + command: ./gradlew clean createFdroidDebugCoverageReport jacocoTestReport --no-daemon --stacktrace --console=plain -PdisablePreDex - run: name: Collect logs from emulator command: adb logcat -d > ./app/build/reports/logcat_emulator.txt + when: always - run: name: Upload code covarage to codecov command: bash <(curl -s https://codecov.io/bash) -F instrumented @@ -170,32 +145,66 @@ jobs: command: ./gradlew jacocoTestReport --no-daemon --stacktrace --console=plain -PdisablePreDex - run: 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 + command: if [ -z ${SONAR_HOST+x} ]; then echo "sonar scan skipped"; else ./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; fi + command: "[[ -v CIRCLE_PR_NUMBER ]] && ./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 || true" + + deploy: + <<: *container_config + steps: + - *attach_workspace + - restore_cache: + <<: *general_cache_key + - run: + name: Decrypt keys + command: | + gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/google-services.json.gpg + 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 publishPlayRelease --no-daemon --stacktrace --console=plain -PenableCrashlytics -PdisablePreDex workflows: version: 2 - build_check_tests: + build-test-deploy: jobs: - - build + - build: + filters: + tags: + only: /.*/ - lint: + filters: + tags: + only: /.*/ requires: - build - app-test: - 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/.editorconfig b/.editorconfig new file mode 100644 index 000000000..35fbd4661 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +[*] +charset=utf-8 +end_of_line=lf +insert_final_newline=true +indent_style=space +indent_size=4 + +[*.json] +indent_size=2 + +[*.{kt,kts}] +disabled_rules=import-ordering,no-wildcard-imports diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..237721cb0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,26 @@ +--- +name: Bug report +about: Utwórz raport błędu, aby pomóc nam ulepszyć Wulkanowego +title: '' +labels: '' +assignees: '' + +--- + +## Co powinno się dziać + + +## Co się dzieje + + +## Jak to zrobić kolejny raz: + + 1. + 2. + 3. + +## Informacje o urządzeniu i dzienniku + + - Wersja aplikacji: + - Wersja Androida: + - Adres URL dziennika: diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..6194a41e9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Zaproponuj nowy pomysł dla Wulkanowego +title: '' +labels: '' +assignees: '' + +--- + +** Czy Twoja prośba o funkcję jest związana z problemem? Proszę opisz.** +Jasny i zwięzły opis problemu. Np. Zawsze jestem sfrustrowany, gdy [...] + +** Opisz żądane rozwiązanie ** +Jasny i zwięzły opis tego, co chcesz, aby się wydarzyło. + +** Opisz alternatywy, które rozważałeś ** +Jasny i zwięzły opis wszelkich rozważanych alternatywnych rozwiązań lub funkcji. + +** Dodatkowy kontekst ** +Dodaj inny kontekst lub zrzuty ekranu dotyczące żądania funkcji tutaj. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..5cb45fc88 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: +- package-ecosystem: gradle + directory: "/" + schedule: + interval: weekly + open-pull-requests-limit: 10 + target-branch: develop + ignore: + - dependency-name: io.github.wulkanowy:sdk + reviewers: + - Faierbel \ No newline at end of file diff --git a/.github/workflows/deploy-store.yml b/.github/workflows/deploy-store.yml new file mode 100644 index 000000000..12338feff --- /dev/null +++ b/.github/workflows/deploy-store.yml @@ -0,0 +1,76 @@ +name: Deploy to app stores + +on: + release: + types: [ created ] + +jobs: + + deploy-google-play: + name: Deploy to google play + runs-on: ubuntu-latest + timeout-minutes: 10 + environment: google-play + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 + with: + java-version: 11 + - uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }} + - name: Decrypt keys + env: + ENCRYPT_KEY: ${{ secrets.ENCRYPT_KEY }} + SERVICES_ENCRYPT_KEY: ${{ secrets.SERVICES_ENCRYPT_KEY }} + run: | + gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/google-services.json.gpg + gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg + - name: Upload apk to google play + env: + PLAY_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }} + PLAY_KEY_ALIAS: ${{ secrets.PLAY_KEY_ALIAS }} + PLAY_KEY_PASSWORD: ${{ secrets.PLAY_KEY_PASSWORD }} + ANDROID_PUBLISHER_CREDENTIALS: ${{ secrets.ANDROID_PUBLISHER_CREDENTIALS }} + ADMOB_PROJECT_ID: ${{ secrets.ADMOB_PROJECT_ID }} + SINGLE_SUPPORT_AD_ID: ${{ secrets.SINGLE_SUPPORT_AD_ID }} + SET_BUILD_TIMESTAMP: ${{ secrets.SET_BUILD_TIMESTAMP }} + run: ./gradlew publishPlayReleaseApps -PenableFirebase --stacktrace; + + deploy-app-gallery: + name: Deploy to AppGallery + runs-on: ubuntu-latest + timeout-minutes: 10 + environment: app-gallery + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 + with: + java-version: 11 + - uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }} + - name: Decrypt keys + env: + ENCRYPT_KEY: ${{ secrets.ENCRYPT_KEY }} + SERVICES_ENCRYPT_KEY: ${{ secrets.SERVICES_ENCRYPT_KEY }} + run: | + gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/agconnect-services.json.gpg + gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg + - name: Prepare credentials + env: + AGC_CREDENTIALS: ${{ secrets.AGC_CREDENTIALS }} + run: echo $AGC_CREDENTIALS > ./app/src/release/agconnect-credentials.json + - name: Build and publish HMS version + env: + PLAY_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }} + PLAY_KEY_ALIAS: ${{ secrets.PLAY_KEY_ALIAS }} + PLAY_KEY_PASSWORD: ${{ secrets.PLAY_KEY_PASSWORD }} + SET_BUILD_TIMESTAMP: ${{ secrets.SET_BUILD_TIMESTAMP }} + run: ./gradlew bundleHmsRelease --stacktrace && ./gradlew publishHuaweiAppGalleryHmsRelease --stacktrace diff --git a/.github/workflows/deploy-test.yml b/.github/workflows/deploy-test.yml new file mode 100644 index 000000000..88edca05d --- /dev/null +++ b/.github/workflows/deploy-test.yml @@ -0,0 +1,144 @@ +name: Deploy to app tests + +on: + push: +# branches: [ develop ] + branches: [ '!*' ] + pull_request_target: +# branches: [ develop ] + branches: [ '!*' ] + + workflow_dispatch: + +jobs: + + deploy-appcenter: + name: App Center + runs-on: ubuntu-latest + timeout-minutes: 10 + environment: app-center + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 + with: + java-version: 11 + - uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }} + - name: Set run number with offset + env: + BUILD_NUMBER_OFFSET: ${{ secrets.BUILD_NUMBER_OFFSET }} + run: echo "RUN_NUMBER=$((GITHUB_RUN_NUMBER+BUILD_NUMBER_OFFSET))" >> $GITHUB_ENV + - name: Prepare build configuration + run: | + sed -i -e "s#applicationIdSuffix \".dev\"#applicationIdSuffix \".${GITHUB_HEAD_REF//[-.\/]/_}\"#" app/build.gradle + sed -i -e "s#.dev\"#.${GITHUB_HEAD_REF//[-.\/]/_}\"#" app/src/debug/google-services.json + sed -i -e "s#.dev\"#.${GITHUB_HEAD_REF//[-.\/]/_}\"#" app/src/debug/agconnect-services.json + sed -i -e '/versionNameSuffix/d' app/build.gradle + - name: Add signing config + run: | + cat >> app/build.gradle <> $GITHUB_ENV + - name: Add signing config + run: | + cat >> app/build.gradle < - - - - - \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 000000000..1f93faefd --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,123 @@ + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 000000000..0f7bc519d --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index 48777522e..000000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 7f68460d8..000000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..04db3a616 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,68 @@ +language: android +jdk: oraclejdk8 + +env: + global: + - ANDROID_API_LEVEL=30 + - ANDROID_BUILD_TOOLS_VERSION=30.0.2 + +cache: + directories: + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ + +branches: + only: + - develop + - 0.24.0 + +android: + licenses: + - android-sdk-preview-license-.+ + - android-sdk-license-.+ + - google-gdk-license-.+ + components: + - tools + - platform-tools + # The BuildTools version used by your project + - build-tools-$ANDROID_BUILD_TOOLS_VERSION + # The SDK version used to compile your project + - android-$ANDROID_API_LEVEL + # Additional components + - extra-google-google_play_services + - extra-google-m2repository + - extra-android-m2repository + - addon-google_apis-google-$ANDROID_API_LEVEL + # Android emulator + - android-22 + - sys-img-armeabi-v7a-android-22 + +before_install: + - yes | sdkmanager "platforms;android-30" + - yes | sdkmanager "build-tools;30.0.2" + +before_script: + # Launch emulator before the execution + - echo no | android create avd --force -n test -t android-22 --abi armeabi-v7a + - emulator -avd test -no-audio -no-window & + - android-wait-for-emulator + - adb shell input keyevent 82 & + - "curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | sudo bash" + +script: + - ./gradlew dependencies --stacktrace --daemon + - fossa --no-ansi || true + - ./gradlew -Pcoverage testFdroidDebugUnitTest --stacktrace --daemon + - ./gradlew -Pcoverage connectedFdroidDebugAndroidTest --stacktrace --daemon + - ./gradlew -Pcoverage jacocoTestReport --stacktrace --daemon + - | + if [ $TRAVIS_TAG ]; then + gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/google-services.json.gpg; + gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/agconnect-services.json.gpg; + gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/key.p12.gpg; + gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg; + ./gradlew publishPlayRelease -PenableFirebase --stacktrace; + fi + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/LICENSE b/LICENSE index f48b21143..c97032f74 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2017 wulkanowy + Copyright 2022 Wulkanowy Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.cs.md b/README.cs.md new file mode 100644 index 000000000..5c1e5ea71 --- /dev/null +++ b/README.cs.md @@ -0,0 +1,78 @@ +[English version of README](README.en.md) + +[Deutsche Version von README](README.de.md) + +[Polska wersja README](README.md) + +[Slovenská verzia README](README.sk.md) + +# Wulkanowy + +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wulkanowy/wulkanowy/Tests/develop?style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) +[![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) +[![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) +[![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) +[![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases) + +Neoficiální klient deníku VULCAN UONET+ pro žáka a rodiče + +## Funkce + +* přihlášení pomocí emailu a hesla +* funkce z webové stránky deníku: + * známky + * statistiky známek + * frekvence + * procento frekvence + * zkoušky + * plán lekce + * dokončené lekce + * zprávy + * domácí úkoly + * poznámky + * šťastné číslo + * další lekce + * školní setkání + * informace o žáku a škole +* výpočet průměru nezávisle na preferencích školy +* upozornění, např. o nových známkách +* podpora více účtů s možností přejmenování žáků +* tmavý a černý (AMOLED) motiv +* offline režim +* žádné reklamy + +## Stáhnout + +Aktuální verzi si můžete stáhnout z Google Play, F-Droid nebo Huawei AppGallery + +[Nyní na Google Play](https://play.google.com/store/apps/details?id=io.github.wulkanowy) +[Stáhnout s F-Droid](https://f-droid.org/packages/io.github.wulkanowy/) +[Objevuj v AppGallery](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=) + +Můžete si také stáhnout [vývojovou verzi](https://wulkanowy.github.io/#download), která zahrnuje nové funkce připravované pro příští vydání + +## Postaveno s + + +* [Wulkanowy SDK](https://github.com/wulkanowy/sdk) +* [Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html) +* [Hilt](https://dagger.dev/hilt/) +* [Room](https://developer.android.com/topic/libraries/architecture/room) +* [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager) + +## Spolupráce + +Přispějte do projektu vytvořením PR nebo odesláním issue na GitHub. + +Pro zájemce o překlad aplikace do různých jazyků poskytujeme Crowdin: +https://crowdin.com/project/wulkanowy2 + +## Licence + +Tento projekt je licencován pod licencí Apache License 2.0 - podrobnosti v souboru [LICENSE](LICENSE) diff --git a/README.de.md b/README.de.md new file mode 100644 index 000000000..3f806e9fd --- /dev/null +++ b/README.de.md @@ -0,0 +1,74 @@ +[Polska wersja README](README.md) + +[English version of README](README.en.md) + +# Wulkanowy + +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wulkanowy/wulkanowy/Tests/develop?style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) +[![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) +[![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) +[![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) +[![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases) + +Inoffizieller Android VULCAN UONET+ Registrierungsclient für Schüler und ihre Eltern + +## Merkmale + +* Einloggen mit E-Mail und Passwort +* Funktionen von der Registerwebsite: + * Noten + * Notenstatistik + * Anwesenheit + * Prozentsatz der Anwesenheit + * Prüfungen + * Stundenplan + * Unterricht abgeschlossen + * Nachrichten + * Hausaufgaben + * Anmerkungen + * Glückszahl + * Zusätzliche Lektionen + * Schulkonferenzen + * Schüler- und Schulinformationen +* Berechnung des Durchschnitts unabhängig von den Präferenzen der Schule +* Benachrichtigungen, z. B. über eine neue Note +* Unterstützung für mehrere Konten mit der Möglichkeit, den Namen des Schülers zu ändern +* dunkles und schwarzes (AMOLED) Thema +* Offline-Modus +* keine Werbung + +## Herunterladen + +Die aktuelle Version können Sie von der Google Play, F-Droid oder Huawei AppGallery store herunterladen + +[Get it on Google Play](https://play.google.com/store/apps/details?id=io.github.wulkanowy) +[Get it on F-Droid](https://f-droid.org/packages/io.github.wulkanowy/) +[Explore it on AppGallery](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=) + +Sie können auch ein [Entwicklungsversion herunterladen](https://wulkanowy.github.io/#download) das beinhaltet neue Funktionen, die für die nächste Version vorbereitet werden + +## Gebaut mit + + +* [Wulkanowy SDK](https://github.com/wulkanowy/sdk) +* [Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html) +* [Hilt](https://dagger.dev/hilt/) +* [Room](https://developer.android.com/topic/libraries/architecture/room) +* [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager) + +## Beitragen + +Bitte tragen Sie zum Projekt bei, indem Sie entweder eine PR erstellen oder ein Issue auf GitHub einreichen. + +Für Personen, die daran interessiert sind, die Anwendung in verschiedene Sprachen zu übersetzen, bieten wir Crowdin +https://crowdin.com/project/wulkanowy2 + +## Lizenz + +Dieses Projekt ist unter der Apache License 2.0 lizenziert - siehe die [LIZENZ](LICENSE) Datei für Details diff --git a/README.en.md b/README.en.md new file mode 100644 index 000000000..1ac2a6721 --- /dev/null +++ b/README.en.md @@ -0,0 +1,78 @@ +[Polska wersja README](README.md) + +[Deutsche Version von README](README.de.md) + +[Česká verze README](README.cs.md) + +[Slovenská verzia README](README.sk.md) + +# Wulkanowy + +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wulkanowy/wulkanowy/Tests/develop?style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) +[![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) +[![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) +[![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) +[![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases) + +Unofficial android VULCAN UONET+ register client for both students and their parents + +## Features + +* logging in using the email and password +* functions from the register website: + * grades + * grade statistics + * attendance + * percentage of attendance + * exams + * timetable + * completed lessons + * messages + * homework + * notes + * lucky number + * additional lessons + * school conferences + * student and school information +* calculation of the average independently of school's preferences +* notifications, e.g. about a new grade +* support for multiple accounts with the ability to rename students +* dark and black (AMOLED) theme +* offline mode +* no ads + +## Download + +You can download the current version from the Google Play, F-Droid or Huawei AppGallery store + +[Get it on Google Play](https://play.google.com/store/apps/details?id=io.github.wulkanowy) +[Get it on F-Droid](https://f-droid.org/packages/io.github.wulkanowy/) +[Explore it on AppGallery](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=) + +You can also download a [development version](https://wulkanowy.github.io/#download) that includes new features being prepared for the next release + +## Built With + + +* [Wulkanowy SDK](https://github.com/wulkanowy/sdk) +* [Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html) +* [Hilt](https://dagger.dev/hilt/) +* [Room](https://developer.android.com/topic/libraries/architecture/room) +* [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager) + +## Contributing + +Please contribute to the project either by creating a PR or submitting an issue on GitHub. + +For people interested in translating the application into different languages, we provide Crowdin +https://crowdin.com/project/wulkanowy2 + +## License + +This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details diff --git a/README.md b/README.md index 65cf14af7..e7c7d4c5e 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,79 @@ +[English version of README](README.en.md) + +[Deutsche Version von README](README.de.md) + +[Česká verze README](README.cs.md) + +[Slovenská verzia README](README.sk.md) + # 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) +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wulkanowy/wulkanowy/Tests/develop?style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) [![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/) -[![Known Vulnerabilities](https://snyk.io/test/github/wulkanowy/wulkanowy/badge.svg?targetFile=app%2Fbuild.gradle&style=flat-square)](https://snyk.io/test/github/wulkanowy/wulkanowy?targetFile=app%2Fbuild.gradle) -[![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) +[![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) +[![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases) -[Pobierz wersję rozwojową](https://bitrise-redirector.herokuapp.com/v0.1/apps/daeff1893f3c8128/builds/master/artifacts/app-debug-bitrise-signed.apk) +Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica -Wulkanowy to aplikacja na androida polepszająca wygodę używania dziennika UONET+. +## Funkcje + +* logowanie za pomocą e-maila i hasła +* funkcje ze strony internetowej dziennika: + * oceny + * statystyki ocen + * frekwencja + * procent frekwencji + * sprawdziany + * plan lekcji + * lekcje zrealizowane + * wiadomości + * zadania domowe + * uwagi + * szczęśliwy numerek + * dodatkowe lekcje + * zebrania w szkole + * informacje o uczniu i szkole +* obliczanie średniej niezależnie od preferencji szkoły +* powiadomienia np. o nowej ocenie +* obsługa wielu kont wraz z możliwością zmiany nazwy ucznia +* ciemny i czarny (AMOLED) motyw +* tryb offline +* brak reklam + +## Pobierz + +Aktualną wersję możesz pobrać ze sklepu Google Play, F-Droid lub Huawei AppGallery + +[Pobierz z Google Play](https://play.google.com/store/apps/details?id=io.github.wulkanowy) +[Pobierz z F-Droid](https://f-droid.org/packages/io.github.wulkanowy/) +[Odkrywaj w AppGallery](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=) + + +Możesz także pobrać [wersję rozwojową](https://wulkanowy.github.io/#download), która zawiera nowe funkcje przygotowywane do następnego wydania + + +## Zbudowana za pomocą + +* [Wulkanowy SDK](https://github.com/wulkanowy/sdk) +* [Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html) +* [Hilt](https://dagger.dev/hilt/) +* [Room](https://developer.android.com/topic/libraries/architecture/room) +* [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager) + +## Współpraca + +Wnieś swój wkład w projekt, tworząc PR lub wysyłając issue na GitHub. + +Dla osób zainteresowanych tłumaczeniem aplikacji na różne języki udostępniamy Crowdina +https://crowdin.com/project/wulkanowy2 + +## Licencja + +Ten projekt udostępniany jest na licencji Apache License 2.0 - szczegóły w pliku [LICENSE](LICENSE) diff --git a/README.sk.md b/README.sk.md new file mode 100644 index 000000000..2f3ba41dd --- /dev/null +++ b/README.sk.md @@ -0,0 +1,78 @@ +[English version of README](README.en.md) + +[Deutsche Version von README](README.de.md) + +[Polska wersja README](README.md) + +[Česká verze README](README.cs.md) + +# Wulkanowy + +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wulkanowy/wulkanowy/Tests/develop?style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) +[![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) +[![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) +[![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) +[![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases) + +Neoficiálny klient denníka VULCAN UONET+ pre žiaka a rodičov + +## Funkcie + +* prihlásenie pomocou emailu a hesla +* funkcie z webovej stránky denníka: + * známky + * štatistiky známok + * frekvencia + * percento frekvencie + * skúšky + * plán lekcie + * dokončené lekcie + * správy + * domáce úlohy + * poznámky + * šťastné číslo + * ďalšie lekcie + * školské stretnutie + * informácie o žiakovi a škole +* výpočet priemeru nezávisle od preferencií školy +* upozornenia, napr. o nových známkach +* podpora viacerých účtov s možnosťou premenovania žiakov +* tmavý a čierny (AMOLED) motív +* offline režim +* žiadne reklamy + +## Stiahnuť + +Aktuálnu verziu si môžete stiahnuť z Google Play, F-Droid alebo Huawei AppGallery + +[Nyní na Google Play](https://play.google.com/store/apps/details?id=io.github.wulkanowy) +[Stiahnuť s F-Droid](https://f-droid.org/packages/io.github.wulkanowy/) +[Objavíte v AppGallery](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=) + +Môžete si tiež stiahnuť [vývojovú verziu](https://wulkanowy.github.io/#download), ktorá zahrňuje nové funkcie pripravované pre budúce vydanie + +## Postavené s + + +* [Wulkanowy SDK](https://github.com/wulkanowy/sdk) +* [Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html) +* [Hilt](https://dagger.dev/hilt/) +* [Room](https://developer.android.com/topic/libraries/architecture/room) +* [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager) + +## Spolupráca + +Prispejte do projektu vytvorením PR alebo odoslaním issue na GitHub. + +Pre záujemcov o preklad aplikácie do rôznych jazykov poskytujeme Crowdin: +https://crowdin.com/project/wulkanowy2 + +## Licencia + +Tento projekt je licencovaný pod licenciou Apache License 2.0 - podrobnosti v súbore [LICENSE](LICENSE) diff --git a/api/build.gradle b/api/build.gradle deleted file mode 100644 index 001d25791..000000000 --- a/api/build.gradle +++ /dev/null @@ -1,117 +0,0 @@ -apply plugin: 'java-library' -apply plugin: 'org.sonarqube' -apply plugin: 'jacoco' -apply plugin: 'com.jfrog.bintray' -apply plugin: 'com.github.dcendents.android-maven' - -compileJava.options.encoding = "UTF-8" -compileTestJava.options.encoding = "UTF-8" - -ext { - PUBLISH_GROUP_ID = GROUP_ID - PUBLISH_ARTIFACT_ID = 'api' - PUBLISH_VERSION = System.getenv('GIT_TAG') -} - -test { - testLogging { - events "passed", "skipped", "failed", "standardOut", "standardError" - outputs.upToDateWhen {false} - showStandardStreams = true - } -} - -jacocoTestReport { - reports { - xml.enabled true - } -} - -dependencies { - implementation "org.jsoup:jsoup:$jsoup" - implementation "org.apache.commons:commons-lang3:$apacheLang" - implementation "com.google.code.gson:gson:$gson" - - testImplementation "junit:junit:$junit" - testImplementation "org.mockito:mockito-core:$mockito" -} - -version = PUBLISH_VERSION -group = GROUP_ID - -sonarqube { - properties { - property "sonar.projectName", GROUP_ID + ":api" - property "sonar.projectKey", GROUP_ID + ":wulkanowy-api" - } -} - -def siteUrl = 'https://github.com/wulkanowy/wulkanowy' -def gitUrl = 'https://github.com/wulkanowy/wulkanowy.git' - -bintray { - user = System.getenv('BINTRAY_USER') - key = System.getenv('BINTRAY_KEY') - configurations = ['archives'] - pkg { - repo = 'wulkanowy' - name = 'api' - userOrg = 'wulkanowy' - licenses = ['Apache-2.0'] - vcsUrl = gitUrl - labels = ['aar', 'android', 'wulkanowy', 'api'] - publicDownloadNumbers = true - publish = true - - version { - name = PUBLISH_VERSION - vcsTag = PUBLISH_VERSION - released = new Date() - } - } -} - -install { - repositories.mavenInstaller { - pom { - project { - packaging 'aar' - name 'Bintray publish Gradle aar' - url siteUrl - licenses { - license { - name 'The Apache Software License, Version 2.0' - url 'http://www.apache.org/licenses/LICENSE-2.0.txt' - } - } - developers { - developer { - id 'mklkj' - name 'Mikołaj Pich' - email 'm.pich@outlook.com' - } - } - scm { - connection gitUrl - developerConnection gitUrl - url siteUrl - } - } - } - } -} - -task sourcesJar(type: Jar, dependsOn: classes) { - classifier = 'sources' - from sourceSets.main.allSource -} - -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.destinationDir -} - -artifacts { - archives sourcesJar - archives javadocJar -} diff --git a/api/src/main/java/io/github/wulkanowy/api/Client.java b/api/src/main/java/io/github/wulkanowy/api/Client.java deleted file mode 100644 index c3abb2ee6..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/Client.java +++ /dev/null @@ -1,184 +0,0 @@ -package io.github.wulkanowy.api; - -import org.jsoup.Connection; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; - -import java.io.IOException; -import java.util.Date; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import io.github.wulkanowy.api.login.Login; - -public class Client { - - private String protocol = "https"; - - private String host = "vulcan.net.pl"; - - private String email; - - private String password; - - private String symbol = "Default"; - - private Login login; - - private Date lastSuccessRequest = new Date(); - - private Cookies cookies = new Cookies(); - - Client(String email, String password, String symbol) { - this.email = email; - this.password = password; - this.symbol = symbol; - - setFullEndpointInfo(email); - } - - private void setFullEndpointInfo(String info) { - String[] creds = info.split("\\\\"); - - email = info; - - if (creds.length > 2) { - String[] url = creds[0].split("://"); - - protocol = url[0]; - host = url[1]; - email = creds[2]; - } - } - - private void login() throws IOException, VulcanException { - if (isLoggedIn()) { - return; - } - - this.symbol = getLogin().login(email, password, symbol); - } - - private boolean isLoggedIn() { - return getCookies().size() > 0 && - 29 > TimeUnit.MILLISECONDS.toMinutes(new Date().getTime() - lastSuccessRequest.getTime()); - - } - - Login getLogin() { - if (null != login) { - return login; - } - - login = new Login(this); - - return login; - } - - public String getSymbol() { - return symbol; - } - - public void setSymbol(String symbol) { - this.symbol = symbol; - } - - private Map getCookies() { - return cookies.getItems(); - } - - String getHost() { - return host; - } - - String getFilledUrl(String url) { - return url - .replace("{schema}", protocol) - .replace("{host}", host.replace(":", "%253A")) - .replace("{symbol}", symbol); - } - - Document getPageByUrl(String url) throws IOException, VulcanException { - login(); - - Connection.Response response = Jsoup.connect(getFilledUrl(url)) - .followRedirects(true) - .cookies(getCookies()) - .execute(); - - this.cookies.addItems(response.cookies()); - - Document doc = checkForErrors(response.parse()); - - lastSuccessRequest = new Date(); - - return doc; - } - - public Document postPageByUrl(String url, String[][] params) throws IOException, VulcanException { - Connection connection = Jsoup.connect(getFilledUrl(url)); - - for (String[] data : params) { - connection.data(data[0], data[1]); - } - - Connection.Response response = connection - .followRedirects(true) - .method(Connection.Method.POST) - .cookies(getCookies()) - .execute(); - - this.cookies.addItems(response.cookies()); - - return checkForErrors(response.parse()); - } - - public String getJsonStringByUrl(String url) throws IOException, VulcanException { - login(); - - Connection.Response response = Jsoup.connect(getFilledUrl(url)) - .followRedirects(true) - .ignoreContentType(true) - .cookies(getCookies()) - .execute(); - - this.cookies.addItems(response.cookies()); - - return response.body(); - } - - public String postJsonStringByUrl(String url, String[][] params) throws IOException, VulcanException { - login(); - - Connection connection = Jsoup.connect(getFilledUrl(url)); - - for (String[] data : params) { - connection.data(data[0], data[1]); - } - - Connection.Response response = connection - .followRedirects(true) - .ignoreContentType(true) - .method(Connection.Method.POST) - .cookies(getCookies()) - .execute(); - - this.cookies.addItems(response.cookies()); - - return response.body(); - } - - Document checkForErrors(Document doc) throws VulcanException { - 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); - } - - 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 deleted file mode 100644 index dfe4c4b54..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/Cookies.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.wulkanowy.api; - -import java.util.HashMap; -import java.util.Map; - -class Cookies { - - private Map jar = new HashMap<>(); - - Map getItems() { - return jar; - } - - void addItems(Map items) { - jar.putAll(items); - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/NotLoggedInErrorException.java b/api/src/main/java/io/github/wulkanowy/api/NotLoggedInErrorException.java deleted file mode 100644 index 179fc8ccc..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/NotLoggedInErrorException.java +++ /dev/null @@ -1,8 +0,0 @@ -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 64a07c976..000000000 --- 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 deleted file mode 100644 index adbffd4b0..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/SnP.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.api; - -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; - -import java.io.IOException; -import java.util.List; - -public interface SnP { - - String getId(); - - StudentAndParent storeContextCookies() throws IOException, VulcanException; - - String getRowDataChildValue(Element e, int index); - - Document getSnPPageDocument(String url) throws IOException, VulcanException; - - List getSemesters() throws IOException, VulcanException; - - List getSemesters(Document gradesPage); - - Semester getCurrentSemester(List semesterList); -} diff --git a/api/src/main/java/io/github/wulkanowy/api/StudentAndParent.java b/api/src/main/java/io/github/wulkanowy/api/StudentAndParent.java deleted file mode 100644 index 8f67da39c..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/StudentAndParent.java +++ /dev/null @@ -1,114 +0,0 @@ -package io.github.wulkanowy.api; - -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -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; - - StudentAndParent(Client client, String id) { - this.client = client; - this.id = id; - } - - private String getBaseUrl() { - return BASE_URL.replace("{ID}", getId()); - } - - public String getId() { - return id; - } - - public StudentAndParent storeContextCookies() throws IOException, VulcanException { - client.getPageByUrl(getSnpHomePageUrl()); - return this; - } - - String getSnpHomePageUrl() throws IOException, VulcanException { - if (null != getId()) { - return getBaseUrl(); - } - - // 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("You are probably not logged in. Force login"); - } - - String snpPageUrl = studentTileLink.attr("href"); - - this.id = getExtractedIdFromUrl(snpPageUrl); - - return snpPageUrl; - } - - String getExtractedIdFromUrl(String snpPageUrl) throws NotLoggedInErrorException { - String[] path = snpPageUrl.split(client.getHost())[1].split("/"); - - if (5 != path.length) { - throw new NotLoggedInErrorException("You are probably not logged in"); - } - - return path[2]; - } - - public String getRowDataChildValue(Element e, int index) { - return e.select(".daneWiersz .wartosc").get(index - 1).text(); - } - - public Document getSnPPageDocument(String url) throws IOException, VulcanException { - return client.getPageByUrl(getBaseUrl() + url); - } - - public List getSemesters() throws IOException, VulcanException { - return getSemesters(getSnPPageDocument(GRADES_PAGE_URL)); - } - - public List getSemesters(Document gradesPage) { - Elements semesterOptions = gradesPage.select("#okresyKlasyfikacyjneDropDownList option"); - - List semesters = new ArrayList<>(); - - for (Element e : semesterOptions) { - Semester semester = new Semester() - .setId(e.text()) - .setNumber(e.attr("value")); - - if ("selected".equals(e.attr("selected"))) { - semester.setCurrent(true); - } - - semesters.add(semester); - } - - return semesters; - } - - public Semester getCurrentSemester(List semesterList) { - Semester current = null; - for (Semester s : semesterList) { - if (s.isCurrent()) { - current = s; - break; - } - } - - return current; - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/Vulcan.java b/api/src/main/java/io/github/wulkanowy/api/Vulcan.java deleted file mode 100644 index 75b939ddf..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/Vulcan.java +++ /dev/null @@ -1,111 +0,0 @@ -package io.github.wulkanowy.api; - -import java.io.IOException; - -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.grades.GradesList; -import io.github.wulkanowy.api.grades.SubjectsList; -import io.github.wulkanowy.api.messages.Messages; -import io.github.wulkanowy.api.notes.AchievementsList; -import io.github.wulkanowy.api.notes.NotesList; -import io.github.wulkanowy.api.school.SchoolInfo; -import io.github.wulkanowy.api.school.TeachersInfo; -import io.github.wulkanowy.api.timetable.Timetable; -import io.github.wulkanowy.api.user.BasicInformation; -import io.github.wulkanowy.api.user.FamilyInformation; - -public class Vulcan { - - private String id; - - private SnP snp; - - private Client client; - - public void setCredentials(String email, String password, String symbol, String id) { - client = new Client(email, password, symbol); - - this.id = id; - } - - public Client getClient() throws NotLoggedInErrorException { - if (null == client) { - throw new NotLoggedInErrorException("Use setCredentials() method first"); - } - - return client; - } - - public String getSymbol() throws NotLoggedInErrorException { - return getClient().getSymbol(); - - } - - public SnP getStudentAndParent() throws IOException, VulcanException { - if (null != this.snp) { - return this.snp; - } - - this.snp = new StudentAndParent(getClient(), id).storeContextCookies(); - - return this.snp; - } - - public String getId() throws IOException, VulcanException { - return getStudentAndParent().getId(); - } - - public AttendanceTable getAttendanceTable() throws IOException, VulcanException { - return new AttendanceTable(getStudentAndParent()); - } - - 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, VulcanException { - return new GradesList(getStudentAndParent()); - } - - public SubjectsList getSubjectsList() throws IOException, VulcanException { - return new SubjectsList(getStudentAndParent()); - } - - public AchievementsList getAchievementsList() throws IOException, VulcanException { - return new AchievementsList(getStudentAndParent()); - } - - public NotesList getNotesList() throws IOException, VulcanException { - return new NotesList(getStudentAndParent()); - } - - public SchoolInfo getSchoolInfo() throws IOException, VulcanException { - return new SchoolInfo(getStudentAndParent()); - } - - public TeachersInfo getTeachersInfo() throws IOException, VulcanException { - return new TeachersInfo(getStudentAndParent()); - } - - public Timetable getTimetable() throws IOException, VulcanException { - return new Timetable(getStudentAndParent()); - } - - public BasicInformation getBasicInformation() throws IOException, VulcanException { - return new BasicInformation(getStudentAndParent()); - } - - public FamilyInformation getFamilyInformation() throws IOException, VulcanException { - return new FamilyInformation(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 deleted file mode 100644 index 723cab91c..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/VulcanException.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.github.wulkanowy.api; - -public abstract class VulcanException extends Exception { - - protected VulcanException(String message) { - super(message); - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/VulcanOfflineException.java b/api/src/main/java/io/github/wulkanowy/api/VulcanOfflineException.java deleted file mode 100644 index 24ab48e6c..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/VulcanOfflineException.java +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index 29b6a6d99..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceStatistics.java +++ /dev/null @@ -1,89 +0,0 @@ -package io.github.wulkanowy.api.attendance; - -import org.apache.commons.lang3.math.NumberUtils; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; - -import java.io.IOException; -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; - -public class AttendanceStatistics { - - private SnP snp; - - private String attendancePageUrl = "Frekwencja.mvc"; - - public AttendanceStatistics(SnP snp) { - this.snp = snp; - } - - public Types getTypesTable() throws IOException, VulcanException { - return getTypesTable(""); - } - - public Types getTypesTable(String tick) throws IOException, VulcanException { - return getTypesTable(tick, -1); - } - - public List getSubjectList() throws IOException, VulcanException { - Element mainContainer = snp.getSnPPageDocument(attendancePageUrl) - .select(".mainContainer #idPrzedmiot").first(); - - List subjectList = new ArrayList<>(); - - for (Element subject : mainContainer.select("option")) { - subjectList.add(new Subject() - .setId(Integer.parseInt(subject.attr("value"))) - .setName(subject.text()) - ); - } - - return subjectList; - } - - public Types getTypesTable(String tick, Integer subjectId) throws IOException, VulcanException { - Element mainContainer = snp.getSnPPageDocument((attendancePageUrl - + "?data={tick}&idPrzedmiot={subject}") - .replace("{tick}", tick) - .replace("{subject}", subjectId.toString()) - ).select(".mainContainer").first(); - - Element table = mainContainer.select("table:nth-of-type(2)").first(); - - Elements headerCells = table.select("thead th"); - List typeList = new ArrayList<>(); - - Elements typesRows = table.select("tbody tr"); - - // fill types with months - for (Element row : typesRows) { - Elements monthsCells = row.select("td"); - - List monthList = new ArrayList<>(); - - // iterate over month in type, first column is empty, last is `total`; (0, n-1) - for (int i = 1; i < monthsCells.size() - 1; i++) { - monthList.add(new Month() - .setValue(NumberUtils.toInt(monthsCells.get(i).text(), 0)) - .setName(headerCells.get(i).text())); - } - - typeList.add(new Type() - .setTotal(NumberUtils.toInt(monthsCells.last().text(), 0)) - .setName(monthsCells.get(0).text()) - .setMonthList(monthList)); - } - - String total = mainContainer.select("h2").text().split(": ")[1]; - - return new Types() - .setTotal(NumberUtils.toDouble(total.replace("%", "").replace(",", "."))) - .setTypeList(typeList); - } -} 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 deleted file mode 100644 index c45cbe6a4..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java +++ /dev/null @@ -1,117 +0,0 @@ -package io.github.wulkanowy.api.attendance; - -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 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; - -public class AttendanceTable { - - private final static String ATTENDANCE_PAGE_URL = "Frekwencja.mvc?data="; - - private SnP snp; - - public AttendanceTable(SnP snp) { - this.snp = snp; - } - - public Week getWeekTable() throws IOException, ParseException, VulcanException { - return getWeekTable(""); - } - - public Week getWeekTable(String tick) throws IOException, ParseException, VulcanException { - Element table = snp.getSnPPageDocument(ATTENDANCE_PAGE_URL + tick) - - .select(".mainContainer .presentData").first(); - - Elements headerCells = table.select("thead th"); - List days = new ArrayList<>(); - - for (int i = 1; i < headerCells.size(); i++) { - String[] dayHeaderCell = headerCells.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(); - day.setDayName(dayHeaderCell[0]); - day.setDate(sdf.format(d)); - days.add(day); - } - - Elements hoursInDays = table.select("tbody tr"); - - // fill days in week with lessons - for (Element row : hoursInDays) { - Elements hours = row.select("td"); - - // fill hours in day - int size = hours.size(); - for (int i = 1; i < size; i++) { - Lesson lesson = new Lesson(); - lesson.setDate(days.get(i - 1).getDate()); - lesson.setNumber(hours.get(0).text()); - - addLessonDetails(lesson, hours.get(i)); - - days.get(i - 1).setLesson(lesson); - } - } - - return new Week() - .setStartDayDate(days.get(0).getDate()) - .setDays(days); - } - - 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; - } - - switch (cell.select("div").attr("class")) { - case LessonTypes.CLASS_PRESENCE: - lesson.setPresence(true); - break; - case LessonTypes.CLASS_ABSENCE_UNEXCUSED: - lesson.setAbsenceUnexcused(true); - break; - case LessonTypes.CLASS_ABSENCE_EXCUSED: - lesson.setAbsenceExcused(true); - break; - case LessonTypes.CLASS_ABSENCE_FOR_SCHOOL_REASONS: - lesson.setAbsenceForSchoolReasons(true); - break; - case LessonTypes.CLASS_UNEXCUSED_LATENESS: - lesson.setUnexcusedLateness(true); - break; - case LessonTypes.CLASS_EXCUSED_LATENESS: - lesson.setExcusedLateness(true); - break; - case LessonTypes.CLASS_EXEMPTION: - lesson.setExemption(true); - break; - - default: - lesson.setEmpty(true); - break; - } - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/attendance/LessonTypes.java b/api/src/main/java/io/github/wulkanowy/api/attendance/LessonTypes.java deleted file mode 100644 index a9a1fac4e..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/attendance/LessonTypes.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.api.attendance; - -class LessonTypes { - - static final String CLASS_NOT_EXIST = "x-sp-nieobecny-w-oddziale"; - - static final String CLASS_PRESENCE = "x-obecnosc"; - - static final String CLASS_ABSENCE_UNEXCUSED = "x-nieobecnosc-nieuspr"; - - static final String CLASS_ABSENCE_EXCUSED = "x-nieobecnosc-uspr"; - - static final String CLASS_ABSENCE_FOR_SCHOOL_REASONS = "x-nieobecnosc-przycz-szkol"; - - static final String CLASS_UNEXCUSED_LATENESS = "x-sp-nieusprawiedliwione"; - - static final String CLASS_EXCUSED_LATENESS = "x-sp-spr"; - - static final String CLASS_EXEMPTION = "x-sp-zwolnienie"; - - private LessonTypes() { - throw new IllegalStateException("Utility class"); - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/attendance/Type.java b/api/src/main/java/io/github/wulkanowy/api/attendance/Type.java deleted file mode 100644 index 6deb003e1..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/attendance/Type.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.github.wulkanowy.api.attendance; - -import java.util.ArrayList; -import java.util.List; - -import io.github.wulkanowy.api.generic.Month; - -public class Type { - - private String name = ""; - - private int total = 0; - - private List monthList = new ArrayList<>(); - - public String getName() { - return name; - } - - public Type setName(String name) { - this.name = name; - return this; - } - - public int getTotal() { - return total; - } - - public Type setTotal(int total) { - this.total = total; - return this; - } - - public List getMonthList() { - return monthList; - } - - public Type setMonthList(List monthList) { - this.monthList = monthList; - return this; - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/attendance/Types.java b/api/src/main/java/io/github/wulkanowy/api/attendance/Types.java deleted file mode 100644 index d1b1777de..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/attendance/Types.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.wulkanowy.api.attendance; - -import java.util.ArrayList; -import java.util.List; - -public class Types { - - private double total = 0; - - private List typeList = new ArrayList<>(); - - public double getTotal() { - return total; - } - - public Types setTotal(double total) { - this.total = total; - return this; - } - - public List getTypeList() { - return typeList; - } - - public Types setTypeList(List typeList) { - this.typeList = typeList; - return this; - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/exams/Exam.java b/api/src/main/java/io/github/wulkanowy/api/exams/Exam.java deleted file mode 100644 index 466bfe4f7..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/exams/Exam.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.github.wulkanowy.api.exams; - -public class Exam { - - private String subjectAndGroup = ""; - - private String type = ""; - - private String description = ""; - - private String teacher = ""; - - private String entryDate = ""; - - public String getSubjectAndGroup() { - return subjectAndGroup; - } - - public Exam setSubjectAndGroup(String subjectAndGroup) { - this.subjectAndGroup = subjectAndGroup; - return this; - } - - public String getType() { - return type; - } - - public Exam setType(String type) { - this.type = type; - return this; - } - - public String getDescription() { - return description; - } - - public Exam setDescription(String description) { - this.description = description; - return this; - } - - public String getTeacher() { - return teacher; - } - - public Exam setTeacher(String teacher) { - this.teacher = teacher; - return this; - } - - public String getEntryDate() { - return entryDate; - } - - public Exam setEntryDate(String entryDate) { - this.entryDate = entryDate; - return this; - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/exams/ExamDay.java b/api/src/main/java/io/github/wulkanowy/api/exams/ExamDay.java deleted file mode 100644 index 8127e631e..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/exams/ExamDay.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.github.wulkanowy.api.exams; - -import java.util.ArrayList; -import java.util.List; - -import io.github.wulkanowy.api.generic.Day; - -public class ExamDay extends Day { - - private List examList = new ArrayList<>(); - - public List getExamList() { - return examList; - } - - public void addExam(Exam exam) { - this.examList.add(exam); - } -} 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 deleted file mode 100644 index 3dd056d9a..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/exams/ExamsWeek.java +++ /dev/null @@ -1,65 +0,0 @@ -package io.github.wulkanowy.api.exams; - -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; - -import java.io.IOException; -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; - -public class ExamsWeek { - - private static final String EXAMS_PAGE_URL = "Sprawdziany.mvc/Terminarz?rodzajWidoku=2&data="; - - private final SnP snp; - - public ExamsWeek(SnP snp) { - this.snp = snp; - } - - public Week getCurrent() throws IOException, VulcanException { - return getWeek("", true); - } - - public Week getWeek(String tick, final boolean onlyNotEmpty) throws IOException, VulcanException { - Document examsPage = snp.getSnPPageDocument(EXAMS_PAGE_URL + tick); - Elements examsDays = examsPage.select(".mainContainer > div:not(.navigation)"); - - List days = new ArrayList<>(); - - for (Element item : examsDays) { - ExamDay day = new ExamDay(); - Element dayHeading = item.select("h2").first(); - - if (null == dayHeading && onlyNotEmpty) { - continue; - } - - if (null != dayHeading) { - day.setDate(dayHeading.text().split(", ")[1]); - } - - Elements exams = item.select("article"); - for (Element e : exams) { - day.addExam(new Exam() - .setSubjectAndGroup(snp.getRowDataChildValue(e, 1)) - .setType(snp.getRowDataChildValue(e, 2)) - .setDescription(snp.getRowDataChildValue(e, 3)) - .setTeacher(snp.getRowDataChildValue(e, 4).split(", ")[0]) - .setEntryDate(snp.getRowDataChildValue(e, 4).split(", ")[1]) - ); - } - - days.add(day); - } - - return new Week() - .setStartDayDate(examsDays.select("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 deleted file mode 100644 index 6bf26c040..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/generic/Day.java +++ /dev/null @@ -1,63 +0,0 @@ -package io.github.wulkanowy.api.generic; - -import java.util.ArrayList; -import java.util.List; - -public class Day { - - private List lessons = new ArrayList<>(); - - protected String date = ""; - - private String dayName = ""; - - private boolean isFreeDay = false; - - private String freeDayName = ""; - - public Lesson getLesson(int index) { - return lessons.get(index); - } - - public List getLessons() { - return lessons; - } - - public Day setLesson(Lesson lesson) { - this.lessons.add(lesson); - return this; - } - - public String getDate() { - return date; - } - - public Day setDate(String date) { - this.date = date; - return this; - } - - public String getDayName() { - return dayName; - } - - public void 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; - } -} 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 deleted file mode 100644 index 8dd653277..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/generic/Lesson.java +++ /dev/null @@ -1,240 +0,0 @@ -package io.github.wulkanowy.api.generic; - -public class Lesson { - - private String number = ""; - - private String subject = ""; - - private String teacher = ""; - - private String room = ""; - - private String description = ""; - - private String groupName = ""; - - private String startTime = ""; - - private String endTime = ""; - - private String date = ""; - - private boolean isEmpty = false; - - private boolean isDivisionIntoGroups = false; - - private boolean isPlanning = false; - - private boolean isRealized = false; - - private boolean isMovedOrCanceled = false; - - private boolean isNewMovedInOrChanged = false; - - private boolean isNotExist = false; - - private boolean isPresence = false; - - private boolean isAbsenceUnexcused = false; - - private boolean isAbsenceExcused = false; - - private boolean isUnexcusedLateness = false; - - private boolean isAbsenceForSchoolReasons = false; - - private boolean isExcusedLateness = false; - - private boolean isExemption = false; - - public String getNumber() { - return number; - } - - public void setNumber(String number) { - this.number = number; - } - - public String getSubject() { - return subject; - } - - public Lesson setSubject(String subject) { - this.subject = subject; - return this; - } - - public String getTeacher() { - return teacher; - } - - public Lesson setTeacher(String teacher) { - this.teacher = teacher; - return this; - } - - public String getRoom() { - return room; - } - - public Lesson setRoom(String room) { - this.room = room; - return this; - } - - public String getDescription() { - return description; - } - - public Lesson setDescription(String description) { - this.description = description; - return this; - } - - public String getGroupName() { - return groupName; - } - - public void setGroupName(String groupName) { - this.groupName = groupName; - } - - public String getStartTime() { - return startTime; - } - - public void setStartTime(String startTime) { - this.startTime = startTime; - } - - public String getEndTime() { - return endTime; - } - - public void setEndTime(String endTime) { - this.endTime = endTime; - } - - public String getDate() { - return date; - } - - public Lesson setDate(String date) { - this.date = date; - return this; - } - - public boolean isEmpty() { - return isEmpty; - } - - public Lesson setEmpty(boolean empty) { - isEmpty = empty; - return this; - } - - public boolean isDivisionIntoGroups() { - return isDivisionIntoGroups; - } - - public void setDivisionIntoGroups(boolean divisionIntoGroups) { - isDivisionIntoGroups = divisionIntoGroups; - } - - public boolean isPlanning() { - return isPlanning; - } - - public void setPlanning(boolean planning) { - isPlanning = planning; - } - - public boolean isRealized() { - return isRealized; - } - - public void setRealized(boolean realized) { - isRealized = realized; - } - - public boolean isMovedOrCanceled() { - return isMovedOrCanceled; - } - - public void setMovedOrCanceled(boolean movedOrCanceled) { - isMovedOrCanceled = movedOrCanceled; - } - - public boolean isNewMovedInOrChanged() { - return isNewMovedInOrChanged; - } - - public void setNewMovedInOrChanged(boolean newMovedInOrChanged) { - isNewMovedInOrChanged = newMovedInOrChanged; - } - - public boolean isNotExist() { - return isNotExist; - } - - public void setNotExist(boolean notExist) { - isNotExist = notExist; - } - - public boolean isPresence() { - return isPresence; - } - - public void setPresence(boolean presence) { - isPresence = presence; - } - - public boolean isAbsenceUnexcused() { - return isAbsenceUnexcused; - } - - public void setAbsenceUnexcused(boolean absenceUnexcused) { - isAbsenceUnexcused = absenceUnexcused; - } - - public boolean isAbsenceExcused() { - return isAbsenceExcused; - } - - public void setAbsenceExcused(boolean absenceExcused) { - isAbsenceExcused = absenceExcused; - } - - public boolean isUnexcusedLateness() { - return isUnexcusedLateness; - } - - public void setUnexcusedLateness(boolean unexcusedLateness) { - isUnexcusedLateness = unexcusedLateness; - } - - public boolean isAbsenceForSchoolReasons() { - return isAbsenceForSchoolReasons; - } - - public void setAbsenceForSchoolReasons(boolean absenceForSchoolReasons) { - isAbsenceForSchoolReasons = absenceForSchoolReasons; - } - - public boolean isExcusedLateness() { - return isExcusedLateness; - } - - public void setExcusedLateness(boolean excusedLateness) { - isExcusedLateness = excusedLateness; - } - - public boolean isExemption() { - return isExemption; - } - - public void setExemption(boolean exemption) { - isExemption = exemption; - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/generic/Month.java b/api/src/main/java/io/github/wulkanowy/api/generic/Month.java deleted file mode 100644 index e38ef3a32..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/generic/Month.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.api.generic; - -public class Month { - - private String name = ""; - - private int value = 0; - - public String getName() { - return name; - } - - public Month setName(String name) { - this.name = name; - return this; - } - - public int getValue() { - return value; - } - - public Month setValue(int value) { - this.value = value; - return this; - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/generic/Subject.java b/api/src/main/java/io/github/wulkanowy/api/generic/Subject.java deleted file mode 100644 index fcf27bef3..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/generic/Subject.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.api.generic; - -public class Subject { - - private int id = -1; - - private String name = ""; - - public int getId() { - return id; - } - - public Subject setId(int id) { - this.id = id; - return this; - } - - public String getName() { - return name; - } - - public Subject setName(String name) { - this.name = name; - return this; - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/generic/Week.java b/api/src/main/java/io/github/wulkanowy/api/generic/Week.java deleted file mode 100644 index 1e46f3948..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/generic/Week.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.api.generic; - -import java.util.ArrayList; -import java.util.List; - -public class Week { - - private List days = new ArrayList<>(); - - private String startDayDate = ""; - - public T getDay(int index) { - return days.get(index); - } - - public List getDays() { - return days; - } - - public Week setDays(List days) { - this.days = days; - return this; - } - - public String getStartDayDate() { - return startDayDate; - } - - public Week setStartDayDate(String startDayDate) { - this.startDayDate = startDayDate; - 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 deleted file mode 100644 index 564b3e47c..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/grades/Grade.java +++ /dev/null @@ -1,112 +0,0 @@ -package io.github.wulkanowy.api.grades; - -public class Grade { - - protected String value = ""; - - private String subject = ""; - - private String color = ""; - - private String symbol = ""; - - private String description = ""; - - private String weight = ""; - - private String date = ""; - - private String teacher = ""; - - private String semester = ""; - - public String getSubject() { - return subject; - } - - public Grade setSubject(String subject) { - this.subject = subject; - - return this; - } - - public String getValue() { - return value; - } - - public Grade setValue(String value) { - this.value = value; - - return this; - } - - public String getColor() { - return color; - } - - public Grade setColor(String color) { - this.color = color; - - return this; - } - - public String getSymbol() { - return symbol; - } - - public Grade setSymbol(String symbol) { - this.symbol = symbol; - - return this; - } - - public String getDescription() { - return description; - } - - public Grade setDescription(String description) { - this.description = description; - - return this; - } - - public String getWeight() { - return weight; - } - - public Grade setWeight(String weight) { - this.weight = weight; - - return this; - } - - public String getDate() { - return date; - } - - public Grade setDate(String date) { - this.date = date; - - return this; - } - - public String getTeacher() { - return teacher; - } - - public Grade setTeacher(String teacher) { - this.teacher = teacher; - - 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 deleted file mode 100644 index ce8c0f770..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/grades/GradesList.java +++ /dev/null @@ -1,83 +0,0 @@ -package io.github.wulkanowy.api.grades; - -import org.jsoup.nodes.Document; -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; - -public class GradesList { - - private static final String GRADES_PAGE_URL = "Oceny/Wszystkie?details=2&okres="; - - private SnP snp = null; - - private List grades = new ArrayList<>(); - - public GradesList(SnP snp) { - this.snp = snp; - } - - private String getGradesPageUrl() { - return GRADES_PAGE_URL; - } - - public List getAll() throws IOException, ParseException, VulcanException { - return getAll(""); - } - - public List getAll(String semester) throws IOException, ParseException, VulcanException { - Document gradesPage = snp.getSnPPageDocument(getGradesPageUrl() + semester); - Elements gradesRows = gradesPage.select(".ocenySzczegoly-table > tbody > tr"); - Semester currentSemester = snp.getCurrentSemester(snp.getSemesters(gradesPage)); - - 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()) - ); - } - - return grades; - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/grades/Subject.java b/api/src/main/java/io/github/wulkanowy/api/grades/Subject.java deleted file mode 100644 index c7917810e..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/grades/Subject.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.wulkanowy.api.grades; - -public class Subject { - - private String name; - - private String predictedRating; - - private String finalRating; - - public String getName() { - return name; - } - - public Subject setName(String name) { - this.name = name; - - return this; - } - - public String getPredictedRating() { - return predictedRating; - } - - public Subject setPredictedRating(String predictedRating) { - this.predictedRating = predictedRating; - - return this; - } - - public String getFinalRating() { - return finalRating; - } - - public Subject setFinalRating(String finalRating) { - this.finalRating = finalRating; - - return this; - } -} 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 deleted file mode 100644 index 993594137..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/grades/SubjectsList.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.wulkanowy.api.grades; - -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; - -import java.io.IOException; -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 SnP snp = null; - - public SubjectsList(SnP snp) { - this.snp = snp; - } - - public List getAll() throws IOException, VulcanException { - Document subjectPage = snp.getSnPPageDocument(SUBJECTS_PAGE_URL); - - Elements rows = subjectPage.select(".ocenyZwykle-table > tbody > tr"); - - List subjects = new ArrayList<>(); - - for (Element subjectRow : rows) { - subjects.add(new Subject() - .setName(subjectRow.select("td:nth-child(1)").text()) - .setPredictedRating(subjectRow.select("td:nth-last-child(2)").text()) - .setFinalRating(subjectRow.select("td:nth-last-child(1)").text()) - ); - } - - return subjects; - } -} 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 deleted file mode 100644 index de3901ff9..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/login/AccountPermissionException.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.wulkanowy.api.login; - -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 deleted file mode 100644 index 13efd4a13..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/login/BadCredentialsException.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.wulkanowy.api.login; - -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 deleted file mode 100644 index e7aecec09..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/login/Login.java +++ /dev/null @@ -1,93 +0,0 @@ -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 java.io.IOException; - -import io.github.wulkanowy.api.Client; -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" + - "{schema}%253a%252f%252fuonetplus.{host}%252f{symbol}%252fLoginEndpoint.aspx%26wctx%3D" + - "{schema}%253a%252f%252fuonetplus.{host}%252f{symbol}%252fLoginEndpoint.aspx"; - - private static final String LOGIN_ENDPOINT_PAGE_URL = - "{schema}://uonetplus.{host}/{symbol}/LoginEndpoint.aspx"; - - private Client client; - - private String symbol; - - public Login(Client client) { - this.client = client; - } - - public String login(String email, String password, String symbol) throws VulcanException, IOException { - String certificate = sendCredentials(email, password, symbol); - - return sendCertificate(certificate, symbol); - } - - String sendCredentials(String email, String password, String symbol) throws IOException, VulcanException { - this.symbol = symbol; - - Document html = client.postPageByUrl(LOGIN_PAGE_URL, new String[][]{ - {"LoginName", email}, - {"Password", password} - }); - - Element errorMessage = html.select(".ErrorMessage").first(); - if (null != errorMessage) { - throw new BadCredentialsException(errorMessage.text()); - } - - return html.select("input[name=wresult]").attr("value"); - } - - String sendCertificate(String certificate, String defaultSymbol) throws IOException, VulcanException { - this.symbol = findSymbol(defaultSymbol, certificate); - client.setSymbol(this.symbol); - - String title = client.postPageByUrl(LOGIN_ENDPOINT_PAGE_URL, new String[][]{ - {"wa", "wsignin1.0"}, - {"wresult", certificate} - }).select("title").text(); - - if ("Logowanie".equals(title)) { - throw new AccountPermissionException("No account access. Try another symbol"); - } - - if (!"Uonet+".equals(title)) { - throw new LoginErrorException("Could not log in, unknown error"); - } - - return this.symbol; - } - - private String findSymbol(String symbol, String certificate) { - if ("Default".equals(symbol)) { - return findSymbolInCertificate(certificate); - } - - return symbol; - } - - String findSymbolInCertificate(String certificate) { - Elements els = Jsoup - .parse(certificate.replaceAll(":", ""), "", Parser.xmlParser()) - .select("[AttributeName=\"UserInstance\"] samlAttributeValue"); - - if (els.isEmpty()) { - return ""; - } - - return els.get(1).text(); - } -} 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 deleted file mode 100644 index be7439df0..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/login/LoginErrorException.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.wulkanowy.api.login; - -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/messages/BadRequestException.java b/api/src/main/java/io/github/wulkanowy/api/messages/BadRequestException.java deleted file mode 100644 index 14dca67b7..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/messages/BadRequestException.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.wulkanowy.api.messages; - -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/Message.java b/api/src/main/java/io/github/wulkanowy/api/messages/Message.java deleted file mode 100644 index 331ec8196..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/messages/Message.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.wulkanowy.api.messages; - -import com.google.gson.annotations.SerializedName; - -public class Message { - - @SerializedName("Nieprzeczytana") - public boolean unread; - - @SerializedName("Data") - public String date; - - @SerializedName("Tresc") - public String content; - - @SerializedName("Temat") - public String subject; - - @SerializedName("NadawcaNazwa") - public String sender; - - @SerializedName("IdWiadomosci") - public int messageID; - - @SerializedName("IdNadawca") - public int senderID; - - @SerializedName("Id") - public int id; -} 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 deleted file mode 100644 index ec2a3fba4..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/messages/Messages.java +++ /dev/null @@ -1,99 +0,0 @@ -package io.github.wulkanowy.api.messages; - -import com.google.gson.Gson; -import com.google.gson.JsonParseException; - -import java.io.IOException; -import java.util.List; - -import io.github.wulkanowy.api.Client; -import io.github.wulkanowy.api.NotLoggedInErrorException; -import io.github.wulkanowy.api.VulcanException; - -public class Messages { - - private static final String BASE_URL = "{schema}://uonetplus-uzytkownik.{host}/{symbol}/"; - - private static final String LIST_BASE_URL = BASE_URL + "Wiadomosc.mvc/"; - - private static final String RECEIVED_URL = LIST_BASE_URL + "GetWiadomosciOdebrane"; - - private static final String SENT_URL = LIST_BASE_URL + "GetWiadomosciWyslane"; - - private static final String DELETED_URL = LIST_BASE_URL + "GetWiadomosciUsuniete"; - - private static final String MESSAGE_URL = LIST_BASE_URL + "GetTrescWiadomosci"; - - public static final int RECEIVED_FOLDER = 1; - - public static final int SENT_FOLDER = 2; - - public static final int DELETED_FOLDER = 3; - - private static final String ERROR_TITLE = "Błąd strony"; - - private Client client; - - public Messages(Client client) { - this.client = client; - } - - public List getReceived() throws IOException, VulcanException { - return getMessages(RECEIVED_URL); - } - - public List getSent() throws IOException, VulcanException { - return getMessages(SENT_URL); - } - - public List getDeleted() throws IOException, VulcanException { - return getMessages(DELETED_URL); - } - - private List getMessages(String url) throws IOException, VulcanException { - String res = client.getJsonStringByUrl(url); - - List messages; - - try { - messages = new Gson().fromJson(res, MessagesContainer.class).data; - } catch (JsonParseException e) { - if (res.contains(ERROR_TITLE)) { - throw new BadRequestException(ERROR_TITLE); - } - - throw new NotLoggedInErrorException("You are probably not logged in"); - } - - return messages; - } - - 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)} - }); - - Message message; - - try { - message = new Gson().fromJson(res, MessageContainer.class).data; - } catch (JsonParseException e) { - if (res.contains(ERROR_TITLE)) { - throw new BadRequestException(ERROR_TITLE); - } - - throw new NotLoggedInErrorException("You are probably not logged in. Force login"); - } - - return message; - } - - private static class MessagesContainer { - private List data; - } - - private static class MessageContainer { - private Message data; - } -} 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 deleted file mode 100644 index 11d013429..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/notes/AchievementsList.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.wulkanowy.api.notes; - -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; - -import java.io.IOException; -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<>(); - - public AchievementsList(SnP snp) { - this.snp = snp; - } - - public List getAllAchievements() throws IOException, VulcanException { - Element pageFragment = snp.getSnPPageDocument(NOTES_PAGE_URL) - .select(".mainContainer > div").get(1); - Elements items = pageFragment.select("article"); - - for (Element item : items) { - achievements.add(item.text()); - } - - return achievements; - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/notes/Note.java b/api/src/main/java/io/github/wulkanowy/api/notes/Note.java deleted file mode 100644 index b1fa68b93..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/notes/Note.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.github.wulkanowy.api.notes; - -public class Note { - - private String date; - - private String teacher; - - private String category; - - private String content; - - public String getDate() { - return date; - } - - public Note setDate(String date) { - this.date = date; - return this; - } - - public String getTeacher() { - return teacher; - } - - public Note setTeacher(String teacher) { - this.teacher = teacher; - return this; - } - - public String getCategory() { - return category; - } - - public Note setCategory(String category) { - this.category = category; - return this; - } - - public String getContent() { - return content; - } - - public Note setContent(String content) { - this.content = content; - return this; - } -} 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 deleted file mode 100644 index 0c8a30b6b..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/notes/NotesList.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.github.wulkanowy.api.notes; - -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import io.github.wulkanowy.api.SnP; -import io.github.wulkanowy.api.VulcanException; - -public class NotesList { - - private static final String NOTES_PAGE_URL = "UwagiOsiagniecia.mvc/Wszystkie"; - - private SnP snp = null; - - private List notes = new ArrayList<>(); - - public NotesList(SnP snp) { - this.snp = snp; - } - - 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"); - - int index = 0; - for (Element item : items) { - notes.add(new Note() - .setDate(dates.get(index++).text()) - .setTeacher(snp.getRowDataChildValue(item, 1)) - .setCategory(snp.getRowDataChildValue(item, 2)) - .setContent(snp.getRowDataChildValue(item, 3)) - ); - } - - return notes; - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/school/SchoolData.java b/api/src/main/java/io/github/wulkanowy/api/school/SchoolData.java deleted file mode 100644 index f135163a2..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/school/SchoolData.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.github.wulkanowy.api.school; - -public class SchoolData { - - private String name = ""; - - private String address = ""; - - private String phoneNumber = ""; - - private String headmaster = ""; - - private String[] pedagogue; - - public String getName() { - return name; - } - - public SchoolData setName(String name) { - this.name = name; - return this; - } - - public String getAddress() { - return address; - } - - public SchoolData setAddress(String address) { - this.address = address; - return this; - } - - public String getPhoneNumber() { - return phoneNumber; - } - - public SchoolData setPhoneNumber(String phoneNumber) { - this.phoneNumber = phoneNumber; - return this; - } - - public String getHeadmaster() { - return headmaster; - } - - public SchoolData setHeadmaster(String headmaster) { - this.headmaster = headmaster; - return this; - } - - public String[] getPedagogues() { - return pedagogue; - } - - public SchoolData setPedagogue(String[] pedagogue) { - this.pedagogue = pedagogue; - return this; - } -} 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 deleted file mode 100644 index 51a7278bd..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/school/SchoolInfo.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.github.wulkanowy.api.school; - -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; - - public SchoolInfo(SnP snp) { - this.snp = snp; - } - - public SchoolData getSchoolData() throws IOException, VulcanException { - Element e = snp.getSnPPageDocument(SCHOOL_PAGE_URL) - .select(".mainContainer > article").get(0); - - return new SchoolData() - .setName(snp.getRowDataChildValue(e, 1)) - .setAddress(snp.getRowDataChildValue(e, 2)) - .setPhoneNumber(snp.getRowDataChildValue(e, 3)) - .setHeadmaster(snp.getRowDataChildValue(e, 4)) - .setPedagogue(snp.getRowDataChildValue(e, 5).split(", ")); - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/school/Subject.java b/api/src/main/java/io/github/wulkanowy/api/school/Subject.java deleted file mode 100644 index 0a3c0957d..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/school/Subject.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.api.school; - -public class Subject { - - private String name = ""; - - private String[] teachers; - - public String getName() { - return name; - } - - public Subject setName(String name) { - this.name = name; - return this; - } - - public String[] getTeachers() { - return teachers; - } - - public Subject setTeachers(String[] teachers) { - this.teachers = teachers; - return this; - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/school/TeachersData.java b/api/src/main/java/io/github/wulkanowy/api/school/TeachersData.java deleted file mode 100644 index 7d41162e5..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/school/TeachersData.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.wulkanowy.api.school; - -import java.util.List; - -public class TeachersData { - - private String className = ""; - - private String[] classTeacher; - - private List subjects; - - public String getClassName() { - return className; - } - - public TeachersData setClassName(String className) { - this.className = className; - return this; - } - - public String[] getClassTeacher() { - return classTeacher; - } - - public TeachersData setClassTeacher(String[] classTeacher) { - this.classTeacher = classTeacher; - return this; - } - - public List getSubjects() { - return subjects; - } - - public TeachersData setSubjects(List subjects) { - this.subjects = subjects; - return this; - } -} 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 deleted file mode 100644 index bbf5f5d7f..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/school/TeachersInfo.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.github.wulkanowy.api.school; - -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; - -import java.io.IOException; -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; - - public TeachersInfo(SnP snp) { - this.snp = snp; - } - - 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(); - - List subjects = new ArrayList<>(); - - for (Element subject : rows) { - subjects.add(new Subject() - .setName(subject.select("td").get(1).text()) - .setTeachers(subject.select("td").get(2).text().split(", ")) - ); - } - - return new TeachersData() - .setClassName(description.split(", ")[0].split(": ")[1].trim()) - .setClassTeacher(description.split("Wychowawcy:")[1].trim().split(", ")) - .setSubjects(subjects); - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/timetable/LessonTypes.java b/api/src/main/java/io/github/wulkanowy/api/timetable/LessonTypes.java deleted file mode 100644 index be1f44e91..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/timetable/LessonTypes.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.wulkanowy.api.timetable; - -class LessonTypes { - - static final String CLASS_PLANNING = "x-treelabel-ppl"; - - static final String CLASS_REALIZED = "x-treelabel-rlz"; - - static final String CLASS_MOVED_OR_CANCELED = "x-treelabel-inv"; - - static final String CLASS_NEW_MOVED_IN_OR_CHANGED = "x-treelabel-zas"; - - private LessonTypes() { - throw new IllegalStateException("Utility class"); - } -} 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 deleted file mode 100644 index f575edb38..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/timetable/Timetable.java +++ /dev/null @@ -1,216 +0,0 @@ -package io.github.wulkanowy.api.timetable; - -import org.apache.commons.lang3.StringUtils; -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 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; - -public class Timetable { - - private static final String TIMETABLE_PAGE_URL = "Lekcja.mvc/PlanLekcji?data="; - - private SnP snp; - - public Timetable(SnP snp) { - this.snp = snp; - } - - public Week getWeekTable() throws IOException, ParseException, VulcanException { - return getWeekTable(""); - } - - public Week getWeekTable(final String tick) throws IOException, ParseException, VulcanException { - Element table = snp.getSnPPageDocument(TIMETABLE_PAGE_URL + tick) - .select(".mainContainer .presentData").first(); - - List days = getDays(table.select("thead th")); - - setLessonToDays(table, days); - - return new Week() - .setStartDayDate(days.get(0).getDate()) - .setDays(days); - } - - private List getDays(Elements tableHeaderCells) throws ParseException { - List days = new ArrayList<>(); - - for (int i = 2; i < 7; 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(); - day.setDayName(dayHeaderCell[0]); - day.setDate(sdf.format(d)); - - if (tableHeaderCells.get(i).hasClass("free-day")) { - day.setFreeDay(true); - day.setFreeDayName(dayHeaderCell[2]); - } - - days.add(day); - } - - return days; - } - - private void setLessonToDays(Element table, List days) { - for (Element row : table.select("tbody tr")) { - Elements hours = row.select("td"); - - // fill hours in day - for (int i = 2; i < hours.size(); i++) { - Lesson lesson = new Lesson(); - - String[] startEndEnd = hours.get(1).text().split(" "); - lesson.setStartTime(startEndEnd[0]); - lesson.setEndTime(startEndEnd[1]); - lesson.setDate(days.get(i - 2).getDate()); - lesson.setNumber(hours.get(0).text()); - - addLessonDetails(lesson, hours.get(i).select("div")); - - days.get(i - 2).setLesson(lesson); - } - } - } - - private void addLessonDetails(Lesson lesson, Elements e) { - moveWarningToLessonNode(e); - - switch (e.size()) { - case 1: - addLessonInfoFromElement(lesson, e.first()); - break; - case 2: - addLessonInfoFromElement(lesson, e.last()); - break; - case 3: - addLessonInfoFromElement(lesson, e.get(1)); - break; - default: - lesson.setEmpty(true); - break; - } - } - - private void moveWarningToLessonNode(Elements e) { - Elements warn = e.select(".uwaga-panel"); - - if (!warn.isEmpty()) { - e.select(".x-treelabel-rlz").last().text("(" + warn.text() + ")"); - e.remove(1); - } - } - - private void addLessonInfoFromElement(Lesson lesson, Element e) { - Elements spans = e.select("span"); - - addTypeInfo(lesson, spans); - addNormalLessonInfo(lesson, spans); - addChangesInfo(lesson, spans); - addGroupLessonInfo(lesson, spans); - } - - private void addTypeInfo(Lesson lesson, Elements spans) { - if (spans.first().hasClass(LessonTypes.CLASS_PLANNING)) { - lesson.setPlanning(true); - } - - if (spans.first().hasClass(LessonTypes.CLASS_MOVED_OR_CANCELED)) { - lesson.setMovedOrCanceled(true); - } - - if (spans.first().hasClass(LessonTypes.CLASS_NEW_MOVED_IN_OR_CHANGED)) { - lesson.setNewMovedInOrChanged(true); - } - - if (spans.last().hasClass(LessonTypes.CLASS_REALIZED) || "".equals(spans.first().attr("class"))) { - lesson.setRealized(true); - } - } - - private void addNormalLessonInfo(Lesson lesson, Elements spans) { - if (3 == spans.size()) { - lesson.setSubject(spans.get(0).text()); - lesson.setTeacher(spans.get(1).text()); - lesson.setRoom(spans.get(2).text()); - } - } - - private void addChangesInfo(Lesson lesson, Elements spans) { - if (!spans.last().hasClass(LessonTypes.CLASS_REALIZED)) { - return; - } - - if (7 == spans.size()) { - lesson.setSubject(spans.get(3).text()); - lesson.setTeacher(spans.get(4).text()); - lesson.setRoom(spans.get(5).text()); - lesson.setMovedOrCanceled(false); - lesson.setNewMovedInOrChanged(true); - lesson.setDescription(StringUtils.substringBetween(spans.last().text(), "(", ")") - + " (poprzednio: " + spans.get(0).text() + ")"); - } else if (9 == spans.size()) { - String[] subjectAndGroupInfo = getLessonAndGroupInfoFromSpan(spans.get(4)); - lesson.setSubject(subjectAndGroupInfo[0]); - lesson.setGroupName(subjectAndGroupInfo[1]); - lesson.setTeacher(spans.get(6).text()); - lesson.setRoom(spans.get(7).text()); - lesson.setMovedOrCanceled(false); - lesson.setNewMovedInOrChanged(true); - lesson.setDivisionIntoGroups(true); - lesson.setDescription(StringUtils.substringBetween(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(), "(", ")")); - } - } - - private void addGroupLessonInfo(Lesson lesson, Elements spans) { - if (4 == spans.size() && !spans.last().hasClass(LessonTypes.CLASS_REALIZED)) { - lesson.setRoom(spans.last().text()); - } - - if ((4 == spans.size() && !spans.last().hasClass(LessonTypes.CLASS_REALIZED) || 5 == spans.size())) { - String[] subjectAndGroupInfo = getLessonAndGroupInfoFromSpan(spans.get(0)); - lesson.setSubject(subjectAndGroupInfo[0]); - lesson.setGroupName(subjectAndGroupInfo[1]); - lesson.setTeacher(spans.get(2).text()); - lesson.setDivisionIntoGroups(true); - } - - if (5 == spans.size()) { - lesson.setRoom(spans.get(3).text()); - } - } - - private String[] getLessonAndGroupInfoFromSpan(Element span) { - String[] subjectNameArray = span.text().split(" "); - String groupName = subjectNameArray[subjectNameArray.length - 1]; - - return new String[]{ - span.text().replace(" " + groupName, ""), - StringUtils.substringBetween(groupName, "[", "]") - }; - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/user/AddressData.java b/api/src/main/java/io/github/wulkanowy/api/user/AddressData.java deleted file mode 100644 index 3d6fc6d54..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/user/AddressData.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.wulkanowy.api.user; - -public class AddressData { - - private String address = ""; - - private String registeredAddress = ""; - - private String correspondenceAddress = ""; - - public String getAddress() { - return address; - } - - public AddressData setAddress(String address) { - this.address = address; - - return this; - } - - public String getRegisteredAddress() { - return registeredAddress; - } - - public AddressData setRegisteredAddress(String registeredAddress) { - this.registeredAddress = registeredAddress; - - return this; - } - - public String getCorrespondenceAddress() { - return correspondenceAddress; - } - - public AddressData setCorrespondenceAddress(String correspondenceAddress) { - this.correspondenceAddress = correspondenceAddress; - - return this; - } -} 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 deleted file mode 100644 index 4edc5117d..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/user/BasicInformation.java +++ /dev/null @@ -1,70 +0,0 @@ -package io.github.wulkanowy.api.user; - -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; - -import java.io.IOException; - -import io.github.wulkanowy.api.SnP; -import io.github.wulkanowy.api.VulcanException; - -public class BasicInformation { - - private static final String STUDENT_DATA_PAGE_URL = "Uczen.mvc/DanePodstawowe"; - - private static final String CONTENT_QUERY = ".mainContainer > article"; - - private Document studentDataPageDocument; - - private SnP snp; - - public BasicInformation(SnP snp) { - this.snp = snp; - } - - public Document getStudentDataPageDocument() throws IOException, VulcanException { - if (null == studentDataPageDocument) { - studentDataPageDocument = snp.getSnPPageDocument(STUDENT_DATA_PAGE_URL); - } - - return studentDataPageDocument; - } - - public PersonalData getPersonalData() throws IOException, VulcanException { - Element e = getStudentDataPageDocument().select(CONTENT_QUERY).get(0); - - String name = snp.getRowDataChildValue(e, 1); - String[] names = name.split(" "); - - return new PersonalData() - .setName(name) - .setFirstName(names[0]) - .setSurname(names[names.length - 1]) - .setFirstAndLastName(names[0] + " " + names[names.length - 1]) - .setDateAndBirthPlace(snp.getRowDataChildValue(e, 2)) - .setPesel(snp.getRowDataChildValue(e, 3)) - .setGender(snp.getRowDataChildValue(e, 4)) - .setPolishCitizenship("Tak".equals(snp.getRowDataChildValue(e, 5))) - .setFamilyName(snp.getRowDataChildValue(e, 6)) - .setParentsNames(snp.getRowDataChildValue(e, 7)); - } - - public AddressData getAddressData() throws IOException, VulcanException { - Element e = getStudentDataPageDocument().select(CONTENT_QUERY).get(1); - - return new AddressData() - .setAddress(snp.getRowDataChildValue(e, 1)) - .setRegisteredAddress(snp.getRowDataChildValue(e, 2)) - .setCorrespondenceAddress(snp.getRowDataChildValue(e, 3)); - - } - - public ContactDetails getContactDetails() throws IOException, VulcanException { - Element e = getStudentDataPageDocument().select(CONTENT_QUERY).get(2); - - return new ContactDetails() - .setPhoneNumber(snp.getRowDataChildValue(e, 1)) - .setCellPhoneNumber(snp.getRowDataChildValue(e, 2)) - .setEmail(snp.getRowDataChildValue(e, 3)); - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/user/ContactDetails.java b/api/src/main/java/io/github/wulkanowy/api/user/ContactDetails.java deleted file mode 100644 index c8556944e..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/user/ContactDetails.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.wulkanowy.api.user; - -public class ContactDetails { - - private String phoneNumber = ""; - - private String cellPhoneNumber = ""; - - private String email = ""; - - public String getPhoneNumber() { - return phoneNumber; - } - - public ContactDetails setPhoneNumber(String phoneNumber) { - this.phoneNumber = phoneNumber; - return this; - } - - public String getCellPhoneNumber() { - return cellPhoneNumber; - } - - public ContactDetails setCellPhoneNumber(String cellPhoneNumber) { - this.cellPhoneNumber = cellPhoneNumber; - return this; - } - - public String getEmail() { - return email; - } - - public ContactDetails setEmail(String email) { - this.email = email; - return this; - } -} 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 deleted file mode 100644 index f5a459ee8..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/user/FamilyInformation.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.wulkanowy.api.user; - -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import io.github.wulkanowy.api.SnP; -import io.github.wulkanowy.api.VulcanException; - -public class FamilyInformation { - - private static final String STUDENT_DATA_PAGE_URL = "Uczen.mvc/DanePodstawowe"; - - private SnP snp; - - public FamilyInformation(SnP snp) { - this.snp = snp; - } - - public List getFamilyMembers() throws IOException, VulcanException { - Elements membersElements = snp.getSnPPageDocument(STUDENT_DATA_PAGE_URL) - .select(".mainContainer > article:nth-of-type(n+4)"); - - List familyMembers = new ArrayList<>(); - - for (Element e : membersElements) { - familyMembers.add(new FamilyMember() - .setName(snp.getRowDataChildValue(e, 1)) - .setKinship(snp.getRowDataChildValue(e, 2)) - .setAddress(snp.getRowDataChildValue(e, 3)) - .setTelephones(snp.getRowDataChildValue(e, 4)) - .setEmail(snp.getRowDataChildValue(e, 5)) - ); - } - - return familyMembers; - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/user/FamilyMember.java b/api/src/main/java/io/github/wulkanowy/api/user/FamilyMember.java deleted file mode 100644 index dfb966745..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/user/FamilyMember.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.github.wulkanowy.api.user; - -public class FamilyMember { - - private String name = ""; - - private String kinship = ""; - - private String address = ""; - - private String telephones = ""; - - private String email = ""; - - public String getName() { - return name; - } - - public FamilyMember setName(String name) { - this.name = name; - return this; - } - - public String getKinship() { - return kinship; - } - - public FamilyMember setKinship(String kinship) { - this.kinship = kinship; - return this; - } - - public String getAddress() { - return address; - } - - public FamilyMember setAddress(String address) { - this.address = address; - return this; - } - - public String getTelephones() { - return telephones; - } - - public FamilyMember setTelephones(String telephones) { - this.telephones = telephones; - return this; - } - - public String getEmail() { - return email; - } - - public FamilyMember setEmail(String email) { - this.email = email; - return this; - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/user/PersonalData.java b/api/src/main/java/io/github/wulkanowy/api/user/PersonalData.java deleted file mode 100644 index d2991e613..000000000 --- a/api/src/main/java/io/github/wulkanowy/api/user/PersonalData.java +++ /dev/null @@ -1,114 +0,0 @@ -package io.github.wulkanowy.api.user; - -public class PersonalData { - - private String name = ""; - - private String firstName = ""; - - private String surname = ""; - - private String firstAndLastName = ""; - - private String dateAndBirthPlace = ""; - - private String pesel = ""; - - private String gender = ""; - - private boolean isPolishCitizenship; - - private String familyName = ""; - - private String parentsNames = ""; - - public String getName() { - return name; - } - - public PersonalData setName(String name) { - this.name = name; - return this; - } - - public String getFirstName() { - return firstName; - } - - public PersonalData setFirstName(String firstName) { - this.firstName = firstName; - return this; - } - - public String getSurname() { - return surname; - } - - public PersonalData setSurname(String surname) { - this.surname = surname; - return this; - } - - public String getFirstAndLastName() { - return firstAndLastName; - } - - public PersonalData setFirstAndLastName(String firstAndLastName) { - this.firstAndLastName = firstAndLastName; - return this; - } - - public String getDateAndBirthPlace() { - return dateAndBirthPlace; - } - - public PersonalData setDateAndBirthPlace(String dateAndBirthPlace) { - this.dateAndBirthPlace = dateAndBirthPlace; - return this; - } - - public String getPesel() { - return pesel; - } - - public PersonalData setPesel(String pesel) { - this.pesel = pesel; - return this; - } - - public String getGender() { - return gender; - } - - public PersonalData setGender(String gender) { - this.gender = gender; - return this; - } - - public boolean isPolishCitizenship() { - return isPolishCitizenship; - } - - public PersonalData setPolishCitizenship(boolean polishCitizenship) { - isPolishCitizenship = polishCitizenship; - return this; - } - - public String getFamilyName() { - return familyName; - } - - public PersonalData setFamilyName(String familyName) { - this.familyName = familyName; - return this; - } - - public String getParentsNames() { - return parentsNames; - } - - public PersonalData setParentsNames(String parentsNames) { - this.parentsNames = parentsNames; - return this; - } -} diff --git a/api/src/test/java/io/github/wulkanowy/api/ClientTest.java b/api/src/test/java/io/github/wulkanowy/api/ClientTest.java deleted file mode 100644 index 4aa1be915..000000000 --- a/api/src/test/java/io/github/wulkanowy/api/ClientTest.java +++ /dev/null @@ -1,80 +0,0 @@ -package io.github.wulkanowy.api; - -import org.hamcrest.CoreMatchers; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.junit.Assert; -import org.junit.Test; - -import io.github.wulkanowy.api.login.Login; - -public class ClientTest { - - private String getFixtureAsString(String fixtureFileName) { - return FixtureHelper.getAsString(getClass().getResourceAsStream(fixtureFileName)); - } - - @Test - public void setFullEndpointInfoTest() throws Exception { - Client client = new Client("http://fakelog.net\\\\admin", "pass", "Default"); - - Assert.assertEquals("fakelog.net", client.getHost()); - Assert.assertEquals("Default", client.getSymbol()); - } - - @Test - public void checkForNoErrorsTest() throws Exception { - Client client = new Client("", "", ""); - - Document doc = Jsoup.parse(getFixtureAsString("login/Logowanie-success.html")); - - Assert.assertEquals(doc, client.checkForErrors(doc)); - } - - @Test(expected = VulcanOfflineException.class) - public void checkForErrorsOffline() throws Exception { - Client client = new Client("", "", ""); - - Document doc = Jsoup.parse(getFixtureAsString("login/PrzerwaTechniczna.html")); - - client.checkForErrors(doc); - } - - @Test(expected = NotLoggedInErrorException.class) - public void checkForErrors() throws Exception { - Client client = new Client("", "", ""); - - Document doc = Jsoup.parse(getFixtureAsString("login/Logowanie-notLoggedIn.html")); - - client.checkForErrors(doc); - } - - @Test - public void getClientTest() throws Exception { - Client client = new Client("", "", ""); - - Assert.assertThat(client.getLogin(), CoreMatchers.instanceOf(Login.class)); - } - - @Test - public void getClientTwiceTest() throws Exception { - Client client = new Client("", "", ""); - - Assert.assertEquals(client.getLogin(), client.getLogin()); - } - - @Test - public void getFilledUrlTest() throws Exception { - Client client = new Client("http://fakelog.cf\\\\admin", "", "symbol123"); - - Assert.assertEquals("http://uonetplus.fakelog.cf/symbol123/LoginEndpoint.aspx", - client.getFilledUrl("{schema}://uonetplus.{host}/{symbol}/LoginEndpoint.aspx")); - } - - @Test - public void getSymbolTest() throws Exception { - Client client = new Client("", "", "symbol4321"); - - Assert.assertEquals("symbol4321", client.getSymbol()); - } -} diff --git a/api/src/test/java/io/github/wulkanowy/api/FixtureHelper.java b/api/src/test/java/io/github/wulkanowy/api/FixtureHelper.java deleted file mode 100644 index 94dd47b87..000000000 --- a/api/src/test/java/io/github/wulkanowy/api/FixtureHelper.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.github.wulkanowy.api; - -import java.io.InputStream; -import java.util.Scanner; - -public class FixtureHelper { - - public static String getAsString(InputStream inputStream) { - Scanner s = new Scanner(inputStream).useDelimiter("\\A"); - return s.hasNext() ? s.next() : ""; - } -} diff --git a/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTest.java b/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTest.java deleted file mode 100644 index 5cde9b644..000000000 --- a/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTest.java +++ /dev/null @@ -1,123 +0,0 @@ -package io.github.wulkanowy.api; - -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import java.util.ArrayList; -import java.util.List; - -public class StudentAndParentTest { - - private Client client; - - @Before - public void setUp() throws Exception { - String input = FixtureHelper.getAsString( - getClass().getResourceAsStream("OcenyWszystkie-semester.html")); - Document gradesPageDocument = Jsoup.parse(input); - - client = Mockito.mock(Client.class); - Mockito.when(client.getPageByUrl(Mockito.anyString())).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, null); - - 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, null); - - 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/")); - } - - @Test - public void getSemestersTest() throws Exception { - SnP snp = new StudentAndParent(client, "123456"); - List semesters = snp.getSemesters(); - - Assert.assertEquals(2, semesters.size()); - - Assert.assertEquals("1", semesters.get(0).getId()); - Assert.assertEquals("1234", semesters.get(0).getNumber()); - Assert.assertFalse(semesters.get(0).isCurrent()); - - Assert.assertEquals("2", semesters.get(1).getId()); - Assert.assertEquals("1235", semesters.get(1).getNumber()); - Assert.assertTrue(semesters.get(1).isCurrent()); - } - - @Test - public void getCurrentSemesterTest() throws Exception { - List semesters = new ArrayList<>(); - semesters.add(new Semester().setNumber("1500100900").setId("1").setCurrent(false)); - semesters.add(new Semester().setNumber("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()); - } - - @Test - public void getCurrentSemesterFromEmptyTest() throws Exception { - SnP snp = new StudentAndParent(client, ""); - List semesters = new ArrayList<>(); - - Assert.assertNull(snp.getCurrentSemester(semesters)); - } -} diff --git a/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTestCase.java b/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTestCase.java deleted file mode 100644 index b18076cfa..000000000 --- a/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTestCase.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.api; - -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.mockito.Mockito; - -public abstract class StudentAndParentTestCase { - - protected StudentAndParent getSnp(String fixtureFileName) throws Exception { - String input = FixtureHelper.getAsString(getClass().getResourceAsStream(fixtureFileName)); - - Document tablePageDocument = Jsoup.parse(input); - - StudentAndParent snp = Mockito.mock(StudentAndParent.class); - Mockito.when(snp.getSnPPageDocument(Mockito.anyString())) - .thenReturn(tablePageDocument); - Mockito.when(snp.getSemesters(Mockito.any(Document.class))).thenCallRealMethod(); - Mockito.when(snp.getCurrentSemester(Mockito.anyList())) - .thenCallRealMethod(); - Mockito.when(snp.getRowDataChildValue(Mockito.any(Element.class), - Mockito.anyInt())).thenCallRealMethod(); - - return snp; - } -} diff --git a/api/src/test/java/io/github/wulkanowy/api/VulcanTest.java b/api/src/test/java/io/github/wulkanowy/api/VulcanTest.java deleted file mode 100644 index bda66e8d1..000000000 --- a/api/src/test/java/io/github/wulkanowy/api/VulcanTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.github.wulkanowy.api; - -import org.hamcrest.CoreMatchers; -import org.junit.Assert; -import org.junit.Test; - -public class VulcanTest { - - @Test(expected = NotLoggedInErrorException.class) - public void getClientWithoutLoginTest() throws Exception { - Vulcan vulcan = new Vulcan(); - - vulcan.getClient(); - } - - @Test - public void getClientTest() throws Exception { - Vulcan vulcan = new Vulcan(); - vulcan.setCredentials("email", "password", "symbol", null); - - Assert.assertThat(vulcan.getClient(), CoreMatchers.instanceOf(Client.class)); - } - - @Test - public void getClientTwiceTest() throws Exception { - Vulcan vulcan = new Vulcan(); - vulcan.setCredentials("email", "password", "symbol", null); - - Assert.assertEquals(vulcan.getClient(), vulcan.getClient()); - } -} diff --git a/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceStatisticsTest.java b/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceStatisticsTest.java deleted file mode 100644 index ca50f3789..000000000 --- a/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceStatisticsTest.java +++ /dev/null @@ -1,144 +0,0 @@ -package io.github.wulkanowy.api.attendance; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import java.util.List; - -import io.github.wulkanowy.api.StudentAndParentTestCase; -import io.github.wulkanowy.api.generic.Month; - -public class AttendanceStatisticsTest extends StudentAndParentTestCase { - - private AttendanceStatistics excellent; - - private AttendanceStatistics full; - - @Before - public void setUp() throws Exception { - this.excellent = new AttendanceStatistics(getSnp("Frekwencja-excellent.html")); - this.full = new AttendanceStatistics(getSnp("Frekwencja-full.html")); - } - - @Test - public void getSubjectList() throws Exception { - Assert.assertEquals(26, excellent.getSubjectList().size()); - Assert.assertEquals(23, full.getSubjectList().size()); - } - - @Test - public void getSubjectListId() throws Exception { - Assert.assertEquals(-1, excellent.getSubjectList().get(0).getId()); - Assert.assertEquals(63, excellent.getSubjectList().get(10).getId()); - Assert.assertEquals(0, excellent.getSubjectList().get(25).getId()); - - Assert.assertEquals(-1, full.getSubjectList().get(0).getId()); - Assert.assertEquals(108, full.getSubjectList().get(14).getId()); - Assert.assertEquals(492, full.getSubjectList().get(21).getId()); - } - - @Test - public void getSubjectListName() throws Exception { - Assert.assertEquals("Wszystkie", excellent.getSubjectList().get(0).getName()); - Assert.assertEquals("Fizyka", excellent.getSubjectList().get(8).getName()); - Assert.assertEquals("Sieci komputerowe i administrowanie sieciami", - excellent.getSubjectList().get(21).getName()); - - Assert.assertEquals("Praktyka zawodowa", full.getSubjectList().get(11).getName()); - Assert.assertEquals("Użytkowanie urządzeń peryferyjnych komputera", - full.getSubjectList().get(16).getName()); - Assert.assertEquals("Brak opisu lekcji", full.getSubjectList().get(22).getName()); - } - - @Test - public void getTypesTotal() throws Exception { - Assert.assertEquals(100.0, excellent.getTypesTable().getTotal(), 0); - Assert.assertEquals(80.94, full.getTypesTable().getTotal(), 0); - } - - @Test - public void getTypeName() throws Exception { - List typeList1 = excellent.getTypesTable().getTypeList(); - Assert.assertEquals("Obecność", typeList1.get(0).getName()); - Assert.assertEquals("Nieobecność nieusprawiedliwiona", typeList1.get(1).getName()); - Assert.assertEquals("Nieobecność usprawiedliwiona", typeList1.get(2).getName()); - Assert.assertEquals("Nieobecność z przyczyn szkolnych", typeList1.get(3).getName()); - - List typeList2 = full.getTypesTable().getTypeList(); - Assert.assertEquals("Spóźnienie nieusprawiedliwione", typeList2.get(4).getName()); - Assert.assertEquals("Spóźnienie usprawiedliwione", typeList2.get(5).getName()); - Assert.assertEquals("Zwolnienie", typeList2.get(6).getName()); - } - - @Test - public void getTypeTotal() throws Exception { - List typeList1 = excellent.getTypesTable().getTypeList(); - Assert.assertEquals(1211, typeList1.get(0).getTotal()); - Assert.assertEquals(0, typeList1.get(1).getTotal()); - Assert.assertEquals(0, typeList1.get(2).getTotal()); - Assert.assertEquals(0, typeList1.get(3).getTotal()); - Assert.assertEquals(0, typeList1.get(4).getTotal()); - Assert.assertEquals(0, typeList1.get(5).getTotal()); - Assert.assertEquals(0, typeList1.get(6).getTotal()); - - List typeList2 = full.getTypesTable().getTypeList(); - Assert.assertEquals(822, typeList2.get(0).getTotal()); - Assert.assertEquals(6, typeList2.get(1).getTotal()); - Assert.assertEquals(192, typeList2.get(2).getTotal()); - Assert.assertEquals(7, typeList2.get(3).getTotal()); - Assert.assertEquals(12, typeList2.get(4).getTotal()); - Assert.assertEquals(1, typeList2.get(5).getTotal()); - Assert.assertEquals(2, typeList2.get(6).getTotal()); - } - - @Test - public void getTypeList() throws Exception { - List typesList1 = excellent.getTypesTable().getTypeList(); - Assert.assertEquals(12, typesList1.get(0).getMonthList().size()); - Assert.assertEquals(12, typesList1.get(5).getMonthList().size()); - - List typesList2 = full.getTypesTable().getTypeList(); - Assert.assertEquals(12, typesList2.get(0).getMonthList().size()); - Assert.assertEquals(12, typesList2.get(5).getMonthList().size()); - } - - @Test - public void getMonthList() throws Exception { - List typeList1 = excellent.getTypesTable().getTypeList(); - Assert.assertEquals(12, typeList1.get(0).getMonthList().size()); - - List typeList2 = full.getTypesTable().getTypeList(); - Assert.assertEquals(12, typeList2.get(0).getMonthList().size()); - } - - @Test - public void getMonthName() throws Exception { - List monthsList1 = excellent.getTypesTable().getTypeList().get(0).getMonthList(); - Assert.assertEquals("IX", monthsList1.get(0).getName()); - Assert.assertEquals("III", monthsList1.get(6).getName()); - Assert.assertEquals("VIII", monthsList1.get(11).getName()); - - List monthsList2 = full.getTypesTable().getTypeList().get(0).getMonthList(); - Assert.assertEquals("XI", monthsList2.get(2).getName()); - Assert.assertEquals("II", monthsList2.get(5).getName()); - Assert.assertEquals("VI", monthsList2.get(9).getName()); - } - - @Test - public void getMonthValue() throws Exception { - List monthsList1 = excellent.getTypesTable().getTypeList().get(0).getMonthList(); - Assert.assertEquals(142, monthsList1.get(0).getValue()); - Assert.assertEquals(131, monthsList1.get(4).getValue()); - Assert.assertEquals(139, monthsList1.get(7).getValue()); - Assert.assertEquals(114, monthsList1.get(9).getValue()); - Assert.assertEquals(0, monthsList1.get(11).getValue()); - - List typeList1 = full.getTypesTable().getTypeList(); - Assert.assertEquals(135, typeList1.get(0).getMonthList().get(0).getValue()); - Assert.assertEquals(7, typeList1.get(3).getMonthList().get(5).getValue()); - Assert.assertEquals(1, typeList1.get(5).getMonthList().get(0).getValue()); - Assert.assertEquals(27, typeList1.get(2).getMonthList().get(9).getValue()); - Assert.assertEquals(0, typeList1.get(0).getMonthList().get(11).getValue()); - } -} 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 deleted file mode 100644 index 40ff4f065..000000000 --- a/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceTableTest.java +++ /dev/null @@ -1,165 +0,0 @@ -package io.github.wulkanowy.api.attendance; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import io.github.wulkanowy.api.StudentAndParentTestCase; - -public class AttendanceTableTest extends StudentAndParentTestCase { - - private AttendanceTable excellent; - - private AttendanceTable full; - - @Before - public void setUp() throws Exception { - excellent = new AttendanceTable(getSnp("Frekwencja-excellent.html")); - full = new AttendanceTable(getSnp("Frekwencja-full.html")); - } - - @Test - public void getWeekStartByDate() throws Exception { - Assert.assertEquals("2015-08-31", excellent.getWeekTable().getStartDayDate()); - Assert.assertEquals("2016-09-05", full.getWeekTable().getStartDayDate()); - } - - @Test - public void getWeekDaysNumber() throws Exception { - Assert.assertEquals(5, excellent.getWeekTable().getDays().size()); - Assert.assertEquals(5, full.getWeekTable().getDays().size()); - } - - @Test - public void getDayLessonsNumber() throws Exception { - Assert.assertEquals(14, excellent.getWeekTable().getDay(0).getLessons().size()); - Assert.assertEquals(14, full.getWeekTable().getDay(0).getLessons().size()); - } - - @Test - public void getDayDate() throws Exception { - 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("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 - public void getLessonSubject() throws Exception { - Assert.assertEquals("", - excellent.getWeekTable().getDay(0).getLesson(7).getSubject()); - Assert.assertEquals("Uroczyste rozpoczęcie roku szkolnego 2015/2016", - excellent.getWeekTable().getDay(1).getLesson(1).getSubject()); - Assert.assertEquals("Geografia", - excellent.getWeekTable().getDay(3).getLesson(4).getSubject()); - - Assert.assertEquals("Naprawa komputera", - full.getWeekTable().getDay(1).getLesson(8).getSubject()); - Assert.assertEquals("Religia", - full.getWeekTable().getDay(3).getLesson(1).getSubject()); - Assert.assertEquals("Metodologia programowania", - full.getWeekTable().getDay(4).getLesson(5).getSubject()); - } - - @Test - public void getLessonIsNotExist() throws Exception { - Assert.assertTrue(excellent.getWeekTable().getDay(0).getLesson(5).isNotExist()); - Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(1).isNotExist()); - Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(12).isNotExist()); - - Assert.assertFalse(full.getWeekTable().getDay(1).getLesson(12).isAbsenceUnexcused()); - Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isAbsenceUnexcused()); - Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(5).isAbsenceUnexcused()); - } - - @Test - public void getLessonIsEmpty() throws Exception { - Assert.assertTrue(excellent.getWeekTable().getDay(0).getLesson(0).isEmpty()); - Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(6).isEmpty()); - Assert.assertTrue(excellent.getWeekTable().getDay(4).getLesson(12).isEmpty()); - - Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(9).isEmpty()); - Assert.assertFalse(full.getWeekTable().getDay(2).getLesson(5).isEmpty()); - Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(2).isEmpty()); - } - - @Test - public void getLessonIsPresence() throws Exception { - Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(7).isPresence()); - Assert.assertTrue(excellent.getWeekTable().getDay(1).getLesson(1).isPresence()); - Assert.assertTrue(excellent.getWeekTable().getDay(3).getLesson(7).isPresence()); - - Assert.assertTrue(full.getWeekTable().getDay(0).getLesson(1).isPresence()); - Assert.assertTrue(full.getWeekTable().getDay(2).getLesson(6).isPresence()); - Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(7).isPresence()); - } - - @Test - public void getLessonIsAbsenceUnexcused() throws Exception { - Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(7).isAbsenceUnexcused()); - Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(0).isAbsenceUnexcused()); - Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(4).isAbsenceUnexcused()); - - Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(8).isAbsenceUnexcused()); - Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isAbsenceUnexcused()); - Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(8).isAbsenceUnexcused()); - } - - @Test - public void getLessonIsAbsenceExcused() throws Exception { - Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(7).isAbsenceExcused()); - Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(0).isAbsenceExcused()); - Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(4).isAbsenceExcused()); - - Assert.assertFalse(full.getWeekTable().getDay(2).getLesson(5).isAbsenceExcused()); - Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isAbsenceExcused()); - Assert.assertTrue(full.getWeekTable().getDay(4).getLesson(3).isAbsenceExcused()); - } - - @Test - public void getLessonIsAbsenceForSchoolReasons() throws Exception { - Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(4).isAbsenceForSchoolReasons()); - Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(8).isAbsenceForSchoolReasons()); - Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(12).isAbsenceForSchoolReasons()); - - Assert.assertTrue(full.getWeekTable().getDay(2).getLesson(5).isAbsenceForSchoolReasons()); - Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isAbsenceForSchoolReasons()); - Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(8).isAbsenceForSchoolReasons()); - } - - @Test - public void getLessonIsUnexcusedLateness() throws Exception { - Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(4).isUnexcusedLateness()); - Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(8).isUnexcusedLateness()); - Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(12).isUnexcusedLateness()); - - Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(6).isUnexcusedLateness()); - Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isUnexcusedLateness()); - Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(8).isUnexcusedLateness()); - } - - @Test - public void getLessonIsExcusedLateness() throws Exception { - Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(4).isExcusedLateness()); - Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(8).isExcusedLateness()); - Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(12).isExcusedLateness()); - - Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(7).isExcusedLateness()); - Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isExcusedLateness()); - Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(8).isExcusedLateness()); - } - - @Test - public void getLessonIsExemption() throws Exception { - Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(4).isExemption()); - Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(8).isExemption()); - Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(12).isExemption()); - - Assert.assertFalse(full.getWeekTable().getDay(2).getLesson(5).isExemption()); - Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isExemption()); - Assert.assertTrue(full.getWeekTable().getDay(4).getLesson(8).isExemption()); - } -} 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 deleted file mode 100644 index 9b57ddd68..000000000 --- a/api/src/test/java/io/github/wulkanowy/api/exams/ExamsWeekTest.java +++ /dev/null @@ -1,97 +0,0 @@ -package io.github.wulkanowy.api.exams; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import java.util.List; - -import io.github.wulkanowy.api.StudentAndParentTestCase; - -public class ExamsWeekTest extends StudentAndParentTestCase { - - private ExamsWeek onePerDay; - - @Before - public void getCurrent() throws Exception { - onePerDay = new ExamsWeek(getSnp("Sprawdziany-one-per-day.html")); - } - - @Test - public void getWeekTest() throws Exception { - Assert.assertEquals("23.10.2017", onePerDay.getCurrent().getStartDayDate()); - } - - @Test - public void getDaysListTest() throws Exception { - Assert.assertEquals(3, onePerDay.getCurrent().getDays().size()); - Assert.assertEquals(7, onePerDay.getWeek("", false).getDays().size()); - } - - @Test - public void getExamsListTest() throws Exception { - List notEmpty = onePerDay.getCurrent().getDays(); - Assert.assertEquals(1, notEmpty.get(0).getExamList().size()); - Assert.assertEquals(1, notEmpty.get(1).getExamList().size()); - Assert.assertEquals(1, notEmpty.get(2).getExamList().size()); - - List emptyToo = onePerDay.getWeek("", false).getDays(); - Assert.assertEquals(1, emptyToo.get(0).getExamList().size()); - Assert.assertEquals(1, emptyToo.get(1).getExamList().size()); - Assert.assertEquals(1, emptyToo.get(4).getExamList().size()); - } - - @Test - 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()); - } - - @Test - public void getExamSubjectAndGroupTest() throws Exception { - List dayList = onePerDay.getCurrent().getDays(); - - Assert.assertEquals("Sieci komputerowe 3Ti|zaw2", dayList.get(0).getExamList().get(0).getSubjectAndGroup()); - Assert.assertEquals("Język angielski 3Ti|J1", dayList.get(1).getExamList().get(0).getSubjectAndGroup()); - Assert.assertEquals("Metodologia programowania 3Ti|zaw2", dayList.get(2).getExamList().get(0).getSubjectAndGroup()); - } - - @Test - public void getExamTypeTest() throws Exception { - List dayList = onePerDay.getCurrent().getDays(); - - Assert.assertEquals("Sprawdzian", dayList.get(0).getExamList().get(0).getType()); - Assert.assertEquals("Sprawdzian", dayList.get(1).getExamList().get(0).getType()); - Assert.assertEquals("Sprawdzian", dayList.get(2).getExamList().get(0).getType()); - } - - @Test - public void getExamDescriptionTest() throws Exception { - List dayList = onePerDay.getCurrent().getDays(); - - Assert.assertEquals("Łącza danych", dayList.get(0).getExamList().get(0).getDescription()); - Assert.assertEquals("Czasy teraźniejsze", dayList.get(1).getExamList().get(0).getDescription()); - Assert.assertEquals("", dayList.get(2).getExamList().get(0).getDescription()); - } - - @Test - public void getExamTeacherTest() throws Exception { - List dayList = onePerDay.getCurrent().getDays(); - - Assert.assertEquals("Adam Wiśniewski [AW]", dayList.get(0).getExamList().get(0).getTeacher()); - Assert.assertEquals("Natalia Nowak [NN]", dayList.get(1).getExamList().get(0).getTeacher()); - Assert.assertEquals("Małgorzata Nowacka [MN]", dayList.get(2).getExamList().get(0).getTeacher()); - } - - @Test - 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()); - } -} 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 deleted file mode 100644 index bee5b4fee..000000000 --- a/api/src/test/java/io/github/wulkanowy/api/grades/GradesListTest.java +++ /dev/null @@ -1,114 +0,0 @@ -package io.github.wulkanowy.api.grades; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import java.util.List; - -import io.github.wulkanowy.api.StudentAndParentTestCase; - -public class GradesListTest extends StudentAndParentTestCase { - - private GradesList filled; - - @Before - public void setUp() throws Exception { - filled = new GradesList(getSnp("OcenyWszystkie-filled.html")); - } - - @Test - public void getAllTest() throws Exception { - Assert.assertEquals(6, filled.getAll().size()); // 2 items are skipped - } - - @Test - public void getSubjectTest() throws Exception { - List list = filled.getAll(); - - Assert.assertEquals("Zajęcia z wychowawcą", list.get(0).getSubject()); - Assert.assertEquals("Język angielski", list.get(3).getSubject()); - Assert.assertEquals("Wychowanie fizyczne", list.get(4).getSubject()); - Assert.assertEquals("Język polski", list.get(5).getSubject()); - } - - @Test - public void getValueTest() throws Exception { - List list = filled.getAll(); - - Assert.assertEquals("5", list.get(0).getValue()); - Assert.assertEquals("5", list.get(3).getValue()); - Assert.assertEquals("1", list.get(4).getValue()); - Assert.assertEquals("1", list.get(5).getValue()); - } - - @Test - public void getColorTest() throws Exception { - List list = filled.getAll(); - - Assert.assertEquals("000000", list.get(0).getColor()); - Assert.assertEquals("1289F7", list.get(3).getColor()); - Assert.assertEquals("6ECD07", list.get(4).getColor()); - Assert.assertEquals("6ECD07", list.get(5).getColor()); - } - - @Test - public void getSymbolTest() throws Exception { - 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()); - } - - @Test - public void getDescriptionTest() throws Exception { - 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()); - } - - @Test - public void getWeightTest() throws Exception { - List list = filled.getAll(); - - Assert.assertEquals("1,00", list.get(0).getWeight()); - Assert.assertEquals("3,00", list.get(3).getWeight()); - Assert.assertEquals("8,00", list.get(4).getWeight()); - Assert.assertEquals("5,00", list.get(5).getWeight()); - } - - @Test - public void getDateTest() throws Exception { - List list = filled.getAll(); - - Assert.assertEquals("2017-03-21", list.get(0).getDate()); - Assert.assertEquals("2017-06-02", list.get(3).getDate()); - Assert.assertEquals("2017-04-02", list.get(4).getDate()); - Assert.assertEquals("2017-02-06", list.get(5).getDate()); - } - - @Test - public void getTeacherTest() throws Exception { - 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/grades/SubjectsListTest.java b/api/src/test/java/io/github/wulkanowy/api/grades/SubjectsListTest.java deleted file mode 100644 index d32283781..000000000 --- a/api/src/test/java/io/github/wulkanowy/api/grades/SubjectsListTest.java +++ /dev/null @@ -1,82 +0,0 @@ -package io.github.wulkanowy.api.grades; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import java.util.List; - -import io.github.wulkanowy.api.StudentAndParentTestCase; - -public class SubjectsListTest extends StudentAndParentTestCase { - - private SubjectsList std; - - private SubjectsList average; - - @Before - public void setUp() throws Exception { - std = new SubjectsList(getSnp("OcenyWszystkie-subjects.html")); - average = new SubjectsList(getSnp("OcenyWszystkie-subjects-average.html")); - } - - @Test - public void getAllTest() throws Exception { - Assert.assertEquals(5, std.getAll().size()); - Assert.assertEquals(5, average.getAll().size()); - } - - @Test - public void getNameTest() throws Exception { - List stdList = std.getAll(); - - Assert.assertEquals("Zachowanie", stdList.get(0).getName()); - Assert.assertEquals("Praktyka zawodowa", stdList.get(1).getName()); - Assert.assertEquals("Metodologia programowania", stdList.get(2).getName()); - Assert.assertEquals("Podstawy przedsiębiorczości", stdList.get(3).getName()); - Assert.assertEquals("Wychowanie do życia w rodzinie", stdList.get(4).getName()); - - List averageList = average.getAll(); - Assert.assertEquals("Zachowanie", averageList.get(0).getName()); - Assert.assertEquals("Język polski", averageList.get(1).getName()); - Assert.assertEquals("Wychowanie fizyczne", averageList.get(2).getName()); - Assert.assertEquals("Język angielski", averageList.get(3).getName()); - Assert.assertEquals("Wiedza o społeczeństwie", averageList.get(4).getName()); - } - - @Test - public void getPredictedRatingTest() throws Exception { - List stdList = std.getAll(); - - Assert.assertEquals("bardzo dobre", stdList.get(0).getPredictedRating()); - Assert.assertEquals("-", stdList.get(1).getPredictedRating()); - Assert.assertEquals("bardzo dobry", stdList.get(2).getPredictedRating()); - Assert.assertEquals("3/4", stdList.get(3).getPredictedRating()); - Assert.assertEquals("-", stdList.get(4).getPredictedRating()); - - List averageList = average.getAll(); - Assert.assertEquals("bardzo dobre", averageList.get(0).getPredictedRating()); - Assert.assertEquals("-", averageList.get(1).getPredictedRating()); - Assert.assertEquals("bardzo dobry", averageList.get(2).getPredictedRating()); - Assert.assertEquals("4/5", averageList.get(3).getPredictedRating()); - Assert.assertEquals("-", averageList.get(4).getPredictedRating()); - } - - @Test - public void getFinalRatingTest() throws Exception { - List stdList = std.getAll(); - - Assert.assertEquals("bardzo dobre", stdList.get(0).getFinalRating()); - Assert.assertEquals("celujący", stdList.get(1).getFinalRating()); - Assert.assertEquals("celujący", stdList.get(2).getFinalRating()); - Assert.assertEquals("dostateczny", stdList.get(3).getFinalRating()); - Assert.assertEquals("-", stdList.get(4).getFinalRating()); - - List averageList = average.getAll(); - Assert.assertEquals("bardzo dobre", averageList.get(0).getFinalRating()); - Assert.assertEquals("dobry", averageList.get(1).getFinalRating()); - Assert.assertEquals("celujący", averageList.get(2).getFinalRating()); - Assert.assertEquals("bardzo dobry", averageList.get(3).getFinalRating()); - Assert.assertEquals("-", averageList.get(4).getFinalRating()); - } -} 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 deleted file mode 100644 index 69118018b..000000000 --- a/api/src/test/java/io/github/wulkanowy/api/login/LoginTest.java +++ /dev/null @@ -1,96 +0,0 @@ -package io.github.wulkanowy.api.login; - -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.github.wulkanowy.api.Client; -import io.github.wulkanowy.api.FixtureHelper; - -public class LoginTest { - - private String getFixtureAsString(String fixtureFileName) { - return FixtureHelper.getAsString(getClass().getResourceAsStream(fixtureFileName)); - } - - private Client getClient(String fixtureFileName) throws Exception { - Document doc = Jsoup.parse(getFixtureAsString(fixtureFileName)); - - Client client = Mockito.mock(Client.class); - Mockito.when(client.postPageByUrl(Mockito.anyString(), Mockito.any(String[][].class))).thenReturn(doc); - - return client; - } - - @Test - public void loginTest() throws Exception { - Login login = new Login(getClient("Logowanie-success.html")); - - Assert.assertEquals("d123", login.login("a@a", "pswd", "d123")); - } - - @Test(expected = BadCredentialsException.class) - public void sendWrongCredentialsTest() throws Exception { - Login login = new Login(getClient("Logowanie-error.html")); - - login.sendCredentials("a@a", "pswd", "d123"); - } - - @Test - public void sendCredentialsCertificateTest() throws Exception { - Login login = new Login(getClient("Logowanie-certyfikat.html")); - - Assert.assertEquals( - getFixtureAsString("cert.xml").replaceAll("\\s+",""), - login.sendCredentials("a@a", "passwd", "d123").replaceAll("\\s+","") - ); - } - - @Test - public void sendCertificateNotDefaultSymbolSuccessTest() throws Exception { - Login login = new Login(getClient("Logowanie-success.html")); - - Assert.assertEquals("wulkanowyschool321", - login.sendCertificate("", "wulkanowyschool321")); - } - - @Test - public void sendCertificateDefaultSymbolSuccessTest() throws Exception { - Login login = new Login(getClient("Logowanie-success.html")); - - Assert.assertEquals("demo12345", - login.sendCertificate(getFixtureAsString("cert.xml"), "Default")); - } - - @Test(expected = AccountPermissionException.class) - public void sendCertificateAccountPermissionTest() throws Exception { - Login login = new Login(getClient("Logowanie-brak-dostepu.html")); - - login.sendCertificate(getFixtureAsString("cert.xml"), "demo123"); - } - - @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 - public void findSymbolInCertificateTest() throws Exception { - Login login = new Login(getClient("Logowanie-certyfikat.html")); - - String certificate = getFixtureAsString("cert.xml"); - - Assert.assertEquals("demo12345", login.findSymbolInCertificate(certificate)); - } - - @Test - public void findSymbolInInvalidCertificateTest() throws Exception { - Login login = new Login(getClient("Logowanie-certyfikat.html")); - - Assert.assertEquals("", login.findSymbolInCertificate("")); // change to real cert with empty symbols - } -} 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 deleted file mode 100644 index 14fa4627e..000000000 --- a/api/src/test/java/io/github/wulkanowy/api/messages/MessagesTest.java +++ /dev/null @@ -1,89 +0,0 @@ -package io.github.wulkanowy.api.messages; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import java.util.List; - -import io.github.wulkanowy.api.Client; -import io.github.wulkanowy.api.FixtureHelper; -import io.github.wulkanowy.api.NotLoggedInErrorException; - -public class MessagesTest { - - private Client getFixtureAsString(String fixtureFileName) throws Exception { - Client client = Mockito.mock(Client.class); - Mockito.when(client.getJsonStringByUrl(Mockito.anyString())) - .thenReturn(FixtureHelper.getAsString(getClass().getResourceAsStream(fixtureFileName))); - Mockito.when(client.postJsonStringByUrl(Mockito.anyString(), Mockito.any())) - .thenReturn(FixtureHelper.getAsString(getClass().getResourceAsStream(fixtureFileName))); - return client; - } - - @Test - public void getMessages() throws Exception { - Client client = getFixtureAsString("GetWiadomosciOdebrane.json"); - - Messages messages = new Messages(client); - List messageList = messages.getReceived(); - - Assert.assertEquals(true, messageList.get(1).unread); - Assert.assertEquals("2016-03-15 09:00:00", messageList.get(0).date); - Assert.assertEquals(null, messageList.get(0).content); - Assert.assertEquals("Kowalski Jan", messageList.get(0).sender); - Assert.assertEquals(12347, messageList.get(2).id); - } - - @Test - public void getMessagesEmpty() throws Exception { - Client client = getFixtureAsString("GetWiadomosciUsuniete-empty.json"); - - Messages messages = new Messages(client); - List messageList = messages.getSent(); - - Assert.assertTrue(messageList.isEmpty()); - } - - @Test(expected = NotLoggedInErrorException.class) - public void getMessagesError() throws Exception { - Client client = getFixtureAsString("UndefinedError.txt"); - - Messages messages = new Messages(client); - messages.getDeleted(); - } - - @Test(expected = BadRequestException.class) - public void getMessagesBadRequest() throws Exception { - Client client = getFixtureAsString("PageError.html"); - - Messages messages = new Messages(client); - messages.getDeleted(); - } - - @Test - public void getMessage() throws Exception { - Client client = getFixtureAsString("GetTrescWiadomosci.json"); - - Messages messages = new Messages(client); - Message message = messages.getMessage(123, Messages.RECEIVED_FOLDER); - Assert.assertEquals(12345, message.id); - Assert.assertEquals("Witam, …. \nPozdrawiam Krzysztof Czerkas", message.content); - } - - @Test(expected = NotLoggedInErrorException.class) - public void getMessageError() throws Exception { - Client client = getFixtureAsString("UndefinedError.txt"); - - Messages messages = new Messages(client); - messages.getMessage(321, Messages.SENT_FOLDER); - } - - @Test(expected = BadRequestException.class) - public void getMessageBadRequest() throws Exception { - Client client = getFixtureAsString("PageError.html"); - - Messages messages = new Messages(client); - messages.getMessage(1, Messages.DELETED_FOLDER); - } -} diff --git a/api/src/test/java/io/github/wulkanowy/api/notes/AchievementsListTest.java b/api/src/test/java/io/github/wulkanowy/api/notes/AchievementsListTest.java deleted file mode 100644 index 0d7774bb4..000000000 --- a/api/src/test/java/io/github/wulkanowy/api/notes/AchievementsListTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.wulkanowy.api.notes; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import java.util.List; - -import io.github.wulkanowy.api.StudentAndParentTestCase; - -public class AchievementsListTest extends StudentAndParentTestCase { - - private AchievementsList filledAchievementsList; - - private AchievementsList emptyAchievementsList; - - @Before - public void setUp() throws Exception { - filledAchievementsList = new AchievementsList(getSnp("UwagiOsiagniecia-filled.html")); - emptyAchievementsList = new AchievementsList(getSnp("UwagiOsiagniecia-empty.html")); - } - - @Test - public void getAllAchievementsTest() throws Exception { - Assert.assertEquals(2, filledAchievementsList.getAllAchievements().size()); - Assert.assertEquals(0, emptyAchievementsList.getAllAchievements().size()); - } - - @Test - public void getAchievements() throws Exception { - List filledList = filledAchievementsList.getAllAchievements(); - - Assert.assertEquals("I miejsce w ogólnopolskim konkursie ortograficznym", filledList.get(0)); - Assert.assertEquals("III miejsce w ogólnopolskim konkursie plastycznym", filledList.get(1)); - } -} 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 deleted file mode 100644 index d76c06486..000000000 --- a/api/src/test/java/io/github/wulkanowy/api/notes/NotesListTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package io.github.wulkanowy.api.notes; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import java.util.List; - -import io.github.wulkanowy.api.StudentAndParentTestCase; - -public class NotesListTest extends StudentAndParentTestCase { - - private NotesList filled; - - private NotesList empty; - - @Before - public void setUp() throws Exception { - filled = new NotesList(getSnp("UwagiOsiagniecia-filled.html")); - empty = new NotesList(getSnp("UwagiOsiagniecia-empty.html")); - } - - @Test - public void getAllNotesTest() throws Exception { - Assert.assertEquals(3, filled.getAllNotes().size()); - Assert.assertEquals(0, empty.getAllNotes().size()); - } - - @Test - 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()); - } - - @Test - public void getTeacherTest() throws Exception { - List filledList = filled.getAllNotes(); - - Assert.assertEquals("Jan Kowalski [JK]", filledList.get(0).getTeacher()); - Assert.assertEquals("Kochański Leszek [KL]", filledList.get(2).getTeacher()); - } - - @Test - public void getCategoryTest() throws Exception { - List filledList = filled.getAllNotes(); - - Assert.assertEquals("Zaangażowanie społeczne", filledList.get(0).getCategory()); - Assert.assertEquals("Zachowanie na lekcji", filledList.get(2).getCategory()); - } - - @Test - public void getContentTest() throws Exception { - List filledList = filled.getAllNotes(); - - Assert.assertEquals("Pomoc przy pikniku charytatywnym", filledList.get(0).getContent()); - Assert.assertEquals("Przeszkadzanie w prowadzeniu lekcji", filledList.get(2).getContent()); - } -} diff --git a/api/src/test/java/io/github/wulkanowy/api/school/SchoolInfoTest.java b/api/src/test/java/io/github/wulkanowy/api/school/SchoolInfoTest.java deleted file mode 100644 index 947db9b8f..000000000 --- a/api/src/test/java/io/github/wulkanowy/api/school/SchoolInfoTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.github.wulkanowy.api.school; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import io.github.wulkanowy.api.StudentAndParentTestCase; - -public class SchoolInfoTest extends StudentAndParentTestCase { - - private SchoolInfo schoolInfo; - - @Before - public void setUp() throws Exception { - schoolInfo = new SchoolInfo(getSnp("Szkola.html")); - } - - @Test - public void getNameTest() throws Exception { - Assert.assertEquals("Zespół Szkół nr 64", schoolInfo.getSchoolData().getName()); - } - - @Test - public void getAddressTest() throws Exception { - Assert.assertEquals("ul. Wiśniowa 128, 01-234 Rogalowo, Nibylandia", - schoolInfo.getSchoolData().getAddress()); - } - - @Test - public void getPhoneNumberTest() throws Exception { - Assert.assertEquals("55 5555555", schoolInfo.getSchoolData().getPhoneNumber()); - } - - @Test - public void getHeadmasterTest() throws Exception { - Assert.assertEquals("Antoni Sobczyk", schoolInfo.getSchoolData().getHeadmaster()); - } - - @Test - public void getPedagoguesTest() throws Exception { - Assert.assertArrayEquals(new String[]{ - "Zofia Czerwińska [ZC]", - "Aleksander Krzemiński [AK]", - "Karolina Kowalska [KK]", - "Bartek Dąbrowski [BD]" - }, schoolInfo.getSchoolData().getPedagogues()); - } -} diff --git a/api/src/test/java/io/github/wulkanowy/api/school/TeachersInfoTest.java b/api/src/test/java/io/github/wulkanowy/api/school/TeachersInfoTest.java deleted file mode 100644 index b0da356b1..000000000 --- a/api/src/test/java/io/github/wulkanowy/api/school/TeachersInfoTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package io.github.wulkanowy.api.school; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import java.util.List; - -import io.github.wulkanowy.api.StudentAndParentTestCase; - -public class TeachersInfoTest extends StudentAndParentTestCase { - - private TeachersInfo teachersInfo; - - @Before - public void setUp() throws Exception { - teachersInfo = new TeachersInfo(getSnp("Szkola.html")); - } - - @Test - public void getClassNameTest() throws Exception { - Assert.assertEquals("1a", teachersInfo.getTeachersData().getClassName()); - } - - @Test - public void getClassTeacherTest() throws Exception { - Assert.assertArrayEquals(new String[]{ - "Karolina Kowalska [AN]", - "Antoni Sobczyk [AS]" - }, teachersInfo.getTeachersData().getClassTeacher()); - } - - @Test - public void getTeachersDataSubjectsNameTest() throws Exception { - List subjects = teachersInfo.getTeachersData().getSubjects(); - - Assert.assertEquals("Biologia", subjects.get(0).getName()); - Assert.assertEquals("Język angielski", subjects.get(6).getName()); - } - - @Test - public void getTeachersDataSubjectsTeachersTest() throws Exception { - List subjects = teachersInfo.getTeachersData().getSubjects(); - - Assert.assertArrayEquals(new String[]{"Karolina Kowalska [AN]"}, - subjects.get(0).getTeachers()); - Assert.assertEquals("Karolina Kowalska [AN]", - subjects.get(0).getTeachers()[0]); - - Assert.assertArrayEquals(new String[]{ - "Karolina Kowalska [AN]", - "Mateusz Kowal [MK]", - "Amelia Mazur [AM]" - }, subjects.get(6).getTeachers()); - } -} 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 deleted file mode 100644 index 652676348..000000000 --- a/api/src/test/java/io/github/wulkanowy/api/timetable/TimetableTest.java +++ /dev/null @@ -1,242 +0,0 @@ -package io.github.wulkanowy.api.timetable; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import io.github.wulkanowy.api.StudentAndParentTestCase; - -public class TimetableTest extends StudentAndParentTestCase { - - private Timetable std; - - private Timetable full; - - private Timetable holidays; - - @Before - public void setUp() throws Exception { - std = new Timetable(getSnp("PlanLekcji-std.html")); - full = new Timetable(getSnp("PlanLekcji-full.html")); - holidays = new Timetable(getSnp("PlanLekcji-holidays.html")); - } - - // Week - - @Test - public void getWeekTableTest() throws Exception { - Assert.assertEquals(5, std.getWeekTable().getDays().size()); - Assert.assertEquals(5, full.getWeekTable().getDays().size()); - Assert.assertEquals(5, holidays.getWeekTable().getDays().size()); - } - - @Test - public void getStartDayDateTest() throws Exception { - Assert.assertEquals("2017-06-19", std.getWeekTable().getStartDayDate()); - Assert.assertEquals("2017-06-19", full.getWeekTable().getStartDayDate()); - Assert.assertEquals("2017-07-31", holidays.getWeekTable().getStartDayDate()); - } - - // ExamDay - - @Test - public void getDayNameTest() throws Exception { - Assert.assertEquals("poniedziałek", std.getWeekTable().getDay(0).getDayName()); - Assert.assertEquals("piątek", std.getWeekTable().getDay(4).getDayName()); - Assert.assertEquals("wtorek", full.getWeekTable().getDay(1).getDayName()); - Assert.assertEquals("czwartek", full.getWeekTable().getDay(3).getDayName()); - Assert.assertEquals("środa", holidays.getWeekTable().getDay(2).getDayName()); - } - - @Test - public void getDayDateTest() throws Exception { - Assert.assertEquals("2017-06-19", std.getWeekTable().getDay(0).getDate()); - Assert.assertEquals("2017-06-23", std.getWeekTable().getDay(4).getDate()); - Assert.assertEquals("2017-06-20", full.getWeekTable().getDay(1).getDate()); - Assert.assertEquals("2017-06-22", full.getWeekTable().getDay(3).getDate()); - Assert.assertEquals("2017-08-02", holidays.getWeekTable().getDay(2).getDate()); - } - - @Test - public void getDayIsFreeTest() throws Exception { - Assert.assertFalse(std.getWeekTable().getDay(0).isFreeDay()); - Assert.assertFalse(full.getWeekTable().getDay(2).isFreeDay()); - Assert.assertTrue(holidays.getWeekTable().getDay(4).isFreeDay()); - } - - @Test - public void getDayFreeDayName() throws Exception { - Assert.assertNotEquals("Wakacje", std.getWeekTable().getDay(0).getFreeDayName()); - Assert.assertNotEquals("Ferie letnie", full.getWeekTable().getDay(1).getFreeDayName()); - Assert.assertNotEquals("Wakacje", holidays.getWeekTable().getDay(2).getFreeDayName()); - Assert.assertEquals("Ferie letnie", holidays.getWeekTable().getDay(4).getFreeDayName()); - } - - @Test - public void getDayLessonsTest() throws Exception { - Assert.assertEquals(8, std.getWeekTable().getDay(0).getLessons().size()); - Assert.assertEquals(14, full.getWeekTable().getDay(2).getLessons().size()); - Assert.assertEquals(14, holidays.getWeekTable().getDay(4).getLessons().size()); - } - - // Lesson - - @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()); - } - - @Test - public void getLessonDayTest() throws Exception { - Assert.assertEquals("2017-06-19", std.getWeekTable().getDay(0).getLesson(1).getDate()); - Assert.assertEquals("2017-06-23", std.getWeekTable().getDay(4).getLesson(4).getDate()); - Assert.assertEquals("2017-06-20", full.getWeekTable().getDay(1).getLesson(6).getDate()); - Assert.assertEquals("2017-06-22", full.getWeekTable().getDay(3).getLesson(3).getDate()); - Assert.assertEquals("2017-08-02", holidays.getWeekTable().getDay(2).getLesson(8).getDate()); - } - - @Test - public void getLessonSubjectTest() throws Exception { - Assert.assertEquals("Historia", std.getWeekTable().getDay(0).getLesson(1).getSubject()); - 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 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("", holidays.getWeekTable().getDay(3).getLesson(3).getSubject()); - } - - @Test - public void getLessonTeacherTest() throws Exception { - Assert.assertEquals("Bogatka Katarzyna", std.getWeekTable().getDay(0).getLesson(1).getTeacher()); - Assert.assertEquals("Chlebowski Stanisław", std.getWeekTable().getDay(2).getLesson(4).getTeacher()); - Assert.assertEquals("Kobczyk Iwona", full.getWeekTable().getDay(0).getLesson(1).getTeacher()); - Assert.assertEquals("Bączek Grzegorz", full.getWeekTable().getDay(0).getLesson(7).getTeacher()); - 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("", holidays.getWeekTable().getDay(3).getLesson(3).getTeacher()); - } - - @Test - public void getLessonRoomTest() throws Exception { - Assert.assertEquals("", std.getWeekTable().getDay(3).getLesson(3).getRoom()); - Assert.assertEquals("33", full.getWeekTable().getDay(0).getLesson(7).getRoom()); - Assert.assertEquals("19", full.getWeekTable().getDay(0).getLesson(0).getRoom()); - Assert.assertEquals("32", full.getWeekTable().getDay(1).getLesson(0).getRoom()); - Assert.assertEquals("32", full.getWeekTable().getDay(1).getLesson(8).getRoom()); - Assert.assertEquals("32", full.getWeekTable().getDay(2).getLesson(8).getRoom()); - Assert.assertEquals("G4", full.getWeekTable().getDay(3).getLesson(1).getRoom()); - Assert.assertEquals("37", full.getWeekTable().getDay(4).getLesson(0).getRoom()); - Assert.assertEquals("", holidays.getWeekTable().getDay(3).getLesson(3).getRoom()); - } - - @Test - public void getLessonDescriptionTest() throws Exception { - Assert.assertEquals("", std.getWeekTable().getDay(3).getLesson(3).getDescription()); - Assert.assertEquals("przeniesiona z lekcji 7, 01.12.2017", full.getWeekTable().getDay(1).getLesson(1).getDescription()); - Assert.assertEquals("okienko dla uczniów", full.getWeekTable().getDay(0).getLesson(7).getDescription()); - Assert.assertEquals("przeniesiona z lekcji 7, 20.06.2017", full.getWeekTable().getDay(1).getLesson(2).getDescription()); - Assert.assertEquals("przeniesiona z lekcji 4, 20.06.2017", full.getWeekTable().getDay(1).getLesson(3).getDescription()); - 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("egzamin", full.getWeekTable().getDay(3).getLesson(0).getDescription()); - Assert.assertEquals("", holidays.getWeekTable().getDay(3).getLesson(3).getDescription()); - } - - @Test - public void getLessonGroupNameTest() throws Exception { - Assert.assertEquals("CH", std.getWeekTable().getDay(0).getLesson(2).getGroupName()); - Assert.assertEquals("JNPW", std.getWeekTable().getDay(4).getLesson(0).getGroupName()); - Assert.assertEquals("", full.getWeekTable().getDay(0).getLesson(7).getGroupName()); - Assert.assertEquals("zaw2", full.getWeekTable().getDay(1).getLesson(0).getGroupName()); - Assert.assertEquals("wf2", full.getWeekTable().getDay(1).getLesson(3).getGroupName()); - Assert.assertEquals("zaw1", full.getWeekTable().getDay(3).getLesson(1).getGroupName()); - Assert.assertEquals("", holidays.getWeekTable().getDay(3).getLesson(3).getGroupName()); - } - - @Test - public void getLessonStartTimeTest() throws Exception { - Assert.assertEquals("08:00", std.getWeekTable().getDay(0).getLesson(0).getStartTime()); - Assert.assertEquals("14:10", std.getWeekTable().getDay(3).getLesson(7).getStartTime()); - Assert.assertEquals("07:10", full.getWeekTable().getDay(0).getLesson(0).getStartTime()); - Assert.assertEquals("12:20", full.getWeekTable().getDay(2).getLesson(6).getStartTime()); - Assert.assertEquals("12:20", holidays.getWeekTable().getDay(2).getLesson(6).getStartTime()); - } - - @Test - public void getLessonEndTimeTest() throws Exception { - Assert.assertEquals("08:45", std.getWeekTable().getDay(1).getLesson(0).getEndTime()); - Assert.assertEquals("12:15", std.getWeekTable().getDay(2).getLesson(4).getEndTime()); - Assert.assertEquals("07:55", full.getWeekTable().getDay(1).getLesson(0).getEndTime()); - Assert.assertEquals("19:00", full.getWeekTable().getDay(3).getLesson(13).getEndTime()); - Assert.assertEquals("19:00", holidays.getWeekTable().getDay(3).getLesson(13).getEndTime()); - } - - @Test - public void getLessonIsEmptyTest() throws Exception { - Assert.assertFalse(std.getWeekTable().getDay(1).getLesson(4).isEmpty()); - Assert.assertTrue(std.getWeekTable().getDay(3).getLesson(7).isEmpty()); - Assert.assertFalse(full.getWeekTable().getDay(1).getLesson(1).isEmpty()); - Assert.assertFalse(full.getWeekTable().getDay(1).getLesson(2).isEmpty()); - Assert.assertFalse(full.getWeekTable().getDay(0).getLesson(7).isEmpty()); - Assert.assertTrue(full.getWeekTable().getDay(2).getLesson(9).isEmpty()); - Assert.assertTrue(holidays.getWeekTable().getDay(0).getLesson(5).isEmpty()); - Assert.assertTrue(holidays.getWeekTable().getDay(4).getLesson(13).isEmpty()); - } - - @Test - public void getLessonIsDivisionIntoGroupsTest() throws Exception { - Assert.assertTrue(std.getWeekTable().getDay(0).getLesson(2).isDivisionIntoGroups()); - Assert.assertTrue(std.getWeekTable().getDay(4).getLesson(0).isDivisionIntoGroups()); - Assert.assertFalse(full.getWeekTable().getDay(0).getLesson(7).isDivisionIntoGroups()); - Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(3).isDivisionIntoGroups()); - Assert.assertTrue(full.getWeekTable().getDay(3).getLesson(1).isDivisionIntoGroups()); - Assert.assertFalse(holidays.getWeekTable().getDay(3).getLesson(3).isDivisionIntoGroups()); - } - - @Test - public void getLessonIsPlanningTest() throws Exception { - Assert.assertFalse(std.getWeekTable().getDay(4).getLesson(4).isPlanning()); - Assert.assertFalse(full.getWeekTable().getDay(0).getLesson(1).isPlanning()); - Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(3).isPlanning()); - Assert.assertTrue(full.getWeekTable().getDay(4).getLesson(0).isPlanning()); - Assert.assertFalse(holidays.getWeekTable().getDay(3).getLesson(3).isPlanning()); - } - - @Test - public void getLessonIsRealizedTest() throws Exception { - Assert.assertTrue(std.getWeekTable().getDay(3).getLesson(3).isRealized()); - Assert.assertTrue(full.getWeekTable().getDay(0).getLesson(1).isRealized()); - Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(3).isRealized()); - Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(0).isRealized()); - Assert.assertFalse(holidays.getWeekTable().getDay(3).getLesson(3).isRealized()); - } - - @Test - public void getLessonIsMovedOrCanceledTest() throws Exception { - Assert.assertFalse(std.getWeekTable().getDay(3).getLesson(3).isMovedOrCanceled()); - Assert.assertTrue(full.getWeekTable().getDay(0).getLesson(7).isMovedOrCanceled()); - Assert.assertFalse(full.getWeekTable().getDay(1).getLesson(3).isMovedOrCanceled()); - Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(0).isMovedOrCanceled()); - Assert.assertFalse(holidays.getWeekTable().getDay(3).getLesson(3).isMovedOrCanceled()); - } - - @Test - public void getLessonIsNewMovedInOrChangedTest() throws Exception { - Assert.assertFalse(std.getWeekTable().getDay(3).getLesson(3).isNewMovedInOrChanged()); - Assert.assertFalse(full.getWeekTable().getDay(0).getLesson(1).isNewMovedInOrChanged()); - 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(holidays.getWeekTable().getDay(3).getLesson(3).isNewMovedInOrChanged()); - } -} diff --git a/api/src/test/java/io/github/wulkanowy/api/user/BasicInformationTest.java b/api/src/test/java/io/github/wulkanowy/api/user/BasicInformationTest.java deleted file mode 100644 index 6b729c95a..000000000 --- a/api/src/test/java/io/github/wulkanowy/api/user/BasicInformationTest.java +++ /dev/null @@ -1,106 +0,0 @@ -package io.github.wulkanowy.api.user; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import io.github.wulkanowy.api.StudentAndParentTestCase; - -public class BasicInformationTest extends StudentAndParentTestCase { - - private BasicInformation basicInformation; - - @Before - public void setUp() throws Exception { - basicInformation = new BasicInformation(getSnp("UczenDanePodstawowe.html")); - } - - @Test - public void getPersonalFirstNameTest() throws Exception { - Assert.assertEquals("Maria", basicInformation.getPersonalData().getFirstName()); - } - - @Test - public void getPersonalSurnameTest() throws Exception { - Assert.assertEquals("Kamińska", basicInformation.getPersonalData().getSurname()); - } - - @Test - public void getPersonalFirstAndLastNameTest() throws Exception { - Assert.assertEquals("Maria Kamińska", - basicInformation.getPersonalData().getFirstAndLastName()); - } - - @Test - public void getPersonalNameTest() throws Exception { - Assert.assertEquals("Maria Aneta Kamińska", basicInformation.getPersonalData().getName()); - } - - @Test - public void getPersonalDateAndBirthPlaceTest() throws Exception { - Assert.assertEquals("01.01.1900, Warszawa", - basicInformation.getPersonalData().getDateAndBirthPlace()); - } - - @Test - public void getPersonalPeselTest() throws Exception { - Assert.assertEquals("12345678900", basicInformation.getPersonalData().getPesel()); - } - - @Test - public void getPersonalGenderTest() throws Exception { - Assert.assertEquals("Kobieta", basicInformation.getPersonalData().getGender()); - } - - @Test - public void isPersonalPolishCitizenshipTest() throws Exception { - Assert.assertTrue(basicInformation.getPersonalData().isPolishCitizenship()); - } - - @Test - public void getPersonalFamilyNameTest() throws Exception { - Assert.assertEquals("Nowak", basicInformation.getPersonalData().getFamilyName()); - } - - @Test - public void getPersonalParentsNames() throws Exception { - Assert.assertEquals("Gabriela, Kamil", - basicInformation.getPersonalData().getParentsNames()); - } - - @Test - public void getBasicAddressTest() throws Exception { - Assert.assertEquals("ul. Sportowa 16, 00-123 Warszawa", - basicInformation.getAddressData().getAddress()); - } - - @Test - public void getBasicRegisteredAddressTest() throws Exception { - Assert.assertEquals("ul. Sportowa 17, 00-123 Warszawa", - basicInformation.getAddressData().getRegisteredAddress()); - } - - @Test - public void getBasicCorrespondenceAddressTest() throws Exception { - Assert.assertEquals("ul. Sportowa 18, 00-123 Warszawa", - basicInformation.getAddressData().getCorrespondenceAddress()); - } - - @Test - public void getContactPhoneNumberTest() throws Exception { - Assert.assertEquals("005554433", - basicInformation.getContactDetails().getPhoneNumber()); - } - - @Test - public void getContactCellPhoneNumberTest() throws Exception { - Assert.assertEquals("555444333", - basicInformation.getContactDetails().getCellPhoneNumber()); - } - - @Test - public void getContactEmailTest() throws Exception { - Assert.assertEquals("wulkanowy@example.null", - basicInformation.getContactDetails().getEmail()); - } -} diff --git a/api/src/test/java/io/github/wulkanowy/api/user/FamilyInformationTest.java b/api/src/test/java/io/github/wulkanowy/api/user/FamilyInformationTest.java deleted file mode 100644 index b4f858ece..000000000 --- a/api/src/test/java/io/github/wulkanowy/api/user/FamilyInformationTest.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.github.wulkanowy.api.user; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import java.util.List; - -import io.github.wulkanowy.api.StudentAndParentTestCase; - -public class FamilyInformationTest extends StudentAndParentTestCase { - - private FamilyInformation familyInformation; - - @Before - public void setUp() throws Exception { - familyInformation = new FamilyInformation(getSnp("UczenDanePodstawowe.html")); - } - - @Test - public void getFamilyMembers() throws Exception { - Assert.assertEquals(2, familyInformation.getFamilyMembers().size()); - } - - @Test - public void getNameTest() throws Exception { - List list = familyInformation.getFamilyMembers(); - Assert.assertEquals("Marianna Pająk", list.get(0).getName()); - Assert.assertEquals("Dawid Świątek", list.get(1).getName()); - } - - @Test - public void getKinshipTest() throws Exception { - List list = familyInformation.getFamilyMembers(); - Assert.assertEquals("matka", list.get(0).getKinship()); - Assert.assertEquals("ojciec", list.get(1).getKinship()); - } - - @Test - public void getAddressTest() throws Exception { - List list = familyInformation.getFamilyMembers(); - Assert.assertEquals("ul. Sportowa 16, 00-123 Warszawa", list.get(0).getAddress()); - Assert.assertEquals("ul. Sportowa 18, 00-123 Warszawa", list.get(1).getAddress()); - } - - @Test - public void getTelephonesTest() throws Exception { - List list = familyInformation.getFamilyMembers(); - Assert.assertEquals("555111222", list.get(0).getTelephones()); - Assert.assertEquals("555222111", list.get(1).getTelephones()); - } - - @Test - public void getEmailTest() throws Exception { - List list = familyInformation.getFamilyMembers(); - Assert.assertEquals("wulkanowy@example.null", list.get(0).getEmail()); - Assert.assertEquals("wulkanowy@example.null", list.get(1).getEmail()); - } -} 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 deleted file mode 100644 index f4b712c17..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/OcenyWszystkie-semester.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - Witryna ucznia i rodzica – Oceny - - -
-

Oceny

-
-
- - -
-
-
wersja: 17.05.0000.24042
- - 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 40e691610..000000000 --- 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/attendance/Frekwencja-excellent.html b/api/src/test/resources/io/github/wulkanowy/api/attendance/Frekwencja-excellent.html deleted file mode 100644 index 5eaf50dfe..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/attendance/Frekwencja-excellent.html +++ /dev/null @@ -1,408 +0,0 @@ - - - - - Witryna ucznia i rodzica – Frekwencja - - - -
-

Frekwencja

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Lekcjaponiedziałek
31.08.2015
wtorek
01.09.2015
środa
02.09.2015
czwartek
03.09.2015
piątek
04.09.2015
0
1 -
- Uroczyste rozpoczęcie roku szkolnego 2015/2016 -
-
-
- Wychowanie do życia w rodzinie -
-
-
- Urządzenia techniki komputerowej -
-
2 -
- Język angielski -
-
-
- Język niemiecki -
-
-
- Urządzenia techniki komputerowej -
-
3 -
- Systemy operacyjne -
-
-
- Chemia -
-
-
- Urządzenia techniki komputerowej -
-
4 -
- Systemy operacyjne -
-
-
- Geografia -
-
-
- Urządzenia techniki komputerowej -
-
5 -
- Tworzenie stron internetowych -
-
-
- Matematyka -
-
-
- Język polski -
-
6 -
- Tworzenie stron internetowych -
-
-
- Fizyka -
-
-
- Matematyka -
-
7 -
- Wychowanie fizyczne -
-
-
- Język polski -
-
-
- Historia -
-
8 -
- Wychowanie fizyczne -
-
9
10
11
12
13
-

Statystyki

-
- - -
-

Frekwencja od początku roku szkolnego: 100,00%

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
IXXXIXIIIIIIIIIVVVIVIIVIIIRazem
Obecność14214313911013175126139921141211
Nieobecność nieusprawiedliwiona
Nieobecność usprawiedliwiona
Nieobecność z przyczyn szkolnych
Spóźnienie nieusprawiedliwione
Spóźnienie usprawiedliwione
Zwolnienie
-
-
wersja: 17.07.0002.24480
- - diff --git a/api/src/test/resources/io/github/wulkanowy/api/attendance/Frekwencja-full.html b/api/src/test/resources/io/github/wulkanowy/api/attendance/Frekwencja-full.html deleted file mode 100644 index aa9953053..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/attendance/Frekwencja-full.html +++ /dev/null @@ -1,498 +0,0 @@ - - - - - Witryna ucznia i rodzica – Frekwencja - - - -
-

Frekwencja

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Lekcjaponiedziałek
05.09.2016
wtorek
06.09.2016
środa
07.09.2016
czwartek
08.09.2016
piątek
09.09.2016
0
1 -
- Urządzenia techniki komputerowej -
-
-
- Multimedia i grafika komputerowa -
-
-
- Użytkowanie urządzeń peryferyjnych komputera -
-
-
- Religia -
-
2 -
- Urządzenia techniki komputerowej -
-
-
- Język niemiecki -
-
-
- Użytkowanie urządzeń peryferyjnych komputera -
-
-
- Język niemiecki -
-
-
- Sieci komputerowe i administrowanie sieciami -
-
3 -
- Urządzenia techniki komputerowej -
-
-
- Fizyka -
-
-
- Historia -
-
-
- Wychowanie fizyczne -
-
-
- Wiedza o kulturze -
-
4 -
- Naprawa komputera -
-
-
- Wychowanie fizyczne -
-
-
- Język angielski -
-
-
- Wychowanie fizyczne -
-
-
- Język polski -
-
5 -
- Sieci komputerowe i administrowanie sieciami -
-
-
- Metodologia programowania -
-
-
- Urządzenia techniki komputerowej -
-
-
- Matematyka -
-
-
- Metodologia programowania -
-
6 -
- Język niemiecki -
-
-
- Sieci komputerowe i administrowanie sieciami -
-
-
- Język polski -
-
-
- Podstawy przedsiębiorczości -
-
-
- Matematyka -
-
7 -
- Fizyka -
-
-
- Język polski -
-
-
- Systemy operacyjne -
-
-
- Zajęcia z wychowawcą -
-
-
- Religia -
-
8 -
- Naprawa komputera -
-
-
- Systemy operacyjne -
-
-
- Urządzenia techniki komputerowej -
-
-
- Zajęcia z wychowawcą -
-
9
10
11
12
13
-

Statystyki

-
- - -
-

Frekwencja od początku roku szkolnego: 80,94%

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
IXXXIXIIIIIIIIIVVVIVIIVIIIRazem
Obecność1351031085437100339010359822
Nieobecność nieusprawiedliwiona246
Nieobecność usprawiedliwiona627293044161327192
Nieobecność z przyczyn szkolnych77
Spóźnienie nieusprawiedliwione41222112
Spóźnienie usprawiedliwione11
Zwolnienie112
-
-
wersja: 17.07.0002.24480
- - diff --git a/api/src/test/resources/io/github/wulkanowy/api/exams/Sprawdziany-one-per-day.html b/api/src/test/resources/io/github/wulkanowy/api/exams/Sprawdziany-one-per-day.html deleted file mode 100644 index ecc48cf16..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/exams/Sprawdziany-one-per-day.html +++ /dev/null @@ -1,85 +0,0 @@ - - - - - Witryna ucznia i rodzica – Terminarz sprawdzianów - - -
-

Sprawdziany

-

Tydzień 23.10.2017 - 29.10.2017

-
-

poniedziałek, 23.10.2017

-
-
-
Przedmiot i grupa:
-
Sieci komputerowe 3Ti|zaw2
-
-
-
Rodzaj sprawdzianu:
-
Sprawdzian
-
-
-
Opis:
-
Łącza danych
-
-
-
Nauczyciel i data wpisu:
-
Adam Wiśniewski [AW], 16.10.2017
-
-
-
-
-

wtorek, 24.10.2017

-
-
-
Przedmiot i grupa:
-
Język angielski 3Ti|J1
-
-
-
Rodzaj sprawdzianu:
-
Sprawdzian
-
-
-
Opis:
-
Czasy teraźniejsze
-
-
-
Nauczyciel i data wpisu:
-
Natalia Nowak [NN], 17.10.2017
-
-
-
-
-
-
-

piątek, 27.10.2017

-
-
-
Przedmiot i grupa:
-
Metodologia programowania 3Ti|zaw2
-
-
-
Rodzaj sprawdzianu:
-
Sprawdzian
-
-
-
Opis:
-
-
-
-
Nauczyciel i data wpisu:
-
Małgorzata Nowacka [MN], 16.10.2017
-
-
-
-
-
- -
-
wersja: 17.08.0001.24874
- - 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 deleted file mode 100644 index a229d1d53..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/grades/OcenyWszystkie-filled.html +++ /dev/null @@ -1,108 +0,0 @@ - - - - - Witryna ucznia i rodzica – Oceny - - -
-

Oceny

-
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PrzedmiotOcena cząstkowaOpisWagaDataNauczyciel
ZachowanieBrak ocen
Zajęcia z wychowawcą - 5 - A1, Dzień Kobiet w naszej klasie1,0021.03.2017Patryk Maciejewski
Edukacja dla bezpieczeństwa - 4- - S1, PIERWSZA POMOC I RESUSCYTACJA5,0031.03.2017Weronika Ratajczak
Fizyka - 2 - O, Odpowiedź3,0025.06.2017Jakub Michalak
Język angielski - 5 - BW3, Writing3,0002.06.2017Oliwia Woźniak
Wiedza o społeczeństwieBrak ocen
Wychowanie fizyczne1STR8,0002.04.2017Klaudia Dziedzic
Język polski1K, Kordian5,0006.02.2017Amelia Stępień
-
-
wersja: 17.02.0000.23328
- - diff --git a/api/src/test/resources/io/github/wulkanowy/api/grades/OcenyWszystkie-subjects-average.html b/api/src/test/resources/io/github/wulkanowy/api/grades/OcenyWszystkie-subjects-average.html deleted file mode 100644 index 023b5405e..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/grades/OcenyWszystkie-subjects-average.html +++ /dev/null @@ -1,70 +0,0 @@ - - - - - Witryna ucznia i rodzica – Oceny - - -
-

Oceny

-
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PrzedmiotOceny cząstkoweŚredniaPrzewidywana ocena rocznaOcena roczna
ZachowanieBrak ocen-bardzo dobrebardzo dobre
Język polski03,53-dobry
Wychowanie fizyczne05,05bardzo dobrycelujący
Język angielski04,44/5bardzo dobry
Wiedza o społeczeństwieBrak ocen---
-
-
wersja: 17.02.0000.23328
- - diff --git a/api/src/test/resources/io/github/wulkanowy/api/grades/OcenyWszystkie-subjects.html b/api/src/test/resources/io/github/wulkanowy/api/grades/OcenyWszystkie-subjects.html deleted file mode 100644 index 0f6da4148..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/grades/OcenyWszystkie-subjects.html +++ /dev/null @@ -1,64 +0,0 @@ - - - - - Witryna ucznia i rodzica – Oceny - - -
-

Oceny

-
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PrzedmiotOceny cząstkowePrzewidywana ocena rocznaOcena roczna
Zachowanie-bardzo dobrebardzo dobre
Praktyka zawodowa--celujący
Metodologia programowania-bardzo dobrycelujący
Podstawy przedsiębiorczości-3/4dostateczny
Wychowanie do życia w rodzinie---
-
-
wersja: 17.05.0000.24042
- - diff --git a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-brak-dostepu.html b/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-brak-dostepu.html deleted file mode 100644 index 2911f1d24..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-brak-dostepu.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - Logowanie - - -
-
- Adres example@wulkanowy.io nie został zarejestrowany w dzienniku uczniowskim jako adres rodzica, bądź ucznia. - Jeśli jesteś rodzicem (prawnym opiekunem) ucznia (lub uczniem) szkoły korzystającej z dziennika „UONET +” udaj się do - wychowawcy i poproś o wprowadzenie Twojego adresu e-mail do Twoich danych. -
-
- - 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 deleted file mode 100644 index a8496cd1b..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-certyfikat.html +++ /dev/null @@ -1,17 +0,0 @@ - - - Working... - - -
- - - - -
- - - diff --git a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-error.html b/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-error.html deleted file mode 100644 index 087131882..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-error.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - Logowanie (demo123) - - -
-
-
- Zła nazwa użytkownika lub hasło -
-
-
- - - diff --git a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-notLoggedIn.html b/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-notLoggedIn.html deleted file mode 100644 index f961bf82c..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-notLoggedIn.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - Dziennik UONET+ - - -
-
-
-
-
- - Zaloguj się -
-
-
-
Uonet+ wersja 17.09.0007.26300
-
-
- - diff --git a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-success.html b/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-success.html deleted file mode 100644 index 23c27a3fe..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-success.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - Uonet+ - - -
-
-
- example@wulkanowy.io (wyloguj) -
-
-
- - diff --git a/api/src/test/resources/io/github/wulkanowy/api/login/PrzerwaTechniczna.html b/api/src/test/resources/io/github/wulkanowy/api/login/PrzerwaTechniczna.html deleted file mode 100644 index 062f9b60c..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/login/PrzerwaTechniczna.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - Przerwa techniczna - - -
-
-
-

Przerwa techniczna

-

Aktualnie trwają prace konserwacyjne. Witryna będzie dostępna za kilka minut.

> -
- -
-
- - diff --git a/api/src/test/resources/io/github/wulkanowy/api/login/cert.xml b/api/src/test/resources/io/github/wulkanowy/api/login/cert.xml deleted file mode 100644 index 549b2d424..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/login/cert.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - Default - demo12345 - incorrect value - warszawa - asdf - asdfsdf - - - - - - diff --git a/api/src/test/resources/io/github/wulkanowy/api/messages/GetTrescWiadomosci.json b/api/src/test/resources/io/github/wulkanowy/api/messages/GetTrescWiadomosci.json deleted file mode 100644 index 1ba54a32c..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/messages/GetTrescWiadomosci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "success": true, - "data": { - "Id": 12345, - "Tresc": "Witam, …. \nPozdrawiam Krzysztof Czerkas" - } -} diff --git a/api/src/test/resources/io/github/wulkanowy/api/messages/GetWiadomosciOdebrane.json b/api/src/test/resources/io/github/wulkanowy/api/messages/GetWiadomosciOdebrane.json deleted file mode 100644 index 326390ed6..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/messages/GetWiadomosciOdebrane.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "success": true, - "data": [ - { - "Nieprzeczytana": false, - "Data": "2016-03-15 09:00:00", - "Tresc": null, - "Temat": "Wycieczka", - "NadawcaNazwa": "Kowalski Jan", - "IdWiadomosci": 1234, - "IdNadawca": 4321, - "Id": 12345 - }, - { - "Nieprzeczytana": true, - "Data": "2016-04-20 22:00:00", - "Tresc": null, - "Temat": "\"Dzień dobrego słowa\"", - "NadawcaNazwa": "Pazura Agnieszka", - "IdWiadomosci": 1235, - "IdNadawca": 12, - "Id": 12346 - }, - { - "Nieprzeczytana": false, - "Data": "2016-04-29 10:00:00", - "Tresc": null, - "Temat": "Rozdajemy oceny celujące", - "NadawcaNazwa": "Kowalski Jan", - "IdWiadomosci": 1236, - "IdNadawca": 4321, - "Id": 12347 - } - ] -} diff --git a/api/src/test/resources/io/github/wulkanowy/api/messages/GetWiadomosciUsuniete-empty.json b/api/src/test/resources/io/github/wulkanowy/api/messages/GetWiadomosciUsuniete-empty.json deleted file mode 100644 index 36f89aa1e..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/messages/GetWiadomosciUsuniete-empty.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "success": true, - "data": [] -} diff --git a/api/src/test/resources/io/github/wulkanowy/api/messages/PageError.html b/api/src/test/resources/io/github/wulkanowy/api/messages/PageError.html deleted file mode 100644 index ae976af1f..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/messages/PageError.html +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - Błąd strony - - - -
- -
-
- Wystąpił nieoczekiwany błąd -
-
Wystąpił błąd aplikacji. Prosimy zalogować się ponownie. Jeśli problem będzie się powtarzał, prosimy o kontakt z serwisem.
-
-
-
- -
- - diff --git a/api/src/test/resources/io/github/wulkanowy/api/messages/UndefinedError.txt b/api/src/test/resources/io/github/wulkanowy/api/messages/UndefinedError.txt deleted file mode 100644 index c8d55a96c..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/messages/UndefinedError.txt +++ /dev/null @@ -1 +0,0 @@ -The custom error module does not recognize this error. \ No newline at end of file diff --git a/api/src/test/resources/io/github/wulkanowy/api/notes/UwagiOsiagniecia-empty.html b/api/src/test/resources/io/github/wulkanowy/api/notes/UwagiOsiagniecia-empty.html deleted file mode 100644 index b15bb6e70..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/notes/UwagiOsiagniecia-empty.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - Witryna ucznia i rodzica – Uwagi i osiągnięcia - - -
-
-

Uwagi

-

Brak informacji do wyświetlenia

-
-
-

Osiągnięcia

-

Brak informacji do wyświetlenia

-
-
-
wersja: 17.05.0000.24042
- - diff --git a/api/src/test/resources/io/github/wulkanowy/api/notes/UwagiOsiagniecia-filled.html b/api/src/test/resources/io/github/wulkanowy/api/notes/UwagiOsiagniecia-filled.html deleted file mode 100644 index 50e740585..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/notes/UwagiOsiagniecia-filled.html +++ /dev/null @@ -1,65 +0,0 @@ - - - - - Witryna ucznia i rodzica – Uwagi i osiągnięcia - - -
-
-

Uwagi

-

06.06.2017

-
-
-
Nauczyciel:
-
Jan Kowalski [JK]
-
-
-
Kategoria:
-
Zaangażowanie społeczne
-
-
-
Treść:
-
Pomoc przy pikniku charytatywnym
-
-
-

01.12.2016

-
-
-
Nauczyciel:
-
Ochocka Zofia [PZ]
-
-
-
Kategoria:
-
Reprezentowanie szkoły
-
-
-
Treść:
-
Udział w przygotowaniu spektaklu
-
-
-

01.10.2016

-
-
-
Nauczyciel:
-
Kochański Leszek [KL]
-
-
-
Kategoria:
-
Zachowanie na lekcji
-
-
-
Treść:
-
Przeszkadzanie w prowadzeniu lekcji
-
-
-
-
-

Osiągnięcia

-
I miejsce w ogólnopolskim konkursie ortograficznym
-
III miejsce w ogólnopolskim konkursie plastycznym
-
-
-
wersja: 17.05.0000.24042
- - diff --git a/api/src/test/resources/io/github/wulkanowy/api/school/Szkola.html b/api/src/test/resources/io/github/wulkanowy/api/school/Szkola.html deleted file mode 100644 index 05a698a04..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/school/Szkola.html +++ /dev/null @@ -1,136 +0,0 @@ - - - - - Witryna ucznia i rodzica – Szkoła i nauczyciele - - -
-

Szkoła

-
-
- Nazwa szkoły: - Zespół Szkół nr 64 -
-
- Adres szkoły: - ul. Wiśniowa 128, 01-234 Rogalowo, Nibylandia -
-
- Telefon: - 55 5555555 -
-
- Imię i nazwisko dyrektora: - Antoni Sobczyk -
-
- Imię i nazwisko pedagoga: - Zofia Czerwińska [ZC], Aleksander Krzemiński [AK], Karolina Kowalska [KK], Bartek Dąbrowski [BD] -
-
-

Nauczyciele

-

- Klasa: 1a, Wychowawcy: - Karolina Kowalska [AN], Antoni Sobczyk [AS]

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Lp.PrzedmiotNauczyciel
1BiologiaKarolina Kowalska [AN]
2ChemiaZofia Czerwińska [NA]
3Edukacja dla bezpieczeństwaAleksandra Krajewska [AK]
4FizykaStanisław Krupa [BS]
5GeografiaAleksandra Wójtowicz [AW]
6HistoriaSara Wierzbicka [KB]
7Język angielskiKarolina Kowalska [AN], Mateusz Kowal [MK], Amelia Mazur [AM]
8Język niemieckiMateusz Kowal [MK], Barbara Markowska [BM]
9Język polskiMichał Mazur [MM]
10MatematykaSzymon Wojciechowski [SW]
11PlastykaMichał Mazur [MM]
12ReligiaMaja Wiśniewska [M]
13Wiedza o społeczeństwieKarolina Kowalska [AN]
14Wychowanie do życia w rodzinieZofia Czerwińska [NA]
15Wychowanie fizyczneKarolina Kowalska [AN], Liliana Kowal [LK]
16Zajęcia techniczneBartek Dąbrowski [BD]
17Zajęcia z wychowawcąKarolina Kowalska [AN]
-
-
wersja: 17.02.0000.23328
- - diff --git a/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-full.html b/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-full.html deleted file mode 100644 index 53eb04857..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-full.html +++ /dev/null @@ -1,466 +0,0 @@ - - - - - Witryna ucznia i rodzica – Plan lekcji - - -
-

Plan lekcji

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
LekcjaPora lekcjiponiedziałek
19.06.2017
wtorek
20.06.2017
środa
21.06.2017
czwartek
22.06.2017
piątek
23.06.2017
007:10 07:55 -
- Fizyka [zaw2] - - Bączek Grzegorz - 19 - (uczniowie zwolnieni do domu) -
-
-
- Metodologia programowania [zaw2] - - - 32 -
-
-
- Religia - Cyranka Krystian - 3 - Wychowanie do życia w rodzinie - Nowak Jadwiga - 3 - (zastępstwo) -
-
-
- Język polski - - 16 - (oddział nieobecny) -
- -
egzamin
-
-
- Uroczyste zakończenie roku szkolnego - Baran Małgorzata - 37 -
-
108:00 08:45 -
- Język angielski [J1] - - Kobczyk Iwona - -
-
-
- Metodologia programowania [zaw2] - - Baran Małgorzata - 36 - (zmiana organizacji zajęć) -
-
- Wychowanie fizyczne [zaw2] - - - G3 - (przeniesiona z lekcji 7, 01.12.2017) -
-
-
- Użytkowanie urządzeń peryferyjnych komputera [zaw2] - - Bączek Robert - -
-
-
- Wychowanie fizyczne [zaw1] - - Jarocki Krzysztof - G4 - Wychowanie fizyczne [zaw1] - - Nowicka Irena - G4 - (zastępstwo) -
-
208:50 09:35 -
- Język polski - Bocian Natalia - -
-
-
- Język niemiecki [J1] - - Rożeniec Honorata - 25 - (okienko dla uczniów) -
-
- Język polski - Bocian Natalia - - (przeniesiona z lekcji 7, 20.06.2017) -
-
- Język polski - Bocian Natalia - -
-
-
- Urządzenia techniki komputerowej [zaw2] - - Bocian Grzegorz - -
-
-
- Matematyka - Baran Małgorzata - -
-
309:40 10:25 -
- Język polski - Bocian Natalia - -
-
-
- Fizyka - Bączek Grzegorz - 19 - (okienko dla uczniów) -
-
- Wychowanie fizyczne [wf2] - - Nowicka Irena - - (przeniesiona z lekcji 4, 20.06.2017) -
-
- Wychowanie fizyczne [wf2] - - Nowicka Irena - -
-
-
- Metodologia programowania [zaw2] - - Baran Małgorzata - -
-
-
- Wychowanie fizyczne [wf2] - - Nowicka Irena - -
-
410:40 11:25 -
- Urządzenia techniki komputerowej [zaw2] - - Bocian Grzegorz - -
-
-
- Wychowanie fizyczne [wf2] - - Nowicka Irena - - (przeniesiona na lekcję 3, 20.06.2017) -
-
-
- Matematyka - Baran Małgorzata - -
-
-
- Wychowanie fizyczne [wf2] - - Nowicka Irena - -
-
511:30 12:15 -
- Urządzenia techniki komputerowej [zaw2] - - Bocian Grzegorz - -
-
-
- Podstawy przedsiębiorczości - Bogatka Anna - W12 - (okienko dla uczniów) -
-
-
- Religia - Cyranka Krystian - -
-
-
- Sieci komputerowe i administrowanie sieciami [zaw2] - - Rożeniec Piotr - -
-
612:20 13:05 -
- Matematyka - Baran Małgorzata - -
-
-
- Podstawy przedsiębiorczości - Bogatka Anna - W12 - (okienko dla uczniów) -
-
-
- Język angielski [J1] - - Brodziec Sylwia - -
-
-
- Religia - Cyranka Krystian - -
-
713:10 13:55 -
- Fizyka - Bączek Grzegorz - 33 - (okienko dla uczniów) -
-
-
- Język polski - Bocian Natalia - - (przeniesiona na lekcję 2, 20.06.2017) -
-
-
- Multimedia i grafika komputerowa [zaw2] - - Bocian Konrad - -
-
-
- Wiedza o kulturze - Bocian Natalia - -
-
814:00 14:45 -
- Zajęcia z wychowawcą - Baran Małgorzata - -
-
-
- Naprawa komputera [zaw2] - - Kraska Maciej - 32 - (okienko dla uczniów) -
-
-
- Systemy operacyjne [zaw2] - - Kraska Maciej - 32 -
-
914:50 15:35 -
- Język niemiecki [J1] - - Rożeniec Honorata - 25 - (uczniowie zwolnieni do domu) -
-
1015:40 16:25
1116:35 17:20
1217:25 18:10
1318:15 19:00
-
-
-
- Kursywa- planowane -
-
- Zwykła czcionka- zrealizowane -
-
- Przekreślone- odwołane lub przeniesione -
-
- Pogrubione- nowe lekcje, przeniesione z innego terminu, zastępstwa -
-
-
-
wersja: 17.05.0000.24042
- - diff --git a/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-holidays.html b/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-holidays.html deleted file mode 100644 index 09555cf3f..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-holidays.html +++ /dev/null @@ -1,156 +0,0 @@ - - - - - Witryna ucznia i rodzica – Plan lekcji - - -
-

Plan lekcji

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
LekcjaPora lekcjiponiedziałek
31.07.2017
Ferie letnie
wtorek
01.08.2017
Ferie letnie
środa
02.08.2017
Ferie letnie
czwartek
03.08.2017
Ferie letnie
piątek
04.08.2017
Ferie letnie
007:10 07:55
108:00 08:45
208:50 09:35
309:40 10:25
410:40 11:25
511:30 12:15
612:20 13:05
713:10 13:55
814:00 14:45
914:50 15:35
1015:40 16:25
1116:35 17:20
1217:25 18:10
1318:15 19:00
-
-
-
wersja: 17.05.0000.24042
- - diff --git a/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-std.html b/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-std.html deleted file mode 100644 index 8bcc9794e..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-std.html +++ /dev/null @@ -1,303 +0,0 @@ - - - - - Witryna ucznia i rodzica – Plan lekcji - - -
-

Plan lekcji

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
LekcjaPora lekcjiponiedziałek
19.06.2017
wtorek
20.06.2017
środa
21.06.2017
czwartek
22.06.2017
piątek
23.06.2017
108:00 08:45 -
- Edukacja dla bezpieczeństwa - Kobczyk Iwona - -
-
-
- Język niemiecki [JNPW] - - Dzwoniec Ewa - -
-
-
- Wychowanie do życia w rodzinie - Baran Dominika - -
-
-
- Język niemiecki [JNPW] - - Dzwoniec Ewa - -
-
208:50 09:35 -
- Historia - Bogatka Katarzyna - -
-
-
- Wychowanie fizyczne [CH] - - Brodziec Dominika - -
-
-
- Fizyka - Bocian Łukasz - -
-
-
- Biologia - Kowalska Anna - -
-
-
- Religia - Kraska Maciej - -
-
309:40 10:25 -
- Wychowanie fizyczne [CH] - - Brodziec Dominika - -
-
-
- Język polski - Rożeniec Paulina - -
-
-
- Matematyka - Bączek Dominika - -
-
-
- Plastyka - Rożeniec Paulina - -
-
-
- Zajęcia z wychowawcą - Kowalska Anna - -
-
410:30 11:15 -
- Geografia - Orłowski Konrad - -
-
-
- Matematyka - Bączek Dominika - -
-
-
- Język angielski [JAPN] - - Biegus Kazimiera - -
-
-
- Matematyka - Bączek Dominika - -
-
-
- Historia - Bogatka Katarzyna - -
-
511:30 12:15 -
- Matematyka - Bączek Dominika - -
-
-
- Biologia - Kowalska Anna - -
-
-
- Zajęcia techniczne - Chlebowski Stanisław - -
-
-
- Język angielski [JAPN] - - Biegus Kazimiera - -
-
-
- Język polski - Rożeniec Paulina - -
-
612:30 13:15 -
- Matematyka - Bączek Dominika - -
-
-
- Fizyka - Bocian Łukasz - -
-
-
- Język polski - Rożeniec Paulina - -
-
-
- Wychowanie fizyczne [CH] - - Brodziec Dominika - -
-
-
- Język polski - Rożeniec Paulina - -
-
713:20 14:05 -
- Język angielski [JAPN] - - Biegus Kazimiera - -
-
-
- Religia - Kraska Maciej - -
-
-
- Wychowanie fizyczne [CH] - - Brodziec Dominika - -
-
814:10 14:55
-
-
-
wersja: 17.02.0000.23328
- - diff --git a/api/src/test/resources/io/github/wulkanowy/api/user/UczenDanePodstawowe.html b/api/src/test/resources/io/github/wulkanowy/api/user/UczenDanePodstawowe.html deleted file mode 100644 index c54dd8614..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/user/UczenDanePodstawowe.html +++ /dev/null @@ -1,119 +0,0 @@ - - - - - Witryna ucznia i rodzica – Dane ucznia - - -
-

Dane podstawowe

-

Dane osobowe

-
-
- Imię (imiona) nazwisko: - Maria Aneta Kamińska -
-
- Data i miejsce urodzenia: - 01.01.1900, Warszawa -
-
- PESEL: - 12345678900 -
-
- Płeć: - Kobieta -
-
- Obywatelstwo polskie: - Tak -
-
- Nazwisko rodowe: - Nowak -
-
- Imię matki i ojca: - Gabriela, Kamil -
-
-

Dane adresowe

-
-
- Adres zamieszkania: - ul. Sportowa 16, 00-123 Warszawa -
-
- Adres zameldowania: - ul. Sportowa 17, 00-123 Warszawa -
-
- Adres korespondencji: - ul. Sportowa 18, 00-123 Warszawa -
-
-

Kontakt

-
-
- Telefon: - 005554433 -
-
- Telefon komórkowy: - 555444333 -
-
- E-mail: - wulkanowy@example.null -
-
-

Rodzina

-
-
- Nazwisko i imię: - Marianna Pająk -
-
- Stopień pokrewieństwa: - matka -
-
- Adres: - ul. Sportowa 16, 00-123 Warszawa -
-
- Telefony: - 555111222 -
-
- E-mail: - wulkanowy@example.null -
-
-
-
- Nazwisko i imię: - Dawid Świątek -
-
- Stopień pokrewieństwa: - ojciec -
-
- Adres: - ul. Sportowa 18, 00-123 Warszawa -
-
- Telefony: - 555222111 -
-
- E-mail: - wulkanowy@example.null -
-
-
-
wersja: 17.02.0000.23328
- - diff --git a/app/.gitignore b/app/.gitignore deleted file mode 100644 index 796b96d1c..000000000 --- a/app/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/app/bitrise.jks.gpg b/app/bitrise.jks.gpg new file mode 100644 index 000000000..1dee91bd9 Binary files /dev/null and b/app/bitrise.jks.gpg differ diff --git a/app/build.gradle b/app/build.gradle index 252021af3..188af355d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,106 +1,271 @@ -buildscript { - repositories { - maven { url "https://plugins.gradle.org/m2/" } - maven { url 'https://maven.fabric.io/public' } - } - - dependencies { - classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' - classpath 'io.fabric.tools:gradle:1.25.1' - } -} - -repositories { - maven { url 'https://maven.fabric.io/public' } -} - apply plugin: 'com.android.application' -apply plugin: 'org.greenrobot.greendao' -apply plugin: 'io.fabric' -apply from: '../jacoco.gradle' -apply from: '../android-sonarqube.gradle' +apply plugin: 'kotlin-android' +apply plugin: 'kotlinx-serialization' +apply plugin: 'kotlin-parcelize' +apply plugin: 'kotlin-kapt' +apply plugin: 'dagger.hilt.android.plugin' +apply plugin: 'com.google.gms.google-services' +apply plugin: 'com.google.firebase.crashlytics' +apply plugin: 'com.github.triplet.play' +apply plugin: 'ru.cian.huawei-publish' +apply plugin: 'com.mikepenz.aboutlibraries.plugin' +apply plugin: 'com.huawei.agconnect' +apply from: 'jacoco.gradle' +apply from: 'sonarqube.gradle' +apply from: 'hooks.gradle' android { - compileSdkVersion 26 - buildToolsVersion "26.0.3" + compileSdkVersion 31 + defaultConfig { applicationId "io.github.wulkanowy" testApplicationId "io.github.tests.wulkanowy" - minSdkVersion 15 - targetSdkVersion 26 - versionCode 5 - versionName "0.2.2" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - vectorDrawables.useSupportLibrary = true + minSdkVersion 21 + targetSdkVersion 31 + versionCode 107 + versionName "1.6.3" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + + resValue "string", "app_name", "Wulkanowy" + manifestPlaceholders = [ - fabricApiKey: System.getenv("FABRIC_API_KEY") ?: "null" + firebase_enabled: project.hasProperty("enableFirebase"), + admob_project_id: "" ] + javaCompileOptions { + annotationProcessorOptions { + arguments += [ + "room.schemaLocation": "$projectDir/schemas".toString(), + "room.incremental" : "true" + ] + } + } + + buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "null" + + if (System.env.SET_BUILD_TIMESTAMP) { + buildConfigField "long", "BUILD_TIMESTAMP", String.valueOf(System.currentTimeMillis()) + } else { + buildConfigField "long", "BUILD_TIMESTAMP", "1486235849000" + } + } + + sourceSets { + // https://github.com/robolectric/robolectric/issues/3928#issuecomment-395309991 + debug.assets.srcDirs += files("$projectDir/schemas".toString()) + } + + signingConfigs { + release { + storeFile file("upload-key.jks") + storePassword System.getenv("PLAY_STORE_PASSWORD") + keyAlias System.getenv("PLAY_KEY_ALIAS") + keyPassword System.getenv("PLAY_KEY_PASSWORD") + } } buildTypes { release { - minifyEnabled false + minifyEnabled true + shrinkResources true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.release + buildConfigField "String", "MESSAGES_BASE_URL", "\"https://messages.wulkanowy.net.pl\"" } debug { + minifyEnabled false + shrinkResources false + resValue "string", "app_name", "Wulkanowy DEV" applicationIdSuffix ".dev" versionNameSuffix "-dev" - testCoverageEnabled = true - ext.enableCrashlytics = false + ext.enableCrashlytics = project.hasProperty("enableFirebase") + buildConfigField "String", "MESSAGES_BASE_URL", "\"https://messages.wulkanowy.net.pl\"" } } - testOptions { - unitTests.all { - testLogging { - events "passed", "skipped", "failed", "standardOut", "standardError" - outputs.upToDateWhen { false } - showStandardStreams = true - } + flavorDimensions "platform" + + productFlavors { + hms { + dimension "platform" + manifestPlaceholders = [install_channel: "AppGallery"] + } + + play { + dimension "platform" + manifestPlaceholders = [ + install_channel : "Google Play", + admob_project_id: System.getenv("ADMOB_PROJECT_ID") ?: "ca-app-pub-3940256099942544~3347511713" + ] + buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "\"${System.getenv("SINGLE_SUPPORT_AD_ID") ?: "ca-app-pub-3940256099942544/5354046379"}\"" + } + + fdroid { + dimension "platform" + manifestPlaceholders = [install_channel: "F-Droid"] + } + } + + playConfigs { + play { enabled.set(true) } + } + + buildFeatures { + viewBinding true + } + + bundle { + language { + enableSplit = false + } + } + + testOptions.unitTests { + includeAndroidResources = true + } + + compileOptions { + coreLibraryDesugaringEnabled true + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } + + kotlinOptions { + jvmTarget = "11" + freeCompilerArgs += ["-Xopt-in=kotlin.RequiresOptIn", "-Xjvm-default=all"] + } + + packagingOptions { + exclude 'META-INF/library_release.kotlin_module' + exclude 'META-INF/library-core_release.kotlin_module' + } + + aboutLibraries { + configPath = "app/src/main/res/raw" + } +} + +kapt { + correctErrorTypes true +} + +play { + defaultToAppBundles = false + track = 'production' + releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS + userFraction = 0.50d + updatePriority = 1 + enabled.set(false) +} + +huaweiPublish { + instances { + hmsRelease { + credentialsPath = "$rootDir/app/src/release/agconnect-credentials.json" + buildFormat = "aab" + deployType = "draft" } } } -greendao { - schemaVersion 22 - generateTests = true +ext { + work_manager = "2.7.1" + android_hilt = "1.0.0" + room = "2.4.2" + chucker = "3.5.2" + mockk = "1.12.2" + coroutines = "1.6.1" } dependencies { - implementation project(':api') - implementation "com.android.support:support-v4:$supportVersion" - implementation "com.android.support:design:$supportVersion" - implementation "com.android.support:cardview-v7:$supportVersion" - implementation "com.android.support:customtabs:$supportVersion" - implementation "com.firebase:firebase-jobdispatcher:$firebaseJob" - implementation "org.apache.commons:commons-lang3:$apacheLang" - implementation "org.apache.commons:commons-collections4:$apacheCollections" - implementation "eu.davidea:flexible-adapter:$flexibleAdapter" - implementation "eu.davidea:flexible-adapter-ui:$flexibleUi" - implementation "org.greenrobot:greendao:$greenDao" - implementation "com.github.yuweiguocn:GreenDaoUpgradeHelper:$greenDaoHelper" - implementation "com.jakewharton:butterknife:$butterknife" - implementation "joda-time:joda-time:$jodaTime" - implementation "com.google.dagger:dagger-android-support:$dagger2" - implementation "com.aurelhubert:ahbottomnavigation:$ahbottom" + implementation "io.github.wulkanowy:sdk:1.6.0" - implementation("com.crashlytics.sdk.android:crashlytics:$crashlyticsSdk@aar") { - transitive = true - } - implementation("com.crashlytics.sdk.android:answers:$crashlyticsAnswers@aar") { - transitive = true - } + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' - annotationProcessor "com.google.dagger:dagger-android-processor:$dagger2" - annotationProcessor "com.google.dagger:dagger-compiler:$dagger2" - annotationProcessor "com.jakewharton:butterknife-compiler:$butterknife" + implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines" - debugImplementation "com.amitshekhar.android:debug-db:$debugDb" - debugImplementation "net.zetetic:android-database-sqlcipher:$sqlcipher" + implementation "androidx.core:core-ktx:1.7.0" + implementation 'androidx.core:core-splashscreen:1.0.0-beta02' + implementation "androidx.activity:activity-ktx:1.4.0" + implementation "androidx.appcompat:appcompat:1.4.1" + implementation "androidx.fragment:fragment-ktx:1.4.1" + implementation "androidx.annotation:annotation:1.3.0" - testImplementation "junit:junit:$junit" - testImplementation "org.mockito:mockito-core:$mockito" + implementation "androidx.preference:preference-ktx:1.2.0" + implementation "androidx.recyclerview:recyclerview:1.2.1" + implementation "androidx.viewpager2:viewpager2:1.1.0-beta01" + implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" + implementation "androidx.constraintlayout:constraintlayout:2.1.3" + implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0" + implementation "com.google.android.material:material:1.5.0" + implementation "com.github.wulkanowy:material-chips-input:2.3.1" + implementation "com.github.PhilJay:MPAndroidChart:v3.1.0" + implementation 'com.github.lopspower:CircularImageView:4.2.0' - androidTestImplementation "com.android.support.test:runner:$testRunner" - androidTestImplementation "org.mockito:mockito-android:$mockito" + implementation "androidx.work:work-runtime-ktx:$work_manager" + playImplementation "androidx.work:work-gcm:$work_manager" + + implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.1" + + implementation "androidx.room:room-runtime:$room" + implementation "androidx.room:room-ktx:$room" + kapt "androidx.room:room-compiler:$room" + + implementation "com.google.dagger:hilt-android:$hilt_version" + kapt "com.google.dagger:hilt-android-compiler:$hilt_version" + kapt "androidx.hilt:hilt-compiler:$android_hilt" + implementation "androidx.hilt:hilt-work:$android_hilt" + + implementation 'com.github.ncapdevi:FragNav:3.3.0' + implementation "com.github.YarikSOffice:lingver:1.3.0" + + implementation 'com.squareup.retrofit2:retrofit:2.9.0' + implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0" + implementation "com.squareup.okhttp3:logging-interceptor:4.9.3" + + implementation "com.jakewharton.timber:timber:5.0.1" + implementation "at.favre.lib:slf4j-timber:1.0.1" + implementation 'com.github.bastienpaulfr:Treessence:1.0.5' + implementation "com.mikepenz:aboutlibraries-core:$about_libraries" + implementation "io.coil-kt:coil:1.4.0" + implementation "io.github.wulkanowy:AppKillerManager:3.0.0" + implementation 'me.xdrop:fuzzywuzzy:1.4.0' + implementation 'com.fredporciuncula:flow-preferences:1.6.0' + + playImplementation platform('com.google.firebase:firebase-bom:29.3.1') + playImplementation 'com.google.firebase:firebase-analytics-ktx' + playImplementation 'com.google.firebase:firebase-messaging:' + playImplementation 'com.google.firebase:firebase-crashlytics:' + playImplementation 'com.google.android.play:core:1.10.3' + playImplementation 'com.google.android.play:core-ktx:1.8.1' + playImplementation 'com.google.android.gms:play-services-ads:20.6.0' + + hmsImplementation 'com.huawei.hms:hianalytics:6.4.1.302' + hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.5.300' + + releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker" + + debugImplementation "com.github.ChuckerTeam.Chucker:library:$chucker" + debugImplementation 'com.github.amitshekhariitbhu.Android-Debug-Database:debug-db:1.0.6' + debugImplementation 'com.github.haroldadmin:WhatTheStack:1.0.0-alpha04' + + testImplementation "junit:junit:4.13.2" + testImplementation "io.mockk:mockk:$mockk" + testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines" + testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" + + testImplementation 'org.robolectric:robolectric:4.7.3' + testImplementation "androidx.test:runner:1.4.0" + testImplementation "androidx.test.ext:junit:1.1.3" + testImplementation "androidx.test:core:1.4.0" + testImplementation "androidx.room:room-testing:$room" + testImplementation "com.google.dagger:hilt-android-testing:$hilt_version" + kaptTest "com.google.dagger:hilt-android-compiler:$hilt_version" + + androidTestImplementation "androidx.test:core:1.4.0" + androidTestImplementation "androidx.test:runner:1.4.0" + androidTestImplementation "androidx.test.ext:junit:1.1.3" + androidTestImplementation "io.mockk:mockk-android:$mockk" + androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" } diff --git a/app/hooks.gradle b/app/hooks.gradle new file mode 100644 index 000000000..038fa9fb2 --- /dev/null +++ b/app/hooks.gradle @@ -0,0 +1,10 @@ +apply plugin: "com.star-zero.gradle.githook" + +githook { + failOnMissingHooksDir = false + hooks { + "pre-push" { + shell = "./app/play-publish-lint.sh" + } + } +} diff --git a/app/jacoco.gradle b/app/jacoco.gradle new file mode 100644 index 000000000..f253673e6 --- /dev/null +++ b/app/jacoco.gradle @@ -0,0 +1,51 @@ +apply plugin: "jacoco" + +jacoco { + toolVersion "0.8.7" + reportsDirectory.set(file("$buildDir/reports")) +} + +tasks.withType(Test) { + jacoco.includeNoLocationClasses = true + jacoco.excludes = ['jdk.internal.*'] +} + +task jacocoTestReport(type: JacocoReport) { + + group = "Reporting" + description = "Generate Jacoco coverage reports" + + reports { + xml.required.set(true) + html.required.set(true) + } + + def excludes = ['**/R.class', + '**/R$*.class', + '**/BuildConfig.*', + '**/Manifest*.*', + '**/*Test*.*', + 'android/**/*.*', + '**/*Module.*', + '**/*Dagger*.*', + '**/*MembersInjector*.*', + '**/*_Provide*Factory*.*', + '**/*_Factory.*'] + + classDirectories.setFrom(fileTree( + dir: "$buildDir/intermediates/classes/debug", + excludes: excludes + ) + fileTree( + dir: "$buildDir/tmp/kotlin-classes/fdroidDebug", + excludes: excludes + )) + + sourceDirectories.setFrom(files([ + "src/main/java", + "src/fdroid/java" + ])) + executionData.setFrom(fileTree( + dir: project.projectDir, + includes: ["**/*.exec", "**/*.ec"] + )) +} diff --git a/app/play-publish-lint.sh b/app/play-publish-lint.sh new file mode 100755 index 000000000..d3354b1ad --- /dev/null +++ b/app/play-publish-lint.sh @@ -0,0 +1,7 @@ +#!/bin/bash - + +content=$(cat < "app/src/main/play/release-notes/pl-PL/default.txt") || exit +if [[ "${#content}" -gt 500 ]]; then + echo >&2 "Release notes content has reached the limit of 500 characters" + exit 1 +fi diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 34ed4c38c..fd9482613 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,25 +1,26 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in C:\Users\RicomenPL\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html +# General +-dontobfuscate -# Add any project specific keep options here: -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} +#Config for wulkanowy +-keep class io.github.wulkanowy.** {*;} -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile +#Config for firebase crashlitycs +-keepattributes SourceFile,LineNumberTable +-keep public class * extends java.lang.Exception + + +#Config for Okio and OkHttp +-dontwarn javax.annotation.** +-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase +-dontwarn org.codehaus.mojo.animal_sniffer.* +-dontwarn okhttp3.internal.platform.ConscryptPlatform + + +#Config for MPAndroidChart +-keep class com.github.mikephil.charting.** { *; } + + +#Config for Material Components +-keep class com.google.android.material.tabs.** { *; } diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/11.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/11.json new file mode 100644 index 000000000..e42a4a719 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/11.json @@ -0,0 +1,1325 @@ +{ + "formatVersion": 1, + "database": { + "version": 11, + "identityHash": "478af7daed6ac4563e71826fb70cc8c8", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `endpoint` TEXT NOT NULL, `loginType` TEXT NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "endpoint", + "columnName": "endpoint", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "loginType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Students_email_symbol_student_id_school_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` INTEGER NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predictedGrade` TEXT NOT NULL, `finalGrade` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predictedGrade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "finalGrade", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"478af7daed6ac4563e71826fb70cc8c8\")" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/12.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/12.json new file mode 100644 index 000000000..32f943554 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/12.json @@ -0,0 +1,1332 @@ +{ + "formatVersion": 1, + "database": { + "version": 12, + "identityHash": "972ad26e6488d9a8239f6bd8597af61d", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `endpoint` TEXT NOT NULL, `loginType` TEXT NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "endpoint", + "columnName": "endpoint", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "loginType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` INTEGER NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predictedGrade` TEXT NOT NULL, `finalGrade` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predictedGrade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "finalGrade", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"972ad26e6488d9a8239f6bd8597af61d\")" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/13.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/13.json new file mode 100644 index 000000000..ab554665e --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/13.json @@ -0,0 +1,1356 @@ +{ + "formatVersion": 1, + "database": { + "version": 13, + "identityHash": "18643bb64804b8268ec9395e3dd55ecb", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `endpoint` TEXT NOT NULL, `loginType` TEXT NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "endpoint", + "columnName": "endpoint", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "loginType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` INTEGER NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predictedGrade` TEXT NOT NULL, `finalGrade` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predictedGrade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "finalGrade", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"18643bb64804b8268ec9395e3dd55ecb\")" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/14.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/14.json new file mode 100644 index 000000000..82b764922 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/14.json @@ -0,0 +1,1386 @@ +{ + "formatVersion": 1, + "database": { + "version": 14, + "identityHash": "b22945c41e7841ff2e6b16af346dde0c", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `endpoint` TEXT NOT NULL, `loginType` TEXT NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "endpoint", + "columnName": "endpoint", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "loginType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` INTEGER NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'b22945c41e7841ff2e6b16af346dde0c')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/15.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/15.json new file mode 100644 index 000000000..6f2d1d1da --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/15.json @@ -0,0 +1,1430 @@ +{ + "formatVersion": 1, + "database": { + "version": 15, + "identityHash": "84b300bf53c7dd70b60a29a842275bb2", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `endpoint` TEXT NOT NULL, `loginType` TEXT NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "endpoint", + "columnName": "endpoint", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "loginType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` INTEGER NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '84b300bf53c7dd70b60a29a842275bb2')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/16.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/16.json new file mode 100644 index 000000000..34df45ebe --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/16.json @@ -0,0 +1,1480 @@ +{ + "formatVersion": 1, + "database": { + "version": 16, + "identityHash": "1eccdcb09adc922713ef67f298ec77a7", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `endpoint` TEXT NOT NULL, `loginType` TEXT NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "endpoint", + "columnName": "endpoint", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "loginType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` INTEGER NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1eccdcb09adc922713ef67f298ec77a7')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/17.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/17.json new file mode 100644 index 000000000..8b777b7bf --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/17.json @@ -0,0 +1,1530 @@ +{ + "formatVersion": 1, + "database": { + "version": 17, + "identityHash": "8bcb3c86f1ddbdf7e20cfa8050525b95", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `endpoint` TEXT NOT NULL, `loginType` TEXT NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "endpoint", + "columnName": "endpoint", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "loginType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` INTEGER NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '8bcb3c86f1ddbdf7e20cfa8050525b95')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/18.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/18.json new file mode 100644 index 000000000..4f7497fe0 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/18.json @@ -0,0 +1,1592 @@ +{ + "formatVersion": 1, + "database": { + "version": 18, + "identityHash": "73b1dcfe0cf84170ba102b2818dd0191", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `endpoint` TEXT NOT NULL, `loginType` TEXT NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "endpoint", + "columnName": "endpoint", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "loginType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` INTEGER NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '73b1dcfe0cf84170ba102b2818dd0191')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/19.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/19.json new file mode 100644 index 000000000..1e4593bb3 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/19.json @@ -0,0 +1,1628 @@ +{ + "formatVersion": 1, + "database": { + "version": 19, + "identityHash": "294f40cebf8314f9776208827240e65f", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '294f40cebf8314f9776208827240e65f')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/20.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/20.json new file mode 100644 index 000000000..925d787aa --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/20.json @@ -0,0 +1,1634 @@ +{ + "formatVersion": 1, + "database": { + "version": 20, + "identityHash": "37a216a7afcea922b66001ca5ed6cf74", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '37a216a7afcea922b66001ca5ed6cf74')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/21.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/21.json new file mode 100644 index 000000000..dfad69f5d --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/21.json @@ -0,0 +1,1652 @@ +{ + "formatVersion": 1, + "database": { + "version": 21, + "identityHash": "f905d8a3ff4718d30ae7413a5a717b1f", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f905d8a3ff4718d30ae7413a5a717b1f')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/22.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/22.json new file mode 100644 index 000000000..1eb8bc19c --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/22.json @@ -0,0 +1,1658 @@ +{ + "formatVersion": 1, + "database": { + "version": 22, + "identityHash": "798956844fdb3f64073921184e3acbfd", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '798956844fdb3f64073921184e3acbfd')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/23.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/23.json new file mode 100644 index 000000000..f75a72acf --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/23.json @@ -0,0 +1,1682 @@ +{ + "formatVersion": 1, + "database": { + "version": 23, + "identityHash": "f2505bf0c76c3ae4567856536d790909", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f2505bf0c76c3ae4567856536d790909')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/24.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/24.json new file mode 100644 index 000000000..17ae7d798 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/24.json @@ -0,0 +1,1732 @@ +{ + "formatVersion": 1, + "database": { + "version": 24, + "identityHash": "9bbf60310b56a855839164e2aae031f9", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '9bbf60310b56a855839164e2aae031f9')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/25.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/25.json new file mode 100644 index 000000000..474824df6 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/25.json @@ -0,0 +1,1744 @@ +{ + "formatVersion": 1, + "database": { + "version": 25, + "identityHash": "d101f5a26a024f62e6fee161e421b882", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd101f5a26a024f62e6fee161e421b882')" + ] + } +} diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/26.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/26.json new file mode 100644 index 000000000..21005f9c6 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/26.json @@ -0,0 +1,1768 @@ +{ + "formatVersion": 1, + "database": { + "version": 26, + "identityHash": "43f8777ffe95a5a2850ee981db3dbe2e", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '43f8777ffe95a5a2850ee981db3dbe2e')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/27.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/27.json new file mode 100644 index 000000000..46b777513 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/27.json @@ -0,0 +1,1774 @@ +{ + "formatVersion": 1, + "database": { + "version": 27, + "identityHash": "b24d2d9662e26dd31f2eea78a297558c", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'b24d2d9662e26dd31f2eea78a297558c')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/28.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/28.json new file mode 100644 index 000000000..c7c4c0331 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/28.json @@ -0,0 +1,1842 @@ +{ + "formatVersion": 1, + "database": { + "version": 28, + "identityHash": "3a449a55ea73fbfbb7973f1f3f834e10", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '3a449a55ea73fbfbb7973f1f3f834e10')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/29.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/29.json new file mode 100644 index 000000000..3e863c578 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/29.json @@ -0,0 +1,1898 @@ +{ + "formatVersion": 1, + "database": { + "version": 29, + "identityHash": "30e4647c7dd84a6ac9b2f9f3ef7d3264", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '30e4647c7dd84a6ac9b2f9f3ef7d3264')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/30.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/30.json new file mode 100644 index 000000000..309f4ac09 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/30.json @@ -0,0 +1,1954 @@ +{ + "formatVersion": 1, + "database": { + "version": 30, + "identityHash": "891980e378373d0a17bd341f9b07cc74", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '891980e378373d0a17bd341f9b07cc74')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/31.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/31.json new file mode 100644 index 000000000..4935a9018 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/31.json @@ -0,0 +1,2136 @@ +{ + "formatVersion": 1, + "database": { + "version": 31, + "identityHash": "d642512ffa5fe81ae9308c9c55612539", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `first_guardian_full_name` TEXT NOT NULL, `first_guardian_kinship` TEXT NOT NULL, `first_guardian_address` TEXT NOT NULL, `first_guardian_phones` TEXT NOT NULL, `first_guardian_email` TEXT NOT NULL, `second_guardian_full_name` TEXT NOT NULL, `second_guardian_kinship` TEXT NOT NULL, `second_guardian_address` TEXT NOT NULL, `second_guardian_phones` TEXT NOT NULL, `second_guardian_email` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd642512ffa5fe81ae9308c9c55612539')" + ] + } +} diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/32.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/32.json new file mode 100644 index 000000000..3621be48a --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/32.json @@ -0,0 +1,2142 @@ +{ + "formatVersion": 1, + "database": { + "version": 32, + "identityHash": "9531cdc8b3f0e62db5ab6ebe66837a28", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nick", + "columnName": "nick", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `first_guardian_full_name` TEXT NOT NULL, `first_guardian_kinship` TEXT NOT NULL, `first_guardian_address` TEXT NOT NULL, `first_guardian_phones` TEXT NOT NULL, `first_guardian_email` TEXT NOT NULL, `second_guardian_full_name` TEXT NOT NULL, `second_guardian_kinship` TEXT NOT NULL, `second_guardian_address` TEXT NOT NULL, `second_guardian_phones` TEXT NOT NULL, `second_guardian_email` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '9531cdc8b3f0e62db5ab6ebe66837a28')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/33.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/33.json new file mode 100644 index 000000000..255c196e5 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/33.json @@ -0,0 +1,2142 @@ +{ + "formatVersion": 1, + "database": { + "version": 33, + "identityHash": "c024cc4e19e009a03303e2bfe5c34b48", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nick", + "columnName": "nick", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c024cc4e19e009a03303e2bfe5c34b48')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/34.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/34.json new file mode 100644 index 000000000..6a56ac64e --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/34.json @@ -0,0 +1,2142 @@ +{ + "formatVersion": 1, + "database": { + "version": 34, + "identityHash": "c024cc4e19e009a03303e2bfe5c34b48", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nick", + "columnName": "nick", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c024cc4e19e009a03303e2bfe5c34b48')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/35.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/35.json new file mode 100644 index 000000000..f166b2d72 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/35.json @@ -0,0 +1,2148 @@ +{ + "formatVersion": 1, + "database": { + "version": 35, + "identityHash": "15fb91ec180fe60bf400cfa729c3418d", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nick", + "columnName": "nick", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "avatarColor", + "columnName": "avatar_color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '15fb91ec180fe60bf400cfa729c3418d')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/36.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/36.json new file mode 100644 index 000000000..2fae53021 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/36.json @@ -0,0 +1,2160 @@ +{ + "formatVersion": 1, + "database": { + "version": 36, + "identityHash": "a217c2fc4e756a014f203e7499f88a90", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nick", + "columnName": "nick", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "avatarColor", + "columnName": "avatar_color", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a217c2fc4e756a014f203e7499f88a90')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/37.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/37.json new file mode 100644 index 000000000..1aaf96f6c --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/37.json @@ -0,0 +1,2204 @@ +{ + "formatVersion": 1, + "database": { + "version": 37, + "identityHash": "98169e620b79a6633f80b5bd32162ed2", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nick", + "columnName": "nick", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "avatarColor", + "columnName": "avatar_color", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableHeaders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '98169e620b79a6633f80b5bd32162ed2')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/38.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/38.json new file mode 100644 index 000000000..1625a9f61 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/38.json @@ -0,0 +1,2248 @@ +{ + "formatVersion": 1, + "database": { + "version": 38, + "identityHash": "02c6cf89e47f5ee42ee04880eb91cff2", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nick", + "columnName": "nick", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "avatarColor", + "columnName": "avatar_color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableHeaders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SchoolAnnouncements", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '02c6cf89e47f5ee42ee04880eb91cff2')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/39.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/39.json new file mode 100644 index 000000000..d93351ac8 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/39.json @@ -0,0 +1,2260 @@ +{ + "formatVersion": 1, + "database": { + "version": 39, + "identityHash": "73b2b5a236cacd91df734ec4141bc590", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nick", + "columnName": "nick", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "avatarColor", + "columnName": "avatar_color", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableHeaders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SchoolAnnouncements", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '73b2b5a236cacd91df734ec4141bc590')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/40.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/40.json new file mode 100644 index 000000000..362c7f0e0 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/40.json @@ -0,0 +1,2316 @@ +{ + "formatVersion": 1, + "database": { + "version": 40, + "identityHash": "e2fba6244951713b4e9b217adc5d1a23", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nick", + "columnName": "nick", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "avatarColor", + "columnName": "avatar_color", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableHeaders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SchoolAnnouncements", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notifications", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'e2fba6244951713b4e9b217adc5d1a23')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/41.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/41.json new file mode 100644 index 000000000..9d008060a --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/41.json @@ -0,0 +1,2322 @@ +{ + "formatVersion": 1, + "database": { + "version": 41, + "identityHash": "d9ce44a78495a358606612bd91603c0f", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nick", + "columnName": "nick", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "avatarColor", + "columnName": "avatar_color", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableHeaders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SchoolAnnouncements", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notifications", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd9ce44a78495a358606612bd91603c0f')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/42.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/42.json new file mode 100644 index 000000000..a5faa57b7 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/42.json @@ -0,0 +1,2396 @@ +{ + "formatVersion": 1, + "database": { + "version": 42, + "identityHash": "5c8b7f9409294ecdebf9f74a44f8e883", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nick", + "columnName": "nick", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "avatarColor", + "columnName": "avatar_color", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableHeaders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SchoolAnnouncements", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notifications", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AdminMessages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "versionMin", + "columnName": "version_name", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMax", + "columnName": "version_max", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "targetRegisterHost", + "columnName": "target_register_host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "targetFlavor", + "columnName": "target_flavor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "destinationUrl", + "columnName": "destination_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "priority", + "columnName": "priority", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5c8b7f9409294ecdebf9f74a44f8e883')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/43.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/43.json new file mode 100644 index 000000000..22c0d8125 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/43.json @@ -0,0 +1,2408 @@ +{ + "formatVersion": 1, + "database": { + "version": 43, + "identityHash": "66946510bb620ae82686a5a1a31aba18", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nick", + "columnName": "nick", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "avatarColor", + "columnName": "avatar_color", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableHeaders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SchoolAnnouncements", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notifications", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AdminMessages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "versionMin", + "columnName": "version_name", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMax", + "columnName": "version_max", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "targetRegisterHost", + "columnName": "target_register_host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "targetFlavor", + "columnName": "target_flavor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "destinationUrl", + "columnName": "destination_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "priority", + "columnName": "priority", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '66946510bb620ae82686a5a1a31aba18')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/44.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/44.json new file mode 100644 index 000000000..4dc9834d2 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/44.json @@ -0,0 +1,2414 @@ +{ + "formatVersion": 1, + "database": { + "version": 44, + "identityHash": "e3437dc0b229a325bbeb3e964a500530", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nick", + "columnName": "nick", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "avatarColor", + "columnName": "avatar_color", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableHeaders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SchoolAnnouncements", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notifications", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AdminMessages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "versionMin", + "columnName": "version_name", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMax", + "columnName": "version_max", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "targetRegisterHost", + "columnName": "target_register_host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "targetFlavor", + "columnName": "target_flavor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "destinationUrl", + "columnName": "destination_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "priority", + "columnName": "priority", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDismissible", + "columnName": "is_dismissible", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'e3437dc0b229a325bbeb3e964a500530')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/45.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/45.json new file mode 100644 index 000000000..57f3d431d --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/45.json @@ -0,0 +1,2430 @@ +{ + "formatVersion": 1, + "database": { + "version": 45, + "identityHash": "f310243440ca00cbc35e62ebaca5c7d8", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nick", + "columnName": "nick", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "avatarColor", + "columnName": "avatar_color", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "repeatId", + "columnName": "repeat_id", + "affinity": "BLOB", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableHeaders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SchoolAnnouncements", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notifications", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AdminMessages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "versionMin", + "columnName": "version_name", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMax", + "columnName": "version_max", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "targetRegisterHost", + "columnName": "target_register_host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "targetFlavor", + "columnName": "target_flavor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "destinationUrl", + "columnName": "destination_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "priority", + "columnName": "priority", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDismissible", + "columnName": "is_dismissible", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f310243440ca00cbc35e62ebaca5c7d8')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/46.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/46.json new file mode 100644 index 000000000..04518141c --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/46.json @@ -0,0 +1,2430 @@ +{ + "formatVersion": 1, + "database": { + "version": 46, + "identityHash": "f310243440ca00cbc35e62ebaca5c7d8", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nick", + "columnName": "nick", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "avatarColor", + "columnName": "avatar_color", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "repeatId", + "columnName": "repeat_id", + "affinity": "BLOB", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableHeaders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SchoolAnnouncements", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notifications", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AdminMessages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "versionMin", + "columnName": "version_name", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMax", + "columnName": "version_max", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "targetRegisterHost", + "columnName": "target_register_host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "targetFlavor", + "columnName": "target_flavor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "destinationUrl", + "columnName": "destination_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "priority", + "columnName": "priority", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDismissible", + "columnName": "is_dismissible", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f310243440ca00cbc35e62ebaca5c7d8')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/47.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/47.json new file mode 100644 index 000000000..3f8291eac --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/47.json @@ -0,0 +1,2438 @@ +{ + "formatVersion": 1, + "database": { + "version": 47, + "identityHash": "ac88c80d4bb923b22f22ce4f91521306", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nick", + "columnName": "nick", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "avatarColor", + "columnName": "avatar_color", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "kindergartenDiaryId", + "columnName": "kindergarten_diary_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "kindergarten_diary_id", + "semester_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "repeatId", + "columnName": "repeat_id", + "affinity": "BLOB", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableHeaders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SchoolAnnouncements", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notifications", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AdminMessages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "versionMin", + "columnName": "version_name", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMax", + "columnName": "version_max", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "targetRegisterHost", + "columnName": "target_register_host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "targetFlavor", + "columnName": "target_flavor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "destinationUrl", + "columnName": "destination_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "priority", + "columnName": "priority", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDismissible", + "columnName": "is_dismissible", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ac88c80d4bb923b22f22ce4f91521306')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/48.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/48.json new file mode 100644 index 000000000..1c11aae91 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/48.json @@ -0,0 +1,2445 @@ +{ + "formatVersion": 1, + "database": { + "version": 48, + "identityHash": "95751b933ad9f835ffc1805f4ef71bdb", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nick", + "columnName": "nick", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "avatarColor", + "columnName": "avatar_color", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "kindergartenDiaryId", + "columnName": "kindergarten_diary_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "kindergarten_diary_id", + "semester_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "repeatId", + "columnName": "repeat_id", + "affinity": "BLOB", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableHeaders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SchoolAnnouncements", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notifications", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "destination", + "columnName": "destination", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'" + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AdminMessages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "versionMin", + "columnName": "version_name", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMax", + "columnName": "version_max", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "targetRegisterHost", + "columnName": "target_register_host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "targetFlavor", + "columnName": "target_flavor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "destinationUrl", + "columnName": "destination_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "priority", + "columnName": "priority", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDismissible", + "columnName": "is_dismissible", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '95751b933ad9f835ffc1805f4ef71bdb')" + ] + } +} \ No newline at end of file diff --git a/android-sonarqube.gradle b/app/sonarqube.gradle similarity index 73% rename from android-sonarqube.gradle rename to app/sonarqube.gradle index b9284eeac..3dad10344 100644 --- a/android-sonarqube.gradle +++ b/app/sonarqube.gradle @@ -4,16 +4,13 @@ sonarqube { //noinspection GroovyAssignabilityCheck properties { - def files = fileTree("${rootProject.projectDir}/api/build/libs/").filter { it.isFile() }.files.name - def libraries = project.android.sdkDirectory.getPath() + "/platforms/android-27/android.jar," + - "${project.rootDir}/api/build/libs/" + files[0] + def libraries = project.android.sdkDirectory.getPath() + "/platforms/android-28/android.jar" - property "sonar.projectName", GROUP_ID + ":app" - property "sonar.projectKey", GROUP_ID + ":app" + property "sonar.projectName", "io.github.wulkanowy:app" + property "sonar.projectKey", "io.github.wulkanowy:app" property "sonar.sources", "src/main/java" - property "sonar.exclusions", "build/**,**/*.png,*.iml, **/*generated*," + - "src/**/entities/*.java, src/androidTest/**/entities/*.java" + property "sonar.exclusions", "build/**,**/*.png,*.iml, **/*generated*," property "sonar.import_unknown_files", true // Defines where the java files are @@ -32,5 +29,6 @@ sonarqube { property "sonar.java.coveragePlugin", "jacoco" property "sonar.android.lint.report", "build/reports/lint-results.xml" property "sonar.jacoco.reportPaths", fileTree(dir: project.projectDir, includes: ['**/*.exec', '**/*.ec']) + property "sonar.coverage.jacoco.xmlReportPaths", "build/reports/jacocoTestReport/jacocoTestReport.xml" } } diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AccountTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AccountTest.java deleted file mode 100644 index e5330b621..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AccountTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.test.AbstractDaoTestLongPk; - -public class AccountTest extends AbstractDaoTestLongPk { - - public AccountTest() { - super(AccountDao.class); - } - - @Override - protected Account createEntity(Long key) { - Account entity = new Account(); - entity.setId(key); - return entity; - } - -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLessonTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLessonTest.java deleted file mode 100644 index 492d642d1..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLessonTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.test.AbstractDaoTestLongPk; - -import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; -import io.github.wulkanowy.data.db.dao.entities.AttendanceLessonDao; - -public class AttendanceLessonTest extends AbstractDaoTestLongPk { - - public AttendanceLessonTest() { - super(AttendanceLessonDao.class); - } - - @Override - protected AttendanceLesson createEntity(Long key) { - AttendanceLesson entity = new AttendanceLesson(); - entity.setId(key); - entity.setIsPresence(false); - entity.setIsAbsenceUnexcused(false); - entity.setIsAbsenceExcused(false); - entity.setIsUnexcusedLateness(false); - entity.setIsAbsenceForSchoolReasons(false); - entity.setIsExcusedLateness(false); - entity.setIsExemption(false); - return entity; - } - -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/DayTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/DayTest.java deleted file mode 100644 index 34c4c4c5e..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/DayTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.test.AbstractDaoTestLongPk; - -public class DayTest extends AbstractDaoTestLongPk { - - public DayTest() { - super(DayDao.class); - } - - @Override - protected Day createEntity(Long key) { - Day entity = new Day(); - entity.setId(key); - entity.setIsFreeDay(false); - return entity; - } - -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/GradeTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/GradeTest.java deleted file mode 100644 index ea0265591..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/GradeTest.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.test.AbstractDaoTestLongPk; - -public class GradeTest extends AbstractDaoTestLongPk { - - public GradeTest() { - super(GradeDao.class); - } - - @Override - protected Grade createEntity(Long key) { - Grade entity = new Grade(); - entity.setId(key); - entity.setIsNew(false); - entity.setRead(false); - return entity; - } - -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SubjectTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SubjectTest.java deleted file mode 100644 index 81a2e724f..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SubjectTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.test.AbstractDaoTestLongPk; - -public class SubjectTest extends AbstractDaoTestLongPk { - - public SubjectTest() { - super(SubjectDao.class); - } - - @Override - protected Subject createEntity(Long key) { - Subject entity = new Subject(); - entity.setId(key); - return entity; - } - -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/TimetableLessonTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/TimetableLessonTest.java deleted file mode 100644 index b215b6bef..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/TimetableLessonTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.test.AbstractDaoTestLongPk; - -import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; -import io.github.wulkanowy.data.db.dao.entities.TimetableLessonDao; - -public class TimetableLessonTest extends AbstractDaoTestLongPk { - - public TimetableLessonTest() { - super(TimetableLessonDao.class); - } - - @Override - protected TimetableLesson createEntity(Long key) { - TimetableLesson entity = new TimetableLesson(); - entity.setId(key); - entity.setIsEmpty(false); - entity.setIsDivisionIntoGroups(false); - entity.setIsPlanning(false); - entity.setIsRealized(false); - entity.setIsMovedOrCanceled(false); - entity.setIsNewMovedInOrChanged(false); - return entity; - } - -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/WeekTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/WeekTest.java deleted file mode 100644 index 86e7a8be5..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/WeekTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.test.AbstractDaoTestLongPk; - -public class WeekTest extends AbstractDaoTestLongPk { - - public WeekTest() { - super(WeekDao.class); - } - - @Override - protected Week createEntity(Long key) { - Week entity = new Week(); - entity.setId(key); - return entity; - } - -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/utils/security/ScramblerTest.java b/app/src/androidTest/java/io/github/wulkanowy/utils/security/ScramblerTest.java deleted file mode 100644 index 2fd1904ba..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/utils/security/ScramblerTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.utils.security; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SdkSuppress; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; - -@SmallTest -@RunWith(AndroidJUnit4.class) -public class ScramblerTest { - - @Test - @SdkSuppress(minSdkVersion = 18) - public void encryptDecryptTest() throws Exception { - Context targetContext = InstrumentationRegistry.getTargetContext(); - - Assert.assertEquals("PASS", Scrambler.decrypt("TEST", - Scrambler.encrypt("TEST", "PASS", targetContext))); - } -} \ No newline at end of file diff --git a/app/src/androidTest/java/io/github/wulkanowy/utils/security/ScramblerTest.kt b/app/src/androidTest/java/io/github/wulkanowy/utils/security/ScramblerTest.kt new file mode 100644 index 000000000..0c47e6bb6 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/utils/security/ScramblerTest.kt @@ -0,0 +1,47 @@ +package io.github.wulkanowy.utils.security + +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SdkSuppress +import androidx.test.filters.SmallTest +import org.junit.Test +import org.junit.runner.RunWith +import java.security.KeyStore +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class ScramblerTest { + + @Test + fun encryptDecryptTest() { + assertEquals("TEST", decrypt(encrypt("TEST", + ApplicationProvider.getApplicationContext()))) + } + + @Test + fun emptyTextEncryptTest() { + assertFailsWith { + decrypt("") + } + + assertFailsWith { + encrypt("", ApplicationProvider.getApplicationContext()) + } + } + + @Test + @SdkSuppress(minSdkVersion = 18) + fun emptyKeyStoreTest() { + val text = encrypt("test", ApplicationProvider.getApplicationContext()) + + val keyStore = KeyStore.getInstance("AndroidKeyStore") + keyStore.load(null) + keyStore.deleteEntry("wulkanowy_password") + + assertFailsWith { + decrypt(text) + } + } +} diff --git a/app/src/debug/agconnect-services.json b/app/src/debug/agconnect-services.json new file mode 100644 index 000000000..48192df01 --- /dev/null +++ b/app/src/debug/agconnect-services.json @@ -0,0 +1,33 @@ +{ + "agcgw":{ + "backurl":"connect-dre.dbankcloud.cn", + "url":"connect-dre.hispace.hicloud.com" + }, + "client":{ + "cp_id":"890048000024105546", + "product_id":"", + "client_id":"", + "client_secret":"", + "app_id":"101440411", + "package_name":"io.github.wulkanowy.dev", + "api_key":"" + }, + "service":{ + "analytics":{ + "collector_url":"datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn", + "resource_id":"p1", + "channel_id":"" + }, + "search":{ + "url":"https://search-dre.cloud.huawei.com" + }, + "cloudstorage":{ + "storage_url":"https://ops-dre.agcstorage.link" + }, + "ml":{ + "mlservice_url":"ml-api-dre.ai.dbankcloud.com,ml-api-dre.ai.dbankcloud.cn" + } + }, + "region":"DE", + "configuration_version":"1.0" +} diff --git a/app/src/debug/google-services.json b/app/src/debug/google-services.json new file mode 100644 index 000000000..e9303986b --- /dev/null +++ b/app/src/debug/google-services.json @@ -0,0 +1,42 @@ +{ + "project_info": { + "project_number": "", + "firebase_url": "", + "project_id": "", + "storage_bucket": "" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:1091101852179:android:b558a25f65d088b1", + "android_client_info": { + "package_name": "io.github.wulkanowy.dev" + } + }, + "oauth_client": [ + { + "client_id": "", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 1, + "other_platform_oauth_client": [] + }, + "ads_service": { + "status": 2 + } + } + } + ], + "configuration_version": "1" +} diff --git a/app/src/debug/google-services.json.gpg b/app/src/debug/google-services.json.gpg new file mode 100644 index 000000000..736f7906d Binary files /dev/null and b/app/src/debug/google-services.json.gpg differ diff --git a/app/src/debug/res/drawable/ic_launcher_foreground_dev.xml b/app/src/debug/res/drawable/ic_launcher_foreground_dev.xml new file mode 100644 index 000000000..799ea0374 --- /dev/null +++ b/app/src/debug/res/drawable/ic_launcher_foreground_dev.xml @@ -0,0 +1,34 @@ + + + + + + + + diff --git a/app/src/debug/res/drawable/ic_stat_grade.xml b/app/src/debug/res/drawable/ic_stat_grade.xml new file mode 100644 index 000000000..832eba838 --- /dev/null +++ b/app/src/debug/res/drawable/ic_stat_grade.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/debug/res/drawable/ic_stat_luckynumber.xml b/app/src/debug/res/drawable/ic_stat_luckynumber.xml new file mode 100644 index 000000000..4f3eb98e1 --- /dev/null +++ b/app/src/debug/res/drawable/ic_stat_luckynumber.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/debug/res/drawable/ic_stat_message.xml b/app/src/debug/res/drawable/ic_stat_message.xml new file mode 100644 index 000000000..8fe12de45 --- /dev/null +++ b/app/src/debug/res/drawable/ic_stat_message.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/debug/res/drawable/ic_stat_note.xml b/app/src/debug/res/drawable/ic_stat_note.xml new file mode 100644 index 000000000..d30f22336 --- /dev/null +++ b/app/src/debug/res/drawable/ic_stat_note.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/debug/res/drawable/ic_stat_timetable.xml b/app/src/debug/res/drawable/ic_stat_timetable.xml new file mode 100644 index 000000000..ac99d4a89 --- /dev/null +++ b/app/src/debug/res/drawable/ic_stat_timetable.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 000000000..7dbec2cb9 --- /dev/null +++ b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 000000000..7dbec2cb9 --- /dev/null +++ b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/debug/res/mipmap-hdpi/ic_launcher.png b/app/src/debug/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..5b688d7cb Binary files /dev/null and b/app/src/debug/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 000000000..81e723ecc Binary files /dev/null and b/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/debug/res/mipmap-mdpi/ic_launcher.png b/app/src/debug/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..48b13240c Binary files /dev/null and b/app/src/debug/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 000000000..394b57076 Binary files /dev/null and b/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/debug/res/mipmap-xhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..ff8bfa3e9 Binary files /dev/null and b/app/src/debug/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 000000000..365b4d663 Binary files /dev/null and b/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..96be1ed4f Binary files /dev/null and b/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..463c089b3 Binary files /dev/null and b/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..57c7416f1 Binary files /dev/null and b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..53d6f5bbd Binary files /dev/null and b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/debug/res/values-pl/strings.xml b/app/src/debug/res/values-pl/strings.xml deleted file mode 100644 index c90641ddb..000000000 --- a/app/src/debug/res/values-pl/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - Wulkanowy DEV - diff --git a/app/src/debug/res/values/preferences_defaults.xml b/app/src/debug/res/values/preferences_defaults.xml new file mode 100644 index 000000000..f5a12464f --- /dev/null +++ b/app/src/debug/res/values/preferences_defaults.xml @@ -0,0 +1,4 @@ + + + true + diff --git a/app/src/debug/res/values/strings.xml b/app/src/debug/res/values/strings.xml deleted file mode 100644 index c90641ddb..000000000 --- a/app/src/debug/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - Wulkanowy DEV - diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt new file mode 100644 index 000000000..3bf7e1693 --- /dev/null +++ b/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt @@ -0,0 +1,22 @@ +package io.github.wulkanowy.utils + +import android.app.Activity +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +@Suppress("UNUSED_PARAMETER") +class AnalyticsHelper @Inject constructor() { + + fun logEvent(name: String, vararg params: Pair) { + // do nothing + } + + fun setCurrentScreen(activity: Activity, name: String?) { + // do nothing + } + + fun popCurrentScreen(name: String?) { + // do nothing + } +} diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/CrashLogUtils.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/CrashLogUtils.kt new file mode 100644 index 000000000..5d58270d4 --- /dev/null +++ b/app/src/fdroid/java/io/github/wulkanowy/utils/CrashLogUtils.kt @@ -0,0 +1,13 @@ +@file:Suppress("UNUSED_PARAMETER") + +package io.github.wulkanowy.utils + +import timber.log.Timber + +open class TimberTreeNoOp : Timber.Tree() { + override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {} +} + +class CrashLogTree : TimberTreeNoOp() + +class CrashLogExceptionTree : TimberTreeNoOp() diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/InAppReviewHelper.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/InAppReviewHelper.kt new file mode 100644 index 000000000..8615d975a --- /dev/null +++ b/app/src/fdroid/java/io/github/wulkanowy/utils/InAppReviewHelper.kt @@ -0,0 +1,18 @@ +package io.github.wulkanowy.utils + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.ui.modules.main.MainActivity +import javax.inject.Singleton +import javax.inject.Inject + +@Suppress("UNUSED_PARAMETER", "unused") +@Singleton +class InAppReviewHelper @Inject constructor( + @ApplicationContext private val context: Context +) { + + fun showInAppReview(activity: MainActivity) { + // do nothing + } +} \ No newline at end of file diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/UpdateHelper.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/UpdateHelper.kt new file mode 100644 index 000000000..3abab9629 --- /dev/null +++ b/app/src/fdroid/java/io/github/wulkanowy/utils/UpdateHelper.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.utils + +import android.app.Activity +import android.view.View +import javax.inject.Inject + +@Suppress("UNUSED_PARAMETER") +class UpdateHelper @Inject constructor() { + + lateinit var messageContainer: View + + fun checkAndInstallUpdates(activity: Activity) {} + + fun onActivityResult(requestCode: Int, resultCode: Int) {} + + fun onResume(activity: Activity) {} +} diff --git a/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt b/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt new file mode 100644 index 000000000..5d33825f1 --- /dev/null +++ b/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt @@ -0,0 +1,39 @@ +package io.github.wulkanowy.utils + +import android.app.Activity +import android.content.Context +import android.os.Bundle +import com.huawei.hms.analytics.HiAnalytics +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AnalyticsHelper @Inject constructor( + @ApplicationContext private val context: Context +) { + + private val analytics by lazy { HiAnalytics.getInstance(context) } + + fun logEvent(name: String, vararg params: Pair) { + Bundle().apply { + params.forEach { + if (it.second == null) return@forEach + when (it.second) { + is String, is String? -> putString(it.first, it.second as String) + is Int, is Int? -> putInt(it.first, it.second as Int) + is Boolean, is Boolean? -> putBoolean(it.first, it.second as Boolean) + } + } + analytics.onEvent(name, this) + } + } + + fun setCurrentScreen(activity: Activity, name: String?) { + analytics.pageStart(name, activity::class.simpleName) + } + + fun popCurrentScreen(name: String?) { + analytics.pageEnd(name) + } +} diff --git a/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt b/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt new file mode 100644 index 000000000..b0c34f413 --- /dev/null +++ b/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt @@ -0,0 +1,37 @@ +package io.github.wulkanowy.utils + +import android.util.Log +import com.huawei.agconnect.crash.AGConnectCrash +import fr.bipi.tressence.base.FormatterPriorityTree + +class CrashLogTree : FormatterPriorityTree(Log.VERBOSE) { + + private val connectCrash by lazy { AGConnectCrash.getInstance() } + + override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { + if (skipLog(priority, tag, message, t)) return + + connectCrash.log(format(priority, tag, message)) + } +} + +class CrashLogExceptionTree : FormatterPriorityTree(Log.ERROR, ExceptionFilter) { + + private val connectCrash by lazy { AGConnectCrash.getInstance() } + + override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { + if (skipLog(priority, tag, message, t)) return + + // Disabled due to a bug in the Huawei library + + /*connectCrash.setCustomKey("priority", priority) + connectCrash.setCustomKey("tag", tag.orEmpty()) + connectCrash.setCustomKey("message", message) + + if (t != null) { + connectCrash.recordException(t) + } else { + connectCrash.recordException(StackTraceRecorder(format(priority, tag, message))) + }*/ + } +} diff --git a/app/src/hms/java/io/github/wulkanowy/utils/InAppReviewHelper.kt b/app/src/hms/java/io/github/wulkanowy/utils/InAppReviewHelper.kt new file mode 100644 index 000000000..fb9bcae6c --- /dev/null +++ b/app/src/hms/java/io/github/wulkanowy/utils/InAppReviewHelper.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.utils + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.ui.modules.main.MainActivity +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class InAppReviewHelper @Inject constructor( + @ApplicationContext private val context: Context +) { + + fun showInAppReview(activity: MainActivity) { + // do nothing + } +} \ No newline at end of file diff --git a/app/src/hms/java/io/github/wulkanowy/utils/UpdateHelper.kt b/app/src/hms/java/io/github/wulkanowy/utils/UpdateHelper.kt new file mode 100644 index 000000000..3abab9629 --- /dev/null +++ b/app/src/hms/java/io/github/wulkanowy/utils/UpdateHelper.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.utils + +import android.app.Activity +import android.view.View +import javax.inject.Inject + +@Suppress("UNUSED_PARAMETER") +class UpdateHelper @Inject constructor() { + + lateinit var messageContainer: View + + fun checkAndInstallUpdates(activity: Activity) {} + + fun onActivityResult(requestCode: Int, resultCode: Int) {} + + fun onResume(activity: Activity) {} +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f1733681e..72fee08a7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,54 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + android:supportsRtl="false" + android:theme="@style/WulkanowyTheme" + tools:ignore="GoogleAppIndexingWarning,UnusedAttribute"> + android:name=".ui.modules.splash.SplashActivity" + android:exported="true" + android:screenOrientation="portrait" + android:theme="@style/WulkanowyTheme.SplashScreen" + tools:ignore="LockedOrientationActivity"> - + android:label="@string/main_title" + android:theme="@style/WulkanowyTheme.NoActionBar" + android:windowSoftInputMode="adjustPan" /> + + + + + + + + + + + + android:name=".services.widgets.TimetableWidgetService" + android:permission="android.permission.BIND_REMOTEVIEWS" /> + - + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/assets/contributors.json b/app/src/main/assets/contributors.json new file mode 100644 index 000000000..b2849931a --- /dev/null +++ b/app/src/main/assets/contributors.json @@ -0,0 +1,54 @@ +[ + { + "displayName": "Mikołaj Pich", + "githubUsername": "mklkj" + }, + { + "displayName": "Rafał Borcz", + "githubUsername": "Faierbel" + }, + { + "displayName": "Dominik Korsa", + "githubUsername": "dominik-korsa" + }, + { + "displayName": "Kacper Ziubryniewicz", + "githubUsername": "kapi2289" + }, + { + "displayName": "doteq", + "githubUsername": "doteq" + }, + { + "displayName": "Paweł Krzyś", + "githubUsername": "pavuloff" + }, + { + "displayName": "Piotr Romanowski", + "githubUsername": "v0idzz" + }, + { + "displayName": "Dinolek", + "githubUsername": "Dinolek" + }, + { + "displayName": "Mateusz Idziejczak", + "githubUsername": "Luncenok" + }, + { + "displayName": "Daniel Olczyk", + "githubUsername": "MRmlik12" + }, + { + "displayName": "Damian Czupryn", + "githubUsername": "Daxxxis" + }, + { + "displayName": "Kamil Studziński", + "githubUsername": "studzinskik" + }, + { + "displayName": "Tomasz F.", + "githubUsername": "Pengwius" + } +] diff --git a/app/src/main/assets/message-print-page.html b/app/src/main/assets/message-print-page.html new file mode 100644 index 000000000..8da7dec6e --- /dev/null +++ b/app/src/main/assets/message-print-page.html @@ -0,0 +1,94 @@ + + + + + %SUBJECT% | Wulkanowy + + + +

%SUBJECT%

+
+
+ %INFO% +
+ +
+
+

Treść wiadomości

+ %CONTENT% +
+ + diff --git a/app/src/main/assets/wulkanowy-logo-black.svg b/app/src/main/assets/wulkanowy-logo-black.svg new file mode 100644 index 000000000..9bfbe2c02 --- /dev/null +++ b/app/src/main/assets/wulkanowy-logo-black.svg @@ -0,0 +1,74 @@ + +image/svg+xml \ No newline at end of file diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png deleted file mode 100644 index 7cad5c2f4..000000000 Binary files a/app/src/main/ic_launcher-web.png and /dev/null differ diff --git a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.java b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.java deleted file mode 100644 index 8e5318e1a..000000000 --- a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.java +++ /dev/null @@ -1,71 +0,0 @@ -package io.github.wulkanowy; - -import android.app.Application; - -import com.crashlytics.android.Crashlytics; -import com.crashlytics.android.core.CrashlyticsCore; - -import org.greenrobot.greendao.query.QueryBuilder; - -import javax.inject.Inject; - -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.utils.Log; -import io.fabric.sdk.android.Fabric; -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.di.component.ApplicationComponent; -import io.github.wulkanowy.di.component.DaggerApplicationComponent; -import io.github.wulkanowy.di.modules.ApplicationModule; -import io.github.wulkanowy.utils.LogUtils; - -public class WulkanowyApp extends Application { - - protected ApplicationComponent applicationComponent; - - @Inject - RepositoryContract repository; - - @Override - public void onCreate() { - super.onCreate(); - applicationComponent = DaggerApplicationComponent - .builder() - .applicationModule(new ApplicationModule(this)) - .build(); - applicationComponent.inject(this); - - if (BuildConfig.DEBUG) { - enableDebugLog(); - } - initializeFabric(); - initializeUserSession(); - } - - private void initializeUserSession() { - if (repository.getCurrentUserId() != 0) { - try { - repository.initLastUser(); - } catch (Exception e) { - LogUtils.error("An error occurred when the application was started", e); - } - } - } - - private void enableDebugLog() { - QueryBuilder.LOG_VALUES = true; - FlexibleAdapter.enableLogs(Log.Level.DEBUG); - } - - private void initializeFabric() { - Fabric.with(new Fabric.Builder(this) - .kits(new Crashlytics.Builder() - .core(new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build()) - .build()) - .debuggable(BuildConfig.DEBUG) - .build()); - } - - public ApplicationComponent getApplicationComponent() { - return applicationComponent; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt new file mode 100644 index 000000000..b5103e3ec --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt @@ -0,0 +1,74 @@ +package io.github.wulkanowy + +import android.app.Application +import android.util.Log.* +import androidx.hilt.work.HiltWorkerFactory +import androidx.work.Configuration +import com.yariksoffice.lingver.Lingver +import dagger.hilt.android.HiltAndroidApp +import fr.bipi.tressence.file.FileLoggerTree +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.ui.base.ThemeManager +import io.github.wulkanowy.utils.* +import timber.log.Timber +import javax.inject.Inject + +@HiltAndroidApp +class WulkanowyApp : Application(), Configuration.Provider { + + @Inject + lateinit var workerFactory: HiltWorkerFactory + + @Inject + lateinit var themeManager: ThemeManager + + @Inject + lateinit var appInfo: AppInfo + + @Inject + lateinit var preferencesRepository: PreferencesRepository + + @Inject + lateinit var analyticsHelper: AnalyticsHelper + + override fun onCreate() { + super.onCreate() + initializeAppLanguage() + themeManager.applyDefaultTheme() + initLogging() + } + + private fun initLogging() { + if (appInfo.isDebug) { + Timber.plant(DebugLogTree()) + Timber.plant( + FileLoggerTree.Builder() + .withFileName("wulkanowy.%g.log") + .withDirName(applicationContext.filesDir.absolutePath) + .withFileLimit(10) + .withMinPriority(DEBUG) + .build() + ) + } else { + Timber.plant(CrashLogExceptionTree()) + Timber.plant(CrashLogTree()) + } + registerActivityLifecycleCallbacks(ActivityLifecycleLogger()) + } + + private fun initializeAppLanguage() { + Lingver.init(this) + + if (preferencesRepository.appLanguage == "system") { + Lingver.getInstance().setFollowSystemLocale(this) + analyticsHelper.logEvent("language", "startup" to appInfo.systemLanguage) + } else { + analyticsHelper.logEvent("language", "startup" to preferencesRepository.appLanguage) + } + } + + override fun getWorkManagerConfiguration() = Configuration.Builder() + .setWorkerFactory(workerFactory) + .setMinimumLoggingLevel(if (appInfo.isDebug) VERBOSE else INFO) + .build() +} diff --git a/app/src/main/java/io/github/wulkanowy/data/DataModule.kt b/app/src/main/java/io/github/wulkanowy/data/DataModule.kt new file mode 100644 index 000000000..cac3ffc23 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/DataModule.kt @@ -0,0 +1,245 @@ +package io.github.wulkanowy.data + +import android.content.Context +import android.content.SharedPreferences +import androidx.preference.PreferenceManager +import com.chuckerteam.chucker.api.ChuckerCollector +import com.chuckerteam.chucker.api.ChuckerInterceptor +import com.chuckerteam.chucker.api.RetentionManager +import com.fredporciuncula.flow.preferences.FlowSharedPreferences +import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import io.github.wulkanowy.data.api.AdminMessageService +import io.github.wulkanowy.data.db.AppDatabase +import io.github.wulkanowy.data.db.SharedPrefProvider +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.AppInfo +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.Json +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.create +import timber.log.Timber +import java.util.concurrent.TimeUnit +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal class DataModule { + + @Singleton + @Provides + fun provideSdk(chuckerInterceptor: ChuckerInterceptor) = + Sdk().apply { + androidVersion = android.os.Build.VERSION.RELEASE + buildTag = android.os.Build.MODEL + setSimpleHttpLogger { Timber.d(it) } + + // for debug only + addInterceptor(chuckerInterceptor, network = true) + } + + @Singleton + @Provides + fun provideChuckerCollector( + @ApplicationContext context: Context, + prefRepository: PreferencesRepository + ) = ChuckerCollector( + context = context, + showNotification = prefRepository.isDebugNotificationEnable, + retentionPeriod = RetentionManager.Period.ONE_HOUR + ) + + @Singleton + @Provides + fun provideChuckerInterceptor( + @ApplicationContext context: Context, + chuckerCollector: ChuckerCollector + ) = ChuckerInterceptor.Builder(context) + .collector(chuckerCollector) + .alwaysReadResponseBody(true) + .build() + + @Singleton + @Provides + fun provideOkHttpClient(chuckerInterceptor: ChuckerInterceptor): OkHttpClient = + OkHttpClient.Builder() + .addNetworkInterceptor(chuckerInterceptor) + .addInterceptor(HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.BASIC + }) + .connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .build() + + @OptIn(ExperimentalSerializationApi::class) + @Singleton + @Provides + fun provideRetrofit( + okHttpClient: OkHttpClient, + json: Json, + appInfo: AppInfo + ): Retrofit = Retrofit.Builder() + .baseUrl(appInfo.messagesBaseUrl) + .client(okHttpClient) + .addConverterFactory(json.asConverterFactory("application/json".toMediaType())) + .build() + + @Singleton + @Provides + fun provideAdminMessageService(retrofit: Retrofit): AdminMessageService = retrofit.create() + + @Singleton + @Provides + fun provideDatabase( + @ApplicationContext context: Context, + sharedPrefProvider: SharedPrefProvider, + appInfo: AppInfo + ) = AppDatabase.newInstance(context, sharedPrefProvider, appInfo) + + @Singleton + @Provides + fun provideSharedPref(@ApplicationContext context: Context): SharedPreferences = + PreferenceManager.getDefaultSharedPreferences(context) + + @OptIn(ExperimentalCoroutinesApi::class) + @Singleton + @Provides + fun provideFlowSharedPref(sharedPreferences: SharedPreferences) = + FlowSharedPreferences(sharedPreferences) + + @Singleton + @Provides + fun provideJson() = Json { + ignoreUnknownKeys = true + } + + @Singleton + @Provides + fun provideStudentDao(database: AppDatabase) = database.studentDao + + @Singleton + @Provides + fun provideSemesterDao(database: AppDatabase) = database.semesterDao + + @Singleton + @Provides + fun provideGradeDao(database: AppDatabase) = database.gradeDao + + @Singleton + @Provides + fun provideGradeSummaryDao(database: AppDatabase) = database.gradeSummaryDao + + @Singleton + @Provides + fun provideGradePartialStatisticsDao(database: AppDatabase) = database.gradePartialStatisticsDao + + @Singleton + @Provides + fun provideGradeSemesterStatisticsDao(database: AppDatabase) = + database.gradeSemesterStatisticsDao + + @Singleton + @Provides + fun provideGradePointsStatisticsDao(database: AppDatabase) = database.gradePointsStatisticsDao + + @Singleton + @Provides + fun provideMessagesDao(database: AppDatabase) = database.messagesDao + + @Singleton + @Provides + fun provideMessageAttachmentsDao(database: AppDatabase) = database.messageAttachmentDao + + @Singleton + @Provides + fun provideExamDao(database: AppDatabase) = database.examsDao + + @Singleton + @Provides + fun provideAttendanceDao(database: AppDatabase) = database.attendanceDao + + @Singleton + @Provides + fun provideAttendanceSummaryDao(database: AppDatabase) = database.attendanceSummaryDao + + @Singleton + @Provides + fun provideTimetableDao(database: AppDatabase) = database.timetableDao + + @Singleton + @Provides + fun provideNoteDao(database: AppDatabase) = database.noteDao + + @Singleton + @Provides + fun provideHomeworkDao(database: AppDatabase) = database.homeworkDao + + @Singleton + @Provides + fun provideSubjectDao(database: AppDatabase) = database.subjectDao + + @Singleton + @Provides + fun provideLuckyNumberDao(database: AppDatabase) = database.luckyNumberDao + + @Singleton + @Provides + fun provideCompletedLessonsDao(database: AppDatabase) = database.completedLessonsDao + + @Singleton + @Provides + fun provideReportingUnitDao(database: AppDatabase) = database.reportingUnitDao + + @Singleton + @Provides + fun provideRecipientDao(database: AppDatabase) = database.recipientDao + + @Singleton + @Provides + fun provideMobileDevicesDao(database: AppDatabase) = database.mobileDeviceDao + + @Singleton + @Provides + fun provideTeacherDao(database: AppDatabase) = database.teacherDao + + @Singleton + @Provides + fun provideSchoolInfoDao(database: AppDatabase) = database.schoolDao + + @Singleton + @Provides + fun provideConferenceDao(database: AppDatabase) = database.conferenceDao + + @Singleton + @Provides + fun provideTimetableAdditionalDao(database: AppDatabase) = database.timetableAdditionalDao + + @Singleton + @Provides + fun provideStudentInfoDao(database: AppDatabase) = database.studentInfoDao + + @Singleton + @Provides + fun provideTimetableHeaderDao(database: AppDatabase) = database.timetableHeaderDao + + @Singleton + @Provides + fun provideSchoolAnnouncementDao(database: AppDatabase) = database.schoolAnnouncementDao + + @Singleton + @Provides + fun provideNotificationDao(database: AppDatabase) = database.notificationDao + + @Singleton + @Provides + fun provideAdminMessageDao(database: AppDatabase) = database.adminMessagesDao +} diff --git a/app/src/main/java/io/github/wulkanowy/data/Repository.java b/app/src/main/java/io/github/wulkanowy/data/Repository.java deleted file mode 100644 index d25be8b5e..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/Repository.java +++ /dev/null @@ -1,159 +0,0 @@ -package io.github.wulkanowy.data; - -import java.io.IOException; -import java.text.ParseException; -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import io.github.wulkanowy.api.VulcanException; -import io.github.wulkanowy.data.db.dao.entities.Account; -import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; -import io.github.wulkanowy.data.db.dao.entities.DaoSession; -import io.github.wulkanowy.data.db.dao.entities.Grade; -import io.github.wulkanowy.data.db.dao.entities.GradeDao; -import io.github.wulkanowy.data.db.dao.entities.Week; -import io.github.wulkanowy.data.db.dao.entities.WeekDao; -import io.github.wulkanowy.data.db.resources.ResourcesContract; -import io.github.wulkanowy.data.db.shared.SharedPrefContract; -import io.github.wulkanowy.data.sync.SyncContract; -import io.github.wulkanowy.data.sync.account.AccountSyncContract; -import io.github.wulkanowy.data.sync.attendance.AttendanceSyncContract; -import io.github.wulkanowy.data.sync.timetable.TimetableSyncContract; -import io.github.wulkanowy.di.annotations.SyncGrades; -import io.github.wulkanowy.di.annotations.SyncSubjects; -import io.github.wulkanowy.utils.security.CryptoException; - -@Singleton -public class Repository implements RepositoryContract { - - private final SharedPrefContract sharedPref; - - private final ResourcesContract resources; - - private final DaoSession daoSession; - - private final AccountSyncContract accountSync; - - private final AttendanceSyncContract attendanceSync; - - private final TimetableSyncContract timetableSync; - - private final SyncContract gradeSync; - - private final SyncContract subjectSync; - - @Inject - Repository(SharedPrefContract sharedPref, - ResourcesContract resources, - DaoSession daoSession, - AccountSyncContract accountSync, - AttendanceSyncContract attendanceSync, - TimetableSyncContract timetableSync, - @SyncGrades SyncContract gradeSync, - @SyncSubjects SyncContract subjectSync) { - this.sharedPref = sharedPref; - this.resources = resources; - this.daoSession = daoSession; - this.accountSync = accountSync; - this.attendanceSync = attendanceSync; - this.timetableSync = timetableSync; - this.gradeSync = gradeSync; - this.subjectSync = subjectSync; - } - - @Override - public long getCurrentUserId() { - return sharedPref.getCurrentUserId(); - } - - @Override - public String[] getSymbolsKeysArray() { - return resources.getSymbolsKeysArray(); - } - - @Override - public String[] getSymbolsValuesArray() { - return resources.getSymbolsValuesArray(); - } - - @Override - public String getErrorLoginMessage(Exception e) { - return resources.getErrorLoginMessage(e); - } - - @Override - public String getAttendanceLessonDescription(AttendanceLesson lesson) { - return resources.getAttendanceLessonDescription(lesson); - } - - @Override - public void registerUser(String email, String password, String symbol) throws VulcanException, - IOException, CryptoException { - accountSync.registerUser(email, password, symbol); - } - - @Override - public void initLastUser() throws VulcanException, IOException, CryptoException { - accountSync.initLastUser(); - } - - @Override - public void syncGrades() throws VulcanException, IOException, ParseException { - gradeSync.sync(); - } - - @Override - public void syncSubjects() throws VulcanException, IOException, ParseException { - subjectSync.sync(); - } - - @Override - public void syncAttendance() throws ParseException, IOException, VulcanException { - attendanceSync.syncAttendance(); - } - - @Override - public void syncAttendance(String date) throws ParseException, IOException, VulcanException { - attendanceSync.syncAttendance(date); - } - - @Override - public void syncTimetable() throws VulcanException, IOException, ParseException { - timetableSync.syncTimetable(); - } - - @Override - public void syncTimetable(String date) throws VulcanException, IOException, ParseException { - timetableSync.syncTimetable(date); - } - - @Override - public void syncAll() throws VulcanException, IOException, ParseException { - syncSubjects(); - syncGrades(); - syncAttendance(); - syncTimetable(); - } - - @Override - public Account getCurrentUser() { - return daoSession.getAccountDao().load(sharedPref.getCurrentUserId()); - } - - @Override - public Week getWeek(String date) { - return daoSession.getWeekDao().queryBuilder() - .where(WeekDao.Properties.StartDayDate.eq(date), - WeekDao.Properties.UserId.eq(getCurrentUserId())) - .unique(); - } - - @Override - public List getNewGrades() { - return daoSession.getGradeDao().queryBuilder() - .where(GradeDao.Properties.IsNew.eq(1)) - .list(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/RepositoryContract.java b/app/src/main/java/io/github/wulkanowy/data/RepositoryContract.java deleted file mode 100644 index f06c47620..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/RepositoryContract.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.wulkanowy.data; - -import java.io.IOException; -import java.text.ParseException; -import java.util.List; - -import javax.inject.Singleton; - -import io.github.wulkanowy.api.VulcanException; -import io.github.wulkanowy.data.db.dao.entities.Account; -import io.github.wulkanowy.data.db.dao.entities.Grade; -import io.github.wulkanowy.data.db.dao.entities.Week; -import io.github.wulkanowy.data.db.resources.ResourcesContract; -import io.github.wulkanowy.data.sync.account.AccountSyncContract; -import io.github.wulkanowy.data.sync.attendance.AttendanceSyncContract; -import io.github.wulkanowy.data.sync.timetable.TimetableSyncContract; - -@Singleton -public interface RepositoryContract extends ResourcesContract, AccountSyncContract, - AttendanceSyncContract, TimetableSyncContract { - - long getCurrentUserId(); - - void syncGrades() throws VulcanException, IOException, ParseException; - - void syncSubjects() throws VulcanException, IOException, ParseException; - - void syncAll() throws VulcanException, IOException, ParseException; - - Account getCurrentUser(); - - Week getWeek(String date); - - List getNewGrades(); -} diff --git a/app/src/main/java/io/github/wulkanowy/data/Resource.kt b/app/src/main/java/io/github/wulkanowy/data/Resource.kt new file mode 100644 index 000000000..44f8a1b48 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/Resource.kt @@ -0,0 +1,173 @@ +package io.github.wulkanowy.data + +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import timber.log.Timber + +sealed class Resource { + + open class Loading : Resource() + + data class Intermediate(val data: T) : Loading() + + data class Success(val data: T) : Resource() + + data class Error(val error: Throwable) : Resource() +} + +val Resource.dataOrNull: T? + get() = when (this) { + is Resource.Success -> this.data + is Resource.Intermediate -> this.data + is Resource.Loading -> null + is Resource.Error -> null + } + +val Resource.errorOrNull: Throwable? + get() = when (this) { + is Resource.Error -> this.error + else -> null + } + +fun resourceFlow(block: suspend () -> T) = flow { + emit(Resource.Loading()) + emit(Resource.Success(block())) +}.catch { emit(Resource.Error(it)) } + +fun flatResourceFlow(block: suspend () -> Flow>) = flow { + emit(Resource.Loading()) + emitAll(block().filter { it is Resource.Intermediate || it !is Resource.Loading }) +}.catch { emit(Resource.Error(it)) } + +fun Resource.mapData(block: (T) -> U) = when (this) { + is Resource.Success -> Resource.Success(block(this.data)) + is Resource.Intermediate -> Resource.Intermediate(block(this.data)) + is Resource.Loading -> Resource.Loading() + is Resource.Error -> Resource.Error(this.error) +} + +fun Flow>.logResourceStatus(name: String, showData: Boolean = false) = onEach { + val description = when (it) { + is Resource.Loading -> "started" + is Resource.Intermediate -> "intermediate data received" + if (showData) " (data: `${it.data}`)" else "" + is Resource.Success -> "success" + if (showData) " (data: `${it.data}`)" else "" + is Resource.Error -> "exception occurred: ${it.error}" + } + Timber.i("$name: $description") +} + +fun Flow>.mapResourceData(block: (T) -> U) = map { + it.mapData(block) +} + +fun Flow>.onResourceData(block: suspend (T) -> Unit) = onEach { + when (it) { + is Resource.Success -> block(it.data) + is Resource.Intermediate -> block(it.data) + is Resource.Error, + is Resource.Loading -> Unit + } +} + +fun Flow>.onResourceLoading(block: suspend () -> Unit) = onEach { + if (it is Resource.Loading) { + block() + } +} + +fun Flow>.onResourceIntermediate(block: suspend (T) -> Unit) = onEach { + if (it is Resource.Intermediate) { + block(it.data) + } +} + +fun Flow>.onResourceSuccess(block: suspend (T) -> Unit) = onEach { + if (it is Resource.Success) { + block(it.data) + } +} + +fun Flow>.onResourceError(block: (Throwable) -> Unit) = onEach { + if (it is Resource.Error) { + block(it.error) + } +} + +fun Flow>.onResourceNotLoading(block: () -> Unit) = onEach { + if (it !is Resource.Loading) { + block() + } +} + +suspend fun Flow>.toFirstResult() = filter { it !is Resource.Loading }.first() + +suspend fun Flow>.waitForResult() = takeWhile { it is Resource.Loading }.collect() + +inline fun networkBoundResource( + mutex: Mutex = Mutex(), + showSavedOnLoading: Boolean = true, + crossinline isResultEmpty: (ResultType) -> Boolean, + crossinline query: () -> Flow, + crossinline fetch: suspend (ResultType) -> RequestType, + crossinline saveFetchResult: suspend (old: ResultType, new: RequestType) -> Unit, + crossinline onFetchFailed: (Throwable) -> Unit = { }, + crossinline shouldFetch: (ResultType) -> Boolean = { true }, + crossinline filterResult: (ResultType) -> ResultType = { it } +) = flow { + emit(Resource.Loading()) + + val data = query().first() + emitAll(if (shouldFetch(data)) { + val filteredResult = filterResult(data) + + if (showSavedOnLoading && !isResultEmpty(filteredResult)) { + emit(Resource.Intermediate(filteredResult)) + } + + try { + val newData = fetch(data) + mutex.withLock { saveFetchResult(query().first(), newData) } + query().map { Resource.Success(filterResult(it)) } + } catch (throwable: Throwable) { + onFetchFailed(throwable) + query().map { Resource.Error(throwable) } + } + } else { + query().map { Resource.Success(filterResult(it)) } + }) +} + +@JvmName("networkBoundResourceWithMap") +inline fun networkBoundResource( + mutex: Mutex = Mutex(), + showSavedOnLoading: Boolean = true, + crossinline isResultEmpty: (T) -> Boolean, + crossinline query: () -> Flow, + crossinline fetch: suspend (ResultType) -> RequestType, + crossinline saveFetchResult: suspend (old: ResultType, new: RequestType) -> Unit, + crossinline onFetchFailed: (Throwable) -> Unit = { }, + crossinline shouldFetch: (ResultType) -> Boolean = { true }, + crossinline mapResult: (ResultType) -> T +) = flow { + emit(Resource.Loading()) + + val data = query().first() + emitAll(if (shouldFetch(data)) { + val mappedResult = mapResult(data) + + if (showSavedOnLoading && !isResultEmpty(mappedResult)) { + emit(Resource.Intermediate(mappedResult)) + } + try { + val newData = fetch(data) + mutex.withLock { saveFetchResult(query().first(), newData) } + query().map { Resource.Success(mapResult(it)) } + } catch (throwable: Throwable) { + onFetchFailed(throwable) + query().map { Resource.Error(throwable) } + } + } else { + query().map { Resource.Success(mapResult(it)) } + }) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/api/AdminMessageService.kt b/app/src/main/java/io/github/wulkanowy/data/api/AdminMessageService.kt new file mode 100644 index 000000000..23f5af24a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/api/AdminMessageService.kt @@ -0,0 +1,12 @@ +package io.github.wulkanowy.data.api + +import io.github.wulkanowy.data.db.entities.AdminMessage +import retrofit2.http.GET +import javax.inject.Singleton + +@Singleton +interface AdminMessageService { + + @GET("/v1.json") + suspend fun getAdminMessages(): List +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt new file mode 100644 index 000000000..379b8738f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt @@ -0,0 +1,178 @@ +package io.github.wulkanowy.data.db + +import android.content.Context +import androidx.room.* +import androidx.room.RoomDatabase.JournalMode.TRUNCATE +import io.github.wulkanowy.data.db.dao.* +import io.github.wulkanowy.data.db.entities.* +import io.github.wulkanowy.data.db.migrations.* +import io.github.wulkanowy.utils.AppInfo +import javax.inject.Singleton + +@Singleton +@Database( + entities = [ + Student::class, + Semester::class, + Exam::class, + Timetable::class, + Attendance::class, + AttendanceSummary::class, + Grade::class, + GradeSummary::class, + GradePartialStatistics::class, + GradePointsStatistics::class, + GradeSemesterStatistics::class, + Message::class, + MessageAttachment::class, + Note::class, + Homework::class, + Subject::class, + LuckyNumber::class, + CompletedLesson::class, + ReportingUnit::class, + Recipient::class, + MobileDevice::class, + Teacher::class, + School::class, + Conference::class, + TimetableAdditional::class, + StudentInfo::class, + TimetableHeader::class, + SchoolAnnouncement::class, + Notification::class, + AdminMessage::class + ], + autoMigrations = [ + AutoMigration(from = 44, to = 45), + AutoMigration(from = 46, to = 47), + AutoMigration(from = 47, to = 48), + ], + version = AppDatabase.VERSION_SCHEMA, + exportSchema = true +) +@TypeConverters(Converters::class) +abstract class AppDatabase : RoomDatabase() { + + companion object { + const val VERSION_SCHEMA = 48 + + fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf( + Migration2(), + Migration3(), + Migration4(), + Migration5(), + Migration6(), + Migration7(), + Migration8(), + Migration9(), + Migration10(), + Migration11(), + Migration12(), + Migration13(), + Migration14(), + Migration15(), + Migration16(), + Migration17(), + Migration18(), + Migration19(sharedPrefProvider), + Migration20(), + Migration21(), + Migration22(), + Migration23(), + Migration24(), + Migration25(), + Migration26(), + Migration27(), + Migration28(), + Migration29(), + Migration30(), + Migration31(), + Migration32(), + Migration33(), + Migration34(), + Migration35(appInfo), + Migration36(), + Migration37(), + Migration38(), + Migration39(), + Migration40(), + Migration41(sharedPrefProvider), + Migration42(), + Migration43(), + Migration44(), + Migration46(), + ) + + fun newInstance( + context: Context, + sharedPrefProvider: SharedPrefProvider, + appInfo: AppInfo + ) = Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database") + .setJournalMode(TRUNCATE) + .fallbackToDestructiveMigrationFrom(VERSION_SCHEMA + 1) + .fallbackToDestructiveMigrationOnDowngrade() + .addMigrations(*getMigrations(sharedPrefProvider, appInfo)) + .build() + } + + abstract val studentDao: StudentDao + + abstract val semesterDao: SemesterDao + + abstract val examsDao: ExamDao + + abstract val timetableDao: TimetableDao + + abstract val attendanceDao: AttendanceDao + + abstract val attendanceSummaryDao: AttendanceSummaryDao + + abstract val gradeDao: GradeDao + + abstract val gradeSummaryDao: GradeSummaryDao + + abstract val gradePartialStatisticsDao: GradePartialStatisticsDao + + abstract val gradePointsStatisticsDao: GradePointsStatisticsDao + + abstract val gradeSemesterStatisticsDao: GradeSemesterStatisticsDao + + abstract val messagesDao: MessagesDao + + abstract val messageAttachmentDao: MessageAttachmentDao + + abstract val noteDao: NoteDao + + abstract val homeworkDao: HomeworkDao + + abstract val subjectDao: SubjectDao + + abstract val luckyNumberDao: LuckyNumberDao + + abstract val completedLessonsDao: CompletedLessonsDao + + abstract val reportingUnitDao: ReportingUnitDao + + abstract val recipientDao: RecipientDao + + abstract val mobileDeviceDao: MobileDeviceDao + + abstract val teacherDao: TeacherDao + + abstract val schoolDao: SchoolDao + + abstract val conferenceDao: ConferenceDao + + abstract val timetableAdditionalDao: TimetableAdditionalDao + + abstract val studentInfoDao: StudentInfoDao + + abstract val timetableHeaderDao: TimetableHeaderDao + + abstract val schoolAnnouncementDao: SchoolAnnouncementDao + + abstract val notificationDao: NotificationDao + + abstract val adminMessagesDao: AdminMessageDao +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt b/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt new file mode 100644 index 000000000..9d3beae1f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt @@ -0,0 +1,71 @@ +package io.github.wulkanowy.data.db + +import androidx.room.TypeConverter +import io.github.wulkanowy.ui.modules.Destination +import io.github.wulkanowy.utils.toTimestamp +import kotlinx.serialization.SerializationException +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import java.time.* +import java.util.* +import java.time.Instant +import java.time.LocalDate +import java.time.Month +import java.time.ZoneOffset +import java.util.* + +class Converters { + + private val json = Json + + @TypeConverter + fun timestampToLocalDate(value: Long?): LocalDate? = + value?.let(::Date)?.toInstant()?.atZone(ZoneOffset.UTC)?.toLocalDate() + + @TypeConverter + fun dateToTimestamp(date: LocalDate?): Long? = date?.toTimestamp() + + @TypeConverter + fun instantToTimestamp(instant: Instant?): Long? = instant?.toEpochMilli() + + @TypeConverter + fun timestampToInstant(timestamp: Long?): Instant? = timestamp?.let(Instant::ofEpochMilli) + + @TypeConverter + fun monthToInt(month: Month?) = month?.value + + @TypeConverter + fun intToMonth(value: Int?) = value?.let { Month.of(it) } + + @TypeConverter + fun intListToJson(list: List): String { + return json.encodeToString(list) + } + + @TypeConverter + fun jsonToIntList(value: String): List { + return json.decodeFromString(value) + } + + @TypeConverter + fun stringPairListToJson(list: List>): String { + return json.encodeToString(list) + } + + @TypeConverter + fun jsonToStringPairList(value: String): List> { + return try { + json.decodeFromString(value) + } catch (e: SerializationException) { + emptyList() // handle errors from old gson Pair serialized data + } + } + + @TypeConverter + fun destinationToString(destination: Destination) = json.encodeToString(destination) + + @TypeConverter + fun stringToDestination(destination: String): Destination = json.decodeFromString(destination) + +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefProvider.kt b/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefProvider.kt new file mode 100644 index 000000000..4929f0469 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefProvider.kt @@ -0,0 +1,41 @@ +package io.github.wulkanowy.data.db + +import android.content.SharedPreferences +import androidx.core.content.edit +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SharedPrefProvider @Inject constructor( + private val sharedPref: SharedPreferences +) { + + companion object { + const val APP_VERSION_CODE_KEY = "app_version_code" + } + + fun putLong(key: String, value: Long, sync: Boolean = false) { + sharedPref.edit(sync) { putLong(key, value) } + } + + fun getLong(key: String, defaultValue: Long) = sharedPref.getLong(key, defaultValue) + + fun getString(key: String) = sharedPref.getString(key, null) + + fun getString(key: String, defaultValue: String): String = + sharedPref.getString(key, defaultValue) ?: defaultValue + + fun getBoolean(key: String, defaultValue: Boolean): Boolean = + sharedPref.getBoolean(key, defaultValue) + + fun putBoolean(key: String, value: Boolean, sync: Boolean = false) = + sharedPref.edit(sync) { putBoolean(key, value) } + + fun putString(key: String, value: String?, sync: Boolean = false) { + sharedPref.edit(sync) { putString(key, value) } + } + + fun delete(key: String) { + sharedPref.edit().remove(key).apply() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/AdminMessageDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/AdminMessageDao.kt new file mode 100644 index 000000000..87f4812da --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/AdminMessageDao.kt @@ -0,0 +1,25 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import androidx.room.Transaction +import io.github.wulkanowy.data.db.entities.AdminMessage +import kotlinx.coroutines.flow.Flow +import javax.inject.Singleton + +@Singleton +@Dao +abstract class AdminMessageDao : BaseDao { + + @Query("SELECT * FROM AdminMessages") + abstract fun loadAll(): Flow> + + @Transaction + open suspend fun removeOldAndSaveNew( + oldMessages: List, + newMessages: List + ) { + deleteAll(oldMessages) + insertAll(newMessages) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceDao.kt new file mode 100644 index 000000000..c6c255a1f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceDao.kt @@ -0,0 +1,21 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.Attendance +import kotlinx.coroutines.flow.Flow +import java.time.LocalDate +import javax.inject.Singleton + +@Singleton +@Dao +interface AttendanceDao : BaseDao { + + @Query("SELECT * FROM Attendance WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :start AND date <= :end") + fun loadAll( + diaryId: Int, + studentId: Int, + start: LocalDate, + end: LocalDate + ): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceSummaryDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceSummaryDao.kt new file mode 100644 index 000000000..4218855ca --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceSummaryDao.kt @@ -0,0 +1,13 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.AttendanceSummary +import kotlinx.coroutines.flow.Flow + +@Dao +interface AttendanceSummaryDao : BaseDao { + + @Query("SELECT * FROM AttendanceSummary WHERE diary_id = :diaryId AND student_id = :studentId AND subject_id = :subjectId") + fun loadAll(diaryId: Int, studentId: Int, subjectId: Int): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt new file mode 100644 index 000000000..048e9e3cd --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Update + +interface BaseDao { + + @Insert + suspend fun insertAll(items: List): List + + @Update + suspend fun updateAll(items: List) + + @Delete + suspend fun deleteAll(items: List) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt new file mode 100644 index 000000000..097ad7c81 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt @@ -0,0 +1,16 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.CompletedLesson +import kotlinx.coroutines.flow.Flow +import java.time.LocalDate +import javax.inject.Singleton + +@Singleton +@Dao +interface CompletedLessonsDao : BaseDao { + + @Query("SELECT * FROM CompletedLesson WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") + fun loadAll(studentId: Int, diaryId: Int, from: LocalDate, end: LocalDate): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/ConferenceDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/ConferenceDao.kt new file mode 100644 index 000000000..ca9da9ea9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/ConferenceDao.kt @@ -0,0 +1,16 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.Conference +import kotlinx.coroutines.flow.Flow +import java.time.Instant +import javax.inject.Singleton + +@Dao +@Singleton +interface ConferenceDao : BaseDao { + + @Query("SELECT * FROM Conferences WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :startDate") + fun loadAll(diaryId: Int, studentId: Int, startDate: Instant): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/DbHelper.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/DbHelper.java deleted file mode 100644 index a67e9b7f5..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/DbHelper.java +++ /dev/null @@ -1,61 +0,0 @@ -package io.github.wulkanowy.data.db.dao; - -import android.content.Context; -import android.database.sqlite.SQLiteDatabase; - -import com.github.yuweiguocn.library.greendao.MigrationHelper; - -import org.greenrobot.greendao.database.Database; -import org.greenrobot.greendao.database.StandardDatabase; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import io.github.wulkanowy.BuildConfig; -import io.github.wulkanowy.data.db.dao.entities.AccountDao; -import io.github.wulkanowy.data.db.dao.entities.DaoMaster; -import io.github.wulkanowy.data.db.dao.entities.GradeDao; -import io.github.wulkanowy.data.db.dao.entities.SubjectDao; -import io.github.wulkanowy.data.db.shared.SharedPrefContract; -import io.github.wulkanowy.di.annotations.ApplicationContext; -import io.github.wulkanowy.di.annotations.DatabaseInfo; -import io.github.wulkanowy.utils.LogUtils; - -@Singleton -public class DbHelper extends DaoMaster.OpenHelper { - - private SharedPrefContract sharedPref; - - @Inject - DbHelper(@ApplicationContext Context context, @DatabaseInfo String dbName, - SharedPrefContract sharedPref) { - super(context, dbName); - this.sharedPref = sharedPref; - } - - @Override - @SuppressWarnings("unchecked") - public void onUpgrade(Database db, int oldVersion, int newVersion) { - MigrationHelper.DEBUG = BuildConfig.DEBUG; - MigrationHelper.migrate(db, new MigrationHelper.ReCreateAllTableListener() { - @Override - public void onCreateAllTables(Database db, boolean ifNotExists) { - DaoMaster.createAllTables(db, ifNotExists); - } - @Override - public void onDropAllTables(Database db, boolean ifExists) { - DaoMaster.dropAllTables(db, ifExists); - } - }, AccountDao.class, SubjectDao.class, GradeDao.class); - } - - @Override - public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { - Database database = new StandardDatabase(db); - DaoMaster.dropAllTables(database, true); - onCreate(database); - sharedPref.setCurrentUserId(0); - - LogUtils.info("Cleaning user data oldVersion=" + oldVersion + " newVersion=" + newVersion); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/ExamDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/ExamDao.kt new file mode 100644 index 000000000..311eeb9c5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/ExamDao.kt @@ -0,0 +1,16 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.Exam +import kotlinx.coroutines.flow.Flow +import java.time.LocalDate +import javax.inject.Singleton + +@Singleton +@Dao +interface ExamDao : BaseDao { + + @Query("SELECT * FROM Exams WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") + fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeDao.kt new file mode 100644 index 000000000..12e70bde6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeDao.kt @@ -0,0 +1,15 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.Grade +import kotlinx.coroutines.flow.Flow +import javax.inject.Singleton + +@Singleton +@Dao +interface GradeDao : BaseDao { + + @Query("SELECT * FROM Grades WHERE semester_id = :semesterId AND student_id = :studentId") + fun loadAll(semesterId: Int, studentId: Int): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradePartialStatisticsDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradePartialStatisticsDao.kt new file mode 100644 index 000000000..bce6ce572 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradePartialStatisticsDao.kt @@ -0,0 +1,13 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.GradePartialStatistics +import kotlinx.coroutines.flow.Flow + +@Dao +interface GradePartialStatisticsDao : BaseDao { + + @Query("SELECT * FROM GradePartialStatistics WHERE student_id = :studentId AND semester_id = :semesterId") + fun loadAll(semesterId: Int, studentId: Int): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradePointsStatisticsDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradePointsStatisticsDao.kt new file mode 100644 index 000000000..e8074f003 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradePointsStatisticsDao.kt @@ -0,0 +1,18 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.GradePointsStatistics +import kotlinx.coroutines.flow.Flow +import javax.inject.Singleton + +@Singleton +@Dao +interface GradePointsStatisticsDao : BaseDao { + + @Query("SELECT * FROM GradesPointsStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName") + fun loadSubject(semesterId: Int, studentId: Int, subjectName: String): Flow> + + @Query("SELECT * FROM GradesPointsStatistics WHERE student_id = :studentId AND semester_id = :semesterId") + fun loadAll(semesterId: Int, studentId: Int): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSemesterStatisticsDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSemesterStatisticsDao.kt new file mode 100644 index 000000000..09ae81714 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSemesterStatisticsDao.kt @@ -0,0 +1,13 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics +import kotlinx.coroutines.flow.Flow + +@Dao +interface GradeSemesterStatisticsDao : BaseDao { + + @Query("SELECT * FROM GradeSemesterStatistics WHERE student_id = :studentId AND semester_id = :semesterId") + fun loadAll(semesterId: Int, studentId: Int): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSummaryDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSummaryDao.kt new file mode 100644 index 000000000..fc9ad66ed --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSummaryDao.kt @@ -0,0 +1,15 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.GradeSummary +import kotlinx.coroutines.flow.Flow +import javax.inject.Singleton + +@Singleton +@Dao +interface GradeSummaryDao : BaseDao { + + @Query("SELECT * FROM GradesSummary WHERE student_id = :studentId AND semester_id = :semesterId") + fun loadAll(semesterId: Int, studentId: Int): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/HomeworkDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/HomeworkDao.kt new file mode 100644 index 000000000..2092de49d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/HomeworkDao.kt @@ -0,0 +1,16 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.Homework +import kotlinx.coroutines.flow.Flow +import java.time.LocalDate +import javax.inject.Singleton + +@Singleton +@Dao +interface HomeworkDao : BaseDao { + + @Query("SELECT * FROM Homework WHERE semester_id = :semesterId AND student_id = :studentId AND date >= :from AND date <= :end") + fun loadAll(semesterId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/LuckyNumberDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/LuckyNumberDao.kt new file mode 100644 index 000000000..d9aa24364 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/LuckyNumberDao.kt @@ -0,0 +1,19 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.LuckyNumber +import kotlinx.coroutines.flow.Flow +import java.time.LocalDate +import javax.inject.Singleton + +@Singleton +@Dao +interface LuckyNumberDao : BaseDao { + + @Query("SELECT * FROM LuckyNumbers WHERE student_id = :studentId AND date = :date") + fun load(studentId: Int, date: LocalDate): Flow + + @Query("SELECT * FROM LuckyNumbers WHERE student_id = :studentId AND date >= :start AND date <= :end") + fun getAll(studentId: Int, start: LocalDate, end: LocalDate): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessageAttachmentDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessageAttachmentDao.kt new file mode 100644 index 000000000..b69083a1a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessageAttachmentDao.kt @@ -0,0 +1,13 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import io.github.wulkanowy.data.db.entities.MessageAttachment + +@Dao +interface MessageAttachmentDao : BaseDao { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertAttachments(items: List): List +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt new file mode 100644 index 000000000..729ba6a68 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt @@ -0,0 +1,19 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import androidx.room.Transaction +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.MessageWithAttachment +import kotlinx.coroutines.flow.Flow + +@Dao +interface MessagesDao : BaseDao { + + @Transaction + @Query("SELECT * FROM Messages WHERE student_id = :studentId AND message_id = :messageId") + fun loadMessageWithAttachment(studentId: Int, messageId: Int): Flow + + @Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder ORDER BY date DESC") + fun loadAll(studentId: Int, folder: Int): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt new file mode 100644 index 000000000..081e859a5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt @@ -0,0 +1,13 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.MobileDevice +import kotlinx.coroutines.flow.Flow + +@Dao +interface MobileDeviceDao : BaseDao { + + @Query("SELECT * FROM MobileDevices WHERE student_id = :userLoginId ORDER BY date DESC") + fun loadAll(userLoginId: Int): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/NoteDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/NoteDao.kt new file mode 100644 index 000000000..e89a4135a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/NoteDao.kt @@ -0,0 +1,15 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.Note +import kotlinx.coroutines.flow.Flow +import javax.inject.Singleton + +@Singleton +@Dao +interface NoteDao : BaseDao { + + @Query("SELECT * FROM Notes WHERE student_id = :studentId") + fun loadAll(studentId: Int): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/NotificationDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/NotificationDao.kt new file mode 100644 index 000000000..c5ae21bc2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/NotificationDao.kt @@ -0,0 +1,15 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.Notification +import kotlinx.coroutines.flow.Flow +import javax.inject.Singleton + +@Singleton +@Dao +interface NotificationDao : BaseDao { + + @Query("SELECT * FROM Notifications WHERE student_id = :studentId OR student_id = -1") + fun loadAll(studentId: Long): Flow> +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt new file mode 100644 index 000000000..c2787ac3b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt @@ -0,0 +1,14 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.Recipient +import javax.inject.Singleton + +@Singleton +@Dao +interface RecipientDao : BaseDao { + + @Query("SELECT * FROM Recipients WHERE student_id = :studentId AND unit_id = :unitId AND role = :role") + suspend fun loadAll(studentId: Int, unitId: Int, role: Int): List +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt new file mode 100644 index 000000000..ca697eda8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.ReportingUnit +import javax.inject.Singleton + +@Singleton +@Dao +interface ReportingUnitDao : BaseDao { + + @Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId") + suspend fun load(studentId: Int): List + + @Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId AND real_id = :unitId") + suspend fun loadOne(studentId: Int, unitId: Int): ReportingUnit? +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/SchoolAnnouncementDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/SchoolAnnouncementDao.kt new file mode 100644 index 000000000..15655f4ae --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/SchoolAnnouncementDao.kt @@ -0,0 +1,15 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.SchoolAnnouncement +import kotlinx.coroutines.flow.Flow +import javax.inject.Singleton + +@Dao +@Singleton +interface SchoolAnnouncementDao : BaseDao { + + @Query("SELECT * FROM SchoolAnnouncements WHERE student_id = :studentId ORDER BY date DESC") + fun loadAll(studentId: Int): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/SchoolDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/SchoolDao.kt new file mode 100644 index 000000000..f39791f63 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/SchoolDao.kt @@ -0,0 +1,15 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.School +import kotlinx.coroutines.flow.Flow +import javax.inject.Singleton + +@Singleton +@Dao +interface SchoolDao : BaseDao { + + @Query("SELECT * FROM School WHERE student_id = :studentId AND class_id = :classId") + fun load(studentId: Int, classId: Int): Flow +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt new file mode 100644 index 000000000..4d171907c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt @@ -0,0 +1,19 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.Semester +import javax.inject.Singleton + +@Singleton +@Dao +interface SemesterDao : BaseDao { + + @Insert(onConflict = OnConflictStrategy.IGNORE) + suspend fun insertSemesters(items: List): List + + @Query("SELECT * FROM Semesters WHERE student_id = :studentId AND class_id = :classId") + suspend fun loadAll(studentId: Int, classId: Int): List +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt new file mode 100644 index 000000000..87b3e0b32 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt @@ -0,0 +1,51 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.* +import androidx.room.OnConflictStrategy.ABORT +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import javax.inject.Singleton + +@Singleton +@Dao +abstract class StudentDao { + + @Insert(onConflict = ABORT) + abstract suspend fun insertAll(student: List): List + + @Delete + abstract suspend fun delete(student: Student) + + @Update(entity = Student::class) + abstract suspend fun update(studentNickAndAvatar: StudentNickAndAvatar) + + @Query("SELECT * FROM Students WHERE is_current = 1") + abstract suspend fun loadCurrent(): Student? + + @Query("SELECT * FROM Students WHERE id = :id") + abstract suspend fun loadById(id: Long): Student? + + @Query("SELECT * FROM Students") + abstract suspend fun loadAll(): List + + @Transaction + @Query("SELECT * FROM Students") + abstract suspend fun loadStudentsWithSemesters(): List + + @Transaction + @Query("SELECT * FROM Students WHERE id = :id") + abstract suspend fun loadStudentWithSemestersById(id: Long): StudentWithSemesters? + + @Query("UPDATE Students SET is_current = 1 WHERE id = :id") + abstract suspend fun updateCurrent(id: Long) + + @Query("UPDATE Students SET is_current = 0") + abstract suspend fun resetCurrent() + + @Transaction + open suspend fun switchCurrent(id: Long) { + resetCurrent() + updateCurrent(id) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentInfoDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentInfoDao.kt new file mode 100644 index 000000000..5ec86af12 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentInfoDao.kt @@ -0,0 +1,15 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.StudentInfo +import kotlinx.coroutines.flow.Flow +import javax.inject.Singleton + +@Singleton +@Dao +interface StudentInfoDao : BaseDao { + + @Query("SELECT * FROM StudentInfo WHERE student_id = :studentId") + fun loadStudentInfo(studentId: Int): Flow +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/SubjectDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/SubjectDao.kt new file mode 100644 index 000000000..4cd742b56 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/SubjectDao.kt @@ -0,0 +1,13 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.Subject +import kotlinx.coroutines.flow.Flow + +@Dao +interface SubjectDao : BaseDao { + + @Query("SELECT * FROM Subjects WHERE diary_id = :diaryId AND student_id = :studentId") + fun loadAll(diaryId: Int, studentId: Int): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/TeacherDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/TeacherDao.kt new file mode 100644 index 000000000..6adac220d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/TeacherDao.kt @@ -0,0 +1,15 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.Teacher +import kotlinx.coroutines.flow.Flow +import javax.inject.Singleton + +@Singleton +@Dao +interface TeacherDao : BaseDao { + + @Query("SELECT * FROM Teachers WHERE student_id = :studentId AND class_id = :classId") + fun loadAll(studentId: Int, classId: Int): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableAdditionalDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableAdditionalDao.kt new file mode 100644 index 000000000..914ce340a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableAdditionalDao.kt @@ -0,0 +1,25 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.TimetableAdditional +import kotlinx.coroutines.flow.Flow +import java.time.LocalDate +import java.util.UUID +import javax.inject.Singleton + +@Dao +@Singleton +interface TimetableAdditionalDao : BaseDao { + + @Query("SELECT * FROM TimetableAdditional WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") + fun loadAll( + diaryId: Int, + studentId: Int, + from: LocalDate, + end: LocalDate + ): Flow> + + @Query("DELETE FROM TimetableAdditional WHERE repeat_id = :repeatId") + suspend fun deleteAllByRepeatId(repeatId: UUID) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt new file mode 100644 index 000000000..5e6eec668 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt @@ -0,0 +1,16 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.Timetable +import kotlinx.coroutines.flow.Flow +import java.time.LocalDate +import javax.inject.Singleton + +@Singleton +@Dao +interface TimetableDao : BaseDao { + + @Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") + fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableHeaderDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableHeaderDao.kt new file mode 100644 index 000000000..916d19010 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableHeaderDao.kt @@ -0,0 +1,16 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.TimetableHeader +import kotlinx.coroutines.flow.Flow +import java.time.LocalDate +import javax.inject.Singleton + +@Dao +@Singleton +interface TimetableHeaderDao : BaseDao { + + @Query("SELECT * FROM TimetableHeaders WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") + fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Account.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Account.java deleted file mode 100644 index 20a47ac26..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Account.java +++ /dev/null @@ -1,258 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.DaoException; -import org.greenrobot.greendao.annotation.Entity; -import org.greenrobot.greendao.annotation.Generated; -import org.greenrobot.greendao.annotation.Id; -import org.greenrobot.greendao.annotation.Property; -import org.greenrobot.greendao.annotation.ToMany; - -import java.util.List; - -@Entity( - nameInDb = "Accounts", - active = true -) -public class Account { - - @Id(autoincrement = true) - private Long id; - - @Property(nameInDb = "NAME") - private String name; - - @Property(nameInDb = "E-MAIL") - private String email; - - @Property(nameInDb = "PASSWORD") - private String password; - - @Property(nameInDb = "SYMBOL") - private String symbol; - - @Property(nameInDb = "SNPID") - private String snpId; - - @ToMany(referencedJoinProperty = "userId") - private List subjectList; - - @ToMany(referencedJoinProperty = "userId") - private List gradeList; - - @ToMany(referencedJoinProperty = "userId") - private List dayList; - - /** - * Used to resolve relations - */ - @Generated(hash = 2040040024) - private transient DaoSession daoSession; - - /** - * Used for active entity operations. - */ - @Generated(hash = 335469827) - private transient AccountDao myDao; - - @Generated(hash = 735765217) - public Account(Long id, String name, String email, String password, String symbol, - String snpId) { - this.id = id; - this.name = name; - this.email = email; - this.password = password; - this.symbol = symbol; - this.snpId = snpId; - } - - @Generated(hash = 882125521) - public Account() { - } - - public Long getId() { - return id; - } - - public Account setId(Long id) { - this.id = id; - return this; - } - - public String getName() { - return name; - } - - public Account setName(String name) { - this.name = name; - return this; - } - - public String getEmail() { - return email; - } - - public Account setEmail(String email) { - this.email = email; - return this; - } - - public String getPassword() { - return password; - } - - public Account setPassword(String password) { - this.password = password; - return this; - } - - public String getSymbol() { - return symbol; - } - - public Account setSymbol(String symbol) { - this.symbol = symbol; - return this; - } - - public String getSnpId() { - return this.snpId; - } - - public Account setSnpId(String snpId) { - this.snpId = snpId; - return this; - } - - /** - * To-many relationship, resolved on first access (and after reset). - * Changes to to-many relations are not persisted, make changes to the target entity. - */ - @Generated(hash = 1800750450) - public List getSubjectList() { - if (subjectList == null) { - final DaoSession daoSession = this.daoSession; - if (daoSession == null) { - throw new DaoException("Entity is detached from DAO context"); - } - SubjectDao targetDao = daoSession.getSubjectDao(); - List subjectListNew = targetDao._queryAccount_SubjectList(id); - synchronized (this) { - if (subjectList == null) { - subjectList = subjectListNew; - } - } - } - return subjectList; - } - - /** - * Resets a to-many relationship, making the next get call to query for a fresh result. - */ - @Generated(hash = 594294258) - public synchronized void resetSubjectList() { - subjectList = null; - } - - /** - * To-many relationship, resolved on first access (and after reset). - * Changes to to-many relations are not persisted, make changes to the target entity. - */ - @Generated(hash = 1040074549) - public List getGradeList() { - if (gradeList == null) { - final DaoSession daoSession = this.daoSession; - if (daoSession == null) { - throw new DaoException("Entity is detached from DAO context"); - } - GradeDao targetDao = daoSession.getGradeDao(); - List gradeListNew = targetDao._queryAccount_GradeList(id); - synchronized (this) { - if (gradeList == null) { - gradeList = gradeListNew; - } - } - } - return gradeList; - } - - /** - * Resets a to-many relationship, making the next get call to query for a fresh result. - */ - @Generated(hash = 1939990047) - public synchronized void resetGradeList() { - gradeList = null; - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 128553479) - public void delete() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.delete(this); - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 1942392019) - public void refresh() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.refresh(this); - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 713229351) - public void update() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.update(this); - } - - /** - * To-many relationship, resolved on first access (and after reset). - * Changes to to-many relations are not persisted, make changes to the target entity. - */ - @Generated(hash = 300459794) - public List getDayList() { - if (dayList == null) { - final DaoSession daoSession = this.daoSession; - if (daoSession == null) { - throw new DaoException("Entity is detached from DAO context"); - } - DayDao targetDao = daoSession.getDayDao(); - List dayListNew = targetDao._queryAccount_DayList(id); - synchronized (this) { - if (dayList == null) { - dayList = dayListNew; - } - } - } - return dayList; - } - - /** - * Resets a to-many relationship, making the next get call to query for a fresh result. - */ - @Generated(hash = 1010399236) - public synchronized void resetDayList() { - dayList = null; - } - - /** called by internal mechanisms, do not call yourself. */ - @Generated(hash = 1812283172) - public void __setDaoSession(DaoSession daoSession) { - this.daoSession = daoSession; - myDao = daoSession != null ? daoSession.getAccountDao() : null; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLesson.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLesson.java deleted file mode 100644 index 9250518af..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLesson.java +++ /dev/null @@ -1,254 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.DaoException; -import org.greenrobot.greendao.annotation.Entity; -import org.greenrobot.greendao.annotation.Generated; -import org.greenrobot.greendao.annotation.Id; -import org.greenrobot.greendao.annotation.Index; -import org.greenrobot.greendao.annotation.Property; -import org.greenrobot.greendao.annotation.Transient; - -import java.io.Serializable; - -@Entity( - nameInDb = "AttendanceLessons", - active = true, - indexes = {@Index(value = "dayId,date,number", unique = true)} -) -public class AttendanceLesson implements Serializable { - - @Id(autoincrement = true) - private Long id; - - @Property(nameInDb = "DAY_ID") - private Long dayId; - - @Property(nameInDb = "DATE") - private String date = ""; - - @Property(nameInDb = "NUMBER_OF_LESSON") - private int number = 0; - - @Property(nameInDb = "SUBJECT_NAME") - private String subject = ""; - - @Property(nameInDb = "IS_PRESENCE") - private boolean isPresence = false; - - @Property(nameInDb = "IS_ABSENCE_UNEXCUSED") - private boolean isAbsenceUnexcused = false; - - @Property(nameInDb = "IS_ABSENCE_EXCUSED") - private boolean isAbsenceExcused = false; - - @Property(nameInDb = "IS_UNEXCUSED_LATENESS") - private boolean isUnexcusedLateness = false; - - @Property(nameInDb = "IS_ABSENCE_FOR_SCHOOL_REASONS") - private boolean isAbsenceForSchoolReasons = false; - - @Property(nameInDb = "IS_EXCUSED_LATENESS") - private boolean isExcusedLateness = false; - - @Property(nameInDb = "IS_EXEMPTION") - private boolean isExemption = false; - - @Transient - private String description = ""; - - private static final long serialVersionUID = 42L; - - /** - * Used to resolve relations - */ - @Generated(hash = 2040040024) - private transient DaoSession daoSession; - - /** - * Used for active entity operations. - */ - @Generated(hash = 1936953859) - private transient AttendanceLessonDao myDao; - - @Generated(hash = 1428129046) - public AttendanceLesson(Long id, Long dayId, String date, int number, - String subject, boolean isPresence, boolean isAbsenceUnexcused, - boolean isAbsenceExcused, boolean isUnexcusedLateness, - boolean isAbsenceForSchoolReasons, boolean isExcusedLateness, - boolean isExemption) { - this.id = id; - this.dayId = dayId; - this.date = date; - this.number = number; - this.subject = subject; - this.isPresence = isPresence; - this.isAbsenceUnexcused = isAbsenceUnexcused; - this.isAbsenceExcused = isAbsenceExcused; - this.isUnexcusedLateness = isUnexcusedLateness; - this.isAbsenceForSchoolReasons = isAbsenceForSchoolReasons; - this.isExcusedLateness = isExcusedLateness; - this.isExemption = isExemption; - } - - @Generated(hash = 921806575) - public AttendanceLesson() { - } - - public Long getId() { - return this.id; - } - - public void setId(Long id) { - this.id = id; - } - - public Long getDayId() { - return this.dayId; - } - - public void setDayId(Long dayId) { - this.dayId = dayId; - } - - public String getDate() { - return this.date; - } - - public AttendanceLesson setDate(String date) { - this.date = date; - return this; - } - - public int getNumber() { - return this.number; - } - - public AttendanceLesson setNumber(int number) { - this.number = number; - return this; - } - - public String getSubject() { - return this.subject; - } - - public AttendanceLesson setSubject(String subject) { - this.subject = subject; - return this; - } - - public boolean getIsPresence() { - return this.isPresence; - } - - public AttendanceLesson setIsPresence(boolean isPresence) { - this.isPresence = isPresence; - return this; - } - - public boolean getIsAbsenceUnexcused() { - return this.isAbsenceUnexcused; - } - - public AttendanceLesson setIsAbsenceUnexcused(boolean isAbsenceUnexcused) { - this.isAbsenceUnexcused = isAbsenceUnexcused; - return this; - } - - public boolean getIsAbsenceExcused() { - return this.isAbsenceExcused; - } - - public AttendanceLesson setIsAbsenceExcused(boolean isAbsenceExcused) { - this.isAbsenceExcused = isAbsenceExcused; - return this; - } - - public boolean getIsUnexcusedLateness() { - return this.isUnexcusedLateness; - } - - public AttendanceLesson setIsUnexcusedLateness(boolean isUnexcusedLateness) { - this.isUnexcusedLateness = isUnexcusedLateness; - return this; - } - - public boolean getIsAbsenceForSchoolReasons() { - return this.isAbsenceForSchoolReasons; - } - - public AttendanceLesson setIsAbsenceForSchoolReasons(boolean isAbsenceForSchoolReasons) { - this.isAbsenceForSchoolReasons = isAbsenceForSchoolReasons; - return this; - } - - public boolean getIsExcusedLateness() { - return this.isExcusedLateness; - } - - public AttendanceLesson setIsExcusedLateness(boolean isExcusedLateness) { - this.isExcusedLateness = isExcusedLateness; - return this; - } - - public boolean getIsExemption() { - return this.isExemption; - } - - public AttendanceLesson setIsExemption(boolean isExemption) { - this.isExemption = isExemption; - return this; - } - - public String getDescription() { - return description; - } - - public AttendanceLesson setDescription(String description) { - this.description = description; - return this; - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 128553479) - public void delete() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.delete(this); - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 1942392019) - public void refresh() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.refresh(this); - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 713229351) - public void update() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.update(this); - } - - /** called by internal mechanisms, do not call yourself. */ - @Generated(hash = 1157101112) - public void __setDaoSession(DaoSession daoSession) { - this.daoSession = daoSession; - myDao = daoSession != null ? daoSession.getAttendanceLessonDao() : null; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Day.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Day.java deleted file mode 100644 index fb3ce4143..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Day.java +++ /dev/null @@ -1,239 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.DaoException; -import org.greenrobot.greendao.annotation.Entity; -import org.greenrobot.greendao.annotation.Generated; -import org.greenrobot.greendao.annotation.Id; -import org.greenrobot.greendao.annotation.Index; -import org.greenrobot.greendao.annotation.OrderBy; -import org.greenrobot.greendao.annotation.Property; -import org.greenrobot.greendao.annotation.ToMany; - -import java.util.List; - -@Entity( - nameInDb = "Days", - active = true, - indexes = {@Index(value = "userId,weekId,date", unique = true)} -) -public class Day { - - @Id(autoincrement = true) - private Long id; - - @Property(nameInDb = "USER_ID") - private Long userId; - - @Property(nameInDb = "WEEK_ID") - private Long weekId; - - @Property(nameInDb = "DATE") - private String date = ""; - - @Property(nameInDb = "DAY_NAME") - private String dayName = ""; - - @Property(nameInDb = "IS_FREE_DAY") - private boolean isFreeDay = false; - - @Property(nameInDb = "FREE_DAY_NAME") - private String freeDayName = ""; - - @ToMany(referencedJoinProperty = "dayId") - private List timetableLessons; - - @ToMany(referencedJoinProperty = "dayId") - @OrderBy("number ASC") - private List attendanceLessons; - - /** - * Used to resolve relations - */ - @Generated(hash = 2040040024) - private transient DaoSession daoSession; - - /** - * Used for active entity operations. - */ - @Generated(hash = 312167767) - private transient DayDao myDao; - - @Generated(hash = 723729681) - public Day(Long id, Long userId, Long weekId, String date, String dayName, - boolean isFreeDay, String freeDayName) { - this.id = id; - this.userId = userId; - this.weekId = weekId; - this.date = date; - this.dayName = dayName; - this.isFreeDay = isFreeDay; - this.freeDayName = freeDayName; - } - - @Generated(hash = 866989762) - public Day() { - } - - public Long getId() { - return this.id; - } - - public void setId(Long id) { - this.id = id; - } - - public Long getUserId() { - return userId; - } - - public Long getWeekId() { - return weekId; - } - - public Day setWeekId(Long weekId) { - this.weekId = weekId; - return this; - } - - public Day setUserId(Long userId) { - this.userId = userId; - return this; - } - - public String getDate() { - return date; - } - - public Day setDate(String date) { - this.date = date; - return this; - } - - public String getDayName() { - return dayName; - } - - public Day setDayName(String dayName) { - this.dayName = dayName; - return this; - } - - public boolean getIsFreeDay() { - return this.isFreeDay; - } - - public Day setIsFreeDay(boolean isFreeDay) { - this.isFreeDay = isFreeDay; - return this; - } - - public String getFreeDayName() { - return freeDayName; - } - - public Day setFreeDayName(String freeDayName) { - this.freeDayName = freeDayName; - return this; - } - - /** - * To-many relationship, resolved on first access (and after reset). - * Changes to to-many relations are not persisted, make changes to the target entity. - */ - @Generated(hash = 218588195) - public List getTimetableLessons() { - if (timetableLessons == null) { - final DaoSession daoSession = this.daoSession; - if (daoSession == null) { - throw new DaoException("Entity is detached from DAO context"); - } - TimetableLessonDao targetDao = daoSession.getTimetableLessonDao(); - List timetableLessonsNew = targetDao - ._queryDay_TimetableLessons(id); - synchronized (this) { - if (timetableLessons == null) { - timetableLessons = timetableLessonsNew; - } - } - } - return timetableLessons; - } - - /** Resets a to-many relationship, making the next get call to query for a fresh result. */ - @Generated(hash = 1687683740) - public synchronized void resetTimetableLessons() { - timetableLessons = null; - } - - /** - * To-many relationship, resolved on first access (and after reset). - * Changes to to-many relations are not persisted, make changes to the target entity. - */ - @Generated(hash = 1166820581) - public List getAttendanceLessons() { - if (attendanceLessons == null) { - final DaoSession daoSession = this.daoSession; - if (daoSession == null) { - throw new DaoException("Entity is detached from DAO context"); - } - AttendanceLessonDao targetDao = daoSession.getAttendanceLessonDao(); - List attendanceLessonsNew = targetDao - ._queryDay_AttendanceLessons(id); - synchronized (this) { - if (attendanceLessons == null) { - attendanceLessons = attendanceLessonsNew; - } - } - } - return attendanceLessons; - } - - /** Resets a to-many relationship, making the next get call to query for a fresh result. */ - @Generated(hash = 1343075564) - public synchronized void resetAttendanceLessons() { - attendanceLessons = null; - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 128553479) - public void delete() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.delete(this); - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 1942392019) - public void refresh() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.refresh(this); - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 713229351) - public void update() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.update(this); - } - - /** called by internal mechanisms, do not call yourself. */ - @Generated(hash = 1409317752) - public void __setDaoSession(DaoSession daoSession) { - this.daoSession = daoSession; - myDao = daoSession != null ? daoSession.getDayDao() : null; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Grade.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Grade.java deleted file mode 100644 index 632f9bcc7..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Grade.java +++ /dev/null @@ -1,331 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; -import org.greenrobot.greendao.DaoException; -import org.greenrobot.greendao.annotation.Entity; -import org.greenrobot.greendao.annotation.Generated; -import org.greenrobot.greendao.annotation.Id; -import org.greenrobot.greendao.annotation.Property; - -import java.io.Serializable; - -import io.github.wulkanowy.R; - -@Entity( - nameInDb = "Grades", - active = true -) -public class Grade implements Serializable { - - @Id(autoincrement = true) - protected Long id; - - @Property(nameInDb = "SUBJECT_ID") - private Long subjectId; - - @Property(nameInDb = "USER_ID") - private Long userId; - - @Property(nameInDb = "SUBJECT") - private String subject = ""; - - @Property(nameInDb = "VALUE") - protected String value = ""; - - @Property(nameInDb = "COLOR") - private String color = ""; - - @Property(nameInDb = "SYMBOL") - private String symbol = ""; - - @Property(nameInDb = "DESCRIPTION") - private String description = ""; - - @Property(nameInDb = "WEIGHT") - private String weight = ""; - - @Property(nameInDb = "DATE") - private String date = ""; - - @Property(nameInDb = "TEACHER") - private String teacher = ""; - - @Property(nameInDb = "SEMESTER") - private String semester = ""; - - @Property(nameInDb = "IS_NEW") - private boolean isNew = false; - - @Property(nameInDb = "READ") - private boolean read = true; - - private static final long serialVersionUID = 42L; - - @Generated(hash = 568899968) - public Grade(Long id, Long subjectId, Long userId, String subject, String value, - String color, String symbol, String description, String weight, - String date, String teacher, String semester, boolean isNew, - boolean read) { - this.id = id; - this.subjectId = subjectId; - this.userId = userId; - this.subject = subject; - this.value = value; - this.color = color; - this.symbol = symbol; - this.description = description; - this.weight = weight; - this.date = date; - this.teacher = teacher; - this.semester = semester; - this.isNew = isNew; - this.read = read; - } - - @Generated(hash = 2042976393) - public Grade() { - } - - /** - * Used to resolve relations - */ - @Generated(hash = 2040040024) - private transient DaoSession daoSession; - - /** - * Used for active entity operations. - */ - @Generated(hash = 681281562) - private transient GradeDao myDao; - - public int getValueColor() { - - String replacedString = value.replaceAll("[^0-9]", ""); - - if (!"".equals(replacedString)) { - switch (Integer.parseInt(replacedString)) { - case 6: - return R.color.six_grade; - case 5: - return R.color.five_grade; - case 4: - return R.color.four_grade; - case 3: - return R.color.three_grade; - case 2: - return R.color.two_grade; - case 1: - return R.color.one_grade; - default: - return R.color.default_grade; - } - } - return R.color.default_grade; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - - if (o == null || getClass() != o.getClass()) return false; - - Grade grade = (Grade) o; - - return new EqualsBuilder() - .append(subject, grade.subject) - .append(value, grade.value) - .append(color, grade.color) - .append(symbol, grade.symbol) - .append(description, grade.description) - .append(weight, grade.weight) - .append(date, grade.date) - .append(teacher, grade.teacher) - .append(semester, grade.semester) - .isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder(17, 37) - .append(subject) - .append(value) - .append(color) - .append(symbol) - .append(description) - .append(weight) - .append(date) - .append(teacher) - .append(semester) - .toHashCode(); - } - - public Long getId() { - return id; - } - - public Grade setId(Long id) { - this.id = id; - return this; - } - - public Long getSubjectId() { - return subjectId; - } - - public Grade setSubjectId(Long subjectId) { - this.subjectId = subjectId; - return this; - } - - public Long getUserId() { - return userId; - } - - public Grade setUserId(Long userId) { - this.userId = userId; - return this; - } - - public String getSubject() { - return subject; - } - - public Grade setSubject(String subject) { - this.subject = subject; - return this; - } - - public String getValue() { - return value; - } - - public Grade setValue(String value) { - this.value = value; - return this; - } - - public String getColor() { - return color; - } - - public Grade setColor(String color) { - this.color = color; - return this; - } - - public String getSymbol() { - return symbol; - } - - public Grade setSymbol(String symbol) { - this.symbol = symbol; - return this; - } - - public String getDescription() { - return description; - } - - public Grade setDescription(String description) { - this.description = description; - return this; - } - - public String getWeight() { - return weight; - } - - public Grade setWeight(String weight) { - this.weight = weight; - return this; - } - - public String getDate() { - return date; - } - - public Grade setDate(String date) { - this.date = date; - return this; - } - - public String getTeacher() { - return teacher; - } - - public Grade setTeacher(String teacher) { - this.teacher = teacher; - return this; - } - - public String getSemester() { - return semester; - } - - public Grade setSemester(String semester) { - this.semester = semester; - return this; - } - - public boolean getIsNew() { - return this.isNew; - } - - public Grade setIsNew(boolean isNew) { - this.isNew = isNew; - return this; - } - - public boolean getRead() { - return this.read; - } - - public Grade setRead(boolean read) { - this.read = read; - return this; - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 128553479) - public void delete() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.delete(this); - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 1942392019) - public void refresh() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.refresh(this); - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 713229351) - public void update() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.update(this); - } - - /** called by internal mechanisms, do not call yourself. */ - @Generated(hash = 1187286414) - public void __setDaoSession(DaoSession daoSession) { - this.daoSession = daoSession; - myDao = daoSession != null ? daoSession.getGradeDao() : null; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Subject.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Subject.java deleted file mode 100644 index 6bcce6229..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Subject.java +++ /dev/null @@ -1,192 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.DaoException; -import org.greenrobot.greendao.annotation.Entity; -import org.greenrobot.greendao.annotation.Generated; -import org.greenrobot.greendao.annotation.Id; -import org.greenrobot.greendao.annotation.Property; -import org.greenrobot.greendao.annotation.ToMany; - -import java.util.List; - -@Entity( - nameInDb = "Subjects", - active = true -) -public class Subject { - - @Id(autoincrement = true) - private Long id; - - @Property(nameInDb = "USER_ID") - private Long userId; - - @Property(nameInDb = "NAME") - private String name; - - @Property(nameInDb = "PREDICTED_RATING") - private String predictedRating; - - @Property(nameInDb = "FINAL_RATING") - private String finalRating; - - @Property(nameInDb = "SEMESTER") - private String semester; - - @ToMany(referencedJoinProperty = "subjectId") - private List gradeList; - - /** - * Used to resolve relations - */ - @Generated(hash = 2040040024) - private transient DaoSession daoSession; - - /** - * Used for active entity operations. - */ - @Generated(hash = 1644932788) - private transient SubjectDao myDao; - - @Generated(hash = 396325764) - public Subject(Long id, Long userId, String name, String predictedRating, - String finalRating, String semester) { - this.id = id; - this.userId = userId; - this.name = name; - this.predictedRating = predictedRating; - this.finalRating = finalRating; - this.semester = semester; - } - - @Generated(hash = 1617906264) - public Subject() { - } - - public Long getId() { - return id; - } - - public Subject setId(Long id) { - this.id = id; - return this; - } - - public Long getUserId() { - return userId; - } - - public Subject setUserId(Long userId) { - this.userId = userId; - return this; - } - - public String getName() { - return name; - } - - public Subject setName(String name) { - this.name = name; - return this; - } - - public String getPredictedRating() { - return predictedRating; - } - - public Subject setPredictedRating(String predictedRating) { - this.predictedRating = predictedRating; - return this; - } - - public String getFinalRating() { - return finalRating; - } - - public Subject setFinalRating(String finalRating) { - this.finalRating = finalRating; - return this; - } - - public String getSemester() { - return semester; - } - - public Subject setSemester(String semester) { - this.semester = semester; - return this; - } - - /** - * To-many relationship, resolved on first access (and after reset). - * Changes to to-many relations are not persisted, make changes to the target entity. - */ - @Generated(hash = 1358847893) - public List getGradeList() { - if (gradeList == null) { - final DaoSession daoSession = this.daoSession; - if (daoSession == null) { - throw new DaoException("Entity is detached from DAO context"); - } - GradeDao targetDao = daoSession.getGradeDao(); - List gradeListNew = targetDao._querySubject_GradeList(id); - synchronized (this) { - if (gradeList == null) { - gradeList = gradeListNew; - } - } - } - return gradeList; - } - - /** - * Resets a to-many relationship, making the next get call to query for a fresh result. - */ - @Generated(hash = 1939990047) - public synchronized void resetGradeList() { - gradeList = null; - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 128553479) - public void delete() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.delete(this); - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 1942392019) - public void refresh() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.refresh(this); - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 713229351) - public void update() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.update(this); - } - - /** called by internal mechanisms, do not call yourself. */ - @Generated(hash = 937984622) - public void __setDaoSession(DaoSession daoSession) { - this.daoSession = daoSession; - myDao = daoSession != null ? daoSession.getSubjectDao() : null; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/TimetableLesson.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/TimetableLesson.java deleted file mode 100644 index 390f679ef..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/TimetableLesson.java +++ /dev/null @@ -1,330 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.DaoException; -import org.greenrobot.greendao.annotation.Entity; -import org.greenrobot.greendao.annotation.Generated; -import org.greenrobot.greendao.annotation.Id; -import org.greenrobot.greendao.annotation.Index; -import org.greenrobot.greendao.annotation.Property; - -import java.io.Serializable; - -@Entity( - nameInDb = "TimetableLessons", - active = true, - indexes = {@Index(value = "dayId,date,startTime,endTime", unique = true)} -) -public class TimetableLesson implements Serializable { - - @Id(autoincrement = true) - private Long id; - - @Property(nameInDb = "DAY_ID") - private Long dayId; - - @Property(nameInDb = "NUMBER_OF_LESSON") - private String number; - - @Property(nameInDb = "SUBJECT_NAME") - private String subject = ""; - - @Property(nameInDb = "TEACHER") - private String teacher = ""; - - @Property(nameInDb = "ROOM") - private String room = ""; - - @Property(nameInDb = "DESCRIPTION") - private String description = ""; - - @Property(nameInDb = "GROUP_NAME") - private String groupName = ""; - - @Property(nameInDb = "START_TIME") - private String startTime = ""; - - @Property(nameInDb = "END_TIME") - private String endTime = ""; - - @Property(nameInDb = "DATE") - private String date = ""; - - @Property(nameInDb = "IS_EMPTY") - private boolean isEmpty = false; - - @Property(nameInDb = "IS_DIVISION_INTO_GROUP") - private boolean isDivisionIntoGroups = false; - - @Property(nameInDb = "IS_PLANNING") - private boolean isPlanning = false; - - @Property(nameInDb = "IS_REALIZED") - private boolean isRealized = false; - - @Property(nameInDb = "IS_MOVED_CANCELED") - private boolean isMovedOrCanceled = false; - - @Property(nameInDb = "IS_NEW_MOVED_IN_CANCELED") - private boolean isNewMovedInOrChanged = false; - - private static final long serialVersionUID = 42L; - - /** - * Used to resolve relations - */ - @Generated(hash = 2040040024) - private transient DaoSession daoSession; - - /** - * Used for active entity operations. - */ - @Generated(hash = 1119360138) - private transient TimetableLessonDao myDao; - - @Generated(hash = 627457324) - public TimetableLesson(Long id, Long dayId, String number, String subject, - String teacher, String room, String description, String groupName, - String startTime, String endTime, String date, boolean isEmpty, - boolean isDivisionIntoGroups, boolean isPlanning, boolean isRealized, - boolean isMovedOrCanceled, boolean isNewMovedInOrChanged) { - this.id = id; - this.dayId = dayId; - this.number = number; - this.subject = subject; - this.teacher = teacher; - this.room = room; - this.description = description; - this.groupName = groupName; - this.startTime = startTime; - this.endTime = endTime; - this.date = date; - this.isEmpty = isEmpty; - this.isDivisionIntoGroups = isDivisionIntoGroups; - this.isPlanning = isPlanning; - this.isRealized = isRealized; - this.isMovedOrCanceled = isMovedOrCanceled; - this.isNewMovedInOrChanged = isNewMovedInOrChanged; - } - - @Generated(hash = 1878030142) - public TimetableLesson() { - } - - public Long getId() { - return this.id; - } - - public void setId(Long id) { - this.id = id; - } - - public Long getDayId() { - return this.dayId; - } - - public void setDayId(Long dayId) { - this.dayId = dayId; - } - - public String getNumber() { - return this.number; - } - - public TimetableLesson setNumber(String number) { - this.number = number; - return this; - } - - public String getSubject() { - return this.subject; - } - - public TimetableLesson setSubject(String subject) { - this.subject = subject; - return this; - } - - public String getTeacher() { - return this.teacher; - } - - public TimetableLesson setTeacher(String teacher) { - this.teacher = teacher; - return this; - } - - public String getRoom() { - return this.room; - } - - public TimetableLesson setRoom(String room) { - this.room = room; - return this; - } - - public String getDescription() { - return this.description; - } - - public TimetableLesson setDescription(String description) { - this.description = description; - return this; - } - - public String getGroupName() { - return this.groupName; - } - - public TimetableLesson setGroupName(String groupName) { - this.groupName = groupName; - return this; - } - - public String getStartTime() { - return this.startTime; - } - - public TimetableLesson setStartTime(String startTime) { - this.startTime = startTime; - return this; - } - - public String getEndTime() { - return this.endTime; - } - - public TimetableLesson setEndTime(String endTime) { - this.endTime = endTime; - return this; - } - - public String getDate() { - return this.date; - } - - public TimetableLesson setDate(String date) { - this.date = date; - return this; - } - - public boolean getIsEmpty() { - return this.isEmpty; - } - - public TimetableLesson setEmpty(boolean isEmpty) { - this.isEmpty = isEmpty; - return this; - } - - public boolean getIsDivisionIntoGroups() { - return this.isDivisionIntoGroups; - } - - public TimetableLesson setDivisionIntoGroups(boolean isDivisionIntoGroups) { - this.isDivisionIntoGroups = isDivisionIntoGroups; - return this; - } - - public boolean getIsPlanning() { - return this.isPlanning; - } - - public TimetableLesson setPlanning(boolean isPlanning) { - this.isPlanning = isPlanning; - return this; - } - - public boolean getIsRealized() { - return this.isRealized; - } - - public TimetableLesson setRealized(boolean isRealized) { - this.isRealized = isRealized; - return this; - } - - public boolean getIsMovedOrCanceled() { - return this.isMovedOrCanceled; - } - - public TimetableLesson setMovedOrCanceled(boolean isMovedOrCanceled) { - this.isMovedOrCanceled = isMovedOrCanceled; - return this; - } - - public boolean getIsNewMovedInOrChanged() { - return this.isNewMovedInOrChanged; - } - - public TimetableLesson setNewMovedInOrChanged(boolean isNewMovedInOrChanged) { - this.isNewMovedInOrChanged = isNewMovedInOrChanged; - return this; - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 128553479) - public void delete() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.delete(this); - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 1942392019) - public void refresh() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.refresh(this); - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 713229351) - public void update() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.update(this); - } - - public void setIsEmpty(boolean isEmpty) { - this.isEmpty = isEmpty; - } - - public void setIsDivisionIntoGroups(boolean isDivisionIntoGroups) { - this.isDivisionIntoGroups = isDivisionIntoGroups; - } - - public void setIsPlanning(boolean isPlanning) { - this.isPlanning = isPlanning; - } - - public void setIsRealized(boolean isRealized) { - this.isRealized = isRealized; - } - - public void setIsMovedOrCanceled(boolean isMovedOrCanceled) { - this.isMovedOrCanceled = isMovedOrCanceled; - } - - public void setIsNewMovedInOrChanged(boolean isNewMovedInOrChanged) { - this.isNewMovedInOrChanged = isNewMovedInOrChanged; - } - - /** called by internal mechanisms, do not call yourself. */ - @Generated(hash = 1885258429) - public void __setDaoSession(DaoSession daoSession) { - this.daoSession = daoSession; - myDao = daoSession != null ? daoSession.getTimetableLessonDao() : null; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Week.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Week.java deleted file mode 100644 index 778f6ca3c..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Week.java +++ /dev/null @@ -1,173 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.DaoException; -import org.greenrobot.greendao.annotation.Entity; -import org.greenrobot.greendao.annotation.Generated; -import org.greenrobot.greendao.annotation.Id; -import org.greenrobot.greendao.annotation.Index; -import org.greenrobot.greendao.annotation.Property; -import org.greenrobot.greendao.annotation.ToMany; - -import java.util.List; - -@Entity( - nameInDb = "Weeks", - active = true, - indexes ={@Index(value = "userId,startDayDate", unique = true)} -) -public class Week { - - @Id(autoincrement = true) - private Long id; - - @Property(nameInDb = "USER_ID") - private Long userId; - - @Property(nameInDb = "START_DATE") - private String startDayDate = ""; - - @Property(nameInDb = "IS_ATTENDANCE_SYNCED") - private boolean isAttendanceSynced = false; - - @Property(nameInDb = "IS_TIMETABLE_SYNCED") - private boolean isTimetableSynced = false; - - @ToMany(referencedJoinProperty = "weekId") - private List dayList; - - /** Used to resolve relations */ - @Generated(hash = 2040040024) - private transient DaoSession daoSession; - - /** Used for active entity operations. */ - @Generated(hash = 1019310398) - private transient WeekDao myDao; - - @Generated(hash = 1745118398) - public Week(Long id, Long userId, String startDayDate, boolean isAttendanceSynced, - boolean isTimetableSynced) { - this.id = id; - this.userId = userId; - this.startDayDate = startDayDate; - this.isAttendanceSynced = isAttendanceSynced; - this.isTimetableSynced = isTimetableSynced; - } - - @Generated(hash = 2135529658) - public Week() { - } - - public Long getId() { - return id; - } - - public Week setId(Long id) { - this.id = id; - return this; - } - - public Long getUserId() { - return userId; - } - - public Week setUserId(Long userId) { - this.userId = userId; - return this; - } - - public String getStartDayDate() { - return startDayDate; - } - - public Week setStartDayDate(String startDayDate) { - this.startDayDate = startDayDate; - return this; - } - - public boolean getIsAttendanceSynced() { - return this.isAttendanceSynced; - } - - public void setIsAttendanceSynced(boolean isAttendanceSynced) { - this.isAttendanceSynced = isAttendanceSynced; - } - - public boolean getIsTimetableSynced() { - return this.isTimetableSynced; - } - - public void setIsTimetableSynced(boolean isTimetableSynced) { - this.isTimetableSynced = isTimetableSynced; - } - - /** - * To-many relationship, resolved on first access (and after reset). - * Changes to to-many relations are not persisted, make changes to the target entity. - */ - @Generated(hash = 1562119145) - public List getDayList() { - if (dayList == null) { - final DaoSession daoSession = this.daoSession; - if (daoSession == null) { - throw new DaoException("Entity is detached from DAO context"); - } - DayDao targetDao = daoSession.getDayDao(); - List dayListNew = targetDao._queryWeek_DayList(id); - synchronized (this) { - if (dayList == null) { - dayList = dayListNew; - } - } - } - return dayList; - } - - /** Resets a to-many relationship, making the next get call to query for a fresh result. */ - @Generated(hash = 1010399236) - public synchronized void resetDayList() { - dayList = null; - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 128553479) - public void delete() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.delete(this); - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 1942392019) - public void refresh() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.refresh(this); - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 713229351) - public void update() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.update(this); - } - - /** called by internal mechanisms, do not call yourself. */ - @Generated(hash = 665278367) - public void __setDaoSession(DaoSession daoSession) { - this.daoSession = daoSession; - myDao = daoSession != null ? daoSession.getWeekDao() : null; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/AdminMessage.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/AdminMessage.kt new file mode 100644 index 000000000..97fec69b7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/AdminMessage.kt @@ -0,0 +1,40 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import kotlinx.serialization.Serializable + +@Serializable +@Entity(tableName = "AdminMessages") +data class AdminMessage( + + @PrimaryKey + val id: Int, + + val title: String, + + val content: String, + + @ColumnInfo(name = "version_name") + val versionMin: Int? = null, + + @ColumnInfo(name = "version_max") + val versionMax: Int? = null, + + @ColumnInfo(name = "target_register_host") + val targetRegisterHost: String? = null, + + @ColumnInfo(name = "target_flavor") + val targetFlavor: String? = null, + + @ColumnInfo(name = "destination_url") + val destinationUrl: String? = null, + + val priority: String, + + val type: String, + + @ColumnInfo(name = "is_dismissible") + val isDismissible: Boolean = false +) diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt new file mode 100644 index 000000000..b40dd52e5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt @@ -0,0 +1,53 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable +import java.time.LocalDate + +@Entity(tableName = "Attendance") +data class Attendance( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "diary_id") + val diaryId: Int, + + @ColumnInfo(name = "time_id") + val timeId: Int, + + val date: LocalDate, + + val number: Int, + + val subject: String, + + val name: String, + + val presence: Boolean, + + val absence: Boolean, + + val exemption: Boolean, + + val lateness: Boolean, + + val excused: Boolean, + + val deleted: Boolean, + + val excusable: Boolean, + + @ColumnInfo(name = "excuse_status") + val excuseStatus: String? + +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 + + @ColumnInfo(name = "is_notified") + var isNotified: Boolean = true +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/AttendanceSummary.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/AttendanceSummary.kt new file mode 100644 index 000000000..7d628ebaa --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/AttendanceSummary.kt @@ -0,0 +1,43 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable +import java.time.Month + +@Entity(tableName = "AttendanceSummary") +data class AttendanceSummary( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "diary_id") + val diaryId: Int, + + @ColumnInfo(name = "subject_id") + val subjectId: Int, + + val month: Month, + + val presence: Int, + + val absence: Int, + + @ColumnInfo(name = "absence_excused") + val absenceExcused: Int, + + @ColumnInfo(name = "absence_for_school_reasons") + val absenceForSchoolReasons: Int, + + val lateness: Int, + + @ColumnInfo(name = "lateness_excused") + val latenessExcused: Int, + + val exemption: Int +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/CompletedLesson.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/CompletedLesson.kt new file mode 100644 index 000000000..e305d467a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/CompletedLesson.kt @@ -0,0 +1,40 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable +import java.time.LocalDate + +@Entity(tableName = "CompletedLesson") +data class CompletedLesson( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "diary_id") + val diaryId: Int, + + val date: LocalDate, + + val number: Int, + + val subject: String, + + val topic: String, + + val teacher: String, + + @ColumnInfo(name = "teacher_symbol") + val teacherSymbol: String, + + val substitution: String, + + val absence: String, + + val resources: String +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Conference.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Conference.kt new file mode 100644 index 000000000..ba3958dbc --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Conference.kt @@ -0,0 +1,38 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable +import java.time.Instant + +@Entity(tableName = "Conferences") +data class Conference( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "diary_id") + val diaryId: Int, + + val title: String, + + val subject: String, + + val agenda: String, + + @ColumnInfo(name = "present_on_conference") + val presentOnConference: String, + + @ColumnInfo(name = "conference_id") + val conferenceId: Int, + + val date: Instant, +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 + + @ColumnInfo(name = "is_notified") + var isNotified: Boolean = true +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt new file mode 100644 index 000000000..50299e607 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt @@ -0,0 +1,42 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable +import java.time.LocalDate + +@Entity(tableName = "Exams") +data class Exam( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "diary_id") + val diaryId: Int, + + val date: LocalDate, + + @ColumnInfo(name = "entry_date") + val entryDate: LocalDate, + + val subject: String, + + val group: String, + + val type: String, + + val description: String, + + val teacher: String, + + @ColumnInfo(name = "teacher_symbol") + val teacherSymbol: String +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 + + @ColumnInfo(name = "is_notified") + var isNotified: Boolean = true +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt new file mode 100644 index 000000000..a0f1c3a6d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt @@ -0,0 +1,52 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable +import java.time.LocalDate + +@Entity(tableName = "Grades") +data class Grade( + + @ColumnInfo(name = "semester_id") + val semesterId: Int, + + @ColumnInfo(name = "student_id") + val studentId: Int, + + val subject: String, + + val entry: String, + + val value: Double, + + val modifier: Double, + + val comment: String, + + val color: String, + + @ColumnInfo(name = "grade_symbol") + val gradeSymbol: String, + + val description: String, + + val weight: String, + + val weightValue: Double, + + val date: LocalDate, + + val teacher: String +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 + + @ColumnInfo(name = "is_read") + var isRead: Boolean = true + + @ColumnInfo(name = "is_notified") + var isNotified: Boolean = true +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradePartialStatistics.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradePartialStatistics.kt new file mode 100644 index 000000000..db164afde --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradePartialStatistics.kt @@ -0,0 +1,33 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "GradePartialStatistics") +data class GradePartialStatistics( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "semester_id") + val semesterId: Int, + + val subject: String, + + @ColumnInfo(name = "class_average") + val classAverage: String, + + @ColumnInfo(name = "student_average") + val studentAverage: String, + + @ColumnInfo(name = "class_amounts") + val classAmounts: List, + + @ColumnInfo(name = "student_amounts") + val studentAmounts: List + +) { + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradePointsStatistics.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradePointsStatistics.kt new file mode 100644 index 000000000..407f18bd6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradePointsStatistics.kt @@ -0,0 +1,24 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "GradesPointsStatistics") +data class GradePointsStatistics( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "semester_id") + val semesterId: Int, + + val subject: String, + + val others: Double, + + val student: Double +) { + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSemesterStatistics.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSemesterStatistics.kt new file mode 100644 index 000000000..9e08b86bc --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSemesterStatistics.kt @@ -0,0 +1,31 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "GradeSemesterStatistics") +data class GradeSemesterStatistics( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "semester_id") + val semesterId: Int, + + val subject: String, + + val amounts: List, + + @ColumnInfo(name = "student_grade") + val studentGrade: Int +) { + @PrimaryKey(autoGenerate = true) + var id: Long = 0 + + @Transient + var classAverage: String = "" + + @Transient + var studentAverage: String = "" +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt new file mode 100644 index 000000000..a42832ced --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt @@ -0,0 +1,52 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.time.Instant + +@Entity(tableName = "GradesSummary") +data class GradeSummary( + + @ColumnInfo(name = "semester_id") + val semesterId: Int, + + @ColumnInfo(name = "student_id") + val studentId: Int, + + val position: Int, + + val subject: String, + + @ColumnInfo(name = "predicted_grade") + val predictedGrade: String, + + @ColumnInfo(name = "final_grade") + val finalGrade: String, + + @ColumnInfo(name = "proposed_points") + val proposedPoints: String, + + @ColumnInfo(name = "final_points") + val finalPoints: String, + + @ColumnInfo(name = "points_sum") + val pointsSum: String, + + val average: Double +) { + @PrimaryKey(autoGenerate = true) + var id: Long = 0 + + @ColumnInfo(name = "is_predicted_grade_notified") + var isPredictedGradeNotified: Boolean = true + + @ColumnInfo(name = "is_final_grade_notified") + var isFinalGradeNotified: Boolean = true + + @ColumnInfo(name = "predicted_grade_last_change") + var predictedGradeLastChange: Instant = Instant.now() + + @ColumnInfo(name = "final_grade_last_change") + var finalGradeLastChange: Instant = Instant.now() +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Homework.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Homework.kt new file mode 100644 index 000000000..4538cf31f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Homework.kt @@ -0,0 +1,46 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable +import java.time.LocalDate + +@Entity(tableName = "Homework") +data class Homework( + + @ColumnInfo(name = "semester_id") + val semesterId: Int, + + @ColumnInfo(name = "student_id") + val studentId: Int, + + val date: LocalDate, + + @ColumnInfo(name = "entry_date") + val entryDate: LocalDate, + + val subject: String, + + val content: String, + + val teacher: String, + + @ColumnInfo(name = "teacher_symbol") + val teacherSymbol: String, + + val attachments: List> +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 + + @ColumnInfo(name = "is_done") + var isDone: Boolean = false + + @ColumnInfo(name = "is_notified") + var isNotified: Boolean = true + + @ColumnInfo(name = "is_added_by_user") + var isAddedByUser: Boolean = false +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/LuckyNumber.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/LuckyNumber.kt new file mode 100644 index 000000000..7c24c8f5c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/LuckyNumber.kt @@ -0,0 +1,27 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable +import java.time.LocalDate + +@Entity(tableName = "LuckyNumbers") +data class LuckyNumber ( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + val date: LocalDate, + + @ColumnInfo(name = "lucky_number") + val luckyNumber: Int + +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 + + @ColumnInfo(name = "is_notified") + var isNotified: Boolean = true +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt new file mode 100644 index 000000000..8782bc765 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt @@ -0,0 +1,58 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable +import java.time.Instant + +@Entity(tableName = "Messages") +data class Message( + + @ColumnInfo(name = "student_id") + val studentId: Long, + + @ColumnInfo(name = "real_id") + val realId: Int, + + @ColumnInfo(name = "message_id") + val messageId: Int, + + @ColumnInfo(name = "sender_name") + val sender: String, + + @ColumnInfo(name = "sender_id") + val senderId: Int, + + @ColumnInfo(name = "recipient_name") + val recipient: String, + + val subject: String, + + val date: Instant, + + @ColumnInfo(name = "folder_id") + val folderId: Int, + + var unread: Boolean, + + val removed: Boolean, + + @ColumnInfo(name = "has_attachments") + val hasAttachments: Boolean +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 + + @ColumnInfo(name = "is_notified") + var isNotified: Boolean = true + + @ColumnInfo(name = "unread_by") + var unreadBy: Int = 0 + + @ColumnInfo(name = "read_by") + var readBy: Int = 0 + + var content: String = "" +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageAttachment.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageAttachment.kt new file mode 100644 index 000000000..d1886e910 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageAttachment.kt @@ -0,0 +1,26 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable + +@Entity(tableName = "MessageAttachments") +data class MessageAttachment( + + @PrimaryKey + @ColumnInfo(name = "real_id") + val realId: Int, + + @ColumnInfo(name = "message_id") + val messageId: Int, + + @ColumnInfo(name = "one_drive_id") + val oneDriveId: String, + + @ColumnInfo(name = "url") + val url: String, + + @ColumnInfo(name = "filename") + val filename: String +) : Serializable diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithAttachment.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithAttachment.kt new file mode 100644 index 000000000..2e7af0f40 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithAttachment.kt @@ -0,0 +1,12 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.Embedded +import androidx.room.Relation + +data class MessageWithAttachment( + @Embedded + val message: Message, + + @Relation(parentColumn = "message_id", entityColumn = "message_id") + val attachments: List +) diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt new file mode 100644 index 000000000..887e43239 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt @@ -0,0 +1,25 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable +import java.time.Instant + +@Entity(tableName = "MobileDevices") +data class MobileDevice( + + @ColumnInfo(name = "student_id") + val userLoginId: Int, + + @ColumnInfo(name = "device_id") + val deviceId: Int, + + val name: String, + + val date: Instant, +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Note.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Note.kt new file mode 100644 index 000000000..cfd549625 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Note.kt @@ -0,0 +1,43 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable +import java.time.LocalDate + +@Entity(tableName = "Notes") +data class Note( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + val date: LocalDate, + + val teacher: String, + + @ColumnInfo(name = "teacher_symbol") + val teacherSymbol: String, + + val category: String, + + @ColumnInfo(name = "category_type") + val categoryType: Int, + + @ColumnInfo(name = "is_points_show") + val isPointsShow: Boolean, + + val points: Int, + + val content: String +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 + + @ColumnInfo(name = "is_read") + var isRead: Boolean = true + + @ColumnInfo(name = "is_notified") + var isNotified: Boolean = true +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Notification.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Notification.kt new file mode 100644 index 000000000..c3267f24e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Notification.kt @@ -0,0 +1,31 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import io.github.wulkanowy.services.sync.notifications.NotificationType +import io.github.wulkanowy.ui.modules.Destination +import java.time.Instant + +@Entity(tableName = "Notifications") +data class Notification( + + @ColumnInfo(name = "student_id") + val studentId: Long, + + val title: String, + + val content: String, + + val type: NotificationType, + + @ColumnInfo(defaultValue = "{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}") + val destination: Destination, + + val date: Instant, + + val data: String? = null +) { + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt new file mode 100644 index 000000000..223322705 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt @@ -0,0 +1,39 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable + +@kotlinx.serialization.Serializable +@Entity(tableName = "Recipients") +data class Recipient( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "real_id") + val realId: String, + + val name: String, + + @ColumnInfo(name = "real_name") + val realName: String, + + @ColumnInfo(name = "login_id") + val loginId: Int, + + @ColumnInfo(name = "unit_id") + val unitId: Int, + + val role: Int, + + val hash: String + +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 + + override fun toString() = name +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt new file mode 100644 index 000000000..0570a2ffd --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt @@ -0,0 +1,32 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable + +@Entity(tableName = "ReportingUnits") +data class ReportingUnit( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "real_id") + val unitId: Int, + + @ColumnInfo(name = "short") + val shortName: String, + + @ColumnInfo(name = "sender_id") + val senderId: Int, + + @ColumnInfo(name = "sender_name") + val senderName: String, + + val roles: List + +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/School.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/School.kt new file mode 100644 index 000000000..20fae4508 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/School.kt @@ -0,0 +1,30 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable + +@Entity(tableName = "School") +data class School( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "class_id") + val classId: Int, + + val name: String, + + val address: String, + + val contact: String, + + val headmaster: String, + + val pedagogue: String +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/SchoolAnnouncement.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/SchoolAnnouncement.kt new file mode 100644 index 000000000..c8731bded --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/SchoolAnnouncement.kt @@ -0,0 +1,27 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable +import java.time.LocalDate + +@Entity(tableName = "SchoolAnnouncements") +data class SchoolAnnouncement( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + val date: LocalDate, + + val subject: String, + + val content: String +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 + + @ColumnInfo(name = "is_notified") + var isNotified: Boolean = true +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt new file mode 100644 index 000000000..187890c9b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt @@ -0,0 +1,55 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.Index +import androidx.room.PrimaryKey +import java.io.Serializable +import java.time.LocalDate + +@Entity( + tableName = "Semesters", indices = [Index( + value = ["student_id", "diary_id", "kindergarten_diary_id", "semester_id"], + unique = true + )] +) +data class Semester( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "diary_id") + val diaryId: Int, + + @ColumnInfo(name = "kindergarten_diary_id", defaultValue = "0") + val kindergartenDiaryId: Int, + + @ColumnInfo(name = "diary_name") + val diaryName: String, + + @ColumnInfo(name = "school_year") + val schoolYear: Int, + + @ColumnInfo(name = "semester_id") + val semesterId: Int, + + @ColumnInfo(name = "semester_name") + val semesterName: Int, + + val start: LocalDate, + + val end: LocalDate, + + @ColumnInfo(name = "class_id") + val classId: Int, + + @ColumnInfo(name = "unit_id") + val unitId: Int +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 + + @ColumnInfo(name = "is_current") + var current: Boolean = false +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt new file mode 100644 index 000000000..76da9643d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt @@ -0,0 +1,87 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.Index +import androidx.room.PrimaryKey +import java.io.Serializable +import java.time.Instant + +@Entity( + tableName = "Students", + indices = [Index( + value = ["email", "symbol", "student_id", "school_id", "class_id"], + unique = true + )] +) +data class Student( + + @ColumnInfo(name = "scrapper_base_url") + val scrapperBaseUrl: String, + + @ColumnInfo(name = "mobile_base_url") + val mobileBaseUrl: String, + + @ColumnInfo(name = "login_type") + val loginType: String, + + @ColumnInfo(name = "login_mode") + val loginMode: String, + + @ColumnInfo(name = "certificate_key") + val certificateKey: String, + + @ColumnInfo(name = "private_key") + val privateKey: String, + + @ColumnInfo(name = "is_parent") + val isParent: Boolean, + + val email: String, + + var password: String, + + val symbol: String, + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "user_login_id") + val userLoginId: Int, + + @ColumnInfo(name = "user_name") + val userName: String, + + @ColumnInfo(name = "student_name") + val studentName: String, + + @ColumnInfo(name = "school_id") + val schoolSymbol: String, + + @ColumnInfo(name = "school_short") + val schoolShortName: String, + + @ColumnInfo(name = "school_name") + val schoolName: String, + + @ColumnInfo(name = "class_name") + val className: String, + + @ColumnInfo(name = "class_id") + val classId: Int, + + @ColumnInfo(name = "is_current") + val isCurrent: Boolean, + + @ColumnInfo(name = "registration_date") + val registrationDate: Instant, +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 + + var nick = "" + + @ColumnInfo(name = "avatar_color") + var avatarColor = 0L +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentInfo.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentInfo.kt new file mode 100644 index 000000000..7366e547f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentInfo.kt @@ -0,0 +1,85 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Embedded +import androidx.room.Entity +import androidx.room.PrimaryKey +import io.github.wulkanowy.data.enums.Gender +import java.io.Serializable +import java.time.LocalDate + +@Entity(tableName = "StudentInfo") +data class StudentInfo( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "full_name") + val fullName: String, + + @ColumnInfo(name = "first_name") + val firstName: String, + + @ColumnInfo(name = "second_name") + val secondName: String, + + val surname: String, + + @ColumnInfo(name = "birth_date") + val birthDate: LocalDate, + + @ColumnInfo(name = "birth_place") + val birthPlace: String, + + val gender: Gender, + + @ColumnInfo(name = "has_polish_citizenship") + val hasPolishCitizenship: Boolean, + + @ColumnInfo(name = "family_name") + val familyName: String, + + @ColumnInfo(name = "parents_names") + val parentsNames: String, + + val address: String, + + @ColumnInfo(name = "registered_address") + val registeredAddress: String, + + @ColumnInfo(name = "correspondence_address") + val correspondenceAddress: String, + + @ColumnInfo(name = "phone_number") + val phoneNumber: String, + + @ColumnInfo(name = "cell_phone_number") + val cellPhoneNumber: String, + + val email: String, + + @Embedded(prefix = "first_guardian_") + val firstGuardian: StudentGuardian?, + + @Embedded(prefix = "second_guardian_") + val secondGuardian: StudentGuardian? + +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} + +data class StudentGuardian( + + @ColumnInfo(name = "full_name") + val fullName: String, + + val kinship: String, + + val address: String, + + val phones: String, + + val email: String +) : Serializable diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentNickAndAvatar.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentNickAndAvatar.kt new file mode 100644 index 000000000..546059eea --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentNickAndAvatar.kt @@ -0,0 +1,20 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable + +@Entity +data class StudentNickAndAvatar( + + val nick: String, + + @ColumnInfo(name = "avatar_color") + var avatarColor: Long + +) : Serializable { + + @PrimaryKey + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentWithSemesters.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentWithSemesters.kt new file mode 100644 index 000000000..9362a954e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentWithSemesters.kt @@ -0,0 +1,13 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.Embedded +import androidx.room.Relation +import java.io.Serializable + +data class StudentWithSemesters( + @Embedded + val student: Student, + + @Relation(parentColumn = "student_id", entityColumn = "student_id") + val semesters: List +) : Serializable diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Subject.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Subject.kt new file mode 100644 index 000000000..dbaa6f4ed --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Subject.kt @@ -0,0 +1,25 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable + +@Entity(tableName = "Subjects") +data class Subject( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "diary_id") + val diaryId: Int, + + @ColumnInfo(name = "real_id") + val realId: Int, + + val name: String +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Teacher.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Teacher.kt new file mode 100644 index 000000000..52c96f8a3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Teacher.kt @@ -0,0 +1,27 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable + +@Entity(tableName = "Teachers") +data class Teacher( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "class_id") + val classId: Int, + + val subject: String, + + val name: String, + + @ColumnInfo(name = "short_name") + val shortName: String +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt new file mode 100644 index 000000000..d23d388f9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt @@ -0,0 +1,56 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable +import java.time.Instant +import java.time.LocalDate + +@Entity(tableName = "Timetable") +data class Timetable( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "diary_id") + val diaryId: Int, + + val number: Int, + + val start: Instant, + + val end: Instant, + + val date: LocalDate, + + val subject: String, + + val subjectOld: String, + + val group: String, + + val room: String, + + val roomOld: String, + + val teacher: String, + + val teacherOld: String, + + val info: String, + + @ColumnInfo(name = "student_plan") + val isStudentPlan: Boolean, + + val changes: Boolean, + + val canceled: Boolean +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 + + @ColumnInfo(name = "is_notified") + var isNotified: Boolean = true +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/TimetableAdditional.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/TimetableAdditional.kt new file mode 100644 index 000000000..478026102 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/TimetableAdditional.kt @@ -0,0 +1,37 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable +import java.time.Instant +import java.time.LocalDate +import java.util.* + +@Entity(tableName = "TimetableAdditional") +data class TimetableAdditional( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "diary_id") + val diaryId: Int, + + val start: Instant, + + val end: Instant, + + val date: LocalDate, + + val subject: String, +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 + + @ColumnInfo(name = "repeat_id", defaultValue = "NULL") + var repeatId: UUID? = null + + @ColumnInfo(name = "is_added_by_user", defaultValue = "0") + var isAddedByUser: Boolean = false +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/TimetableHeader.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/TimetableHeader.kt new file mode 100644 index 000000000..7f21bf547 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/TimetableHeader.kt @@ -0,0 +1,25 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable +import java.time.LocalDate + +@Entity(tableName = "TimetableHeaders") +data class TimetableHeader( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "diary_id") + val diaryId: Int, + + val date: LocalDate, + + val content: String, +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration10.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration10.kt new file mode 100644 index 000000000..c26a02d1f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration10.kt @@ -0,0 +1,11 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration10 : Migration(9, 10) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Grades_Summary RENAME TO GradesSummary") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt new file mode 100644 index 000000000..6d129bca0 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt @@ -0,0 +1,34 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration11 : Migration(10, 11) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE IF NOT EXISTS Grades_temp ( + id INTEGER PRIMARY KEY NOT NULL, + is_read INTEGER NOT NULL, + is_notified INTEGER NOT NULL, + semester_id INTEGER NOT NULL, + student_id INTEGER NOT NULL, + subject TEXT NOT NULL, + entry TEXT NOT NULL, + value INTEGER NOT NULL, + modifier REAL NOT NULL, + comment TEXT NOT NULL, + color TEXT NOT NULL, + grade_symbol TEXT NOT NULL, + description TEXT NOT NULL, + weight TEXT NOT NULL, + weightValue REAL NOT NULL, + date INTEGER NOT NULL, + teacher TEXT NOT NULL + ) + """) + database.execSQL("INSERT INTO Grades_temp SELECT * FROM Grades") + database.execSQL("DROP TABLE Grades") + database.execSQL("ALTER TABLE Grades_temp RENAME TO Grades") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt new file mode 100644 index 000000000..c827b82ba --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt @@ -0,0 +1,71 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration12 : Migration(11, 12) { + + override fun migrate(database: SupportSQLiteDatabase) { + createTempStudentsTable(database) + replaceStudentTable(database) + updateStudentsWithClassId(database, getStudentsIds(database)) + removeStudentsWithNoClassId(database) + ensureThereIsOnlyOneCurrentStudent(database) + } + + private fun createTempStudentsTable(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE IF NOT EXISTS Students_tmp ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + endpoint TEXT NOT NULL, + loginType TEXT NOT NULL, + email TEXT NOT NULL, + password TEXT NOT NULL, + symbol TEXT NOT NULL, + student_id INTEGER NOT NULL, + student_name TEXT NOT NULL, + school_id TEXT NOT NULL, + school_name TEXT NOT NULL, + is_current INTEGER NOT NULL, + registration_date INTEGER NOT NULL, + class_id INTEGER NOT NULL + ) + """) + database.execSQL("CREATE UNIQUE INDEX index_Students_email_symbol_student_id_school_id_class_id ON Students_tmp (email, symbol, student_id, school_id, class_id)") + } + + private fun replaceStudentTable(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Students ADD COLUMN class_id INTEGER DEFAULT 0 NOT NULL") + database.execSQL("INSERT INTO Students_tmp SELECT * FROM Students") + database.execSQL("DROP TABLE Students") + database.execSQL("ALTER TABLE Students_tmp RENAME TO Students") + } + + private fun getStudentsIds(database: SupportSQLiteDatabase): List { + val students = mutableListOf() + database.query("SELECT student_id FROM Students").use { + if (it.moveToFirst()) { + do { + students.add(it.getInt(0)) + } while (it.moveToNext()) + } + } + + return students + } + + private fun updateStudentsWithClassId(database: SupportSQLiteDatabase, students: List) { + students.forEach { + database.execSQL("UPDATE Students SET class_id = IFNULL((SELECT class_id FROM Semesters WHERE student_id = $it), 0) WHERE student_id = $it") + } + } + + private fun removeStudentsWithNoClassId(database: SupportSQLiteDatabase) { + database.execSQL("DELETE FROM Students WHERE class_id = 0") + } + + private fun ensureThereIsOnlyOneCurrentStudent(database: SupportSQLiteDatabase) { + database.execSQL("UPDATE Students SET is_current = 0") + database.execSQL("UPDATE Students SET is_current = 1 WHERE id = (SELECT MAX(id) FROM Students)") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt new file mode 100644 index 000000000..36de1e837 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt @@ -0,0 +1,68 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration13 : Migration(12, 13) { + + override fun migrate(database: SupportSQLiteDatabase) { + addClassNameToStudents(database, getStudentsIds(database)) + updateSemestersTable(database) + markAtLeastAndOnlyOneSemesterAtCurrent(database, getStudentsAndClassIds(database)) + clearMessagesTable(database) + } + + private fun addClassNameToStudents(database: SupportSQLiteDatabase, students: List>) { + database.execSQL("ALTER TABLE Students ADD COLUMN class_name TEXT DEFAULT \"\" NOT NULL") + + students.forEach { (id, name) -> + val schoolName = name.substringAfter(" - ") + val className = name.substringBefore(" - ", "").replace("Klasa ", "") + database.execSQL("UPDATE Students SET class_name = '$className' WHERE id = '$id'") + database.execSQL("UPDATE Students SET school_name = '$schoolName' WHERE id = '$id'") + } + } + + private fun getStudentsIds(database: SupportSQLiteDatabase): MutableList> { + val students = mutableListOf>() + database.query("SELECT id, school_name FROM Students").use { + if (it.moveToFirst()) { + do { + students.add(it.getInt(0) to it.getString(1)) + } while (it.moveToNext()) + } + } + + return students + } + + private fun updateSemestersTable(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Semesters ADD COLUMN school_year INTEGER DEFAULT 1970 NOT NULL") + database.execSQL("ALTER TABLE Semesters ADD COLUMN start INTEGER DEFAULT 0 NOT NULL") + database.execSQL("ALTER TABLE Semesters ADD COLUMN `end` INTEGER DEFAULT 0 NOT NULL") + } + + private fun getStudentsAndClassIds(database: SupportSQLiteDatabase): List> { + val students = mutableListOf>() + database.query("SELECT student_id, class_id FROM Students").use { + if (it.moveToFirst()) { + do { + students.add(it.getInt(0) to it.getInt(1)) + } while (it.moveToNext()) + } + } + + return students + } + + private fun markAtLeastAndOnlyOneSemesterAtCurrent(database: SupportSQLiteDatabase, students: List>) { + students.forEach { (studentId, classId) -> + database.execSQL("UPDATE Semesters SET is_current = 0 WHERE student_id = '$studentId' AND class_id = '$classId'") + database.execSQL("UPDATE Semesters SET is_current = 1 WHERE id = (SELECT id FROM Semesters WHERE student_id = '$studentId' AND class_id = '$classId' ORDER BY semester_id DESC)") + } + } + + private fun clearMessagesTable(database: SupportSQLiteDatabase) { + database.execSQL("DELETE FROM Messages") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration14.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration14.kt new file mode 100644 index 000000000..4dac0d306 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration14.kt @@ -0,0 +1,26 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration14 : Migration(13, 14) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS GradesSummary") + database.execSQL(""" + CREATE TABLE IF NOT EXISTS GradesSummary ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + semester_id INTEGER NOT NULL, + student_id INTEGER NOT NULL, + position INTEGER NOT NULL, + subject TEXT NOT NULL, + predicted_grade TEXT NOT NULL, + final_grade TEXT NOT NULL, + proposed_points TEXT NOT NULL, + final_points TEXT NOT NULL, + points_sum TEXT NOT NULL, + average REAL NOT NULL + ) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration15.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration15.kt new file mode 100644 index 000000000..5be49a95b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration15.kt @@ -0,0 +1,19 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration15 : Migration(14, 15) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE IF NOT EXISTS MobileDevices ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + student_id INTEGER NOT NULL, + device_id INTEGER NOT NULL, + name TEXT NOT NULL, + date INTEGER NOT NULL + ) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration16.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration16.kt new file mode 100644 index 000000000..7f40c0f8d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration16.kt @@ -0,0 +1,20 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration16 : Migration(15, 16) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE IF NOT EXISTS Teachers ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + student_id INTEGER NOT NULL, + class_id INTEGER NOT NULL, + subject TEXT NOT NULL, + name TEXT NOT NULL, + short_name TEXT NOT NULL + ) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration17.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration17.kt new file mode 100644 index 000000000..e2a2574db --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration17.kt @@ -0,0 +1,29 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration17 : Migration(16, 17) { + + override fun migrate(database: SupportSQLiteDatabase) { + createGradesPointsStatisticsTable(database) + truncateSemestersTable(database) + } + + private fun createGradesPointsStatisticsTable(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE IF NOT EXISTS GradesPointsStatistics( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + student_id INTEGER NOT NULL, + semester_id INTEGER NOT NULL, + subject TEXT NOT NULL, + others REAL NOT NULL, + student REAL NOT NULL + ) + """) + } + + private fun truncateSemestersTable(database: SupportSQLiteDatabase) { + database.execSQL("DELETE FROM Semesters") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration18.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration18.kt new file mode 100644 index 000000000..6c5e56c6a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration18.kt @@ -0,0 +1,22 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration18 : Migration(17, 18) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE IF NOT EXISTS School ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + student_id INTEGER NOT NULL, + class_id INTEGER NOT NULL, + name TEXT NOT NULL, + address TEXT NOT NULL, + contact TEXT NOT NULL, + headmaster TEXT NOT NULL, + pedagogue TEXT NOT NULL + ) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration19.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration19.kt new file mode 100644 index 000000000..d38f1245a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration19.kt @@ -0,0 +1,119 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase +import io.github.wulkanowy.data.db.SharedPrefProvider + +class Migration19(private val sharedPrefProvider: SharedPrefProvider) : Migration(18, 19) { + + override fun migrate(database: SupportSQLiteDatabase) { + migrateMessages(database) + migrateGrades(database) + migrateStudents(database) + migrateSharedPreferences() + } + + private fun migrateMessages(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE Messages") + database.execSQL(""" + CREATE TABLE IF NOT EXISTS Messages ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + is_notified INTEGER NOT NULL, + student_id INTEGER NOT NULL, + real_id INTEGER NOT NULL, + message_id INTEGER NOT NULL, + sender_name TEXT NOT NULL, + sender_id INTEGER NOT NULL, + recipient_name TEXT NOT NULL, + subject TEXT NOT NULL, + content TEXT NOT NULL, + date INTEGER NOT NULL, + folder_id INTEGER NOT NULL, + unread INTEGER NOT NULL, + unread_by INTEGER NOT NULL, + read_by INTEGER NOT NULL, + removed INTEGER NOT NULL + ) + """) + } + + private fun migrateGrades(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE Grades") + database.execSQL(""" + CREATE TABLE IF NOT EXISTS Grades ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + is_read INTEGER NOT NULL, + is_notified INTEGER NOT NULL, + semester_id INTEGER NOT NULL, + student_id INTEGER NOT NULL, + subject TEXT NOT NULL, + entry TEXT NOT NULL, + value REAL NOT NULL, + modifier REAL NOT NULL, + comment TEXT NOT NULL, + color TEXT NOT NULL, + grade_symbol TEXT NOT NULL, + description TEXT NOT NULL, + weight TEXT NOT NULL, + weightValue REAL NOT NULL, + date INTEGER NOT NULL, + teacher TEXT NOT NULL + ) + """) + } + + private fun migrateStudents(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE IF NOT EXISTS Students_tmp ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + scrapper_base_url TEXT NOT NULL, + mobile_base_url TEXT NOT NULL, + is_parent INTEGER NOT NULL, + login_type TEXT NOT NULL, + login_mode TEXT NOT NULL, + certificate_key TEXT NOT NULL, + private_key TEXT NOT NULL, + email TEXT NOT NULL, + password TEXT NOT NULL, + symbol TEXT NOT NULL, + student_id INTEGER NOT NULL, + user_login_id INTEGER NOT NULL, + student_name TEXT NOT NULL, + school_id TEXT NOT NULL, + school_name TEXT NOT NULL, + class_name TEXT NOT NULL, + class_id INTEGER NOT NULL, + is_current INTEGER NOT NULL, + registration_date INTEGER NOT NULL + ) + """) + + database.execSQL("ALTER TABLE Students ADD COLUMN scrapperBaseUrl TEXT NOT NULL DEFAULT \"\";") + database.execSQL("ALTER TABLE Students ADD COLUMN apiBaseUrl TEXT NOT NULL DEFAULT \"\";") + database.execSQL("ALTER TABLE Students ADD COLUMN is_parent INT NOT NULL DEFAULT 0;") + database.execSQL("ALTER TABLE Students ADD COLUMN loginMode TEXT NOT NULL DEFAULT \"\";") + database.execSQL("ALTER TABLE Students ADD COLUMN certificateKey TEXT NOT NULL DEFAULT \"\";") + database.execSQL("ALTER TABLE Students ADD COLUMN privateKey TEXT NOT NULL DEFAULT \"\";") + database.execSQL("ALTER TABLE Students ADD COLUMN user_login_id INTEGER NOT NULL DEFAULT 0;") + + database.execSQL(""" + INSERT INTO Students_tmp( + id, scrapper_base_url, mobile_base_url, is_parent, login_type, login_mode, certificate_key, private_key, email, password, symbol, student_id, user_login_id, student_name, school_id, school_name, school_id, school_name, class_name, class_id, is_current, registration_date) + SELECT + id, endpoint, apiBaseUrl, is_parent, loginType, "SCRAPPER", certificateKey, privateKey, email, password, symbol, student_id, user_login_id, student_name, school_id, school_name, school_id, school_name, class_name, class_id, is_current, registration_date + FROM Students + """) + database.execSQL("DROP TABLE Students") + database.execSQL("ALTER TABLE Students_tmp RENAME TO Students") + database.execSQL("CREATE UNIQUE INDEX index_Students_email_symbol_student_id_school_id_class_id ON Students (email, symbol, student_id, school_id, class_id)") + } + + private fun migrateSharedPreferences() { + if (sharedPrefProvider.getString("grade_modifier_plus", "0.0") == "0.0") { + sharedPrefProvider.putString("grade_modifier_plus", "0.33") + } + if (sharedPrefProvider.getString("grade_modifier_minus", "0.0") == "0.0") { + sharedPrefProvider.putString("grade_modifier_minus", "0.33") + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration2.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration2.kt new file mode 100644 index 000000000..c5a30991a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration2.kt @@ -0,0 +1,18 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration2 : Migration(1, 2) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE IF NOT EXISTS LuckyNumbers ( + id INTEGER PRIMARY KEY NOT NULL, + is_notified INTEGER NOT NULL, + student_id INTEGER NOT NULL, + date INTEGER NOT NULL, + lucky_number INTEGER NOT NULL) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration20.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration20.kt new file mode 100644 index 000000000..2fcfc183d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration20.kt @@ -0,0 +1,42 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration20 : Migration(19, 20) { + + override fun migrate(database: SupportSQLiteDatabase) { + migrateTimetable(database) + truncateSubjects(database) + } + + private fun migrateTimetable(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE Timetable") + database.execSQL(""" + CREATE TABLE IF NOT EXISTS `Timetable` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + `student_id` INTEGER NOT NULL, + `diary_id` INTEGER NOT NULL, + `number` INTEGER NOT NULL, + `start` INTEGER NOT NULL, + `end` INTEGER NOT NULL, + `date` INTEGER NOT NULL, + `subject` TEXT NOT NULL, + `subjectOld` TEXT NOT NULL, + `group` TEXT NOT NULL, + `room` TEXT NOT NULL, + `roomOld` TEXT NOT NULL, + `teacher` TEXT NOT NULL, + `teacherOld` TEXT NOT NULL, + `info` TEXT NOT NULL, + `student_plan` INTEGER NOT NULL, + `changes` INTEGER NOT NULL, + `canceled` INTEGER NOT NULL + ) + """) + } + + private fun truncateSubjects(database: SupportSQLiteDatabase) { + database.execSQL("DELETE FROM Subjects") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration21.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration21.kt new file mode 100644 index 000000000..bc0ff900c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration21.kt @@ -0,0 +1,15 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration21 : Migration(20, 21) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Attendance ADD COLUMN excusable INTEGER NOT NULL DEFAULT 0") + database.execSQL("ALTER TABLE Attendance ADD COLUMN time_id INTEGER NOT NULL DEFAULT 0") + database.execSQL("ALTER TABLE Attendance ADD COLUMN excuse_status TEXT DEFAULT NULL") + + database.execSQL("DELETE FROM Semesters") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration22.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration22.kt new file mode 100644 index 000000000..cf50a6c3e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration22.kt @@ -0,0 +1,11 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration22 : Migration(21, 22) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Students ADD COLUMN school_short TEXT NOT NULL DEFAULT ''") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration23.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration23.kt new file mode 100644 index 000000000..22de94c3f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration23.kt @@ -0,0 +1,14 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration23 : Migration(22, 23) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Notes ADD COLUMN teacher_symbol TEXT NOT NULL DEFAULT ''") + database.execSQL("ALTER TABLE Notes ADD COLUMN category_type INTEGER NOT NULL DEFAULT 0") + database.execSQL("ALTER TABLE Notes ADD COLUMN is_points_show INTEGER NOT NULL DEFAULT 0") + database.execSQL("ALTER TABLE Notes ADD COLUMN points INTEGER NOT NULL DEFAULT 0") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration24.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration24.kt new file mode 100644 index 000000000..604ed4875 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration24.kt @@ -0,0 +1,21 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration24 : Migration(23, 24) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Messages ADD COLUMN has_attachments INTEGER NOT NULL DEFAULT 0") + database.execSQL(""" + CREATE TABLE IF NOT EXISTS MessageAttachments ( + real_id INTEGER NOT NULL, + message_id INTEGER NOT NULL, + one_drive_id TEXT NOT NULL, + url TEXT NOT NULL, + filename TEXT NOT NULL, + PRIMARY KEY(real_id) + ) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration25.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration25.kt new file mode 100644 index 000000000..4749bac73 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration25.kt @@ -0,0 +1,12 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration25 : Migration(24, 25) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Homework ADD COLUMN is_done INTEGER NOT NULL DEFAULT 0") + database.execSQL("ALTER TABLE Homework ADD COLUMN attachments TEXT NOT NULL DEFAULT \"[]\"") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration26.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration26.kt new file mode 100644 index 000000000..7130d86d8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration26.kt @@ -0,0 +1,14 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration26 : Migration(25, 26) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE GradesSummary ADD COLUMN is_predicted_grade_notified INTEGER NOT NULL DEFAULT 1") + database.execSQL("ALTER TABLE GradesSummary ADD COLUMN is_final_grade_notified INTEGER NOT NULL DEFAULT 1") + database.execSQL("ALTER TABLE GradesSummary ADD COLUMN predicted_grade_last_change INTEGER NOT NULL DEFAULT 0") + database.execSQL("ALTER TABLE GradesSummary ADD COLUMN final_grade_last_change INTEGER NOT NULL DEFAULT 0") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration27.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration27.kt new file mode 100644 index 000000000..5c60beead --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration27.kt @@ -0,0 +1,49 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration27 : Migration(26, 27) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Students ADD COLUMN user_name TEXT NOT NULL DEFAULT \"\"") + + val students = getStudentsIdsAndNames(database) + val units = getReportingUnits(database) + + students.forEach { (id, userLoginId, studentName) -> + val userNameFromUnits = units.singleOrNull { (senderId, _) -> senderId == userLoginId }?.second + val normalizedStudentName = studentName.split(" ").asReversed().joinToString(" ") + + val userName = userNameFromUnits ?: normalizedStudentName + database.execSQL("UPDATE Students SET user_name = '$userName' WHERE id = '$id'") + } + } + + private fun getStudentsIdsAndNames(database: SupportSQLiteDatabase): MutableList> { + val students = mutableListOf>() + database.query("SELECT id, user_login_id, student_name FROM Students").use { + if (it.moveToFirst()) { + do { + students.add(Triple(it.getLong(0), it.getInt(1), it.getString(2))) + } while (it.moveToNext()) + } + } + + return students + } + + private fun getReportingUnits(database: SupportSQLiteDatabase): MutableList> { + val units = mutableListOf>() + database.query("SELECT sender_id, sender_name FROM ReportingUnits").use { + if (it.moveToFirst()) { + do { + units.add(it.getInt(0) to it.getString(1)) + } while (it.moveToNext()) + } + } + + + return units + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration28.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration28.kt new file mode 100644 index 000000000..51e7628b5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration28.kt @@ -0,0 +1,23 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration28 : Migration(27, 28) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE IF NOT EXISTS Conferences ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + student_id INTEGER NOT NULL, + diary_id INTEGER NOT NULL, + title TEXT NOT NULL, + subject TEXT NOT NULL, + agenda TEXT NOT NULL, + present_on_conference TEXT NOT NULL, + conference_id INTEGER NOT NULL, + date INTEGER NOT NULL + ) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration29.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration29.kt new file mode 100644 index 000000000..327552d75 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration29.kt @@ -0,0 +1,33 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration29 : Migration(28, 29) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS GradesStatistics") + database.execSQL(""" + CREATE TABLE IF NOT EXISTS GradeSemesterStatistics ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + student_id INTEGER NOT NULL, + semester_id INTEGER NOT NULL, + subject TEXT NOT NULL, + amounts TEXT NOT NULL, + student_grade INTEGER NOT NULL + ) + """) + database.execSQL(""" + CREATE TABLE IF NOT EXISTS GradePartialStatistics ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + student_id INTEGER NOT NULL, + semester_id INTEGER NOT NULL, + subject TEXT NOT NULL, + class_average TEXT NOT NULL, + student_average TEXT NOT NULL, + class_amounts TEXT NOT NULL, + student_amounts TEXT NOT NULL + ) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration3.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration3.kt new file mode 100644 index 000000000..d9699c0f4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration3.kt @@ -0,0 +1,25 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration3 : Migration(2, 3) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE IF NOT EXISTS CompletedLesson ( + id INTEGER PRIMARY KEY NOT NULL, + student_id INTEGER NOT NULL, + diary_id INTEGER NOT NULL, + date INTEGER NOT NULL, + number INTEGER NOT NULL, + subject TEXT NOT NULL, + topic TEXT NOT NULL, + teacher TEXT NOT NULL, + teacher_symbol TEXT NOT NULL, + substitution TEXT NOT NULL, + absence TEXT NOT NULL, + resources TEXT NOT NULL) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration30.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration30.kt new file mode 100644 index 000000000..b33914fec --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration30.kt @@ -0,0 +1,21 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration30 : Migration(29, 30) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE TimetableAdditional ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + student_id INTEGER NOT NULL, + diary_id INTEGER NOT NULL, + start INTEGER NOT NULL, + `end` INTEGER NOT NULL, + date INTEGER NOT NULL, + subject TEXT NOT NULL + ) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration31.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration31.kt new file mode 100644 index 000000000..064a3e5bc --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration31.kt @@ -0,0 +1,42 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration31 : Migration(30, 31) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL( + """CREATE TABLE IF NOT EXISTS StudentInfo ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + student_id INTEGER NOT NULL, + full_name TEXT NOT NULL, + first_name TEXT NOT NULL, + second_name TEXT NOT NULL, + surname TEXT NOT NULL, + birth_date INTEGER NOT NULL, + birth_place TEXT NOT NULL, + gender TEXT NOT NULL, + has_polish_citizenship INTEGER NOT NULL, + family_name TEXT NOT NULL, + parents_names TEXT NOT NULL, + address TEXT NOT NULL, + registered_address TEXT NOT NULL, + correspondence_address TEXT NOT NULL, + phone_number TEXT NOT NULL, + cell_phone_number TEXT NOT NULL, + email TEXT NOT NULL, + first_guardian_full_name TEXT NOT NULL, + first_guardian_kinship TEXT NOT NULL, + first_guardian_address TEXT NOT NULL, + first_guardian_phones TEXT NOT NULL, + first_guardian_email TEXT NOT NULL, + second_guardian_full_name TEXT NOT NULL, + second_guardian_kinship TEXT NOT NULL, + second_guardian_address TEXT NOT NULL, + second_guardian_phones TEXT NOT NULL, + second_guardian_email TEXT NOT NULL) + """ + ) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration32.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration32.kt new file mode 100644 index 000000000..508485e08 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration32.kt @@ -0,0 +1,12 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration32 : Migration(31, 32) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Students ADD COLUMN nick TEXT NOT NULL DEFAULT \"\"") + } +} + diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration33.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration33.kt new file mode 100644 index 000000000..4a57880d4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration33.kt @@ -0,0 +1,45 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration33 : Migration(32, 33) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS StudentInfo") + + database.execSQL( + """CREATE TABLE IF NOT EXISTS StudentInfo ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + student_id INTEGER NOT NULL, + full_name TEXT NOT NULL, + first_name TEXT NOT NULL, + second_name TEXT NOT NULL, + surname TEXT NOT NULL, + birth_date INTEGER NOT NULL, + birth_place TEXT NOT NULL, + gender TEXT NOT NULL, + has_polish_citizenship INTEGER NOT NULL, + family_name TEXT NOT NULL, + parents_names TEXT NOT NULL, + address TEXT NOT NULL, + registered_address TEXT NOT NULL, + correspondence_address TEXT NOT NULL, + phone_number TEXT NOT NULL, + cell_phone_number TEXT NOT NULL, + email TEXT NOT NULL, + first_guardian_full_name TEXT, + first_guardian_kinship TEXT, + first_guardian_address TEXT, + first_guardian_phones TEXT, + first_guardian_email TEXT, + second_guardian_full_name TEXT, + second_guardian_kinship TEXT, + second_guardian_address TEXT, + second_guardian_phones TEXT, + second_guardian_email TEXT) + """ + ) + } +} + diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration34.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration34.kt new file mode 100644 index 000000000..2c57eb00a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration34.kt @@ -0,0 +1,13 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration34 : Migration(33, 34) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DELETE FROM ReportingUnits") + database.execSQL("DELETE FROM Recipients") + } +} + diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration35.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration35.kt new file mode 100644 index 000000000..f63431d00 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration35.kt @@ -0,0 +1,26 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.core.database.getLongOrNull +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase +import io.github.wulkanowy.utils.AppInfo + +class Migration35(private val appInfo: AppInfo) : Migration(34, 35) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Students ADD COLUMN `avatar_color` INTEGER NOT NULL DEFAULT 0") + + database.query("SELECT * FROM Students").use { + while (it.moveToNext()) { + val studentId = it.getLongOrNull(0) + database.execSQL( + """ + UPDATE Students + SET avatar_color = ${appInfo.defaultColorsForAvatar.random()} + WHERE id = $studentId + """ + ) + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration36.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration36.kt new file mode 100644 index 000000000..7ea106585 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration36.kt @@ -0,0 +1,12 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration36 : Migration(35, 36) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Exams ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1") + database.execSQL("ALTER TABLE Homework ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration37.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration37.kt new file mode 100644 index 000000000..a3fcd51a6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration37.kt @@ -0,0 +1,21 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration37 : Migration(36, 37) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL( + """ + CREATE TABLE IF NOT EXISTS TimetableHeaders ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + student_id INTEGER NOT NULL, + diary_id INTEGER NOT NULL, + date INTEGER NOT NULL, + content TEXT NOT NULL + ) + """ + ) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration38.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration38.kt new file mode 100644 index 000000000..1f90f5a44 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration38.kt @@ -0,0 +1,19 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration38 : Migration(37, 38) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE IF NOT EXISTS `SchoolAnnouncements` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + `student_id` INTEGER NOT NULL, + `date` INTEGER NOT NULL, + `subject` TEXT NOT NULL, + `content` TEXT NOT NULL + ) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration39.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration39.kt new file mode 100644 index 000000000..6c0d36dd2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration39.kt @@ -0,0 +1,12 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration39 : Migration(38, 39) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Conferences ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1") + database.execSQL("ALTER TABLE SchoolAnnouncements ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1") + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration4.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration4.kt new file mode 100644 index 000000000..0ae89bdd6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration4.kt @@ -0,0 +1,31 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration4 : Migration(3, 4) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS Messages") + database.execSQL(""" + CREATE TABLE IF NOT EXISTS Messages ( + id INTEGER PRIMARY KEY NOT NULL, + is_notified INTEGER NOT NULL, + content TEXT, + student_id INTEGER NOT NULL, + real_id INTEGER NOT NULL, + message_id INTEGER NOT NULL, + sender_name TEXT NOT NULL, + sender_id INTEGER NOT NULL, + recipient_id INTEGER NOT NULL, + recipient_name TEXT NOT NULL, + subject TEXT NOT NULL, + date INTEGER NOT NULL, + folder_id INTEGER NOT NULL, + unread INTEGER NOT NULL, + unreadBy INTEGER NOT NULL, + readBy INTEGER NOT NULL, + removed INTEGER NOT NULL) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration40.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration40.kt new file mode 100644 index 000000000..6d2795c7c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration40.kt @@ -0,0 +1,23 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration40 : Migration(39, 40) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL( + """ + CREATE TABLE IF NOT EXISTS `Notifications` ( + `student_id` INTEGER NOT NULL, + `title` TEXT NOT NULL, + `content` TEXT NOT NULL, + `type` TEXT NOT NULL, + `date` INTEGER NOT NULL, + `data` TEXT, + `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL + ) + """ + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration41.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration41.kt new file mode 100644 index 000000000..ccaf85755 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration41.kt @@ -0,0 +1,21 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase +import io.github.wulkanowy.data.db.SharedPrefProvider +import io.github.wulkanowy.data.enums.GradeExpandMode + +class Migration41(private val sharedPrefProvider: SharedPrefProvider) : Migration(40, 41) { + + override fun migrate(database: SupportSQLiteDatabase) { + migrateSharedPreferences() + database.execSQL("ALTER TABLE Homework ADD COLUMN is_added_by_user INTEGER NOT NULL DEFAULT 0") + } + + private fun migrateSharedPreferences() { + if (sharedPrefProvider.getBoolean("pref_key_expand_grade", false)) { + sharedPrefProvider.putString("pref_key_expand_grade_mode", GradeExpandMode.ALWAYS_EXPANDED.value) + } + sharedPrefProvider.delete("pref_key_expand_grade") + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration42.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration42.kt new file mode 100644 index 000000000..3d66f301b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration42.kt @@ -0,0 +1,24 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration42 : Migration(41, 42) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL( + """CREATE TABLE IF NOT EXISTS `AdminMessages` ( + `id` INTEGER NOT NULL, + `title` TEXT NOT NULL, + `content` TEXT NOT NULL, + `version_name` INTEGER, + `version_max` INTEGER, + `target_register_host` TEXT, + `target_flavor` TEXT, + `destination_url` TEXT, + `priority` TEXT NOT NULL, + `type` TEXT NOT NULL, + PRIMARY KEY(`id`))""" + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration43.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration43.kt new file mode 100644 index 000000000..68c2834d6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration43.kt @@ -0,0 +1,12 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration43 : Migration(42, 43) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Timetable ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1") + database.execSQL("ALTER TABLE Attendance ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration44.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration44.kt new file mode 100644 index 000000000..7bdcab5f4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration44.kt @@ -0,0 +1,11 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration44 : Migration(43, 44) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE AdminMessages ADD COLUMN is_dismissible INTEGER NOT NULL DEFAULT 0") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration46.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration46.kt new file mode 100644 index 000000000..d3fa5cf93 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration46.kt @@ -0,0 +1,102 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase +import java.time.Instant +import java.time.ZoneId +import java.time.ZoneOffset + +class Migration46 : Migration(45, 46) { + + override fun migrate(database: SupportSQLiteDatabase) { + migrateConferences(database) + migrateMessages(database) + migrateMobileDevices(database) + migrateNotifications(database) + migrateTimetable(database) + migrateTimetableAdditional(database) + } + + private fun migrateConferences(database: SupportSQLiteDatabase) { + database.query("SELECT * FROM Conferences").use { + while (it.moveToNext()) { + val id = it.getLong(it.getColumnIndexOrThrow("id")) + val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date")) + val timestampUtc = timestampLocal.timestampLocalToUTC() + + database.execSQL("UPDATE Conferences SET date = $timestampUtc WHERE id = $id") + } + } + } + + private fun migrateMessages(database: SupportSQLiteDatabase) { + database.query("SELECT * FROM Messages").use { + while (it.moveToNext()) { + val id = it.getLong(it.getColumnIndexOrThrow("id")) + val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date")) + val timestampUtc = timestampLocal.timestampLocalToUTC() + + database.execSQL("UPDATE Messages SET date = $timestampUtc WHERE id = $id") + } + } + } + + private fun migrateMobileDevices(database: SupportSQLiteDatabase) { + database.query("SELECT * FROM MobileDevices").use { + while (it.moveToNext()) { + val id = it.getLong(it.getColumnIndexOrThrow("id")) + val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date")) + val timestampUtc = timestampLocal.timestampLocalToUTC() + + database.execSQL("UPDATE MobileDevices SET date = $timestampUtc WHERE id = $id") + } + } + } + + private fun migrateNotifications(database: SupportSQLiteDatabase) { + database.query("SELECT * FROM Notifications").use { + while (it.moveToNext()) { + val id = it.getLong(it.getColumnIndexOrThrow("id")) + val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date")) + val timestampUtc = timestampLocal.timestampLocalToUTC() + + database.execSQL("UPDATE Notifications SET date = $timestampUtc WHERE id = $id") + } + } + } + + private fun migrateTimetable(database: SupportSQLiteDatabase) { + database.query("SELECT * FROM Timetable").use { + while (it.moveToNext()) { + val id = it.getLong(it.getColumnIndexOrThrow("id")) + val timestampLocalStart = it.getLong(it.getColumnIndexOrThrow("start")) + val timestampLocalEnd = it.getLong(it.getColumnIndexOrThrow("end")) + val timestampUtcStart = timestampLocalStart.timestampLocalToUTC() + val timestampUtcEnd = timestampLocalEnd.timestampLocalToUTC() + + database.execSQL("UPDATE Timetable SET start = $timestampUtcStart, end = $timestampUtcEnd WHERE id = $id") + } + } + } + + private fun migrateTimetableAdditional(database: SupportSQLiteDatabase) { + database.query("SELECT * FROM TimetableAdditional").use { + while (it.moveToNext()) { + val id = it.getLong(it.getColumnIndexOrThrow("id")) + val timestampLocalStart = it.getLong(it.getColumnIndexOrThrow("start")) + val timestampLocalEnd = it.getLong(it.getColumnIndexOrThrow("end")) + val timestampUtcStart = timestampLocalStart.timestampLocalToUTC() + val timestampUtcEnd = timestampLocalEnd.timestampLocalToUTC() + + database.execSQL("UPDATE TimetableAdditional SET start = $timestampUtcStart, end = $timestampUtcEnd WHERE id = $id") + } + } + } + + private fun Long.timestampLocalToUTC(): Long = Instant.ofEpochMilli(this) + .atZone(ZoneOffset.UTC) + .withZoneSameLocal(ZoneId.of("Europe/Warsaw")) + .withZoneSameInstant(ZoneId.systemDefault()) + .toInstant() + .toEpochMilli() +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration5.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration5.kt new file mode 100644 index 000000000..dbcd916ba --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration5.kt @@ -0,0 +1,26 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase +import java.time.LocalDateTime.now +import java.time.ZoneOffset + +class Migration5 : Migration(4, 5) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Students ADD COLUMN registration_date INTEGER DEFAULT 0 NOT NULL") + database.execSQL("UPDATE Students SET registration_date = '${now().atZone(ZoneOffset.UTC).toInstant().toEpochMilli()}'") + database.execSQL("DROP TABLE IF EXISTS Notes") + database.execSQL(""" + CREATE TABLE IF NOT EXISTS Notes ( + id INTEGER PRIMARY KEY NOT NULL, + is_read INTEGER NOT NULL, + is_notified INTEGER NOT NULL, + student_id INTEGER NOT NULL, + date INTEGER NOT NULL, + teacher TEXT NOT NULL, + category TEXT NOT NULL, + content TEXT NOT NULL) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration6.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration6.kt new file mode 100644 index 000000000..fa9436187 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration6.kt @@ -0,0 +1,37 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration6 : Migration(5, 6) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE IF NOT EXISTS ReportingUnits ( + id INTEGER PRIMARY KEY NOT NULL, + student_id INTEGER NOT NULL, + real_id INTEGER NOT NULL, + short TEXT NOT NULL, + sender_id INTEGER NOT NULL, + sender_name TEXT NOT NULL, + roles TEXT NOT NULL) + """) + + database.execSQL(""" + CREATE TABLE IF NOT EXISTS Recipients ( + id INTEGER PRIMARY KEY NOT NULL, + student_id INTEGER NOT NULL, + real_id TEXT NOT NULL, + name TEXT NOT NULL, + real_name TEXT NOT NULL, + login_id INTEGER NOT NULL, + unit_id INTEGER NOT NULL, + role INTEGER NOT NULL, + hash TEXT NOT NULL) + """) + + database.execSQL("DELETE FROM Semesters WHERE 1") + database.execSQL("ALTER TABLE Semesters ADD COLUMN class_id INTEGER DEFAULT 0 NOT NULL") + database.execSQL("ALTER TABLE Semesters ADD COLUMN unit_id INTEGER DEFAULT 0 NOT NULL") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration7.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration7.kt new file mode 100644 index 000000000..120716c81 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration7.kt @@ -0,0 +1,20 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration7 : Migration(6, 7) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE IF NOT EXISTS GradesStatistics ( + id INTEGER PRIMARY KEY NOT NULL, + student_id INTEGER NOT NULL, + semester_id INTEGER NOT NULL, + subject TEXT NOT NULL, + grade INTEGER NOT NULL, + amount INTEGER NOT NULL, + is_semester INTEGER NOT NULL) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration8.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration8.kt new file mode 100644 index 000000000..7009ee129 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration8.kt @@ -0,0 +1,13 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration8 : Migration(7, 8) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Timetable ADD COLUMN subjectOld TEXT DEFAULT \"\" NOT NULL") + database.execSQL("ALTER TABLE Timetable ADD COLUMN roomOld TEXT DEFAULT \"\" NOT NULL") + database.execSQL("ALTER TABLE Timetable ADD COLUMN teacherOld TEXT DEFAULT \"\" NOT NULL") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration9.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration9.kt new file mode 100644 index 000000000..d79a57062 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration9.kt @@ -0,0 +1,30 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration9 : Migration(8, 9) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS Messages") + database.execSQL(""" + CREATE TABLE IF NOT EXISTS Messages ( + id INTEGER PRIMARY KEY NOT NULL, + student_id INTEGER NOT NULL, + real_id INTEGER NOT NULL, + message_id INTEGER NOT NULL, + sender_name TEXT NOT NULL, + sender_id INTEGER NOT NULL, + recipient_name TEXT NOT NULL, + subject TEXT NOT NULL, + date INTEGER NOT NULL, + folder_id INTEGER NOT NULL, + unread INTEGER NOT NULL, + unread_by INTEGER NOT NULL, + read_by INTEGER NOT NULL, + removed INTEGER NOT NULL, + is_notified INTEGER NOT NULL, + content TEXT) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/resources/AppResources.java b/app/src/main/java/io/github/wulkanowy/data/db/resources/AppResources.java deleted file mode 100644 index c35f44c71..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/resources/AppResources.java +++ /dev/null @@ -1,88 +0,0 @@ -package io.github.wulkanowy.data.db.resources; - -import android.content.Context; -import android.content.res.Resources; - -import java.io.IOException; -import java.net.SocketTimeoutException; -import java.net.UnknownHostException; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import io.github.wulkanowy.R; -import io.github.wulkanowy.api.NotLoggedInErrorException; -import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; -import io.github.wulkanowy.di.annotations.ApplicationContext; -import io.github.wulkanowy.utils.AppConstant; -import io.github.wulkanowy.utils.LogUtils; -import io.github.wulkanowy.utils.security.CryptoException; - -@Singleton -public class AppResources implements ResourcesContract { - - private Resources resources; - - @Inject - AppResources(@ApplicationContext Context context) { - resources = context.getResources(); - } - - @Override - public String[] getSymbolsKeysArray() { - return resources.getStringArray(R.array.symbols); - } - - @Override - public String[] getSymbolsValuesArray() { - return resources.getStringArray(R.array.symbols_values); - } - - @Override - public String getErrorLoginMessage(Exception exception) { - LogUtils.error(AppConstant.APP_NAME + " encountered a error", exception); - - if (exception instanceof CryptoException) { - return resources.getString(R.string.encrypt_failed_text); - } else if (exception instanceof UnknownHostException) { - return resources.getString(R.string.noInternet_text); - } else if (exception instanceof SocketTimeoutException) { - return resources.getString(R.string.generic_timeout_error); - } else if (exception instanceof NotLoggedInErrorException || exception instanceof IOException) { - return resources.getString(R.string.login_denied_text); - } else { - return exception.getMessage(); - } - } - - @Override - public String getAttendanceLessonDescription(AttendanceLesson lesson) { - int id = R.string.attendance_present; - - if (lesson.getIsAbsenceForSchoolReasons()) { - id = R.string.attendance_absence_for_school_reasons; - } - - if (lesson.getIsAbsenceExcused()) { - id = R.string.attendance_absence_excused; - } - - if (lesson.getIsAbsenceUnexcused()) { - id = R.string.attendance_absence_unexcused; - } - - if (lesson.getIsExemption()) { - id = R.string.attendance_exemption; - } - - if (lesson.getIsExcusedLateness()) { - id = R.string.attendance_excused_lateness; - } - - if (lesson.getIsUnexcusedLateness()) { - id = R.string.attendance_unexcused_lateness; - } - - return resources.getString(id); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesContract.java b/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesContract.java deleted file mode 100644 index 0ef45a76d..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesContract.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.wulkanowy.data.db.resources; - -import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; - -public interface ResourcesContract { - - String[] getSymbolsKeysArray(); - - String[] getSymbolsValuesArray(); - - String getErrorLoginMessage(Exception e); - - String getAttendanceLessonDescription(AttendanceLesson lesson); -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPref.java b/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPref.java deleted file mode 100644 index ba437f882..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPref.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.data.db.shared; - -import android.content.Context; -import android.content.SharedPreferences; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import io.github.wulkanowy.di.annotations.ApplicationContext; -import io.github.wulkanowy.di.annotations.SharedPreferencesInfo; - -@Singleton -public class SharedPref implements SharedPrefContract { - - private static final String SHARED_KEY_USER_ID = "USER_ID"; - - private final SharedPreferences sharedPreferences; - - @Inject - SharedPref(@ApplicationContext Context context, @SharedPreferencesInfo String sharedName) { - sharedPreferences = context.getSharedPreferences(sharedName, Context.MODE_PRIVATE); - } - - @Override - public long getCurrentUserId() { - return sharedPreferences.getLong(SHARED_KEY_USER_ID, 0); - } - - @Override - public void setCurrentUserId(long userId) { - sharedPreferences.edit().putLong(SHARED_KEY_USER_ID, userId).apply(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPrefContract.java b/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPrefContract.java deleted file mode 100644 index 7f540acfd..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPrefContract.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.github.wulkanowy.data.db.shared; - -public interface SharedPrefContract { - - long getCurrentUserId(); - - void setCurrentUserId(long userId); -} diff --git a/app/src/main/java/io/github/wulkanowy/data/enums/AppTheme.kt b/app/src/main/java/io/github/wulkanowy/data/enums/AppTheme.kt new file mode 100644 index 000000000..438f07323 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/enums/AppTheme.kt @@ -0,0 +1,12 @@ +package io.github.wulkanowy.data.enums + +enum class AppTheme(val value: String) { + SYSTEM("system"), + LIGHT("light"), + DARK("dark"), + BLACK("black"); + + companion object { + fun getByValue(value: String) = values().find { it.value == value } ?: LIGHT + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/enums/Gender.kt b/app/src/main/java/io/github/wulkanowy/data/enums/Gender.kt new file mode 100644 index 000000000..df93dcbef --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/enums/Gender.kt @@ -0,0 +1,3 @@ +package io.github.wulkanowy.data.enums + +enum class Gender { MALE, FEMALE } diff --git a/app/src/main/java/io/github/wulkanowy/data/enums/GradeColorTheme.kt b/app/src/main/java/io/github/wulkanowy/data/enums/GradeColorTheme.kt new file mode 100644 index 000000000..24b095d0e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/enums/GradeColorTheme.kt @@ -0,0 +1,13 @@ +package io.github.wulkanowy.data.enums + +import java.io.Serializable + +enum class GradeColorTheme(val value: String) : Serializable { + VULCAN("vulcan"), + MATERIAL("material"), + GRADE_COLOR("grade_color"); + + companion object { + fun getByValue(value: String) = values().find { it.value == value } ?: VULCAN + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/enums/GradeExpandMode.kt b/app/src/main/java/io/github/wulkanowy/data/enums/GradeExpandMode.kt new file mode 100644 index 000000000..96e4a174d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/enums/GradeExpandMode.kt @@ -0,0 +1,11 @@ +package io.github.wulkanowy.data.enums + +enum class GradeExpandMode(val value: String) { + ONE("one"), + UNLIMITED("any"), + ALWAYS_EXPANDED("always"); + + companion object { + fun getByValue(value: String) = values().find { it.value == value } ?: ONE + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/enums/GradeSortingMode.kt b/app/src/main/java/io/github/wulkanowy/data/enums/GradeSortingMode.kt new file mode 100644 index 000000000..c5c0196c9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/enums/GradeSortingMode.kt @@ -0,0 +1,10 @@ +package io.github.wulkanowy.data.enums + +enum class GradeSortingMode(val value: String) { + ALPHABETIC("alphabetic"), + DATE("date"); + + companion object { + fun getByValue(value: String) = values().find { it.value == value } ?: ALPHABETIC + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/enums/MessageFolder.kt b/app/src/main/java/io/github/wulkanowy/data/enums/MessageFolder.kt new file mode 100644 index 000000000..899ba9085 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/enums/MessageFolder.kt @@ -0,0 +1,7 @@ +package io.github.wulkanowy.data.enums + +enum class MessageFolder(val id: Int = 1) { + RECEIVED(1), + SENT(2), + TRASHED(3) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/enums/SentExcuseStatus.kt b/app/src/main/java/io/github/wulkanowy/data/enums/SentExcuseStatus.kt new file mode 100644 index 000000000..99878152f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/enums/SentExcuseStatus.kt @@ -0,0 +1,7 @@ +package io.github.wulkanowy.data.enums + +enum class SentExcuseStatus(val id: Int = 0) { + WAITING, + ACCEPTED, + DENIED +} diff --git a/app/src/main/java/io/github/wulkanowy/data/enums/TimetableMode.kt b/app/src/main/java/io/github/wulkanowy/data/enums/TimetableMode.kt new file mode 100644 index 000000000..9e294ad7f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/enums/TimetableMode.kt @@ -0,0 +1,11 @@ +package io.github.wulkanowy.data.enums + +enum class TimetableMode(val value: String) { + WHOLE_PLAN("yes"), + ONLY_CURRENT_GROUP("no"), + SMALL_OTHER_GROUP("small"); + + companion object { + fun getByValue(value: String) = values().find { it.value == value } ?: ONLY_CURRENT_GROUP + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/exceptions/NoCurrentStudent.kt b/app/src/main/java/io/github/wulkanowy/data/exceptions/NoCurrentStudent.kt new file mode 100644 index 000000000..58a2396e2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/exceptions/NoCurrentStudent.kt @@ -0,0 +1,3 @@ +package io.github.wulkanowy.data.exceptions + +class NoCurrentStudentException : Exception("There no set current student in database") diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/AttendanceMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/AttendanceMapper.kt new file mode 100644 index 000000000..46e67fdaa --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/AttendanceMapper.kt @@ -0,0 +1,43 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Attendance +import io.github.wulkanowy.data.db.entities.AttendanceSummary +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.pojo.Attendance as SdkAttendance +import io.github.wulkanowy.sdk.pojo.AttendanceSummary as SdkAttendanceSummary + +fun List.mapToEntities(semester: Semester) = map { + Attendance( + studentId = semester.studentId, + diaryId = semester.diaryId, + date = it.date, + timeId = it.timeId, + number = it.number, + subject = it.subject, + name = it.name, + presence = it.presence, + absence = it.absence, + exemption = it.exemption, + lateness = it.lateness, + excused = it.excused, + deleted = it.deleted, + excusable = it.excusable, + excuseStatus = it.excuseStatus?.name + ) +} + +fun List.mapToEntities(semester: Semester, subjectId: Int) = map { + AttendanceSummary( + studentId = semester.studentId, + diaryId = semester.diaryId, + subjectId = subjectId, + month = it.month, + presence = it.presence, + absence = it.absence, + absenceExcused = it.absenceExcused, + absenceForSchoolReasons = it.absenceForSchoolReasons, + lateness = it.lateness, + latenessExcused = it.latenessExcused, + exemption = it.exemption + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/CompletedLessonsMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/CompletedLessonsMapper.kt new file mode 100644 index 000000000..c42126eb7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/CompletedLessonsMapper.kt @@ -0,0 +1,21 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.CompletedLesson +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.pojo.CompletedLesson as SdkCompletedLesson + +fun List.mapToEntities(semester: Semester) = map { + CompletedLesson( + studentId = semester.studentId, + diaryId = semester.diaryId, + date = it.date, + number = it.number, + subject = it.subject, + topic = it.topic, + teacher = it.teacher, + teacherSymbol = it.teacherSymbol, + substitution = it.substitution, + absence = it.absence, + resources = it.resources + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/ConferenceMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/ConferenceMapper.kt new file mode 100644 index 000000000..17a9e5cdb --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/ConferenceMapper.kt @@ -0,0 +1,18 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Conference +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.pojo.Conference as SdkConference + +fun List.mapToEntities(semester: Semester) = map { + Conference( + studentId = semester.studentId, + diaryId = semester.diaryId, + agenda = it.agenda, + conferenceId = it.id, + date = it.dateZoned.toInstant(), + presentOnConference = it.presentOnConference, + subject = it.subject, + title = it.title + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/DirectorInformationMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/DirectorInformationMapper.kt new file mode 100644 index 000000000..d059db816 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/DirectorInformationMapper.kt @@ -0,0 +1,14 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.SchoolAnnouncement +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.sdk.pojo.DirectorInformation as SdkDirectorInformation + +fun List.mapToEntities(student: Student) = map { + SchoolAnnouncement( + studentId = student.userLoginId, + date = it.date, + subject = it.subject, + content = it.content, + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/ExamMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/ExamMapper.kt new file mode 100644 index 000000000..bdb5efbba --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/ExamMapper.kt @@ -0,0 +1,20 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Exam +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.pojo.Exam as SdkExam + +fun List.mapToEntities(semester: Semester) = map { + Exam( + studentId = semester.studentId, + diaryId = semester.diaryId, + date = it.date, + entryDate = it.entryDate, + subject = it.subject, + group = it.group, + type = it.type, + description = it.description, + teacher = it.teacher, + teacherSymbol = it.teacherSymbol + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/GradeMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/GradeMapper.kt new file mode 100644 index 000000000..178de682a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/GradeMapper.kt @@ -0,0 +1,42 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.data.db.entities.GradeSummary +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.pojo.GradeSummary as SdkGradeSummary +import io.github.wulkanowy.sdk.pojo.Grade as SdkGrade + +fun List.mapToEntities(semester: Semester) = map { + Grade( + studentId = semester.studentId, + semesterId = semester.semesterId, + subject = it.subject, + entry = it.entry, + value = it.value, + modifier = it.modifier, + comment = it.comment, + color = it.color, + gradeSymbol = it.symbol, + description = it.description, + weight = it.weight, + weightValue = it.weightValue, + date = it.date, + teacher = it.teacher + ) +} + +@JvmName("mapGradeSummaryToEntities") +fun List.mapToEntities(semester: Semester) = map { + GradeSummary( + semesterId = semester.semesterId, + studentId = semester.studentId, + position = 0, + subject = it.name, + predictedGrade = it.predicted, + finalGrade = it.final, + pointsSum = it.pointsSum, + proposedPoints = it.proposedPoints, + finalPoints = it.finalPoints, + average = it.average + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/GradeStatisticsMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/GradeStatisticsMapper.kt new file mode 100644 index 000000000..b25802d22 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/GradeStatisticsMapper.kt @@ -0,0 +1,79 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.GradePartialStatistics +import io.github.wulkanowy.data.db.entities.GradePointsStatistics +import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.pojos.GradeStatisticsItem +import io.github.wulkanowy.sdk.pojo.GradePointsStatistics as SdkGradePointsStatistics +import io.github.wulkanowy.sdk.pojo.GradeStatisticsSemester as SdkGradeStatisticsSemester +import io.github.wulkanowy.sdk.pojo.GradeStatisticsSubject as SdkGradeStatisticsSubject + +@JvmName("mapToEntitiesSubject") +fun List.mapToEntities(semester: Semester) = map { + GradePartialStatistics( + semesterId = semester.semesterId, + studentId = semester.studentId, + subject = it.subject, + classAverage = it.classAverage, + studentAverage = it.studentAverage, + classAmounts = it.classItems + .sortedBy { item -> item.grade } + .map { item -> item.amount }, + studentAmounts = it.studentItems.map { item -> item.amount } + ) +} + +@JvmName("mapToEntitiesSemester") +fun List.mapToEntities(semester: Semester) = map { + GradeSemesterStatistics( + semesterId = semester.semesterId, + studentId = semester.studentId, + subject = it.subject, + amounts = it.items + .sortedBy { item -> item.grade } + .map { item -> item.amount }, + studentGrade = it.items.singleOrNull { item -> item.isStudentHere }?.grade ?: 0 + ) +} + +@JvmName("mapToEntitiesPoints") +fun List.mapToEntities(semester: Semester) = map { + GradePointsStatistics( + semesterId = semester.semesterId, + studentId = semester.studentId, + subject = it.subject, + others = it.others, + student = it.student + ) +} + +fun List.mapPartialToStatisticItems() = filterNot { it.classAmounts.isEmpty() }.map { + GradeStatisticsItem( + type = GradeStatisticsItem.DataType.PARTIAL, + average = it.classAverage, + partial = it, + points = null, + semester = null + ) +} + +fun List.mapSemesterToStatisticItems() = filterNot { it.amounts.isEmpty() }.map { + GradeStatisticsItem( + type = GradeStatisticsItem.DataType.SEMESTER, + partial = null, + points = null, + average = "", + semester = it + ) +} + +fun List.mapPointsToStatisticsItems() = map { + GradeStatisticsItem( + type = GradeStatisticsItem.DataType.POINTS, + partial = null, + semester = null, + average = "", + points = it + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/HomeworkMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/HomeworkMapper.kt new file mode 100644 index 000000000..880a26d6d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/HomeworkMapper.kt @@ -0,0 +1,21 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.sdk.pojo.Homework as SdkHomework +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.data.db.entities.Semester + +fun List.mapToEntities(semester: Semester) = map { + Homework( + semesterId = semester.semesterId, + studentId = semester.studentId, + date = it.date, + entryDate = it.entryDate, + subject = it.subject, + content = it.content, + teacher = it.teacher, + teacherSymbol = it.teacherSymbol, + attachments = it.attachments.map { attachment -> + attachment.url to attachment.name + } + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/LuckyNumberMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/LuckyNumberMapper.kt new file mode 100644 index 000000000..78ebe1d6e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/LuckyNumberMapper.kt @@ -0,0 +1,12 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.data.db.entities.Student +import java.time.LocalDate +import io.github.wulkanowy.sdk.pojo.LuckyNumber as SdkLuckyNumber + +fun SdkLuckyNumber.mapToEntity(student: Student) = LuckyNumber( + studentId = student.studentId, + date = LocalDate.now(), + luckyNumber = number +) diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt new file mode 100644 index 000000000..13f0ab33e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt @@ -0,0 +1,53 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.MessageAttachment +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.Student +import java.time.Instant +import io.github.wulkanowy.sdk.pojo.Message as SdkMessage +import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment +import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient + +fun List.mapToEntities(student: Student) = map { + Message( + studentId = student.id, + realId = it.id ?: 0, + messageId = it.messageId ?: 0, + sender = it.sender?.name.orEmpty(), + senderId = it.sender?.loginId ?: 0, + recipient = it.recipients.singleOrNull()?.name ?: "Wielu adresatów", + subject = it.subject.trim(), + date = it.dateZoned?.toInstant() ?: Instant.now(), + folderId = it.folderId, + unread = it.unread ?: false, + removed = it.removed, + hasAttachments = it.hasAttachments + ).apply { + content = it.content.orEmpty() + unreadBy = it.unreadBy ?: 0 + readBy = it.readBy ?: 0 + } +} + +fun List.mapToEntities() = map { + MessageAttachment( + realId = it.id, + messageId = it.messageId, + oneDriveId = it.oneDriveId, + url = it.url, + filename = it.filename + ) +} + +fun List.mapFromEntities() = map { + SdkRecipient( + id = it.realId, + name = it.realName, + loginId = it.loginId, + reportingUnitId = it.unitId, + role = it.role, + hash = it.hash, + shortName = it.name + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt new file mode 100644 index 000000000..b1e96a27b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt @@ -0,0 +1,23 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.MobileDevice +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.pojos.MobileDeviceToken +import io.github.wulkanowy.sdk.pojo.Device as SdkDevice +import io.github.wulkanowy.sdk.pojo.Token as SdkToken + +fun List.mapToEntities(semester: Semester) = map { + MobileDevice( + userLoginId = semester.studentId, + date = it.createDateZoned.toInstant(), + deviceId = it.id, + name = it.name + ) +} + +fun SdkToken.mapToMobileDeviceToken() = MobileDeviceToken( + token = token, + symbol = symbol, + pin = pin, + qr = qrCodeImage +) diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/NoteMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/NoteMapper.kt new file mode 100644 index 000000000..70941799b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/NoteMapper.kt @@ -0,0 +1,19 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Note +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.pojo.Note as SdkNote + +fun List.mapToEntities(semester: Semester) = map { + Note( + studentId = semester.studentId, + date = it.date, + teacher = it.teacher, + teacherSymbol = it.teacherSymbol, + category = it.category, + categoryType = it.categoryType.id, + isPointsShow = it.showPoints, + points = it.points, + content = it.content + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/RecipientMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/RecipientMapper.kt new file mode 100644 index 000000000..80bddaab1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/RecipientMapper.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient + +fun List.mapToEntities(userLoginId: Int) = map { + Recipient( + studentId = userLoginId, + realId = it.id, + realName = it.name, + name = it.shortName, + hash = it.hash, + loginId = it.loginId, + role = it.role, + unitId = it.reportingUnitId ?: 0 + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/ReportingUnitMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/ReportingUnitMapper.kt new file mode 100644 index 000000000..6a21d59fc --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/ReportingUnitMapper.kt @@ -0,0 +1,16 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.sdk.pojo.ReportingUnit as SdkReportingUnit + +fun List.mapToEntities(student: Student) = map { + ReportingUnit( + studentId = student.id.toInt(), + unitId = it.id, + roles = it.roles, + senderId = it.senderId, + senderName = it.senderName, + shortName = it.short + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/SchoolInfoMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/SchoolInfoMapper.kt new file mode 100644 index 000000000..dc3a5a9e9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/SchoolInfoMapper.kt @@ -0,0 +1,15 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.School +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.pojo.School as SdkSchool + +fun SdkSchool.mapToEntity(semester: Semester) = School( + studentId = semester.studentId, + classId = semester.classId, + name = name, + address = address, + contact = contact, + headmaster = headmaster, + pedagogue = pedagogue +) diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/SemesterMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/SemesterMapper.kt new file mode 100644 index 000000000..67d68a1e3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/SemesterMapper.kt @@ -0,0 +1,20 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.pojo.Semester as SdkSemester + +fun List.mapToEntities(studentId: Int) = map { + Semester( + studentId = studentId, + diaryId = it.diaryId, + kindergartenDiaryId = it.kindergartenDiaryId, + diaryName = it.diaryName, + schoolYear = it.schoolYear, + semesterId = it.semesterId, + semesterName = it.semesterNumber, + start = it.start, + end = it.end, + classId = it.classId, + unitId = it.unitId + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/StudentInfoMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/StudentInfoMapper.kt new file mode 100644 index 000000000..9e8533901 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/StudentInfoMapper.kt @@ -0,0 +1,38 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.StudentGuardian +import io.github.wulkanowy.data.db.entities.StudentInfo +import io.github.wulkanowy.data.enums.Gender +import io.github.wulkanowy.sdk.pojo.StudentGuardian as SdkStudentGuardian +import io.github.wulkanowy.sdk.pojo.StudentInfo as SdkStudentInfo + +fun SdkStudentInfo.mapToEntity(semester: Semester) = StudentInfo( + studentId = semester.studentId, + fullName = fullName, + firstName = firstName, + secondName = secondName, + surname = surname, + birthDate = birthDate, + birthPlace = birthPlace, + gender = Gender.valueOf(gender.name), + hasPolishCitizenship = hasPolishCitizenship, + familyName = familyName, + parentsNames = parentsNames, + address = address, + registeredAddress = registeredAddress, + correspondenceAddress = correspondenceAddress, + phoneNumber = phoneNumber, + cellPhoneNumber = phoneNumber, + email = email, + firstGuardian = guardianFirst?.mapToEntity(), + secondGuardian = guardianSecond?.mapToEntity() +) + +fun SdkStudentGuardian.mapToEntity() = StudentGuardian( + fullName = fullName, + kinship = kinship, + address = address, + phones = phones, + email = email +) diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/StudentMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/StudentMapper.kt new file mode 100644 index 000000000..a2110d7f5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/StudentMapper.kt @@ -0,0 +1,37 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import java.time.Instant +import io.github.wulkanowy.sdk.pojo.Student as SdkStudent + +fun List.mapToEntities(password: String = "", colors: List) = map { + StudentWithSemesters( + student = Student( + email = it.email, + password = password, + isParent = it.isParent, + symbol = it.symbol, + studentId = it.studentId, + userLoginId = it.userLoginId, + userName = it.userName, + studentName = it.studentName + " " + it.studentSurname, + schoolSymbol = it.schoolSymbol, + schoolShortName = it.schoolShortName, + schoolName = it.schoolName, + className = it.className, + classId = it.classId, + scrapperBaseUrl = it.scrapperBaseUrl, + loginType = it.loginType.name, + isCurrent = false, + registrationDate = Instant.now(), + mobileBaseUrl = it.mobileBaseUrl, + privateKey = it.privateKey, + certificateKey = it.certificateKey, + loginMode = it.loginMode.name, + ).apply { + avatarColor = colors.random() + }, + semesters = it.semesters.mapToEntities(it.studentId) + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/SubjectMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/SubjectMapper.kt new file mode 100644 index 000000000..4dc95aaa7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/SubjectMapper.kt @@ -0,0 +1,14 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Subject +import io.github.wulkanowy.sdk.pojo.Subject as SdkSubject + +fun List.mapToEntities(semester: Semester) = map { + Subject( + studentId = semester.studentId, + diaryId = semester.diaryId, + name = it.name, + realId = it.id + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/TeacherMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/TeacherMapper.kt new file mode 100644 index 000000000..49cb7c294 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/TeacherMapper.kt @@ -0,0 +1,15 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Teacher +import io.github.wulkanowy.sdk.pojo.Teacher as SdkTeacher + +fun List.mapToEntities(semester: Semester) = map { + Teacher( + studentId = semester.studentId, + name = it.name, + subject = it.subject, + shortName = it.short, + classId = semester.classId + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/TimetableMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/TimetableMapper.kt new file mode 100644 index 000000000..e55aa3cf7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/TimetableMapper.kt @@ -0,0 +1,61 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.data.db.entities.TimetableAdditional +import io.github.wulkanowy.data.db.entities.TimetableHeader +import io.github.wulkanowy.data.pojos.TimetableFull +import io.github.wulkanowy.sdk.pojo.TimetableFull as SdkTimetableFull +import io.github.wulkanowy.sdk.pojo.TimetableDayHeader as SdkTimetableHeader +import io.github.wulkanowy.sdk.pojo.Timetable as SdkTimetable +import io.github.wulkanowy.sdk.pojo.TimetableAdditional as SdkTimetableAdditional + +fun SdkTimetableFull.mapToEntities(semester: Semester) = TimetableFull( + lessons = lessons.mapToEntities(semester), + additional = additional.mapToEntities(semester), + headers = headers.mapToEntities(semester) +) + +fun List.mapToEntities(semester: Semester) = map { + Timetable( + studentId = semester.studentId, + diaryId = semester.diaryId, + number = it.number, + start = it.startZoned.toInstant(), + end = it.endZoned.toInstant(), + date = it.date, + subject = it.subject, + subjectOld = it.subjectOld, + group = it.group, + room = it.room, + roomOld = it.roomOld, + teacher = it.teacher, + teacherOld = it.teacherOld, + info = it.info, + isStudentPlan = it.studentPlan, + changes = it.changes, + canceled = it.canceled + ) +} + +@JvmName("mapToEntitiesTimetableAdditional") +fun List.mapToEntities(semester: Semester) = map { + TimetableAdditional( + studentId = semester.studentId, + diaryId = semester.diaryId, + subject = it.subject, + date = it.date, + start = it.startZoned.toInstant(), + end = it.endZoned.toInstant(), + ) +} + +@JvmName("mapToEntitiesTimetableHeaders") +fun List.mapToEntities(semester: Semester) = map { + TimetableHeader( + studentId = semester.studentId, + diaryId = semester.diaryId, + date = it.date, + content = it.content + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/Contributor.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/Contributor.kt new file mode 100644 index 000000000..4165b3f14 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/pojos/Contributor.kt @@ -0,0 +1,9 @@ +package io.github.wulkanowy.data.pojos + +import kotlinx.serialization.Serializable + +@Serializable +class Contributor( + val displayName: String, + val githubUsername: String +) diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/GradeStatisticsItem.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/GradeStatisticsItem.kt new file mode 100644 index 000000000..bdcd049df --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/pojos/GradeStatisticsItem.kt @@ -0,0 +1,25 @@ +package io.github.wulkanowy.data.pojos + +import io.github.wulkanowy.data.db.entities.GradePartialStatistics +import io.github.wulkanowy.data.db.entities.GradePointsStatistics +import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics + +data class GradeStatisticsItem( + + val type: DataType, + + val average: String, + + val partial: GradePartialStatistics?, + + val semester: GradeSemesterStatistics?, + + val points: GradePointsStatistics? + +) { + enum class DataType { + SEMESTER, + PARTIAL, + POINTS, + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/MessageDraft.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/MessageDraft.kt new file mode 100644 index 000000000..2e568e376 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/pojos/MessageDraft.kt @@ -0,0 +1,11 @@ +package io.github.wulkanowy.data.pojos + +import io.github.wulkanowy.ui.modules.message.send.RecipientChipItem +import kotlinx.serialization.Serializable + +@Serializable +data class MessageDraft( + val recipients: List, + val subject: String, + val content: String, +) \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/MobileDeviceToken.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/MobileDeviceToken.kt new file mode 100644 index 000000000..401018211 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/pojos/MobileDeviceToken.kt @@ -0,0 +1,12 @@ +package io.github.wulkanowy.data.pojos + +data class MobileDeviceToken( + + val token: String, + + val symbol: String, + + val pin: String, + + val qr: String +) diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/NotificationData.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/NotificationData.kt new file mode 100644 index 000000000..f4fd0fc8a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/pojos/NotificationData.kt @@ -0,0 +1,19 @@ +package io.github.wulkanowy.data.pojos + +import io.github.wulkanowy.services.sync.notifications.NotificationType +import io.github.wulkanowy.ui.modules.Destination + +data class NotificationData( + val destination: Destination, + val title: String, + val content: String +) + +data class GroupNotificationData( + val notificationDataList: List, + val title: String, + val content: String, + val destination: Destination, + val type: NotificationType +) + diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/TimetableFull.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/TimetableFull.kt new file mode 100644 index 000000000..ce0f6ef53 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/pojos/TimetableFull.kt @@ -0,0 +1,11 @@ +package io.github.wulkanowy.data.pojos + +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.data.db.entities.TimetableAdditional +import io.github.wulkanowy.data.db.entities.TimetableHeader + +data class TimetableFull( + val lessons: List, + val additional: List, + val headers: List, +) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt new file mode 100644 index 000000000..c9655b722 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt @@ -0,0 +1,46 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.api.AdminMessageService +import io.github.wulkanowy.data.db.dao.AdminMessageDao +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.utils.AppInfo +import kotlinx.coroutines.sync.Mutex +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AdminMessageRepository @Inject constructor( + private val adminMessageService: AdminMessageService, + private val adminMessageDao: AdminMessageDao, + private val appInfo: AppInfo +) { + private val saveFetchResultMutex = Mutex() + + suspend fun getAdminMessages(student: Student) = networkBoundResource( + mutex = saveFetchResultMutex, + isResultEmpty = { it == null }, + query = { adminMessageDao.loadAll() }, + fetch = { adminMessageService.getAdminMessages() }, + shouldFetch = { true }, + saveFetchResult = { oldItems, newItems -> + adminMessageDao.removeOldAndSaveNew(oldItems, newItems) + }, + showSavedOnLoading = false, + mapResult = { adminMessages -> + adminMessages.filter { adminMessage -> + val isCorrectRegister = adminMessage.targetRegisterHost?.let { + student.scrapperBaseUrl.contains(it, true) + } ?: true + val isCorrectFlavor = + adminMessage.targetFlavor?.equals(appInfo.buildFlavor, true) ?: true + val isCorrectMaxVersion = + adminMessage.versionMax?.let { it >= appInfo.versionCode } ?: true + val isCorrectMinVersion = + adminMessage.versionMin?.let { it <= appInfo.versionCode } ?: true + + isCorrectRegister && isCorrectFlavor && isCorrectMaxVersion && isCorrectMinVersion + }.maxByOrNull { it.id } + } + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AppCreatorRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AppCreatorRepository.kt new file mode 100644 index 000000000..cbaa12bd3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AppCreatorRepository.kt @@ -0,0 +1,27 @@ +package io.github.wulkanowy.data.repositories + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.data.pojos.Contributor +import io.github.wulkanowy.utils.DispatchersProvider +import kotlinx.coroutines.withContext +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.decodeFromStream +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AppCreatorRepository @Inject constructor( + @ApplicationContext private val context: Context, + private val dispatchers: DispatchersProvider, + private val json: Json, +) { + + @OptIn(ExperimentalSerializationApi::class) + @Suppress("BlockingMethodInNonBlockingContext") + suspend fun getAppCreators() = withContext(dispatchers.io) { + val inputStream = context.assets.open("contributors.json").buffered() + json.decodeFromStream>(inputStream) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt new file mode 100644 index 000000000..9aa6562a6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt @@ -0,0 +1,94 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.AttendanceDao +import io.github.wulkanowy.data.db.entities.Attendance +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.sdk.pojo.Absent +import io.github.wulkanowy.utils.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.sync.Mutex +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.LocalTime +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AttendanceRepository @Inject constructor( + private val attendanceDb: AttendanceDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val saveFetchResultMutex = Mutex() + + private val cacheKey = "attendance" + + fun getAttendance( + student: Student, + semester: Semester, + start: LocalDate, + end: LocalDate, + forceRefresh: Boolean, + notify: Boolean = false, + ) = networkBoundResource( + mutex = saveFetchResultMutex, + isResultEmpty = { it.isEmpty() }, + shouldFetch = { + val isExpired = refreshHelper.shouldBeRefreshed( + key = getRefreshKey(cacheKey, semester, start, end) + ) + it.isEmpty() || forceRefresh || isExpired + }, + query = { + attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday) + }, + fetch = { + sdk.init(student) + .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + .getAttendance(start.monday, end.sunday, semester.semesterId) + .mapToEntities(semester) + }, + saveFetchResult = { old, new -> + attendanceDb.deleteAll(old uniqueSubtract new) + val attendanceToAdd = (new uniqueSubtract old).map { newAttendance -> + newAttendance.apply { if (notify) isNotified = false } + } + attendanceDb.insertAll(attendanceToAdd) + + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end)) + }, + filterResult = { it.filter { item -> item.date in start..end } } + ) + + fun getAttendanceFromDatabase( + semester: Semester, + start: LocalDate, + end: LocalDate + ): Flow> { + return attendanceDb.loadAll(semester.diaryId, semester.studentId, start, end) + } + + suspend fun updateTimetable(timetable: List) { + return attendanceDb.updateAll(timetable) + } + + suspend fun excuseForAbsence( + student: Student, semester: Semester, + absenceList: List, reason: String? = null + ) { + val items = absenceList.map { attendance -> + Absent( + date = LocalDateTime.of(attendance.date, LocalTime.of(0, 0)), + timeId = attendance.timeId + ) + } + sdk.init(student) + .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + .excuseForAbsence(items, reason) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt new file mode 100644 index 000000000..8e0709135 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt @@ -0,0 +1,53 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.uniqueSubtract +import kotlinx.coroutines.sync.Mutex +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AttendanceSummaryRepository @Inject constructor( + private val attendanceDb: AttendanceSummaryDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val saveFetchResultMutex = Mutex() + + private val cacheKey = "attendance_summary" + + fun getAttendanceSummary( + student: Student, + semester: Semester, + subjectId: Int, + forceRefresh: Boolean, + ) = networkBoundResource( + mutex = saveFetchResultMutex, + isResultEmpty = { it.isEmpty() }, + shouldFetch = { + val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester)) + it.isEmpty() || forceRefresh || isExpired + }, + query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId) }, + fetch = { + sdk.init(student) + .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + .getAttendanceSummary(subjectId) + .mapToEntities(semester, subjectId) + }, + saveFetchResult = { old, new -> + attendanceDb.deleteAll(old uniqueSubtract new) + attendanceDb.insertAll(new uniqueSubtract old) + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester)) + } + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt new file mode 100644 index 000000000..8f393cadb --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt @@ -0,0 +1,62 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.CompletedLessonsDao +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.* +import kotlinx.coroutines.sync.Mutex +import java.time.LocalDate +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class CompletedLessonsRepository @Inject constructor( + private val completedLessonsDb: CompletedLessonsDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val saveFetchResultMutex = Mutex() + + private val cacheKey = "completed" + + fun getCompletedLessons( + student: Student, + semester: Semester, + start: LocalDate, + end: LocalDate, + forceRefresh: Boolean, + ) = networkBoundResource( + mutex = saveFetchResultMutex, + isResultEmpty = { it.isEmpty() }, + shouldFetch = { + val isExpired = refreshHelper.shouldBeRefreshed( + key = getRefreshKey(cacheKey, semester, start, end) + ) + it.isEmpty() || forceRefresh || isExpired + }, + query = { + completedLessonsDb.loadAll( + studentId = semester.studentId, + diaryId = semester.diaryId, + from = start.monday, + end = end.sunday + ) + }, + fetch = { + sdk.init(student) + .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + .getCompletedLessons(start.monday, end.sunday) + .mapToEntities(semester) + }, + saveFetchResult = { old, new -> + completedLessonsDb.deleteAll(old uniqueSubtract new) + completedLessonsDb.insertAll(new uniqueSubtract old) + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end)) + }, + filterResult = { it.filter { item -> item.date in start..end } } + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt new file mode 100644 index 000000000..83204cab0 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt @@ -0,0 +1,73 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.ConferenceDao +import io.github.wulkanowy.data.db.entities.Conference +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.uniqueSubtract +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.sync.Mutex +import java.time.Instant +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ConferenceRepository @Inject constructor( + private val conferenceDb: ConferenceDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val saveFetchResultMutex = Mutex() + + private val cacheKey = "conference" + + fun getConferences( + student: Student, + semester: Semester, + forceRefresh: Boolean, + notify: Boolean = false, + startDate: Instant = Instant.EPOCH, + ) = networkBoundResource( + mutex = saveFetchResultMutex, + isResultEmpty = { it.isEmpty() }, + shouldFetch = { + val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester)) + it.isEmpty() || forceRefresh || isExpired + }, + query = { + conferenceDb.loadAll(semester.diaryId, student.studentId, startDate) + }, + fetch = { + sdk.init(student) + .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + .getConferences() + .mapToEntities(semester) + .filter { it.date >= startDate } + }, + saveFetchResult = { old, new -> + val conferencesToSave = (new uniqueSubtract old).onEach { + if (notify) it.isNotified = false + } + + conferenceDb.deleteAll(old uniqueSubtract new) + conferenceDb.insertAll(conferencesToSave) + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester)) + } + ) + + fun getConferenceFromDatabase(semester: Semester): Flow> = + conferenceDb.loadAll( + diaryId = semester.diaryId, + studentId = semester.studentId, + startDate = Instant.EPOCH, + ) + + suspend fun updateConference(conference: List) = conferenceDb.updateAll(conference) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt new file mode 100644 index 000000000..faa80b93e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt @@ -0,0 +1,80 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.ExamDao +import io.github.wulkanowy.data.db.entities.Exam +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.sync.Mutex +import java.time.LocalDate +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ExamRepository @Inject constructor( + private val examDb: ExamDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val saveFetchResultMutex = Mutex() + + private val cacheKey = "exam" + + fun getExams( + student: Student, + semester: Semester, + start: LocalDate, + end: LocalDate, + forceRefresh: Boolean, + notify: Boolean = false, + ) = networkBoundResource( + mutex = saveFetchResultMutex, + isResultEmpty = { it.isEmpty() }, + shouldFetch = { + val isExpired = refreshHelper.shouldBeRefreshed( + key = getRefreshKey(cacheKey, semester, start, end) + ) + it.isEmpty() || forceRefresh || isExpired + }, + query = { + examDb.loadAll( + diaryId = semester.diaryId, + studentId = semester.studentId, + from = start.startExamsDay, + end = start.endExamsDay + ) + }, + fetch = { + sdk.init(student) + .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + .getExams(start.startExamsDay, start.endExamsDay, semester.semesterId) + .mapToEntities(semester) + }, + saveFetchResult = { old, new -> + val examsToSave = (new uniqueSubtract old).onEach { + if (notify) it.isNotified = false + } + + examDb.deleteAll(old uniqueSubtract new) + examDb.insertAll(examsToSave) + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end)) + }, + filterResult = { it.filter { item -> item.date in start..end } } + ) + + fun getExamsFromDatabase(semester: Semester, start: LocalDate): Flow> { + return examDb.loadAll( + diaryId = semester.diaryId, + studentId = semester.studentId, + from = start.startExamsDay, + end = start.endExamsDay + ) + } + + suspend fun updateExam(exam: List) = examDb.updateAll(exam) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt new file mode 100644 index 000000000..f5f895d82 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt @@ -0,0 +1,146 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.GradeDao +import io.github.wulkanowy.data.db.dao.GradeSummaryDao +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.data.db.entities.GradeSummary +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.sync.Mutex +import java.time.Instant +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class GradeRepository @Inject constructor( + private val gradeDb: GradeDao, + private val gradeSummaryDb: GradeSummaryDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val saveFetchResultMutex = Mutex() + + private val cacheKey = "grade" + + fun getGrades( + student: Student, + semester: Semester, + forceRefresh: Boolean, + notify: Boolean = false, + ) = networkBoundResource( + mutex = saveFetchResultMutex, + isResultEmpty = { + //When details is empty and summary is not, app will not use summary cache - edge case + it.first.isEmpty() + }, + shouldFetch = { (details, summaries) -> + val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester)) + details.isEmpty() || summaries.isEmpty() || forceRefresh || isExpired + }, + query = { + val detailsFlow = gradeDb.loadAll(semester.semesterId, semester.studentId) + val summaryFlow = gradeSummaryDb.loadAll(semester.semesterId, semester.studentId) + detailsFlow.combine(summaryFlow) { details, summaries -> details to summaries } + }, + fetch = { + val (details, summary) = sdk.init(student) + .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + .getGrades(semester.semesterId) + + details.mapToEntities(semester) to summary.mapToEntities(semester) + }, + saveFetchResult = { (oldDetails, oldSummary), (newDetails, newSummary) -> + refreshGradeDetails(student, oldDetails, newDetails, notify) + refreshGradeSummaries(oldSummary, newSummary, notify) + + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester)) + } + ) + + private suspend fun refreshGradeDetails( + student: Student, + oldGrades: List, + newDetails: List, + notify: Boolean + ) { + val notifyBreakDate = oldGrades.maxByOrNull { it.date }?.date + ?: student.registrationDate.toLocalDate() + gradeDb.deleteAll(oldGrades uniqueSubtract newDetails) + gradeDb.insertAll((newDetails uniqueSubtract oldGrades).onEach { + if (it.date >= notifyBreakDate) it.apply { + isRead = false + if (notify) isNotified = false + } + }) + } + + private suspend fun refreshGradeSummaries( + oldSummaries: List, + newSummary: List, + notify: Boolean + ) { + gradeSummaryDb.deleteAll(oldSummaries uniqueSubtract newSummary) + gradeSummaryDb.insertAll((newSummary uniqueSubtract oldSummaries).onEach { summary -> + val oldSummary = oldSummaries.find { old -> old.subject == summary.subject } + summary.isPredictedGradeNotified = when { + summary.predictedGrade.isEmpty() -> true + notify && oldSummary?.predictedGrade != summary.predictedGrade -> false + else -> true + } + summary.isFinalGradeNotified = when { + summary.finalGrade.isEmpty() -> true + notify && oldSummary?.finalGrade != summary.finalGrade -> false + else -> true + } + + summary.predictedGradeLastChange = when { + oldSummary == null -> Instant.now() + summary.predictedGrade != oldSummary.predictedGrade -> Instant.now() + else -> oldSummary.predictedGradeLastChange + } + summary.finalGradeLastChange = when { + oldSummary == null -> Instant.now() + summary.finalGrade != oldSummary.finalGrade -> Instant.now() + else -> oldSummary.finalGradeLastChange + } + }) + } + + fun getUnreadGrades(semester: Semester): Flow> { + return gradeDb.loadAll(semester.semesterId, semester.studentId).map { + it.filter { grade -> !grade.isRead } + } + } + + fun getGradesFromDatabase(semester: Semester): Flow> { + return gradeDb.loadAll(semester.semesterId, semester.studentId) + } + + fun getGradesPredictedFromDatabase(semester: Semester): Flow> { + return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId) + } + + fun getGradesFinalFromDatabase(semester: Semester): Flow> { + return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId) + } + + suspend fun updateGrade(grade: Grade) { + return gradeDb.updateAll(listOf(grade)) + } + + suspend fun updateGrades(grades: List) { + return gradeDb.updateAll(grades) + } + + suspend fun updateGradesSummary(gradesSummary: List) { + return gradeSummaryDb.updateAll(gradesSummary) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt new file mode 100644 index 000000000..9fa06c497 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt @@ -0,0 +1,199 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.GradePartialStatisticsDao +import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao +import io.github.wulkanowy.data.db.dao.GradeSemesterStatisticsDao +import io.github.wulkanowy.data.db.entities.GradePartialStatistics +import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapPartialToStatisticItems +import io.github.wulkanowy.data.mappers.mapPointsToStatisticsItems +import io.github.wulkanowy.data.mappers.mapSemesterToStatisticItems +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.uniqueSubtract +import kotlinx.coroutines.sync.Mutex +import java.util.* +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class GradeStatisticsRepository @Inject constructor( + private val gradePartialStatisticsDb: GradePartialStatisticsDao, + private val gradePointsStatisticsDb: GradePointsStatisticsDao, + private val gradeSemesterStatisticsDb: GradeSemesterStatisticsDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val partialMutex = Mutex() + private val semesterMutex = Mutex() + private val pointsMutex = Mutex() + + private val partialCacheKey = "grade_stats_partial" + private val semesterCacheKey = "grade_stats_semester" + private val pointsCacheKey = "grade_stats_points" + + fun getGradesPartialStatistics( + student: Student, + semester: Semester, + subjectName: String, + forceRefresh: Boolean, + ) = networkBoundResource( + mutex = partialMutex, + isResultEmpty = { it.isEmpty() }, + shouldFetch = { + val isExpired = refreshHelper.shouldBeRefreshed( + key = getRefreshKey(partialCacheKey, semester) + ) + it.isEmpty() || forceRefresh || isExpired + }, + query = { gradePartialStatisticsDb.loadAll(semester.semesterId, semester.studentId) }, + fetch = { + sdk.init(student) + .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + .getGradesPartialStatistics(semester.semesterId) + .mapToEntities(semester) + }, + saveFetchResult = { old, new -> + gradePartialStatisticsDb.deleteAll(old uniqueSubtract new) + gradePartialStatisticsDb.insertAll(new uniqueSubtract old) + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(partialCacheKey, semester)) + }, + mapResult = { items -> + when (subjectName) { + "Wszystkie" -> { + val summaryItem = GradePartialStatistics( + studentId = semester.studentId, + semesterId = semester.semesterId, + subject = subjectName, + classAverage = items.map { it.classAverage }.getSummaryAverage(), + studentAverage = items.map { it.studentAverage }.getSummaryAverage(), + classAmounts = items.map { it.classAmounts }.sumGradeAmounts(), + studentAmounts = items.map { it.studentAmounts }.sumGradeAmounts() + ) + listOf(summaryItem) + items + } + else -> items.filter { it.subject == subjectName } + }.mapPartialToStatisticItems() + } + ) + + fun getGradesSemesterStatistics( + student: Student, + semester: Semester, + subjectName: String, + forceRefresh: Boolean, + ) = networkBoundResource( + mutex = semesterMutex, + isResultEmpty = { it.isEmpty() }, + shouldFetch = { + val isExpired = refreshHelper.shouldBeRefreshed( + key = getRefreshKey(semesterCacheKey, semester) + ) + it.isEmpty() || forceRefresh || isExpired + }, + query = { gradeSemesterStatisticsDb.loadAll(semester.semesterId, semester.studentId) }, + fetch = { + sdk.init(student) + .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + .getGradesSemesterStatistics(semester.semesterId) + .mapToEntities(semester) + }, + saveFetchResult = { old, new -> + gradeSemesterStatisticsDb.deleteAll(old uniqueSubtract new) + gradeSemesterStatisticsDb.insertAll(new uniqueSubtract old) + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(semesterCacheKey, semester)) + }, + mapResult = { items -> + val itemsWithAverage = items.map { item -> + item.copy().apply { + val denominator = item.amounts.sum() + classAverage = if (denominator == 0) "" else { + (item.amounts.mapIndexed { gradeValue, amount -> + (gradeValue + 1) * amount + }.sum().toDouble() / denominator).asAverageString() + } + } + } + when (subjectName) { + "Wszystkie" -> { + val summaryItem = GradeSemesterStatistics( + studentId = semester.studentId, + semesterId = semester.semesterId, + subject = subjectName, + amounts = itemsWithAverage.map { it.amounts }.sumGradeAmounts(), + studentGrade = 0, + ).apply { + classAverage = itemsWithAverage.map { it.classAverage }.getSummaryAverage() + studentAverage = items + .mapNotNull { summary -> summary.studentGrade.takeIf { it != 0 } } + .average().asAverageString() + } + listOf(summaryItem) + itemsWithAverage + } + else -> itemsWithAverage.filter { it.subject == subjectName } + }.mapSemesterToStatisticItems() + } + ) + + fun getGradesPointsStatistics( + student: Student, + semester: Semester, + subjectName: String, + forceRefresh: Boolean, + ) = networkBoundResource( + mutex = pointsMutex, + isResultEmpty = { it.isEmpty() }, + shouldFetch = { + val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(pointsCacheKey, semester)) + it.isEmpty() || forceRefresh || isExpired + }, + query = { gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId) }, + fetch = { + sdk.init(student) + .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + .getGradesPointsStatistics(semester.semesterId) + .mapToEntities(semester) + }, + saveFetchResult = { old, new -> + gradePointsStatisticsDb.deleteAll(old uniqueSubtract new) + gradePointsStatisticsDb.insertAll(new uniqueSubtract old) + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(pointsCacheKey, semester)) + }, + mapResult = { items -> + when (subjectName) { + "Wszystkie" -> items + else -> items.filter { it.subject == subjectName } + }.mapPointsToStatisticsItems() + } + ) + + private fun List.getSummaryAverage(): String { + val averages = mapNotNull { + it.replace(",", ".").toDoubleOrNull() + } + + return averages.average() + .asAverageString() + .takeIf { averages.isNotEmpty() } + .orEmpty() + } + + private fun Double.asAverageString(): String = "%.2f".format(Locale.FRANCE, this) + + private fun List>.sumGradeAmounts(): List { + val result = mutableListOf(0, 0, 0, 0, 0, 0) + forEach { + it.forEachIndexed { grade, amount -> + result[grade] += amount + } + } + return result + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt new file mode 100644 index 000000000..f564824de --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt @@ -0,0 +1,84 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.HomeworkDao +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.* +import kotlinx.coroutines.sync.Mutex +import java.time.LocalDate +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class HomeworkRepository @Inject constructor( + private val homeworkDb: HomeworkDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val saveFetchResultMutex = Mutex() + + private val cacheKey = "homework" + + fun getHomework( + student: Student, + semester: Semester, + start: LocalDate, + end: LocalDate, + forceRefresh: Boolean, + notify: Boolean = false, + ) = networkBoundResource( + mutex = saveFetchResultMutex, + isResultEmpty = { it.isEmpty() }, + shouldFetch = { + val isExpired = refreshHelper.shouldBeRefreshed( + key = getRefreshKey(cacheKey, semester, start, end) + ) + it.isEmpty() || forceRefresh || isExpired + }, + query = { + homeworkDb.loadAll( + semesterId = semester.semesterId, + studentId = semester.studentId, + from = start.monday, + end = end.sunday + ) + }, + fetch = { + sdk.init(student) + .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + .getHomework(start.monday, end.sunday) + .mapToEntities(semester) + }, + saveFetchResult = { old, new -> + val homeWorkToSave = (new uniqueSubtract old).onEach { + if (notify) it.isNotified = false + } + val filteredOld = old.filterNot { it.isAddedByUser } + + homeworkDb.deleteAll(filteredOld uniqueSubtract new) + homeworkDb.insertAll(homeWorkToSave) + + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end)) + } + ) + + suspend fun toggleDone(homework: Homework) { + homeworkDb.updateAll(listOf(homework.apply { + isDone = !isDone + })) + } + + fun getHomeworkFromDatabase(semester: Semester, start: LocalDate, end: LocalDate) = + homeworkDb.loadAll(semester.semesterId, semester.studentId, start.monday, end.sunday) + + suspend fun updateHomework(homework: List) = homeworkDb.updateAll(homework) + + suspend fun saveHomework(homework: Homework) = homeworkDb.insertAll(listOf(homework)) + + suspend fun deleteHomework(homework: Homework) = homeworkDb.deleteAll(listOf(homework)) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/LoggerRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/LoggerRepository.kt new file mode 100644 index 000000000..1a8cd6ea3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/LoggerRepository.kt @@ -0,0 +1,37 @@ +package io.github.wulkanowy.data.repositories + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.utils.DispatchersProvider +import kotlinx.coroutines.withContext +import java.io.File +import java.io.FileNotFoundException +import javax.inject.Inject + +class LoggerRepository @Inject constructor( + @ApplicationContext private val context: Context, + private val dispatchers: DispatchersProvider +) { + + suspend fun getLastLogLines() = getLastModified().readText().split("\n") + + suspend fun getLogFiles() = withContext(dispatchers.io) { + File(context.filesDir.absolutePath).listFiles(File::isFile) + ?.filter { it.name.endsWith(".log") }!! + } + + private suspend fun getLastModified() = withContext(dispatchers.io) { + var lastModifiedTime = Long.MIN_VALUE + var chosenFile: File? = null + + File(context.filesDir.absolutePath).listFiles(File::isFile) + ?.forEach { file -> + if (file.lastModified() > lastModifiedTime) { + lastModifiedTime = file.lastModified() + chosenFile = file + } + } + + chosenFile ?: throw FileNotFoundException("Log file not found") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt new file mode 100644 index 000000000..87e8410f1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt @@ -0,0 +1,58 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.LuckyNumberDao +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntity +import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.init +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.sync.Mutex +import java.time.LocalDate +import java.time.LocalDate.now +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class LuckyNumberRepository @Inject constructor( + private val luckyNumberDb: LuckyNumberDao, + private val sdk: Sdk +) { + + private val saveFetchResultMutex = Mutex() + + fun getLuckyNumber( + student: Student, + forceRefresh: Boolean, + notify: Boolean = false, + ) = networkBoundResource( + mutex = saveFetchResultMutex, + isResultEmpty = { it == null }, + shouldFetch = { it == null || forceRefresh }, + query = { luckyNumberDb.load(student.studentId, now()) }, + fetch = { + sdk.init(student).getLuckyNumber(student.schoolShortName)?.mapToEntity(student) + }, + saveFetchResult = { old, new -> + if (new != old) { + old?.let { luckyNumberDb.deleteAll(listOfNotNull(it)) } + luckyNumberDb.insertAll(listOfNotNull((new?.apply { + if (notify) isNotified = false + }))) + } + } + ) + + fun getLuckyNumberHistory(student: Student, start: LocalDate, end: LocalDate) = + luckyNumberDb.getAll(student.studentId, start, end) + + suspend fun getNotNotifiedLuckyNumber(student: Student) = + luckyNumberDb.load(student.studentId, now()).map { + if (it?.isNotified == false) it else null + }.first() + + suspend fun updateLuckyNumber(luckyNumber: LuckyNumber?) = + luckyNumberDb.updateAll(listOfNotNull(luckyNumber)) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt new file mode 100644 index 000000000..05fb97657 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt @@ -0,0 +1,182 @@ +package io.github.wulkanowy.data.repositories + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import io.github.wulkanowy.data.Resource +import io.github.wulkanowy.data.db.SharedPrefProvider +import io.github.wulkanowy.data.db.dao.MessageAttachmentDao +import io.github.wulkanowy.data.db.dao.MessagesDao +import io.github.wulkanowy.data.db.entities.* +import io.github.wulkanowy.data.enums.MessageFolder +import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED +import io.github.wulkanowy.data.mappers.mapFromEntities +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.data.pojos.MessageDraft +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.sdk.pojo.Folder +import io.github.wulkanowy.sdk.pojo.SentMessage +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.uniqueSubtract +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.sync.Mutex +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import timber.log.Timber +import java.time.LocalDateTime.now +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class MessageRepository @Inject constructor( + private val messagesDb: MessagesDao, + private val messageAttachmentDao: MessageAttachmentDao, + private val sdk: Sdk, + @ApplicationContext private val context: Context, + private val refreshHelper: AutoRefreshHelper, + private val sharedPrefProvider: SharedPrefProvider, + private val json: Json, +) { + + private val saveFetchResultMutex = Mutex() + + private val cacheKey = "message" + + @Suppress("UNUSED_PARAMETER") + fun getMessages( + student: Student, + semester: Semester, + folder: MessageFolder, + forceRefresh: Boolean, + notify: Boolean = false, + ): Flow>> = networkBoundResource( + mutex = saveFetchResultMutex, + isResultEmpty = { it.isEmpty() }, + shouldFetch = { + val isExpired = refreshHelper.shouldBeRefreshed( + key = getRefreshKey(cacheKey, student, folder) + ) + it.isEmpty() || forceRefresh || isExpired + }, + query = { messagesDb.loadAll(student.id.toInt(), folder.id) }, + fetch = { + sdk.init(student).getMessages(Folder.valueOf(folder.name), now().minusMonths(3), now()) + .mapToEntities(student) + }, + saveFetchResult = { old, new -> + messagesDb.deleteAll(old uniqueSubtract new) + messagesDb.insertAll((new uniqueSubtract old).onEach { + it.isNotified = !notify + }) + messagesDb.updateAll(getMessagesWithReadByChange(old, new, !notify)) + + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student, folder)) + } + ) + + private fun getMessagesWithReadByChange( + old: List, + new: List, + setNotified: Boolean + ): List { + val oldMeta = old.map { Triple(it, it.readBy, it.unreadBy) } + val newMeta = new.map { Triple(it, it.readBy, it.unreadBy) } + + val updatedItems = newMeta uniqueSubtract oldMeta + + return updatedItems.map { + val oldItem = old.find { item -> item.messageId == it.first.messageId } + it.first.apply { + id = oldItem?.id ?: 0 + isNotified = oldItem?.isNotified ?: setNotified + content = oldItem?.content.orEmpty() + } + } + } + + fun getMessage( + student: Student, + message: Message, + markAsRead: Boolean = false, + ): Flow> = networkBoundResource( + isResultEmpty = { it?.message?.content.isNullOrBlank() }, + shouldFetch = { + checkNotNull(it) { "This message no longer exist!" } + Timber.d("Message content in db empty: ${it.message.content.isEmpty()}") + it.message.unread || it.message.content.isEmpty() + }, + query = { messagesDb.loadMessageWithAttachment(student.id.toInt(), message.messageId) }, + fetch = { + sdk.init(student).getMessageDetails( + messageId = it!!.message.messageId, + folderId = message.folderId, + read = markAsRead, + id = message.realId + ).let { details -> + details.content to details.attachments.mapToEntities() + } + }, + saveFetchResult = { old, (downloadedMessage, attachments) -> + checkNotNull(old) { "Fetched message no longer exist!" } + messagesDb.updateAll(listOf(old.message.apply { + id = old.message.id + unread = !markAsRead + content = content.ifBlank { downloadedMessage } + })) + messageAttachmentDao.insertAttachments(attachments) + Timber.d("Message ${message.messageId} with blank content: ${old.message.content.isBlank()}, marked as read") + } + ) + + fun getMessagesFromDatabase(student: Student): Flow> { + return messagesDb.loadAll(student.id.toInt(), RECEIVED.id) + } + + suspend fun updateMessages(messages: List) { + return messagesDb.updateAll(messages) + } + + suspend fun sendMessage( + student: Student, + subject: String, + content: String, + recipients: List, + ): SentMessage = sdk.init(student).sendMessage( + subject = subject, + content = content, + recipients = recipients.mapFromEntities() + ) + + suspend fun deleteMessages(student: Student, messages: List) { + val folderId = messages.first().folderId + val isDeleted = sdk.init(student) + .deleteMessages(messages = messages.map { it.messageId }, folderId = folderId) + + if (folderId != MessageFolder.TRASHED.id && isDeleted) { + val deletedMessages = messages.map { + it.copy(folderId = MessageFolder.TRASHED.id) + .apply { + id = it.id + content = it.content + } + } + + messagesDb.updateAll(deletedMessages) + } else messagesDb.deleteAll(messages) + } + + suspend fun deleteMessage(student: Student, message: Message) = + deleteMessages(student, listOf(message)) + + var draftMessage: MessageDraft? + get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_send_draft)) + ?.let { json.decodeFromString(it) } + set(value) = sharedPrefProvider.putString( + context.getString(R.string.pref_key_message_send_draft), + value?.let { json.encodeToString(it) } + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt new file mode 100644 index 000000000..eda40cac4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt @@ -0,0 +1,71 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.MobileDeviceDao +import io.github.wulkanowy.data.db.entities.MobileDevice +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.data.mappers.mapToMobileDeviceToken +import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.data.pojos.MobileDeviceToken +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.uniqueSubtract +import kotlinx.coroutines.sync.Mutex +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class MobileDeviceRepository @Inject constructor( + private val mobileDb: MobileDeviceDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val saveFetchResultMutex = Mutex() + + private val cacheKey = "devices" + + fun getDevices( + student: Student, + semester: Semester, + forceRefresh: Boolean, + ) = networkBoundResource( + mutex = saveFetchResultMutex, + isResultEmpty = { it.isEmpty() }, + shouldFetch = { + val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student)) + it.isEmpty() || forceRefresh || isExpired + }, + query = { mobileDb.loadAll(student.userLoginId.takeIf { it != 0 } ?: student.studentId) }, + fetch = { + sdk.init(student) + .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + .getRegisteredDevices() + .mapToEntities(semester) + }, + saveFetchResult = { old, new -> + mobileDb.deleteAll(old uniqueSubtract new) + mobileDb.insertAll(new uniqueSubtract old) + + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student)) + } + ) + + suspend fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice) { + sdk.init(student) + .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + .unregisterDevice(device.deviceId) + + mobileDb.deleteAll(listOf(device)) + } + + suspend fun getToken(student: Student, semester: Semester): MobileDeviceToken { + return sdk.init(student) + .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + .getToken() + .mapToMobileDeviceToken() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt new file mode 100644 index 000000000..e5d7bc5cb --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt @@ -0,0 +1,72 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.NoteDao +import io.github.wulkanowy.data.db.entities.Note +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.sync.Mutex +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class NoteRepository @Inject constructor( + private val noteDb: NoteDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val saveFetchResultMutex = Mutex() + + private val cacheKey = "note" + + fun getNotes( + student: Student, + semester: Semester, + forceRefresh: Boolean, + notify: Boolean = false, + ) = networkBoundResource( + mutex = saveFetchResultMutex, + isResultEmpty = { it.isEmpty() }, + shouldFetch = { + val isExpired = refreshHelper.shouldBeRefreshed( + getRefreshKey(cacheKey, semester) + ) + it.isEmpty() || forceRefresh || isExpired + }, + query = { noteDb.loadAll(student.studentId) }, + fetch = { + sdk.init(student) + .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + .getNotes(semester.semesterId) + .mapToEntities(semester) + }, + saveFetchResult = { old, new -> + noteDb.deleteAll(old uniqueSubtract new) + noteDb.insertAll((new uniqueSubtract old).onEach { + if (it.date >= student.registrationDate.toLocalDate()) it.apply { + isRead = false + if (notify) isNotified = false + } + }) + + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester)) + } + ) + + fun getNotesFromDatabase(student: Student): Flow> { + return noteDb.loadAll(student.studentId) + } + + suspend fun updateNote(note: Note) { + noteDb.updateAll(listOf(note)) + } + + suspend fun updateNotes(notes: List) { + noteDb.updateAll(notes) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/NotificationRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/NotificationRepository.kt new file mode 100644 index 000000000..fca263782 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/NotificationRepository.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.NotificationDao +import io.github.wulkanowy.data.db.entities.Notification +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class NotificationRepository @Inject constructor( + private val notificationDao: NotificationDao, +) { + + fun getNotifications(studentId: Long) = notificationDao.loadAll(studentId) + + suspend fun saveNotification(notification: Notification) = + notificationDao.insertAll(listOf(notification)) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt new file mode 100644 index 000000000..4cd85586f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt @@ -0,0 +1,306 @@ +package io.github.wulkanowy.data.repositories + +import android.content.Context +import android.content.SharedPreferences +import androidx.core.content.edit +import com.fredporciuncula.flow.preferences.FlowSharedPreferences +import com.fredporciuncula.flow.preferences.Preference +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import io.github.wulkanowy.data.enums.* +import io.github.wulkanowy.ui.modules.dashboard.DashboardItem +import io.github.wulkanowy.ui.modules.grade.GradeAverageMode +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import java.time.Instant +import javax.inject.Inject +import javax.inject.Singleton + +@OptIn(ExperimentalCoroutinesApi::class) +@Singleton +class PreferencesRepository @Inject constructor( + @ApplicationContext val context: Context, + private val sharedPref: SharedPreferences, + private val flowSharedPref: FlowSharedPreferences, + private val json: Json, +) { + + val startMenuIndex: Int + get() = getString(R.string.pref_key_start_menu, R.string.pref_default_startup).toInt() + + val isShowPresent: Boolean + get() = getBoolean( + R.string.pref_key_attendance_present, + R.bool.pref_default_attendance_present + ) + + val gradeAverageMode: GradeAverageMode + get() = GradeAverageMode.getByValue( + getString( + R.string.pref_key_grade_average_mode, + R.string.pref_default_grade_average_mode + ) + ) + + val gradeAverageForceCalc: Boolean + get() = getBoolean( + R.string.pref_key_grade_average_force_calc, + R.bool.pref_default_grade_average_force_calc + ) + + val gradeExpandMode: GradeExpandMode + get() = GradeExpandMode.getByValue( + getString( + R.string.pref_key_expand_grade_mode, + R.string.pref_default_expand_grade_mode + ) + ) + + val showAllSubjectsOnStatisticsList: Boolean + get() = getBoolean( + R.string.pref_key_grade_statistics_list, + R.bool.pref_default_grade_statistics_list + ) + + val appThemeKey = context.getString(R.string.pref_key_app_theme) + val appTheme: AppTheme + get() = AppTheme.getByValue(getString(appThemeKey, R.string.pref_default_app_theme)) + + val gradeColorTheme: GradeColorTheme + get() = GradeColorTheme.getByValue( + getString( + R.string.pref_key_grade_color_scheme, + R.string.pref_default_grade_color_scheme + ) + ) + + val appLanguageKey = context.getString(R.string.pref_key_app_language) + val appLanguage + get() = getString(appLanguageKey, R.string.pref_default_app_language) + + val serviceEnableKey = context.getString(R.string.pref_key_services_enable) + val isServiceEnabled: Boolean + get() = getBoolean(serviceEnableKey, R.bool.pref_default_services_enable) + + val servicesIntervalKey = context.getString(R.string.pref_key_services_interval) + val servicesInterval: Long + get() = getString(servicesIntervalKey, R.string.pref_default_services_interval).toLong() + + val servicesOnlyWifiKey = context.getString(R.string.pref_key_services_wifi_only) + val isServicesOnlyWifi: Boolean + get() = getBoolean(servicesOnlyWifiKey, R.bool.pref_default_services_wifi_only) + + val notificationsEnableKey = context.getString(R.string.pref_key_notifications_enable) + val isNotificationsEnable: Boolean + get() = getBoolean(notificationsEnableKey, R.bool.pref_default_notifications_enable) + + val isUpcomingLessonsNotificationsEnableKey = + context.getString(R.string.pref_key_notifications_upcoming_lessons_enable) + var isUpcomingLessonsNotificationsEnable: Boolean + set(value) { + sharedPref.edit { putBoolean(isUpcomingLessonsNotificationsEnableKey, value) } + } + get() = getBoolean( + isUpcomingLessonsNotificationsEnableKey, + R.bool.pref_default_notification_upcoming_lessons_enable + ) + + val isUpcomingLessonsNotificationsPersistentKey = + context.getString(R.string.pref_key_notifications_upcoming_lessons_persistent) + val isUpcomingLessonsNotificationsPersistent: Boolean + get() = getBoolean( + isUpcomingLessonsNotificationsPersistentKey, + R.bool.pref_default_notification_upcoming_lessons_persistent + ) + + val isNotificationPiggybackEnabledKey = + context.getString(R.string.pref_key_notifications_piggyback) + val isNotificationPiggybackEnabled: Boolean + get() = getBoolean( + R.string.pref_key_notifications_piggyback, + R.bool.pref_default_notification_piggyback + ) + + val isNotificationPiggybackRemoveOriginalEnabled: Boolean + get() = getBoolean( + R.string.pref_key_notifications_piggyback_cancel_original, + R.bool.pref_default_notification_piggyback_cancel_original + ) + + val isDebugNotificationEnableKey = context.getString(R.string.pref_key_notification_debug) + val isDebugNotificationEnable: Boolean + get() = getBoolean(isDebugNotificationEnableKey, R.bool.pref_default_notification_debug) + + val gradePlusModifier: Double + get() = getString( + R.string.pref_key_grade_modifier_plus, + R.string.pref_default_grade_modifier_plus + ).toDouble() + + val gradeMinusModifier: Double + get() = getString( + R.string.pref_key_grade_modifier_minus, + R.string.pref_default_grade_modifier_minus + ).toDouble() + + val fillMessageContent: Boolean + get() = getBoolean( + R.string.pref_key_fill_message_content, + R.bool.pref_default_fill_message_content + ) + + val showGroupsInPlan: Boolean + get() = getBoolean( + R.string.pref_key_timetable_show_groups, + R.bool.pref_default_timetable_show_groups + ) + + val showWholeClassPlan: TimetableMode + get() = TimetableMode.getByValue( + getString( + R.string.pref_key_timetable_show_whole_class, + R.string.pref_default_timetable_show_whole_class + ) + ) + + val gradeSortingMode: GradeSortingMode + get() = GradeSortingMode.getByValue( + getString( + R.string.pref_key_grade_sorting_mode, + R.string.pref_default_grade_sorting_mode + ) + ) + + val showTimetableTimers: Boolean + get() = getBoolean( + R.string.pref_key_timetable_show_timers, + R.bool.pref_default_timetable_show_timers + ) + + var isHomeworkFullscreen: Boolean + get() = getBoolean( + R.string.pref_key_homework_fullscreen, + R.bool.pref_default_homework_fullscreen + ) + set(value) = sharedPref.edit().putBoolean("homework_fullscreen", value).apply() + + val showSubjectsWithoutGrades: Boolean + get() = getBoolean( + R.string.pref_key_subjects_without_grades, + R.bool.pref_default_subjects_without_grades + ) + + val isOptionalArithmeticAverage: Boolean + get() = getBoolean( + R.string.pref_key_optional_arithmetic_average, + R.bool.pref_default_optional_arithmetic_average + ) + + var lasSyncDate: Instant? + get() = getLong(R.string.pref_key_last_sync_date, R.string.pref_default_last_sync_date) + .takeIf { it != 0L }?.let(Instant::ofEpochMilli) + set(value) = sharedPref.edit().putLong("last_sync_date", value?.toEpochMilli() ?: 0).apply() + + var dashboardItemsPosition: Map? + get() { + val value = sharedPref.getString(PREF_KEY_DASHBOARD_ITEMS_POSITION, null) ?: return null + + return json.decodeFromString(value) + } + set(value) = sharedPref.edit { + putString( + PREF_KEY_DASHBOARD_ITEMS_POSITION, + json.encodeToString(value) + ) + } + + val selectedDashboardTilesFlow: Flow> + get() = selectedDashboardTilesPreference.asFlow() + .map { set -> + set.map { DashboardItem.Tile.valueOf(it) } + .plus(DashboardItem.Tile.ACCOUNT) + .plus(DashboardItem.Tile.ADMIN_MESSAGE) + .toSet() + } + + var selectedDashboardTiles: Set + get() = selectedDashboardTilesPreference.get() + .map { DashboardItem.Tile.valueOf(it) } + .plus(DashboardItem.Tile.ACCOUNT) + .plus(DashboardItem.Tile.ADMIN_MESSAGE) + .toSet() + set(value) { + val filteredValue = value.filterNot { it == DashboardItem.Tile.ACCOUNT } + .map { it.name } + .toSet() + + selectedDashboardTilesPreference.set(filteredValue) + } + + private val selectedDashboardTilesPreference: Preference> + get() { + val defaultSet = + context.resources.getStringArray(R.array.pref_default_dashboard_tiles).toSet() + val prefKey = context.getString(R.string.pref_key_dashboard_tiles) + + return flowSharedPref.getStringSet(prefKey, defaultSet) + } + + var dismissedAdminMessageIds: List + get() = sharedPref.getStringSet(PREF_KEY_ADMIN_DISMISSED_MESSAGE_IDS, emptySet()) + .orEmpty() + .map { it.toInt() } + set(value) = sharedPref.edit { + putStringSet(PREF_KEY_ADMIN_DISMISSED_MESSAGE_IDS, value.map { it.toString() }.toSet()) + } + + var inAppReviewCount: Int + get() = sharedPref.getInt(PREF_KEY_IN_APP_REVIEW_COUNT, 0) + set(value) = sharedPref.edit().putInt(PREF_KEY_IN_APP_REVIEW_COUNT, value).apply() + + var inAppReviewDate: Instant? + get() = sharedPref.getLong(PREF_KEY_IN_APP_REVIEW_DATE, 0).takeIf { it != 0L } + ?.let(Instant::ofEpochMilli) + set(value) = sharedPref.edit { + putLong(PREF_KEY_IN_APP_REVIEW_DATE, value?.toEpochMilli() ?: 0) + } + + var isAppReviewDone: Boolean + get() = sharedPref.getBoolean(PREF_KEY_IN_APP_REVIEW_DONE, false) + set(value) = sharedPref.edit().putBoolean(PREF_KEY_IN_APP_REVIEW_DONE, value).apply() + + private fun getLong(id: Int, default: Int) = getLong(context.getString(id), default) + + private fun getLong(id: String, default: Int) = + sharedPref.getLong(id, context.resources.getString(default).toLong()) + + private fun getString(id: Int, default: Int) = getString(context.getString(id), default) + + private fun getString(id: String, default: Int) = + sharedPref.getString(id, context.getString(default)) ?: context.getString(default) + + private fun getBoolean(id: Int, default: Int) = getBoolean(context.getString(id), default) + + private fun getBoolean(id: String, default: Int) = + sharedPref.getBoolean(id, context.resources.getBoolean(default)) + + private fun getBoolean(id: Int, default: Boolean) = + sharedPref.getBoolean(context.getString(id), default) + + private companion object { + + private const val PREF_KEY_DASHBOARD_ITEMS_POSITION = "dashboard_items_position" + + private const val PREF_KEY_IN_APP_REVIEW_COUNT = "in_app_review_count" + + private const val PREF_KEY_IN_APP_REVIEW_DATE = "in_app_review_date" + + private const val PREF_KEY_IN_APP_REVIEW_DONE = "in_app_review_done" + + private const val PREF_KEY_ADMIN_DISMISSED_MESSAGE_IDS = "admin_message_dismissed_ids" + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt new file mode 100644 index 000000000..60e6f248f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt @@ -0,0 +1,50 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.RecipientDao +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.uniqueSubtract +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class RecipientRepository @Inject constructor( + private val recipientDb: RecipientDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val cacheKey = "recipient" + + suspend fun refreshRecipients(student: Student, unit: ReportingUnit, role: Int) { + val new = sdk.init(student).getRecipients(unit.unitId, role).mapToEntities(unit.studentId) + val old = recipientDb.loadAll(unit.studentId, unit.unitId, role) + + recipientDb.deleteAll(old uniqueSubtract new) + recipientDb.insertAll(new uniqueSubtract old) + + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student)) + } + + suspend fun getRecipients(student: Student, unit: ReportingUnit, role: Int): List { + val cached = recipientDb.loadAll(unit.studentId, unit.unitId, role) + + val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student)) + return if (cached.isEmpty() || isExpired) { + refreshRecipients(student, unit, role) + recipientDb.loadAll(unit.studentId, unit.unitId, role) + } else cached + } + + suspend fun getMessageRecipients(student: Student, message: Message): List { + return sdk.init(student).getMessageRecipients(message.messageId, message.senderId) + .mapToEntities(student.studentId) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/RecoverRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/RecoverRepository.kt new file mode 100644 index 000000000..5940f477b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/RecoverRepository.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.sdk.Sdk +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class RecoverRepository @Inject constructor(private val sdk: Sdk) { + + suspend fun getReCaptchaSiteKey(host: String, symbol: String): Pair { + return sdk.getPasswordResetCaptchaCode(host, symbol) + } + + suspend fun sendRecoverRequest( + url: String, symbol: String, email: String, reCaptchaResponse: String + ): String = sdk.sendPasswordResetRequest(url, symbol, email, reCaptchaResponse) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt new file mode 100644 index 000000000..b9caf978b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt @@ -0,0 +1,42 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.ReportingUnitDao +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.uniqueSubtract +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ReportingUnitRepository @Inject constructor( + private val reportingUnitDb: ReportingUnitDao, + private val sdk: Sdk +) { + + suspend fun refreshReportingUnits(student: Student) { + val new = sdk.init(student).getReportingUnits().mapToEntities(student) + val old = reportingUnitDb.load(student.id.toInt()) + + reportingUnitDb.deleteAll(old.uniqueSubtract(new)) + reportingUnitDb.insertAll(new.uniqueSubtract(old)) + } + + suspend fun getReportingUnits(student: Student): List { + return reportingUnitDb.load(student.id.toInt()).ifEmpty { + refreshReportingUnits(student) + + reportingUnitDb.load(student.id.toInt()) + } + } + + suspend fun getReportingUnit(student: Student, unitId: Int): ReportingUnit? { + return reportingUnitDb.loadOne(student.id.toInt(), unitId) ?: run { + refreshReportingUnits(student) + + return reportingUnitDb.loadOne(student.id.toInt(), unitId) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt new file mode 100644 index 000000000..cf7ac86cd --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt @@ -0,0 +1,64 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.SchoolAnnouncementDao +import io.github.wulkanowy.data.db.entities.SchoolAnnouncement +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.uniqueSubtract +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.sync.Mutex +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SchoolAnnouncementRepository @Inject constructor( + private val schoolAnnouncementDb: SchoolAnnouncementDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val saveFetchResultMutex = Mutex() + + private val cacheKey = "school_announcement" + + fun getSchoolAnnouncements( + student: Student, + forceRefresh: Boolean, notify: Boolean = false + ) = networkBoundResource( + mutex = saveFetchResultMutex, + isResultEmpty = { it.isEmpty() }, + shouldFetch = { + val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student)) + it.isEmpty() || forceRefresh || isExpired + }, + query = { + schoolAnnouncementDb.loadAll(student.studentId) + }, + fetch = { + sdk.init(student) + .getDirectorInformation() + .mapToEntities(student) + }, + saveFetchResult = { old, new -> + val schoolAnnouncementsToSave = (new uniqueSubtract old).onEach { + if (notify) it.isNotified = false + } + + schoolAnnouncementDb.deleteAll(old uniqueSubtract new) + schoolAnnouncementDb.insertAll(schoolAnnouncementsToSave) + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student)) + } + ) + + fun getSchoolAnnouncementFromDatabase(student: Student): Flow> { + return schoolAnnouncementDb.loadAll(student.studentId) + } + + suspend fun updateSchoolAnnouncement(schoolAnnouncement: List) = + schoolAnnouncementDb.updateAll(schoolAnnouncement) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt new file mode 100644 index 000000000..7972ed084 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt @@ -0,0 +1,59 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.SchoolDao +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntity +import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import kotlinx.coroutines.sync.Mutex +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SchoolRepository @Inject constructor( + private val schoolDb: SchoolDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val saveFetchResultMutex = Mutex() + + private val cacheKey = "school_info" + + fun getSchoolInfo( + student: Student, + semester: Semester, + forceRefresh: Boolean, + ) = networkBoundResource( + mutex = saveFetchResultMutex, + isResultEmpty = { it == null }, + shouldFetch = { + val isExpired = refreshHelper.shouldBeRefreshed( + key = getRefreshKey(cacheKey, student) + ) + it == null || forceRefresh || isExpired + }, + query = { schoolDb.load(semester.studentId, semester.classId) }, + fetch = { + sdk.init(student) + .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + .getSchool() + .mapToEntity(semester) + }, + saveFetchResult = { old, new -> + if (old != null && new != old) { + with(schoolDb) { + deleteAll(listOf(old)) + insertAll(listOf(new)) + } + } else if (old == null) { + schoolDb.insertAll(listOf(new)) + } + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student)) + } + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt new file mode 100644 index 000000000..96f019223 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt @@ -0,0 +1,70 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.SemesterDao +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.* +import kotlinx.coroutines.withContext +import timber.log.Timber +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SemesterRepository @Inject constructor( + private val semesterDb: SemesterDao, + private val sdk: Sdk, + private val dispatchers: DispatchersProvider +) { + + suspend fun getSemesters( + student: Student, + forceRefresh: Boolean = false, + refreshOnNoCurrent: Boolean = false + ) = withContext(dispatchers.io) { + val semesters = semesterDb.loadAll(student.studentId, student.classId) + + if (isShouldFetch(student, semesters, forceRefresh, refreshOnNoCurrent)) { + refreshSemesters(student) + semesterDb.loadAll(student.studentId, student.classId) + } else semesters + } + + private fun isShouldFetch( + student: Student, + semesters: List, + forceRefresh: Boolean, + refreshOnNoCurrent: Boolean + ): Boolean { + val isNoSemesters = semesters.isEmpty() + + val isRefreshOnModeChangeRequired = when { + Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API -> { + semesters.firstOrNull { it.isCurrent }?.let { + 0 == it.diaryId && 0 == it.kindergartenDiaryId + } == true + } + else -> false + } + + val isRefreshOnNoCurrentAppropriate = + refreshOnNoCurrent && !semesters.any { semester -> semester.isCurrent } + + return forceRefresh || isNoSemesters || isRefreshOnModeChangeRequired || isRefreshOnNoCurrentAppropriate + } + + private suspend fun refreshSemesters(student: Student) { + val new = sdk.init(student).getSemesters().mapToEntities(student.studentId) + if (new.isEmpty()) return Timber.i("Empty semester list!") + + val old = semesterDb.loadAll(student.studentId, student.classId) + semesterDb.deleteAll(old.uniqueSubtract(new)) + semesterDb.insertSemesters(new.uniqueSubtract(old)) + } + + suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false) = + withContext(dispatchers.io) { + getSemesters(student, forceRefresh).getCurrentOrLast() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt new file mode 100644 index 000000000..efc82a772 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt @@ -0,0 +1,47 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.StudentInfoDao +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntity +import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.init +import kotlinx.coroutines.sync.Mutex +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class StudentInfoRepository @Inject constructor( + private val studentInfoDao: StudentInfoDao, + private val sdk: Sdk +) { + + private val saveFetchResultMutex = Mutex() + + fun getStudentInfo( + student: Student, + semester: Semester, + forceRefresh: Boolean, + ) = networkBoundResource( + mutex = saveFetchResultMutex, + isResultEmpty = { it == null }, + shouldFetch = { it == null || forceRefresh }, + query = { studentInfoDao.loadStudentInfo(student.studentId) }, + fetch = { + sdk.init(student) + .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + .getStudentInfo().mapToEntity(semester) + }, + saveFetchResult = { old, new -> + if (old != null && new != old) { + with(studentInfoDao) { + deleteAll(listOf(old)) + insertAll(listOf(new)) + } + } else if (old == null) { + studentInfoDao.insertAll(listOf(new)) + } + } + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt new file mode 100644 index 000000000..f006b7d28 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt @@ -0,0 +1,143 @@ +package io.github.wulkanowy.data.repositories + +import android.content.Context +import androidx.room.withTransaction +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.data.db.AppDatabase +import io.github.wulkanowy.data.db.dao.SemesterDao +import io.github.wulkanowy.data.db.dao.StudentDao +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.data.exceptions.NoCurrentStudentException +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.AppInfo +import io.github.wulkanowy.utils.DispatchersProvider +import io.github.wulkanowy.utils.security.decrypt +import io.github.wulkanowy.utils.security.encrypt +import kotlinx.coroutines.withContext +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class StudentRepository @Inject constructor( + @ApplicationContext private val context: Context, + private val dispatchers: DispatchersProvider, + private val studentDb: StudentDao, + private val semesterDb: SemesterDao, + private val sdk: Sdk, + private val appInfo: AppInfo, + private val appDatabase: AppDatabase +) { + + suspend fun isStudentSaved() = getSavedStudents(false).isNotEmpty() + + suspend fun isCurrentStudentSet() = studentDb.loadCurrent()?.isCurrent ?: false + + suspend fun getStudentsApi( + pin: String, + symbol: String, + token: String + ): List = + sdk.getStudentsFromMobileApi(token, pin, symbol, "") + .mapToEntities(colors = appInfo.defaultColorsForAvatar) + + suspend fun getStudentsScrapper( + email: String, + password: String, + scrapperBaseUrl: String, + symbol: String + ): List = + sdk.getStudentsFromScrapper(email, password, scrapperBaseUrl, symbol) + .mapToEntities(password, appInfo.defaultColorsForAvatar) + + suspend fun getStudentsHybrid( + email: String, + password: String, + scrapperBaseUrl: String, + symbol: String + ): List = + sdk.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol) + .mapToEntities(password, appInfo.defaultColorsForAvatar) + + suspend fun getSavedStudents(decryptPass: Boolean = true) = + studentDb.loadStudentsWithSemesters() + .map { + it.apply { + if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { + student.password = withContext(dispatchers.io) { + decrypt(student.password) + } + } + } + } + + suspend fun getSavedStudentById(id: Long, decryptPass: Boolean = true) = + studentDb.loadStudentWithSemestersById(id)?.apply { + if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { + student.password = withContext(dispatchers.io) { + decrypt(student.password) + } + } + } + + suspend fun getStudentById(id: Long, decryptPass: Boolean = true): Student { + val student = studentDb.loadById(id) ?: throw NoCurrentStudentException() + + if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { + student.password = withContext(dispatchers.io) { + decrypt(student.password) + } + } + return student + } + + suspend fun getCurrentStudent(decryptPass: Boolean = true): Student { + val student = studentDb.loadCurrent() ?: throw NoCurrentStudentException() + + if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { + student.password = withContext(dispatchers.io) { + decrypt(student.password) + } + } + return student + } + + suspend fun saveStudents(studentsWithSemesters: List) { + val semesters = studentsWithSemesters.flatMap { it.semesters } + val students = studentsWithSemesters.map { it.student } + .map { + it.apply { + if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.API) { + password = withContext(dispatchers.io) { + encrypt(password, context) + } + } + } + } + .mapIndexed { index, student -> + if (index == 0) { + student.copy(isCurrent = true).apply { avatarColor = student.avatarColor } + } else student + } + + appDatabase.withTransaction { + studentDb.resetCurrent() + semesterDb.insertSemesters(semesters) + studentDb.insertAll(students) + } + } + + suspend fun switchStudent(studentWithSemesters: StudentWithSemesters) { + studentDb.switchCurrent(studentWithSemesters.student.id) + } + + suspend fun logoutStudent(student: Student) = studentDb.delete(student) + + suspend fun updateStudentNickAndAvatar(studentNickAndAvatar: StudentNickAndAvatar) = + studentDb.update(studentNickAndAvatar) + + suspend fun isOneUniqueStudent() = getSavedStudents(false) + .distinctBy { it.student.studentName }.size == 1 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt new file mode 100644 index 000000000..3926122b3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt @@ -0,0 +1,52 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.SubjectDao +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.uniqueSubtract +import kotlinx.coroutines.sync.Mutex +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SubjectRepository @Inject constructor( + private val subjectDao: SubjectDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val saveFetchResultMutex = Mutex() + + private val cacheKey = "subjects" + + fun getSubjects( + student: Student, + semester: Semester, + forceRefresh: Boolean = false, + ) = networkBoundResource( + mutex = saveFetchResultMutex, + isResultEmpty = { it.isEmpty() }, + shouldFetch = { + val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester)) + it.isEmpty() || forceRefresh || isExpired + }, + query = { subjectDao.loadAll(semester.diaryId, semester.studentId) }, + fetch = { + sdk.init(student) + .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + .getSubjects().mapToEntities(semester) + }, + saveFetchResult = { old, new -> + subjectDao.deleteAll(old uniqueSubtract new) + subjectDao.insertAll(new uniqueSubtract old) + + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester)) + } + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt new file mode 100644 index 000000000..acd71e1f4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt @@ -0,0 +1,53 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.TeacherDao +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.uniqueSubtract +import kotlinx.coroutines.sync.Mutex +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class TeacherRepository @Inject constructor( + private val teacherDb: TeacherDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val saveFetchResultMutex = Mutex() + + private val cacheKey = "teachers" + + fun getTeachers( + student: Student, + semester: Semester, + forceRefresh: Boolean, + ) = networkBoundResource( + mutex = saveFetchResultMutex, + isResultEmpty = { it.isEmpty() }, + shouldFetch = { + val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester)) + it.isEmpty() || forceRefresh || isExpired + }, + query = { teacherDb.loadAll(semester.studentId, semester.classId) }, + fetch = { + sdk.init(student) + .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + .getTeachers(semester.semesterId) + .mapToEntities(semester) + }, + saveFetchResult = { old, new -> + teacherDb.deleteAll(old uniqueSubtract new) + teacherDb.insertAll(new uniqueSubtract old) + + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester)) + } + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt new file mode 100644 index 000000000..3145c2a23 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt @@ -0,0 +1,176 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao +import io.github.wulkanowy.data.db.dao.TimetableDao +import io.github.wulkanowy.data.db.dao.TimetableHeaderDao +import io.github.wulkanowy.data.db.entities.* +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.data.pojos.TimetableFull +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper +import io.github.wulkanowy.utils.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.sync.Mutex +import java.time.LocalDate +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class TimetableRepository @Inject constructor( + private val timetableDb: TimetableDao, + private val timetableAdditionalDb: TimetableAdditionalDao, + private val timetableHeaderDb: TimetableHeaderDao, + private val sdk: Sdk, + private val schedulerHelper: TimetableNotificationSchedulerHelper, + private val refreshHelper: AutoRefreshHelper, +) { + + private val saveFetchResultMutex = Mutex() + + private val cacheKey = "timetable" + + enum class TimetableType { + NORMAL, ADDITIONAL + } + + fun getTimetable( + student: Student, + semester: Semester, + start: LocalDate, + end: LocalDate, + forceRefresh: Boolean, + refreshAdditional: Boolean = false, + notify: Boolean = false, + timetableType: TimetableType = TimetableType.NORMAL + ) = networkBoundResource( + mutex = saveFetchResultMutex, + isResultEmpty = { + when (timetableType) { + TimetableType.NORMAL -> it.lessons.isEmpty() + TimetableType.ADDITIONAL -> it.additional.isEmpty() + } + }, + shouldFetch = { (timetable, additional, headers) -> + val refreshKey = getRefreshKey(cacheKey, semester, start, end) + val isExpired = refreshHelper.shouldBeRefreshed(refreshKey) + val isRefreshAdditional = additional.isEmpty() && refreshAdditional + + val isNoData = timetable.isEmpty() || isRefreshAdditional || headers.isEmpty() + + isNoData || forceRefresh || isExpired + }, + query = { getFullTimetableFromDatabase(student, semester, start, end) }, + fetch = { + val timetableFull = sdk.init(student) + .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + .getTimetableFull(start.monday, end.sunday) + + timetableFull.mapToEntities(semester) + }, + saveFetchResult = { timetableOld, timetableNew -> + refreshTimetable(student, timetableOld.lessons, timetableNew.lessons, notify) + refreshAdditional(timetableOld.additional, timetableNew.additional) + refreshDayHeaders(timetableOld.headers, timetableNew.headers) + + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end)) + }, + filterResult = { (timetable, additional, headers) -> + TimetableFull( + lessons = timetable.filter { it.date in start..end }, + additional = additional.filter { it.date in start..end }, + headers = headers.filter { it.date in start..end } + ) + } + ) + + private fun getFullTimetableFromDatabase( + student: Student, + semester: Semester, + start: LocalDate, + end: LocalDate, + ): Flow { + val timetableFlow = timetableDb.loadAll( + diaryId = semester.diaryId, + studentId = semester.studentId, + from = start.monday, + end = end.sunday + ) + val headersFlow = timetableHeaderDb.loadAll( + diaryId = semester.diaryId, + studentId = semester.studentId, + from = start.monday, + end = end.sunday + ) + val additionalFlow = timetableAdditionalDb.loadAll( + diaryId = semester.diaryId, + studentId = semester.studentId, + from = start.monday, + end = end.sunday + ) + return combine(timetableFlow, headersFlow, additionalFlow) { lessons, headers, additional -> + schedulerHelper.scheduleNotifications(lessons, student) + + TimetableFull( + lessons = lessons, + headers = headers, + additional = additional + ) + } + } + + fun getTimetableFromDatabase( + semester: Semester, + from: LocalDate, + end: LocalDate + ): Flow> { + return timetableDb.loadAll(semester.diaryId, semester.studentId, from, end) + } + + suspend fun updateTimetable(timetable: List) { + return timetableDb.updateAll(timetable) + } + + private suspend fun refreshTimetable( + student: Student, + lessonsOld: List, + lessonsNew: List, + notify: Boolean + ) { + val lessonsToRemove = lessonsOld uniqueSubtract lessonsNew + val lessonsToAdd = (lessonsNew uniqueSubtract lessonsOld).map { new -> + new.apply { if (notify) isNotified = false } + } + + timetableDb.deleteAll(lessonsToRemove) + timetableDb.insertAll(lessonsToAdd) + + schedulerHelper.cancelScheduled(lessonsToRemove, student) + schedulerHelper.scheduleNotifications(lessonsToAdd, student) + } + + private suspend fun refreshAdditional( + old: List, + new: List + ) { + val oldFiltered = old.filter { !it.isAddedByUser } + timetableAdditionalDb.deleteAll(oldFiltered uniqueSubtract new) + timetableAdditionalDb.insertAll(new uniqueSubtract old) + } + + private suspend fun refreshDayHeaders(old: List, new: List) { + timetableHeaderDb.deleteAll(old uniqueSubtract new) + timetableHeaderDb.insertAll(new uniqueSubtract old) + } + + suspend fun saveAdditionalList(additionalList: List) = + timetableAdditionalDb.insertAll(additionalList) + + suspend fun deleteAdditional(additional: TimetableAdditional, deleteSeries: Boolean) = + if (deleteSeries) { + timetableAdditionalDb.deleteAllByRepeatId(additional.repeatId!!) + } else { + timetableAdditionalDb.deleteAll(listOf(additional)) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/serializers/LocalDateSerializer.kt b/app/src/main/java/io/github/wulkanowy/data/serializers/LocalDateSerializer.kt new file mode 100644 index 000000000..ba97d37a2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/serializers/LocalDateSerializer.kt @@ -0,0 +1,32 @@ +package io.github.wulkanowy.data.serializers + +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.nullable +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import java.time.LocalDate + +@OptIn(ExperimentalSerializationApi::class) +object LocalDateSerializer : KSerializer { + + override val descriptor = PrimitiveSerialDescriptor("LocalDate", PrimitiveKind.LONG).nullable + + override fun serialize(encoder: Encoder, value: LocalDate?) { + if (value == null) { + encoder.encodeNull() + } else { + encoder.encodeNotNullMark() + encoder.encodeLong(value.toEpochDay()) + } + } + + override fun deserialize(decoder: Decoder): LocalDate? = + if (decoder.decodeNotNullMark()) { + LocalDate.ofEpochDay(decoder.decodeLong()) + } else { + decoder.decodeNull() + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/SyncContract.java b/app/src/main/java/io/github/wulkanowy/data/sync/SyncContract.java deleted file mode 100644 index d9850ab12..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/sync/SyncContract.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.data.sync; - -import java.io.IOException; -import java.text.ParseException; - -import io.github.wulkanowy.api.VulcanException; - -public interface SyncContract { - - void sync() throws VulcanException, IOException, ParseException; -} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/account/AccountSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/account/AccountSync.java deleted file mode 100644 index d56f99905..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/sync/account/AccountSync.java +++ /dev/null @@ -1,78 +0,0 @@ -package io.github.wulkanowy.data.sync.account; - -import android.content.Context; - -import java.io.IOException; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import io.github.wulkanowy.api.Vulcan; -import io.github.wulkanowy.api.VulcanException; -import io.github.wulkanowy.data.db.dao.entities.Account; -import io.github.wulkanowy.data.db.dao.entities.DaoSession; -import io.github.wulkanowy.data.db.shared.SharedPrefContract; -import io.github.wulkanowy.di.annotations.ApplicationContext; -import io.github.wulkanowy.utils.LogUtils; -import io.github.wulkanowy.utils.security.CryptoException; -import io.github.wulkanowy.utils.security.Scrambler; - -@Singleton -public class AccountSync implements AccountSyncContract { - - private final DaoSession daoSession; - - private final SharedPrefContract sharedPref; - - private final Vulcan vulcan; - - private final Context context; - - @Inject - AccountSync(DaoSession daoSession, SharedPrefContract sharedPref, - Vulcan vulcan, @ApplicationContext Context context) { - this.daoSession = daoSession; - this.sharedPref = sharedPref; - this.vulcan = vulcan; - this.context = context; - } - - @Override - public void registerUser(String email, String password, String symbol) - throws VulcanException, IOException, CryptoException { - - LogUtils.debug("Register new user email=" + email); - - vulcan.setCredentials(email, password, symbol, null); - - Account account = new Account() - .setName(vulcan.getBasicInformation().getPersonalData().getFirstAndLastName()) - .setEmail(email) - .setPassword(Scrambler.encrypt(email, password, context)) - .setSymbol(vulcan.getSymbol()) - .setSnpId(vulcan.getStudentAndParent().getId()); - - daoSession.getAccountDao().insert(account); - - sharedPref.setCurrentUserId(account.getId()); - } - - @Override - public void initLastUser() throws VulcanException, IOException, CryptoException { - - long userId = sharedPref.getCurrentUserId(); - - if (userId == 0) { - throw new IOException("Can't find saved user"); - } - - LogUtils.debug("Initialization current user id=" + userId); - - Account account = daoSession.getAccountDao().load(userId); - - vulcan.setCredentials(account.getEmail(), - Scrambler.decrypt(account.getEmail(), account.getPassword()), - account.getSymbol(), - account.getSnpId()); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/account/AccountSyncContract.java b/app/src/main/java/io/github/wulkanowy/data/sync/account/AccountSyncContract.java deleted file mode 100644 index 799d320e3..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/sync/account/AccountSyncContract.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.wulkanowy.data.sync.account; - -import java.io.IOException; - -import io.github.wulkanowy.api.VulcanException; -import io.github.wulkanowy.utils.security.CryptoException; - -public interface AccountSyncContract { - - void registerUser(String email, String password, String symbol) - throws VulcanException, IOException, - CryptoException; - - void initLastUser() throws VulcanException, IOException, - CryptoException; -} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSync.java deleted file mode 100644 index 325fff196..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSync.java +++ /dev/null @@ -1,156 +0,0 @@ -package io.github.wulkanowy.data.sync.attendance; - -import java.io.IOException; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import io.github.wulkanowy.api.Vulcan; -import io.github.wulkanowy.api.VulcanException; -import io.github.wulkanowy.api.generic.Lesson; -import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; -import io.github.wulkanowy.data.db.dao.entities.AttendanceLessonDao; -import io.github.wulkanowy.data.db.dao.entities.DaoSession; -import io.github.wulkanowy.data.db.dao.entities.Day; -import io.github.wulkanowy.data.db.dao.entities.DayDao; -import io.github.wulkanowy.data.db.dao.entities.Week; -import io.github.wulkanowy.data.db.dao.entities.WeekDao; -import io.github.wulkanowy.data.db.shared.SharedPrefContract; -import io.github.wulkanowy.utils.DataObjectConverter; -import io.github.wulkanowy.utils.LogUtils; -import io.github.wulkanowy.utils.TimeUtils; - -@Singleton -public class AttendanceSync implements AttendanceSyncContract { - - private final DaoSession daoSession; - - private final SharedPrefContract sharedPref; - - private final Vulcan vulcan; - - private long userId; - - @Inject - AttendanceSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) { - this.daoSession = daoSession; - this.sharedPref = sharedPref; - this.vulcan = vulcan; - } - - @Override - public void syncAttendance() throws IOException, ParseException, VulcanException { - syncAttendance(null); - } - - @Override - public void syncAttendance(String date) throws IOException, ParseException, VulcanException { - this.userId = sharedPref.getCurrentUserId(); - - io.github.wulkanowy.api.generic.Week weekApi = getWeekFromApi(getNormalizedDate(date)); - Week weekDb = getWeekFromDb(weekApi.getStartDayDate()); - - long weekId = updateWeekInDb(weekDb, weekApi); - - List lessonList = updateDays(weekApi.getDays(), weekId); - - daoSession.getAttendanceLessonDao().saveInTx(lessonList); - - LogUtils.debug("Synchronization attendance lessons (amount = " + lessonList.size() + ")"); - } - - private String getNormalizedDate(String date) throws ParseException { - return null != date ? String.valueOf(TimeUtils.getNetTicks(date)) : ""; - } - - private io.github.wulkanowy.api.generic.Week getWeekFromApi(String date) - throws IOException, ParseException, VulcanException { - return vulcan.getAttendanceTable().getWeekTable(date); - } - - private Week getWeekFromDb(String date) { - return daoSession.getWeekDao() - .queryBuilder() - .where(WeekDao.Properties.UserId.eq(userId), WeekDao.Properties.StartDayDate.eq(date)) - .unique(); - } - - private Long updateWeekInDb(Week dbWeekEntity, io.github.wulkanowy.api.generic.Week fromApi) { - if (dbWeekEntity != null) { - dbWeekEntity.setIsAttendanceSynced(true); - dbWeekEntity.update(); - - return dbWeekEntity.getId(); - } - - Week apiWeekEntity = DataObjectConverter.weekToWeekEntity(fromApi).setUserId(userId); - apiWeekEntity.setIsAttendanceSynced(true); - - return daoSession.getWeekDao().insert(apiWeekEntity); - } - - private List updateDays(List dayListFromApi, long weekId) { - List updatedLessonList = new ArrayList<>(); - - for (io.github.wulkanowy.api.generic.Day dayFromApi : dayListFromApi) { - - Day dbDayEntity = getDayFromDb(dayFromApi.getDate()); - - Day apiDayEntity = DataObjectConverter.dayToDayEntity(dayFromApi); - - long dayId = updateDay(dbDayEntity, apiDayEntity, weekId); - - updateLessons(dayFromApi.getLessons(), updatedLessonList, dayId); - } - - return updatedLessonList; - } - - private Day getDayFromDb(String date) { - return daoSession.getDayDao() - .queryBuilder() - .where(DayDao.Properties.UserId.eq(userId), DayDao.Properties.Date.eq(date)) - .unique(); - } - - private long updateDay(Day dbDayEntity, Day apiDayEntity, long weekId) { - if (null != dbDayEntity) { - return dbDayEntity.getId(); - } - - apiDayEntity.setUserId(userId); - apiDayEntity.setWeekId(weekId); - - return daoSession.getDayDao().insert(apiDayEntity); - } - - private void updateLessons(List lessons, List updatedLessons, long dayId) { - List lessonsFromApiEntities = DataObjectConverter - .lessonsToAttendanceLessonsEntities(lessons); - - for (AttendanceLesson apiLessonEntity : lessonsFromApiEntities) { - AttendanceLesson lessonFromDb = getLessonFromDb(apiLessonEntity, dayId); - - apiLessonEntity.setDayId(dayId); - - if (lessonFromDb != null) { - apiLessonEntity.setId(lessonFromDb.getId()); - } - - if (!"".equals(apiLessonEntity.getSubject())) { - updatedLessons.add(apiLessonEntity); - } - } - } - - private AttendanceLesson getLessonFromDb(AttendanceLesson apiEntity, long dayId) { - return daoSession.getAttendanceLessonDao().queryBuilder() - .where(AttendanceLessonDao.Properties.DayId.eq(dayId), - AttendanceLessonDao.Properties.Date.eq(apiEntity.getDate()), - AttendanceLessonDao.Properties.Number.eq(apiEntity.getNumber())) - .unique(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSyncContract.java b/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSyncContract.java deleted file mode 100644 index dfb4c1b4a..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSyncContract.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.github.wulkanowy.data.sync.attendance; - -import java.io.IOException; -import java.text.ParseException; - -import io.github.wulkanowy.api.VulcanException; - -public interface AttendanceSyncContract { - - void syncAttendance(String date) throws IOException, ParseException, VulcanException; - - void syncAttendance() throws IOException, ParseException, VulcanException; -} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/grades/GradeSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/grades/GradeSync.java deleted file mode 100644 index b132ac1b1..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/sync/grades/GradeSync.java +++ /dev/null @@ -1,91 +0,0 @@ -package io.github.wulkanowy.data.sync.grades; - -import java.io.IOException; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import io.github.wulkanowy.api.Vulcan; -import io.github.wulkanowy.api.VulcanException; -import io.github.wulkanowy.data.db.dao.entities.Account; -import io.github.wulkanowy.data.db.dao.entities.DaoSession; -import io.github.wulkanowy.data.db.dao.entities.Grade; -import io.github.wulkanowy.data.db.dao.entities.SubjectDao; -import io.github.wulkanowy.data.db.shared.SharedPrefContract; -import io.github.wulkanowy.data.sync.SyncContract; -import io.github.wulkanowy.utils.DataObjectConverter; -import io.github.wulkanowy.utils.EntitiesCompare; -import io.github.wulkanowy.utils.LogUtils; - -@Singleton -public class GradeSync implements SyncContract { - - private final DaoSession daoSession; - - private final Vulcan vulcan; - - private final SharedPrefContract sharedPref; - - private Long userId; - - @Inject - GradeSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) { - this.daoSession = daoSession; - this.sharedPref = sharedPref; - this.vulcan = vulcan; - } - - @Override - public void sync() throws IOException, VulcanException, ParseException { - - userId = sharedPref.getCurrentUserId(); - - Account account = daoSession.getAccountDao().load(userId); - resetAccountRelations(account); - - List lastList = getUpdatedList(getComparedList(account)); - - daoSession.getGradeDao().deleteInTx(account.getGradeList()); - daoSession.getGradeDao().insertInTx(lastList); - - LogUtils.debug("Synchronization grades (amount = " + lastList.size() + ")"); - } - - private void resetAccountRelations(Account account) { - account.resetSubjectList(); - account.resetGradeList(); - } - - private List getUpdatedList(List comparedList) { - List updatedList = new ArrayList<>(); - - for (Grade grade : comparedList) { - grade.setUserId(userId); - grade.setSubjectId(getSubjectId(grade.getSubject())); - updatedList.add(grade); - } - return updatedList; - } - - private List getComparedList(Account account) throws IOException, VulcanException, - ParseException { - List gradesFromNet = DataObjectConverter - .gradesToGradeEntities(vulcan.getGradesList().getAll()); - - List gradesFromDb = account.getGradeList(); - - return EntitiesCompare.compareGradeList(gradesFromNet, gradesFromDb); - } - - private Long getSubjectId(String subjectName) { - return daoSession.getSubjectDao().queryBuilder() - .where(SubjectDao.Properties.Name.eq(subjectName), - SubjectDao.Properties.UserId.eq(userId)) - .build() - .uniqueOrThrow() - .getId(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/subjects/SubjectSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/subjects/SubjectSync.java deleted file mode 100644 index 1c5bfc710..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/sync/subjects/SubjectSync.java +++ /dev/null @@ -1,71 +0,0 @@ -package io.github.wulkanowy.data.sync.subjects; - -import java.io.IOException; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import io.github.wulkanowy.api.Vulcan; -import io.github.wulkanowy.api.VulcanException; -import io.github.wulkanowy.data.db.dao.entities.Account; -import io.github.wulkanowy.data.db.dao.entities.DaoSession; -import io.github.wulkanowy.data.db.dao.entities.Subject; -import io.github.wulkanowy.data.db.shared.SharedPrefContract; -import io.github.wulkanowy.data.sync.SyncContract; -import io.github.wulkanowy.utils.DataObjectConverter; -import io.github.wulkanowy.utils.LogUtils; - -@Singleton -public class SubjectSync implements SyncContract { - - private final DaoSession daoSession; - - private final Vulcan vulcan; - - private final SharedPrefContract sharedPref; - - private Long userId; - - @Inject - SubjectSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) { - this.daoSession = daoSession; - this.sharedPref = sharedPref; - this.vulcan = vulcan; - } - - @Override - public void sync() throws VulcanException, IOException, ParseException { - - userId = sharedPref.getCurrentUserId(); - - List lastList = getUpdatedList(getSubjectsFromNet()); - - daoSession.getSubjectDao().deleteInTx(getSubjectsFromDb()); - daoSession.getSubjectDao().insertInTx(lastList); - - LogUtils.debug("Synchronization subjects (amount = " + lastList.size() + ")"); - } - - private List getSubjectsFromNet() throws VulcanException, IOException { - return DataObjectConverter.subjectsToSubjectEntities(vulcan.getSubjectsList().getAll()); - } - - private List getSubjectsFromDb() { - Account account = daoSession.getAccountDao().load(userId); - account.resetSubjectList(); - return account.getSubjectList(); - } - - private List getUpdatedList(List subjectsFromNet) { - List updatedList = new ArrayList<>(); - - for (Subject subject : subjectsFromNet) { - subject.setUserId(userId); - updatedList.add(subject); - } - return updatedList; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/timetable/TimetableSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/timetable/TimetableSync.java deleted file mode 100644 index da6b143f2..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/sync/timetable/TimetableSync.java +++ /dev/null @@ -1,162 +0,0 @@ -package io.github.wulkanowy.data.sync.timetable; - -import java.io.IOException; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import io.github.wulkanowy.api.Vulcan; -import io.github.wulkanowy.api.VulcanException; -import io.github.wulkanowy.api.generic.Lesson; -import io.github.wulkanowy.data.db.dao.entities.DaoSession; -import io.github.wulkanowy.data.db.dao.entities.Day; -import io.github.wulkanowy.data.db.dao.entities.DayDao; -import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; -import io.github.wulkanowy.data.db.dao.entities.TimetableLessonDao; -import io.github.wulkanowy.data.db.dao.entities.Week; -import io.github.wulkanowy.data.db.dao.entities.WeekDao; -import io.github.wulkanowy.data.db.shared.SharedPrefContract; -import io.github.wulkanowy.utils.DataObjectConverter; -import io.github.wulkanowy.utils.LogUtils; -import io.github.wulkanowy.utils.TimeUtils; - -@Singleton -public class TimetableSync implements TimetableSyncContract { - - private final DaoSession daoSession; - - private final SharedPrefContract sharedPref; - - private final Vulcan vulcan; - - private long userId; - - @Inject - TimetableSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) { - this.daoSession = daoSession; - this.sharedPref = sharedPref; - this.vulcan = vulcan; - } - - @Override - public void syncTimetable() throws IOException, ParseException, VulcanException { - syncTimetable(null); - } - - @Override - public void syncTimetable(String date) throws IOException, ParseException, VulcanException { - this.userId = sharedPref.getCurrentUserId(); - - io.github.wulkanowy.api.generic.Week weekApi = getWeekFromApi(getNormalizedDate(date)); - Week weekDb = getWeekFromDb(weekApi.getStartDayDate()); - - long weekId = updateWeekInDb(weekDb, weekApi); - - List lessonList = updateDays(weekApi.getDays(), weekId); - - daoSession.getTimetableLessonDao().saveInTx(lessonList); - - LogUtils.debug("Synchronization timetable lessons (amount = " + lessonList.size() + ")"); - } - - private String getNormalizedDate(String date) throws ParseException { - return null != date ? String.valueOf(TimeUtils.getNetTicks(date)) : ""; - } - - private io.github.wulkanowy.api.generic.Week getWeekFromApi(String date) - throws IOException, VulcanException, ParseException { - return vulcan.getTimetable().getWeekTable(date); - } - - private Week getWeekFromDb(String date) { - return daoSession.getWeekDao() - .queryBuilder() - .where(WeekDao.Properties.UserId.eq(userId), WeekDao.Properties.StartDayDate.eq(date)) - .unique(); - } - - private Long updateWeekInDb(Week dbEntity, io.github.wulkanowy.api.generic.Week fromApi) { - if (dbEntity != null) { - dbEntity.setIsTimetableSynced(true); - dbEntity.update(); - - return dbEntity.getId(); - } - - Week apiEntity = DataObjectConverter.weekToWeekEntity(fromApi).setUserId(userId); - apiEntity.setIsTimetableSynced(true); - - return daoSession.getWeekDao().insert(apiEntity); - } - - private List updateDays(List dayListFromApi, long weekId) { - List updatedLessonList = new ArrayList<>(); - - for (io.github.wulkanowy.api.generic.Day dayFromApi : dayListFromApi) { - - Day dbDayEntity = getDayFromDb(dayFromApi.getDate()); - - Day apiDayEntity = DataObjectConverter.dayToDayEntity(dayFromApi); - - long dayId = updateDay(dbDayEntity, apiDayEntity, weekId); - - updateLessons(dayFromApi.getLessons(), updatedLessonList, dayId); - } - - return updatedLessonList; - } - - private Day getDayFromDb(String date) { - return daoSession.getDayDao() - .queryBuilder() - .where(DayDao.Properties.UserId.eq(userId), DayDao.Properties.Date.eq(date)) - .unique(); - } - - private long updateDay(Day dayFromDb, Day apiDayEntity, long weekId) { - apiDayEntity.setUserId(userId); - apiDayEntity.setWeekId(weekId); - - if (null != dayFromDb) { - apiDayEntity.setId(dayFromDb.getId()); - - daoSession.getDayDao().save(apiDayEntity); - dayFromDb.refresh(); - - return dayFromDb.getId(); - } - - return daoSession.getDayDao().insert(apiDayEntity); - } - - private void updateLessons(List lessons, List updatedLessons, long dayId) { - List lessonsFromApiEntities = DataObjectConverter - .lessonsToTimetableLessonsEntities(lessons); - - for (TimetableLesson apiLessonEntity : lessonsFromApiEntities) { - TimetableLesson lessonFromDb = getLessonFromDb(apiLessonEntity, dayId); - - apiLessonEntity.setDayId(dayId); - - if (lessonFromDb != null) { - apiLessonEntity.setId(lessonFromDb.getId()); - } - - if (!"".equals(apiLessonEntity.getSubject())) { - updatedLessons.add(apiLessonEntity); - } - } - } - - private TimetableLesson getLessonFromDb(TimetableLesson apiEntity, long dayId) { - return daoSession.getTimetableLessonDao().queryBuilder() - .where(TimetableLessonDao.Properties.DayId.eq(dayId), - TimetableLessonDao.Properties.Date.eq(apiEntity.getDate()), - TimetableLessonDao.Properties.StartTime.eq(apiEntity.getStartTime()), - TimetableLessonDao.Properties.EndTime.eq(apiEntity.getEndTime())) - .unique(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/timetable/TimetableSyncContract.java b/app/src/main/java/io/github/wulkanowy/data/sync/timetable/TimetableSyncContract.java deleted file mode 100644 index 9bba209a7..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/sync/timetable/TimetableSyncContract.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.github.wulkanowy.data.sync.timetable; - -import java.io.IOException; -import java.text.ParseException; - -import io.github.wulkanowy.api.VulcanException; - -public interface TimetableSyncContract { - - void syncTimetable(String date) throws VulcanException, IOException, ParseException; - - void syncTimetable() throws VulcanException, IOException, ParseException; -} diff --git a/app/src/main/java/io/github/wulkanowy/di/AppModule.kt b/app/src/main/java/io/github/wulkanowy/di/AppModule.kt new file mode 100644 index 000000000..4efb4d84a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/AppModule.kt @@ -0,0 +1,29 @@ +package io.github.wulkanowy.di + +import android.appwidget.AppWidgetManager +import android.content.Context +import com.yariksoffice.lingver.Lingver +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import io.github.wulkanowy.utils.DispatchersProvider +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal class AppModule { + + @Singleton + @Provides + fun provideDispatchersProvider() = DispatchersProvider() + + @Singleton + @Provides + fun provideAppWidgetManager(@ApplicationContext context: Context): AppWidgetManager = AppWidgetManager.getInstance(context) + + @Singleton + @Provides + fun provideLingver() = Lingver.getInstance() +} diff --git a/app/src/main/java/io/github/wulkanowy/di/annotations/ActivityContext.java b/app/src/main/java/io/github/wulkanowy/di/annotations/ActivityContext.java deleted file mode 100644 index 2a74c32d3..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/annotations/ActivityContext.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.di.annotations; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.inject.Qualifier; - -@Qualifier -@Retention(RetentionPolicy.RUNTIME) -public @interface ActivityContext { -} diff --git a/app/src/main/java/io/github/wulkanowy/di/annotations/ApplicationContext.java b/app/src/main/java/io/github/wulkanowy/di/annotations/ApplicationContext.java deleted file mode 100644 index 04139d998..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/annotations/ApplicationContext.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.di.annotations; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.inject.Qualifier; - -@Qualifier -@Retention(RetentionPolicy.RUNTIME) -public @interface ApplicationContext { -} diff --git a/app/src/main/java/io/github/wulkanowy/di/annotations/DatabaseInfo.java b/app/src/main/java/io/github/wulkanowy/di/annotations/DatabaseInfo.java deleted file mode 100644 index fabcefbaa..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/annotations/DatabaseInfo.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.di.annotations; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.inject.Qualifier; - -@Qualifier -@Retention(RetentionPolicy.RUNTIME) -public @interface DatabaseInfo { -} diff --git a/app/src/main/java/io/github/wulkanowy/di/annotations/PerActivity.java b/app/src/main/java/io/github/wulkanowy/di/annotations/PerActivity.java deleted file mode 100644 index f103994a5..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/annotations/PerActivity.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.di.annotations; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.inject.Scope; - -@Scope -@Retention(RetentionPolicy.RUNTIME) -public @interface PerActivity { -} diff --git a/app/src/main/java/io/github/wulkanowy/di/annotations/PerFragment.java b/app/src/main/java/io/github/wulkanowy/di/annotations/PerFragment.java deleted file mode 100644 index 98f364f76..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/annotations/PerFragment.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.di.annotations; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.inject.Scope; - -@Scope -@Retention(RetentionPolicy.RUNTIME) -public @interface PerFragment { -} diff --git a/app/src/main/java/io/github/wulkanowy/di/annotations/SharedPreferencesInfo.java b/app/src/main/java/io/github/wulkanowy/di/annotations/SharedPreferencesInfo.java deleted file mode 100644 index 919c77a0b..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/annotations/SharedPreferencesInfo.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.di.annotations; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.inject.Qualifier; - -@Qualifier -@Retention(RetentionPolicy.RUNTIME) -public @interface SharedPreferencesInfo { -} diff --git a/app/src/main/java/io/github/wulkanowy/di/annotations/SyncGrades.java b/app/src/main/java/io/github/wulkanowy/di/annotations/SyncGrades.java deleted file mode 100644 index 90e6c02f6..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/annotations/SyncGrades.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.di.annotations; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.inject.Qualifier; - -@Qualifier -@Retention(RetentionPolicy.RUNTIME) -public @interface SyncGrades { -} diff --git a/app/src/main/java/io/github/wulkanowy/di/annotations/SyncSubjects.java b/app/src/main/java/io/github/wulkanowy/di/annotations/SyncSubjects.java deleted file mode 100644 index 81d351bea..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/annotations/SyncSubjects.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.di.annotations; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.inject.Qualifier; - -@Qualifier -@Retention(RetentionPolicy.RUNTIME) -public @interface SyncSubjects { -} diff --git a/app/src/main/java/io/github/wulkanowy/di/component/ActivityComponent.java b/app/src/main/java/io/github/wulkanowy/di/component/ActivityComponent.java deleted file mode 100644 index 3365a317e..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/component/ActivityComponent.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.github.wulkanowy.di.component; - -import dagger.Component; -import io.github.wulkanowy.di.annotations.PerActivity; -import io.github.wulkanowy.di.modules.ActivityModule; -import io.github.wulkanowy.ui.login.LoginActivity; -import io.github.wulkanowy.ui.main.MainActivity; -import io.github.wulkanowy.ui.splash.SplashActivity; - -@PerActivity -@Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) -public interface ActivityComponent { - - void inject(SplashActivity splashActivity); - - void inject(LoginActivity loginActivity); - - void inject(MainActivity mainActivity); -} diff --git a/app/src/main/java/io/github/wulkanowy/di/component/ApplicationComponent.java b/app/src/main/java/io/github/wulkanowy/di/component/ApplicationComponent.java deleted file mode 100644 index 3439a9496..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/component/ApplicationComponent.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.di.component; - -import android.content.Context; - -import javax.inject.Singleton; - -import dagger.Component; -import io.github.wulkanowy.WulkanowyApp; -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.di.annotations.ApplicationContext; -import io.github.wulkanowy.di.modules.ApplicationModule; -import io.github.wulkanowy.services.SyncJob; - -@Singleton -@Component(modules = ApplicationModule.class) -public interface ApplicationComponent { - - @ApplicationContext - Context getContext(); - - RepositoryContract getRepository(); - - void inject(WulkanowyApp wulkanowyApp); - - void inject(SyncJob syncJob); -} diff --git a/app/src/main/java/io/github/wulkanowy/di/component/FragmentComponent.java b/app/src/main/java/io/github/wulkanowy/di/component/FragmentComponent.java deleted file mode 100644 index b26abaaf3..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/component/FragmentComponent.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.di.component; - -import dagger.Component; -import io.github.wulkanowy.di.annotations.PerFragment; -import io.github.wulkanowy.di.modules.FragmentModule; -import io.github.wulkanowy.ui.main.attendance.AttendanceFragment; -import io.github.wulkanowy.ui.main.attendance.AttendanceTabFragment; -import io.github.wulkanowy.ui.main.dashboard.DashboardFragment; -import io.github.wulkanowy.ui.main.grades.GradesFragment; -import io.github.wulkanowy.ui.main.timetable.TimetableFragment; -import io.github.wulkanowy.ui.main.timetable.TimetableTabFragment; - -@PerFragment -@Component(dependencies = ApplicationComponent.class, modules = FragmentModule.class) -public interface FragmentComponent { - - void inject(GradesFragment gradesFragment); - - void inject(AttendanceFragment attendanceFragment); - - void inject(AttendanceTabFragment attendanceTabFragment); - - void inject(DashboardFragment dashboardFragment); - - void inject(TimetableFragment timetableFragment); - - void inject(TimetableTabFragment timetableTabFragment); -} diff --git a/app/src/main/java/io/github/wulkanowy/di/modules/ActivityModule.java b/app/src/main/java/io/github/wulkanowy/di/modules/ActivityModule.java deleted file mode 100644 index 53dc10864..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/modules/ActivityModule.java +++ /dev/null @@ -1,64 +0,0 @@ -package io.github.wulkanowy.di.modules; - - -import android.content.Context; -import android.support.v7.app.AppCompatActivity; - -import dagger.Module; -import dagger.Provides; -import io.github.wulkanowy.di.annotations.ActivityContext; -import io.github.wulkanowy.di.annotations.PerActivity; -import io.github.wulkanowy.ui.login.LoginContract; -import io.github.wulkanowy.ui.login.LoginPresenter; -import io.github.wulkanowy.ui.main.MainContract; -import io.github.wulkanowy.ui.main.MainPagerAdapter; -import io.github.wulkanowy.ui.main.MainPresenter; -import io.github.wulkanowy.ui.splash.SplashContract; -import io.github.wulkanowy.ui.splash.SplashPresenter; - -@Module -public class ActivityModule { - - private AppCompatActivity activity; - - public ActivityModule(AppCompatActivity activity) { - this.activity = activity; - } - - @ActivityContext - @Provides - Context provideContext() { - return activity; - } - - @Provides - AppCompatActivity provideActivity() { - return activity; - } - - @PerActivity - @Provides - SplashContract.Presenter provideSplashPresenter - (SplashPresenter splashPresenter) { - return splashPresenter; - } - - @PerActivity - @Provides - LoginContract.Presenter provideLoginPresenter - (LoginPresenter loginPresenter) { - return loginPresenter; - } - - @PerActivity - @Provides - MainContract.Presenter provideMainPresenter - (MainPresenter mainPresenter) { - return mainPresenter; - } - - @Provides - MainPagerAdapter provideMainPagerAdapter() { - return new MainPagerAdapter(activity.getSupportFragmentManager()); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/di/modules/ApplicationModule.java b/app/src/main/java/io/github/wulkanowy/di/modules/ApplicationModule.java deleted file mode 100644 index e7d2bcc67..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/modules/ApplicationModule.java +++ /dev/null @@ -1,137 +0,0 @@ -package io.github.wulkanowy.di.modules; - -import android.app.Application; -import android.content.Context; - -import com.firebase.jobdispatcher.FirebaseJobDispatcher; -import com.firebase.jobdispatcher.GooglePlayDriver; - -import javax.inject.Singleton; - -import dagger.Module; -import dagger.Provides; -import io.github.wulkanowy.api.Vulcan; -import io.github.wulkanowy.data.Repository; -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.data.db.dao.DbHelper; -import io.github.wulkanowy.data.db.dao.entities.DaoMaster; -import io.github.wulkanowy.data.db.dao.entities.DaoSession; -import io.github.wulkanowy.data.db.resources.AppResources; -import io.github.wulkanowy.data.db.resources.ResourcesContract; -import io.github.wulkanowy.data.db.shared.SharedPref; -import io.github.wulkanowy.data.db.shared.SharedPrefContract; -import io.github.wulkanowy.data.sync.SyncContract; -import io.github.wulkanowy.data.sync.account.AccountSync; -import io.github.wulkanowy.data.sync.account.AccountSyncContract; -import io.github.wulkanowy.data.sync.attendance.AttendanceSync; -import io.github.wulkanowy.data.sync.attendance.AttendanceSyncContract; -import io.github.wulkanowy.data.sync.grades.GradeSync; -import io.github.wulkanowy.data.sync.subjects.SubjectSync; -import io.github.wulkanowy.data.sync.timetable.TimetableSync; -import io.github.wulkanowy.data.sync.timetable.TimetableSyncContract; -import io.github.wulkanowy.di.annotations.ApplicationContext; -import io.github.wulkanowy.di.annotations.DatabaseInfo; -import io.github.wulkanowy.di.annotations.SharedPreferencesInfo; -import io.github.wulkanowy.di.annotations.SyncGrades; -import io.github.wulkanowy.di.annotations.SyncSubjects; -import io.github.wulkanowy.utils.AppConstant; - -@Module -public class ApplicationModule { - - private final Application application; - - public ApplicationModule(Application application) { - this.application = application; - } - - @Provides - Application provideApplication() { - return application; - } - - @ApplicationContext - @Provides - Context provideAppContext() { - return application; - } - - @DatabaseInfo - @Provides - String provideDatabaseName() { - return AppConstant.DATABASE_NAME; - } - - @SharedPreferencesInfo - @Provides - String provideSharedPreferencesName() { - return AppConstant.SHARED_PREFERENCES_NAME; - } - - @Singleton - @Provides - DaoSession provideDaoSession(DbHelper dbHelper) { - return new DaoMaster(dbHelper.getWritableDb()).newSession(); - } - - @Singleton - @Provides - Vulcan provideVulcan() { - return new Vulcan(); - } - - @Singleton - @Provides - RepositoryContract provideRepository(Repository repository) { - return repository; - } - - @Singleton - @Provides - SharedPrefContract provideSharedPref(SharedPref sharedPref) { - return sharedPref; - } - - @Singleton - @Provides - ResourcesContract provideAppResources(AppResources appResources) { - return appResources; - } - - @Singleton - @Provides - AccountSyncContract provideLoginSync(AccountSync accountSync) { - return accountSync; - } - - @SyncGrades - @Singleton - @Provides - SyncContract provideGradesSync(GradeSync gradeSync) { - return gradeSync; - } - - @SyncSubjects - @Singleton - @Provides - SyncContract provideSubjectSync(SubjectSync subjectSync) { - return subjectSync; - } - - @Singleton - @Provides - TimetableSyncContract provideTimetableSync(TimetableSync timetableSync) { - return timetableSync; - } - - @Singleton - @Provides - AttendanceSyncContract provideAttendanceSync(AttendanceSync attendanceSync) { - return attendanceSync; - } - - @Provides - FirebaseJobDispatcher provideDispatcher() { - return new FirebaseJobDispatcher(new GooglePlayDriver(application)); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/di/modules/FragmentModule.java b/app/src/main/java/io/github/wulkanowy/di/modules/FragmentModule.java deleted file mode 100644 index 3479a8c4e..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/modules/FragmentModule.java +++ /dev/null @@ -1,96 +0,0 @@ -package io.github.wulkanowy.di.modules; - -import android.support.v4.app.Fragment; - -import dagger.Module; -import dagger.Provides; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import io.github.wulkanowy.di.annotations.PerFragment; -import io.github.wulkanowy.ui.main.attendance.AttendanceContract; -import io.github.wulkanowy.ui.main.attendance.AttendanceHeaderItem; -import io.github.wulkanowy.ui.main.attendance.AttendancePagerAdapter; -import io.github.wulkanowy.ui.main.attendance.AttendancePresenter; -import io.github.wulkanowy.ui.main.attendance.AttendanceTabContract; -import io.github.wulkanowy.ui.main.attendance.AttendanceTabPresenter; -import io.github.wulkanowy.ui.main.dashboard.DashboardContract; -import io.github.wulkanowy.ui.main.dashboard.DashboardPresenter; -import io.github.wulkanowy.ui.main.grades.GradeHeaderItem; -import io.github.wulkanowy.ui.main.grades.GradesContract; -import io.github.wulkanowy.ui.main.grades.GradesPresenter; -import io.github.wulkanowy.ui.main.timetable.TimetableContract; -import io.github.wulkanowy.ui.main.timetable.TimetableHeaderItem; -import io.github.wulkanowy.ui.main.timetable.TimetablePagerAdapter; -import io.github.wulkanowy.ui.main.timetable.TimetablePresenter; -import io.github.wulkanowy.ui.main.timetable.TimetableTabContract; -import io.github.wulkanowy.ui.main.timetable.TimetableTabPresenter; - -@Module -public class FragmentModule { - - private final Fragment fragment; - - public FragmentModule(Fragment fragment) { - this.fragment = fragment; - } - - @PerFragment - @Provides - GradesContract.Presenter provideGradesPresenter(GradesPresenter gradesPresenter) { - return gradesPresenter; - } - - @PerFragment - @Provides - AttendanceContract.Presenter provideAttendancePresenter(AttendancePresenter attendancePresenter) { - return attendancePresenter; - } - - @PerFragment - @Provides - DashboardContract.Presenter provideDashboardPresenter(DashboardPresenter dashboardPresenter) { - return dashboardPresenter; - } - - @PerFragment - @Provides - AttendanceTabContract.Presenter provideAttendanceTabPresenter(AttendanceTabPresenter timetableTabPresenter) { - return timetableTabPresenter; - } - - @Provides - AttendancePagerAdapter provideAttendancePagerAdapter() { - return new AttendancePagerAdapter(fragment.getChildFragmentManager()); - } - - @Provides - FlexibleAdapter provideAttendanceTabAdapter() { - return new FlexibleAdapter<>(null); - } - - @PerFragment - @Provides - TimetableContract.Presenter provideTimetablePresenter(TimetablePresenter timetablePresenter) { - return timetablePresenter; - } - - @PerFragment - @Provides - TimetableTabContract.Presenter provideTimetableTabPresenter(TimetableTabPresenter timetableTabPresenter) { - return timetableTabPresenter; - } - - @Provides - TimetablePagerAdapter provideTimetablePagerAdapter() { - return new TimetablePagerAdapter(fragment.getChildFragmentManager()); - } - - @Provides - FlexibleAdapter provideTimetableTabAdapter() { - return new FlexibleAdapter<>(null); - } - - @Provides - FlexibleAdapter provideGradesAdapter() { - return new FlexibleAdapter<>(null); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/NotificationService.java b/app/src/main/java/io/github/wulkanowy/services/NotificationService.java deleted file mode 100644 index ab721f46c..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/NotificationService.java +++ /dev/null @@ -1,55 +0,0 @@ -package io.github.wulkanowy.services; - - -import android.annotation.TargetApi; -import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.content.Context; -import android.os.Build; -import android.support.v4.app.NotificationCompat; - -import java.util.Random; - -class NotificationService { - - private static final String CHANNEL_ID = "Wulkanowy_New_Grade_Channel"; - - private static final String CHANNEL_NAME = "New Grade Channel"; - - private NotificationManager manager; - - private Context context; - - NotificationService(Context context) { - this.context = context; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - createChannel(); - } - } - - void notify(Notification notification) { - getManager().notify(new Random().nextInt(1000), notification); - } - - NotificationCompat.Builder notificationBuilder() { - return new NotificationCompat.Builder(context, CHANNEL_ID); - } - - @TargetApi(26) - private void createChannel() { - NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, - NotificationManager.IMPORTANCE_HIGH); - notificationChannel.enableLights(true); - notificationChannel.enableVibration(true); - notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); - getManager().createNotificationChannel(notificationChannel); - } - - private NotificationManager getManager() { - if (manager == null) { - manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - } - return manager; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt b/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt new file mode 100644 index 000000000..1729f1006 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt @@ -0,0 +1,180 @@ +package io.github.wulkanowy.services + +import android.app.AlarmManager +import android.content.Context +import androidx.core.app.NotificationManagerCompat +import androidx.core.content.getSystemService +import androidx.work.WorkManager +import dagger.Binds +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import dagger.multibindings.IntoSet +import io.github.wulkanowy.services.sync.channels.Channel +import io.github.wulkanowy.services.sync.channels.DebugChannel +import io.github.wulkanowy.services.sync.channels.LuckyNumberChannel +import io.github.wulkanowy.services.sync.channels.NewAttendanceChannel +import io.github.wulkanowy.services.sync.channels.NewConferencesChannel +import io.github.wulkanowy.services.sync.channels.NewExamChannel +import io.github.wulkanowy.services.sync.channels.NewGradesChannel +import io.github.wulkanowy.services.sync.channels.NewHomeworkChannel +import io.github.wulkanowy.services.sync.channels.NewMessagesChannel +import io.github.wulkanowy.services.sync.channels.NewNotesChannel +import io.github.wulkanowy.services.sync.channels.NewSchoolAnnouncementsChannel +import io.github.wulkanowy.services.sync.channels.PushChannel +import io.github.wulkanowy.services.sync.channels.TimetableChangeChannel +import io.github.wulkanowy.services.sync.channels.UpcomingLessonsChannel +import io.github.wulkanowy.services.sync.works.AttendanceSummaryWork +import io.github.wulkanowy.services.sync.works.AttendanceWork +import io.github.wulkanowy.services.sync.works.CompletedLessonWork +import io.github.wulkanowy.services.sync.works.ConferenceWork +import io.github.wulkanowy.services.sync.works.ExamWork +import io.github.wulkanowy.services.sync.works.GradeStatisticsWork +import io.github.wulkanowy.services.sync.works.GradeWork +import io.github.wulkanowy.services.sync.works.HomeworkWork +import io.github.wulkanowy.services.sync.works.LuckyNumberWork +import io.github.wulkanowy.services.sync.works.MessageWork +import io.github.wulkanowy.services.sync.works.NoteWork +import io.github.wulkanowy.services.sync.works.RecipientWork +import io.github.wulkanowy.services.sync.works.SchoolAnnouncementWork +import io.github.wulkanowy.services.sync.works.TeacherWork +import io.github.wulkanowy.services.sync.works.TimetableWork +import io.github.wulkanowy.services.sync.works.Work +import javax.inject.Singleton + +@Suppress("unused") +@Module +@InstallIn(SingletonComponent::class) +abstract class ServicesModule { + + companion object { + + @Provides + fun provideWorkManager(@ApplicationContext context: Context) = + WorkManager.getInstance(context) + + @Singleton + @Provides + fun provideNotificationManager(@ApplicationContext context: Context) = + NotificationManagerCompat.from(context) + + @Singleton + @Provides + fun provideAlarmManager(@ApplicationContext context: Context): AlarmManager = + context.getSystemService()!! + } + + @Binds + @IntoSet + abstract fun provideGradeWork(work: GradeWork): Work + + @Binds + @IntoSet + abstract fun provideNoteWork(work: NoteWork): Work + + @Binds + @IntoSet + abstract fun provideAttendanceWork(work: AttendanceWork): Work + + @Binds + @IntoSet + abstract fun provideConferenceWork(work: ConferenceWork): Work + + @Binds + @IntoSet + abstract fun provideExamWork(work: ExamWork): Work + + @Binds + @IntoSet + abstract fun provideAttendanceSummaryWork(work: AttendanceSummaryWork): Work + + @Binds + @IntoSet + abstract fun provideTimetableWork(work: TimetableWork): Work + + @Binds + @IntoSet + abstract fun provideTeacherWork(work: TeacherWork): Work + + @Binds + @IntoSet + abstract fun provideLuckyNumberWork(work: LuckyNumberWork): Work + + @Binds + @IntoSet + abstract fun provideCompletedLessonWork(work: CompletedLessonWork): Work + + @Binds + @IntoSet + abstract fun provideHomeworkWork(work: HomeworkWork): Work + + @Binds + @IntoSet + abstract fun provideMessageWork(work: MessageWork): Work + + @Binds + @IntoSet + abstract fun provideRecipientWork(work: RecipientWork): Work + + @Binds + @IntoSet + abstract fun provideGradeStatistics(work: GradeStatisticsWork): Work + + @Binds + @IntoSet + abstract fun provideSchoolAnnouncementWork(work: SchoolAnnouncementWork): Work + + @Binds + @IntoSet + abstract fun provideDebugChannel(channel: DebugChannel): Channel + + @Binds + @IntoSet + abstract fun provideLuckyNumberChannel(channel: LuckyNumberChannel): Channel + + @Binds + @IntoSet + abstract fun provideNewConferenceChannel(channel: NewConferencesChannel): Channel + + @Binds + @IntoSet + abstract fun provideNewExamChannel(channel: NewExamChannel): Channel + + @Binds + @IntoSet + abstract fun provideNewHomeworkChannel(channel: NewHomeworkChannel): Channel + + @Binds + @IntoSet + abstract fun provideNewGradesChannel(channel: NewGradesChannel): Channel + + @Binds + @IntoSet + abstract fun provideNewMessageChannel(channel: NewMessagesChannel): Channel + + @Binds + @IntoSet + abstract fun provideNewNotesChannel(channel: NewNotesChannel): Channel + + @Binds + @IntoSet + abstract fun provideNewSchoolAnnouncementChannel(channel: NewSchoolAnnouncementsChannel): Channel + + @Binds + @IntoSet + abstract fun providePushChannel(channel: PushChannel): Channel + + @Binds + @IntoSet + abstract fun provideUpcomingLessonsChannel(channel: UpcomingLessonsChannel): Channel + + @Binds + @IntoSet + abstract fun provideChangeTimetableChannel(channel: TimetableChangeChannel): Channel + + @Binds + @IntoSet + abstract fun provideNewAttendanceChannel(channel: NewAttendanceChannel): Channel +} diff --git a/app/src/main/java/io/github/wulkanowy/services/SyncJob.java b/app/src/main/java/io/github/wulkanowy/services/SyncJob.java deleted file mode 100644 index e7b0908f0..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/SyncJob.java +++ /dev/null @@ -1,114 +0,0 @@ -package io.github.wulkanowy.services; - -import android.app.PendingIntent; -import android.content.Context; -import android.support.v4.app.NotificationCompat; - -import com.firebase.jobdispatcher.Constraint; -import com.firebase.jobdispatcher.FirebaseJobDispatcher; -import com.firebase.jobdispatcher.GooglePlayDriver; -import com.firebase.jobdispatcher.JobParameters; -import com.firebase.jobdispatcher.JobService; -import com.firebase.jobdispatcher.Lifetime; -import com.firebase.jobdispatcher.RetryStrategy; -import com.firebase.jobdispatcher.SimpleJobService; -import com.firebase.jobdispatcher.Trigger; - -import java.util.List; - -import javax.inject.Inject; - -import io.github.wulkanowy.R; -import io.github.wulkanowy.WulkanowyApp; -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.data.db.dao.entities.Grade; -import io.github.wulkanowy.ui.main.MainActivity; -import io.github.wulkanowy.utils.LogUtils; - -public class SyncJob extends SimpleJobService { - - private static final int DEFAULT_INTERVAL_START = 60 * 50; - - private static final int DEFAULT_INTERVAL_END = DEFAULT_INTERVAL_START + (60 * 40); - - public static final String EXTRA_INTENT_KEY = "cardId"; - - private List gradeList; - - @Inject - RepositoryContract repository; - - public static void start(Context context) { - FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context)); - - dispatcher.mustSchedule(dispatcher.newJobBuilder() - .setLifetime(Lifetime.FOREVER) - .setService(SyncJob.class) - .setTag("SyncJob") - .setRecurring(true) - .setTrigger(Trigger.executionWindow(DEFAULT_INTERVAL_START, DEFAULT_INTERVAL_END)) - .setConstraints(Constraint.ON_ANY_NETWORK) - .setReplaceCurrent(false) - .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) - .build()); - } - - @Override - public void onCreate() { - super.onCreate(); - ((WulkanowyApp) getApplication()).getApplicationComponent().inject(this); - } - - @Override - public int onRunJob(JobParameters job) { - try { - repository.initLastUser(); - repository.syncAll(); - - gradeList = repository.getNewGrades(); - - if (!gradeList.isEmpty()) { - showNotification(); - } - return JobService.RESULT_SUCCESS; - } catch (Exception e) { - LogUtils.error("During background synchronization an error occurred", e); - return JobService.RESULT_FAIL_RETRY; - } - } - - private void showNotification() { - NotificationService service = new NotificationService(getApplicationContext()); - - service.notify(service.notificationBuilder() - .setContentTitle(getStringTitle()) - .setContentText(getStringContent()) - .setSmallIcon(R.drawable.ic_stat_notify) - .setAutoCancel(true) - .setDefaults(NotificationCompat.DEFAULT_ALL) - .setPriority(NotificationCompat.PRIORITY_HIGH) - .setColor(getResources().getColor(R.color.colorPrimary)) - .setContentIntent(PendingIntent.getActivity(getApplicationContext(), 0, - MainActivity.getStartIntent(getApplicationContext()).putExtra(EXTRA_INTENT_KEY, 0) - , 0 - )) - .build()); - } - - private String getStringTitle() { - if (gradeList.size() == 1) { - return getResources().getQuantityString(R.plurals.newGradePlurals, 1); - } else { - return getResources().getQuantityString(R.plurals.newGradePlurals, 2); - } - } - - private String getStringContent() { - if (gradeList.size() == 1) { - return gradeList.get(0).getSubject(); - } else { - return getResources().getQuantityString(R.plurals.receivedNewGradePlurals, - gradeList.size(), gradeList.size()); - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt new file mode 100644 index 000000000..01a583e13 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt @@ -0,0 +1,160 @@ +package io.github.wulkanowy.services.alarm + +import android.app.PendingIntent +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.os.Build +import android.os.Build.VERSION_CODES.N +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.onResourceError +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.resourceFlow +import io.github.wulkanowy.services.sync.channels.UpcomingLessonsChannel.Companion.CHANNEL_ID +import io.github.wulkanowy.ui.modules.Destination +import io.github.wulkanowy.ui.modules.splash.SplashActivity +import io.github.wulkanowy.utils.PendingIntentCompat +import io.github.wulkanowy.utils.getCompatColor +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.flow.launchIn +import timber.log.Timber +import javax.inject.Inject + +@AndroidEntryPoint +class TimetableNotificationReceiver : BroadcastReceiver() { + + @Inject + lateinit var studentRepository: StudentRepository + + @Inject + lateinit var preferencesRepository: PreferencesRepository + + companion object { + const val NOTIFICATION_TYPE_CURRENT = 1 + const val NOTIFICATION_TYPE_UPCOMING = 2 + const val NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION = 3 + + // FIXME only shows one notification even if there are multiple students. + // Probably want to fix after #721 is merged. + const val NOTIFICATION_ID = 2137 + + const val STUDENT_NAME = "student_name" + const val STUDENT_ID = "student_id" + const val LESSON_TYPE = "type" + const val LESSON_TITLE = "title" + const val LESSON_ROOM = "room" + const val LESSON_NEXT_TITLE = "next_title" + const val LESSON_NEXT_ROOM = "next_room" + const val LESSON_START = "start_timestamp" + const val LESSON_END = "end_timestamp" + } + + @OptIn(DelicateCoroutinesApi::class) + override fun onReceive(context: Context, intent: Intent) { + Timber.d("Receiving intent... ${intent.toUri(0)}") + + resourceFlow { + val showStudentName = !studentRepository.isOneUniqueStudent() + val student = studentRepository.getCurrentStudent(false) + val studentId = intent.getIntExtra(STUDENT_ID, 0) + + if (student.studentId == studentId) { + prepareNotification(context, intent, showStudentName) + } else { + Timber.d("Notification studentId($studentId) differs from current(${student.studentId})") + } + } + .onResourceError { Timber.e(it) } + .launchIn(GlobalScope) + } + + private fun prepareNotification(context: Context, intent: Intent, showStudentName: Boolean) { + val type = intent.getIntExtra(LESSON_TYPE, 0) + val isPersistent = preferencesRepository.isUpcomingLessonsNotificationsPersistent + + if (type == NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION) { + return NotificationManagerCompat.from(context).cancel(NOTIFICATION_ID) + } + + val studentId = intent.getIntExtra(STUDENT_ID, 0) + val studentName = intent.getStringExtra(STUDENT_NAME).takeIf { showStudentName } + + val subject = intent.getStringExtra(LESSON_TITLE) + val room = intent.getStringExtra(LESSON_ROOM) + + val start = intent.getLongExtra(LESSON_START, 0) + val end = intent.getLongExtra(LESSON_END, 0) + + val nextSubject = intent.getStringExtra(LESSON_NEXT_TITLE) + val nextRoom = intent.getStringExtra(LESSON_NEXT_ROOM) + + Timber.d("TimetableNotification receive: type: $type, subject: $subject, start: $start, student: $studentId") + + val notificationTitleResId = + if (type == NOTIFICATION_TYPE_CURRENT) R.string.timetable_now else R.string.timetable_next + val notificationTitle = + context.getString(notificationTitleResId, "($room) $subject".removePrefix("()")) + + val nextLessonText = nextSubject?.let { + context.getString( + R.string.timetable_later, + "($nextRoom) $nextSubject".removePrefix("()") + ) + } + + showNotification( + context = context, + isPersistent = isPersistent, + studentName = studentName, + countDown = if (type == NOTIFICATION_TYPE_CURRENT) end else start, + timeout = end - start, + title = notificationTitle, + next = nextLessonText + ) + } + + private fun showNotification( + context: Context, + isPersistent: Boolean, + studentName: String?, + countDown: Long, + timeout: Long, + title: String, + next: String? + ) { + NotificationManagerCompat.from(context) + .notify(NOTIFICATION_ID, NotificationCompat.Builder(context, CHANNEL_ID) + .setContentTitle(title) + .setContentText(next) + .setAutoCancel(false) + .setWhen(countDown) + .setOngoing(isPersistent) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .apply { + if (Build.VERSION.SDK_INT >= N) setUsesChronometer(true) + } + .setTimeoutAfter(timeout) + .setSmallIcon(R.drawable.ic_stat_timetable) + .setColor(context.getCompatColor(R.color.colorPrimary)) + .setStyle(NotificationCompat.InboxStyle() + .addLine(next) + .also { inboxStyle -> + studentName?.let { inboxStyle.setSummaryText(it) } + }) + .setContentIntent( + PendingIntent.getActivity( + context, + NOTIFICATION_ID, + SplashActivity.getStartIntent(context, Destination.Timetable()), + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE + ) + ) + .build() + ) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationSchedulerHelper.kt b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationSchedulerHelper.kt new file mode 100644 index 000000000..42078d03f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationSchedulerHelper.kt @@ -0,0 +1,193 @@ +package io.github.wulkanowy.services.alarm + +import android.app.AlarmManager +import android.app.AlarmManager.RTC_WAKEUP +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.os.Build +import androidx.core.app.AlarmManagerCompat +import androidx.core.app.NotificationManagerCompat +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_END +import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_NEXT_ROOM +import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_NEXT_TITLE +import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_ROOM +import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_START +import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_TITLE +import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_TYPE +import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.NOTIFICATION_ID +import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.NOTIFICATION_TYPE_CURRENT +import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION +import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.NOTIFICATION_TYPE_UPCOMING +import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.STUDENT_ID +import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.STUDENT_NAME +import io.github.wulkanowy.utils.DispatchersProvider +import io.github.wulkanowy.utils.PendingIntentCompat +import io.github.wulkanowy.utils.nickOrName +import kotlinx.coroutines.withContext +import timber.log.Timber +import java.time.Duration.ofMinutes +import java.time.Instant +import java.time.Instant.now +import java.time.LocalDate +import javax.inject.Inject + +class TimetableNotificationSchedulerHelper @Inject constructor( + @ApplicationContext private val context: Context, + private val alarmManager: AlarmManager, + private val preferencesRepository: PreferencesRepository, + private val dispatchersProvider: DispatchersProvider, +) { + + private fun getRequestCode(time: Instant, studentId: Int): Int = + (time.toEpochMilli() * studentId).toInt() + + private fun getUpcomingLessonTime( + index: Int, + day: List, + lesson: Timetable + ): Instant = day.getOrNull(index - 1)?.end ?: lesson.start.minus(ofMinutes(30)) + + suspend fun cancelScheduled(lessons: List, student: Student) { + val studentId = student.studentId + withContext(dispatchersProvider.io) { + lessons.sortedBy { it.start }.forEachIndexed { index, lesson -> + val upcomingTime = getUpcomingLessonTime(index, lessons, lesson) + cancelScheduledTo( + range = upcomingTime..lesson.start, + requestCode = getRequestCode(upcomingTime, studentId) + ) + cancelScheduledTo( + range = lesson.start..lesson.end, + requestCode = getRequestCode(lesson.start, studentId) + ) + + Timber.d("TimetableNotification canceled: type 1 & 2, subject: ${lesson.subject}, start: ${lesson.start}, student: $studentId") + } + } + } + + private fun cancelScheduledTo(range: ClosedRange, requestCode: Int) { + if (now() in range) cancelNotification() + + alarmManager.cancel( + PendingIntent.getBroadcast( + context, + requestCode, + Intent(), + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE + ) + ) + } + + fun cancelNotification() = + NotificationManagerCompat.from(context).cancel(NOTIFICATION_ID) + + suspend fun scheduleNotifications(lessons: List, student: Student) { + if (!preferencesRepository.isUpcomingLessonsNotificationsEnable) { + return cancelScheduled(lessons, student) + } + + if (!canScheduleExactAlarms()) { + Timber.w("Exact alarms are disabled by user") + preferencesRepository.isUpcomingLessonsNotificationsEnable = false + return + } + + if (lessons.firstOrNull()?.date?.isAfter(LocalDate.now().plusDays(2)) == true) { + Timber.d("Timetable notification scheduling skipped - lessons are too far") + return + } + + withContext(dispatchersProvider.io) { + lessons.groupBy { it.date } + .map { it.value.sortedBy { lesson -> lesson.start } } + .map { it.filter { lesson -> lesson.isStudentPlan } } + .map { day -> + val canceled = day.filter { it.canceled } + val active = day.filter { !it.canceled } + + cancelScheduled(canceled, student) + active.forEachIndexed { index, lesson -> + val intent = createIntent(student, lesson, active.getOrNull(index + 1)) + + if (lesson.start > now()) { + scheduleBroadcast( + intent = intent, + studentId = student.studentId, + notificationType = NOTIFICATION_TYPE_UPCOMING, + time = getUpcomingLessonTime(index, active, lesson) + ) + } + + if (lesson.end > now()) { + scheduleBroadcast( + intent = intent, + studentId = student.studentId, + notificationType = NOTIFICATION_TYPE_CURRENT, + time = lesson.start + ) + if (active.lastIndex == index) { + scheduleBroadcast( + intent = intent, + studentId = student.studentId, + notificationType = NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION, + time = lesson.end + ) + } + } + } + } + } + } + + private fun createIntent(student: Student, lesson: Timetable, nextLesson: Timetable?): Intent { + return Intent(context, TimetableNotificationReceiver::class.java).apply { + putExtra(STUDENT_ID, student.studentId) + putExtra(STUDENT_NAME, student.nickOrName) + putExtra(LESSON_ROOM, lesson.room) + putExtra(LESSON_START, lesson.start.toEpochMilli()) + putExtra(LESSON_END, lesson.end.toEpochMilli()) + putExtra(LESSON_TITLE, lesson.subject) + putExtra(LESSON_NEXT_TITLE, nextLesson?.subject) + putExtra(LESSON_NEXT_ROOM, nextLesson?.room) + } + } + + private fun scheduleBroadcast( + intent: Intent, + studentId: Int, + notificationType: Int, + time: Instant + ) { + try { + AlarmManagerCompat.setExactAndAllowWhileIdle( + alarmManager, RTC_WAKEUP, time.toEpochMilli(), + PendingIntent.getBroadcast(context, getRequestCode(time, studentId), intent.also { + it.putExtra(LESSON_TYPE, notificationType) + }, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE) + ) + Timber.d( + "TimetableNotification scheduled: type: $notificationType, subject: ${ + intent.getStringExtra(LESSON_TITLE) + }, start: $time, student: $studentId" + ) + } catch (e: Throwable) { + Timber.e(e) + } + } + + fun canScheduleExactAlarms(): Boolean { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + try { + alarmManager.canScheduleExactAlarms() + } catch (e: Throwable) { + false + } + } else true + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/piggyback/VulcanNotificationListenerService.kt b/app/src/main/java/io/github/wulkanowy/services/piggyback/VulcanNotificationListenerService.kt new file mode 100644 index 000000000..3c173495a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/piggyback/VulcanNotificationListenerService.kt @@ -0,0 +1,27 @@ +package io.github.wulkanowy.services.piggyback + +import android.service.notification.NotificationListenerService +import android.service.notification.StatusBarNotification +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.services.sync.SyncManager +import javax.inject.Inject + +@AndroidEntryPoint +class VulcanNotificationListenerService : NotificationListenerService() { + + @Inject + lateinit var syncManager: SyncManager + + @Inject + lateinit var preferenceRepository: PreferencesRepository + + override fun onNotificationPosted(statusBarNotification: StatusBarNotification?) { + if (statusBarNotification?.packageName == "pl.edu.vulcan.hebe" && preferenceRepository.isNotificationPiggybackEnabled) { + syncManager.startOneTimeSyncWorker() + if (preferenceRepository.isNotificationPiggybackRemoveOriginalEnabled) { + cancelNotification(statusBarNotification.key) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/services/shortcuts/ShortcutsHelper.kt b/app/src/main/java/io/github/wulkanowy/services/shortcuts/ShortcutsHelper.kt new file mode 100644 index 000000000..5e59aa54b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/shortcuts/ShortcutsHelper.kt @@ -0,0 +1,55 @@ +package io.github.wulkanowy.services.shortcuts + +import android.content.Context +import android.content.Intent +import androidx.core.content.pm.ShortcutInfoCompat +import androidx.core.content.pm.ShortcutManagerCompat +import androidx.core.graphics.drawable.IconCompat +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import io.github.wulkanowy.ui.modules.Destination +import io.github.wulkanowy.ui.modules.splash.SplashActivity +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ShortcutsHelper @Inject constructor(@ApplicationContext private val context: Context) { + + fun initializeShortcuts() { + val shortcutsInfo = listOf( + ShortcutInfoCompat.Builder(context, "grade_shortcut") + .setShortLabel(context.getString(R.string.grade_title)) + .setLongLabel(context.getString(R.string.grade_title)) + .setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_grade)) + .setIntent(SplashActivity.getStartIntent(context, Destination.Grade) + .apply { action = Intent.ACTION_VIEW }) + .build(), + + ShortcutInfoCompat.Builder(context, "attendance_shortcut") + .setShortLabel(context.getString(R.string.attendance_title)) + .setLongLabel(context.getString(R.string.attendance_title)) + .setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_attendance)) + .setIntent(SplashActivity.getStartIntent(context, Destination.Attendance) + .apply { action = Intent.ACTION_VIEW }) + .build(), + + ShortcutInfoCompat.Builder(context, "exam_shortcut") + .setShortLabel(context.getString(R.string.exam_title)) + .setLongLabel(context.getString(R.string.exam_title)) + .setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_exam)) + .setIntent(SplashActivity.getStartIntent(context, Destination.Exam) + .apply { action = Intent.ACTION_VIEW }) + .build(), + + ShortcutInfoCompat.Builder(context, "timetable_shortcut") + .setShortLabel(context.getString(R.string.timetable_title)) + .setLongLabel(context.getString(R.string.timetable_title)) + .setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_timetable)) + .setIntent(SplashActivity.getStartIntent(context, Destination.Timetable()) + .apply { action = Intent.ACTION_VIEW }) + .build() + ) + + shortcutsInfo.forEach { ShortcutManagerCompat.pushDynamicShortcut(context, it) } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt new file mode 100644 index 000000000..c1bed4dd3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt @@ -0,0 +1,100 @@ +package io.github.wulkanowy.services.sync + +import android.os.Build.VERSION.SDK_INT +import android.os.Build.VERSION_CODES.O +import androidx.core.app.NotificationManagerCompat +import androidx.lifecycle.asFlow +import androidx.work.BackoffPolicy.EXPONENTIAL +import androidx.work.Constraints +import androidx.work.Data +import androidx.work.ExistingPeriodicWorkPolicy.KEEP +import androidx.work.ExistingPeriodicWorkPolicy.REPLACE +import androidx.work.ExistingWorkPolicy +import androidx.work.NetworkType.CONNECTED +import androidx.work.NetworkType.UNMETERED +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.PeriodicWorkRequestBuilder +import androidx.work.WorkInfo +import androidx.work.WorkManager +import io.github.wulkanowy.data.db.SharedPrefProvider +import io.github.wulkanowy.data.db.SharedPrefProvider.Companion.APP_VERSION_CODE_KEY +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.services.sync.channels.Channel +import io.github.wulkanowy.utils.AppInfo +import io.github.wulkanowy.utils.isHolidays +import kotlinx.coroutines.flow.Flow +import timber.log.Timber +import java.time.LocalDate.now +import java.util.concurrent.TimeUnit.MINUTES +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SyncManager @Inject constructor( + private val workManager: WorkManager, + private val preferencesRepository: PreferencesRepository, + channels: Set<@JvmSuppressWildcards Channel>, + notificationManager: NotificationManagerCompat, + sharedPrefProvider: SharedPrefProvider, + appInfo: AppInfo +) { + + init { + if (now().isHolidays) stopSyncWorker() + + if (SDK_INT >= O) { + channels.forEach { it.create() } + notificationManager.deleteNotificationChannel("lesson_channel") + notificationManager.deleteNotificationChannel("new_entries_channel") + } + + if (sharedPrefProvider.getLong(APP_VERSION_CODE_KEY, -1L) != appInfo.versionCode.toLong()) { + startPeriodicSyncWorker(true) + sharedPrefProvider.putLong(APP_VERSION_CODE_KEY, appInfo.versionCode.toLong(), true) + } + Timber.i("SyncManager was initialized") + } + + fun startPeriodicSyncWorker(restart: Boolean = false) { + if (preferencesRepository.isServiceEnabled && !now().isHolidays) { + val serviceInterval = preferencesRepository.servicesInterval + + workManager.enqueueUniquePeriodicWork( + SyncWorker::class.java.simpleName, if (restart) REPLACE else KEEP, + PeriodicWorkRequestBuilder(serviceInterval, MINUTES) + .setInitialDelay(10, MINUTES) + .setBackoffCriteria(EXPONENTIAL, 30, MINUTES) + .setConstraints( + Constraints.Builder() + .setRequiredNetworkType(if (preferencesRepository.isServicesOnlyWifi) UNMETERED else CONNECTED) + .build() + ) + .build() + ) + } + } + + // if quiet, no notifications will be sent + fun startOneTimeSyncWorker(quiet: Boolean = false): Flow { + val work = OneTimeWorkRequestBuilder() + .setInputData( + Data.Builder() + .putBoolean("quiet", quiet) + .putBoolean("one_time", true) + .build() + ) + .build() + + workManager.enqueueUniqueWork( + "${SyncWorker::class.java.simpleName}_one_time", + ExistingWorkPolicy.REPLACE, + work + ) + + return workManager.getWorkInfoByIdLiveData(work.id).asFlow() + } + + fun stopSyncWorker() { + workManager.cancelUniqueWork(SyncWorker::class.java.simpleName) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt new file mode 100644 index 000000000..5dddd9a78 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt @@ -0,0 +1,112 @@ +package io.github.wulkanowy.services.sync + +import android.content.Context +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationCompat.BigTextStyle +import androidx.core.app.NotificationCompat.PRIORITY_DEFAULT +import androidx.core.app.NotificationManagerCompat +import androidx.hilt.work.HiltWorker +import androidx.work.CoroutineWorker +import androidx.work.Data +import androidx.work.WorkerParameters +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import io.github.wulkanowy.R +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException +import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException +import io.github.wulkanowy.services.sync.channels.DebugChannel +import io.github.wulkanowy.services.sync.works.Work +import io.github.wulkanowy.utils.DispatchersProvider +import io.github.wulkanowy.utils.getCompatColor +import kotlinx.coroutines.withContext +import timber.log.Timber +import java.time.Instant +import kotlin.random.Random + +@HiltWorker +class SyncWorker @AssistedInject constructor( + @Assisted appContext: Context, + @Assisted workerParameters: WorkerParameters, + private val studentRepository: StudentRepository, + private val semesterRepository: SemesterRepository, + private val works: Set<@JvmSuppressWildcards Work>, + private val preferencesRepository: PreferencesRepository, + private val notificationManager: NotificationManagerCompat, + private val dispatchersProvider: DispatchersProvider +) : CoroutineWorker(appContext, workerParameters) { + + override suspend fun doWork(): Result = withContext(dispatchersProvider.io) { + Timber.i("SyncWorker is starting") + + if (!studentRepository.isCurrentStudentSet()) return@withContext Result.failure() + + val (student, semester) = try { + val student = studentRepository.getCurrentStudent() + val semester = semesterRepository.getCurrentSemester(student, true) + student to semester + } catch (e: Throwable) { + return@withContext getResultFromErrors(listOf(e)) + } + + val exceptions = works.mapNotNull { work -> + try { + Timber.i("${work::class.java.simpleName} is starting") + work.doWork(student, semester, isNotificationsEnabled()) + Timber.i("${work::class.java.simpleName} result: Success") + null + } catch (e: Throwable) { + Timber.w("${work::class.java.simpleName} result: An exception ${e.message} occurred") + if (e is FeatureDisabledException || e is FeatureNotAvailableException) { + null + } else { + Timber.e(e) + e + } + } + } + val result = getResultFromErrors(exceptions) + + if (preferencesRepository.isDebugNotificationEnable) notify(result) + Timber.i("SyncWorker result: $result") + + return@withContext result + } + + private fun isNotificationsEnabled(): Boolean { + val quiet = inputData.getBoolean("quiet", false) + return preferencesRepository.isNotificationsEnable && !quiet + } + + private fun getResultFromErrors(errors: List): Result = when { + errors.isNotEmpty() && inputData.getBoolean("one_time", false) -> { + Result.failure( + Data.Builder() + .putString("error_message", errors.joinToString { it.message.toString() }) + .putString("error_stack", errors.map { it.stackTraceToString() }.toString()) + .build() + ) + } + errors.isNotEmpty() -> Result.retry() + else -> { + preferencesRepository.lasSyncDate = Instant.now() + Result.success() + } + } + + private fun notify(result: Result) { + notificationManager.notify( + Random.nextInt(Int.MAX_VALUE), + NotificationCompat.Builder(applicationContext, DebugChannel.CHANNEL_ID) + .setContentTitle("Debug notification") + .setSmallIcon(R.drawable.ic_stat_all) + .setAutoCancel(true) + .setColor(applicationContext.getCompatColor(R.color.colorPrimary)) + .setStyle(BigTextStyle().bigText("${SyncWorker::class.java.simpleName} result: $result")) + .setPriority(PRIORITY_DEFAULT) + .build() + ) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/Channel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/Channel.kt new file mode 100644 index 000000000..f47c2220c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/channels/Channel.kt @@ -0,0 +1,6 @@ +package io.github.wulkanowy.services.sync.channels + +interface Channel { + + fun create() +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/DebugChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/DebugChannel.kt new file mode 100644 index 000000000..2c7565195 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/channels/DebugChannel.kt @@ -0,0 +1,33 @@ +package io.github.wulkanowy.services.sync.channels + +import android.annotation.TargetApi +import android.app.Notification.VISIBILITY_PUBLIC +import android.app.NotificationChannel +import android.app.NotificationManager.IMPORTANCE_DEFAULT +import android.content.Context +import androidx.core.app.NotificationManagerCompat +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import io.github.wulkanowy.utils.AppInfo +import javax.inject.Inject + +@TargetApi(26) +class DebugChannel @Inject constructor( + private val notificationManager: NotificationManagerCompat, + @ApplicationContext private val context: Context, + private val appInfo: AppInfo +) : Channel { + + companion object { + const val CHANNEL_ID = "debug_channel" + } + + override fun create() { + if (!appInfo.isDebug) return + notificationManager.createNotificationChannel( + NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_debug), IMPORTANCE_DEFAULT) + .apply { + lockscreenVisibility = VISIBILITY_PUBLIC + }) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/LuckyNumberChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/LuckyNumberChannel.kt new file mode 100644 index 000000000..8025bd76c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/channels/LuckyNumberChannel.kt @@ -0,0 +1,32 @@ +package io.github.wulkanowy.services.sync.channels + +import android.annotation.TargetApi +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import androidx.core.app.NotificationManagerCompat +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import javax.inject.Inject + +@TargetApi(26) +class LuckyNumberChannel @Inject constructor( + private val notificationManager: NotificationManagerCompat, + @ApplicationContext private val context: Context +) : Channel { + + companion object { + const val CHANNEL_ID = "lucky_number_channel" + } + + override fun create() { + notificationManager.createNotificationChannel( + NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_lucky_number), NotificationManager.IMPORTANCE_HIGH) + .apply { + enableLights(true) + enableVibration(true) + lockscreenVisibility = Notification.VISIBILITY_PUBLIC + }) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewAttendanceChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewAttendanceChannel.kt new file mode 100644 index 000000000..3110099e5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewAttendanceChannel.kt @@ -0,0 +1,36 @@ +package io.github.wulkanowy.services.sync.channels + +import android.annotation.TargetApi +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import androidx.core.app.NotificationManagerCompat +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import javax.inject.Inject + +@TargetApi(26) +class NewAttendanceChannel @Inject constructor( + private val notificationManager: NotificationManagerCompat, + @ApplicationContext private val context: Context +) : Channel { + + companion object { + const val CHANNEL_ID = "new_attendance_channel" + } + + override fun create() { + notificationManager.createNotificationChannel( + NotificationChannel( + CHANNEL_ID, + context.getString(R.string.channel_new_attendance), + NotificationManager.IMPORTANCE_HIGH + ) + .apply { + enableLights(true) + enableVibration(true) + lockscreenVisibility = Notification.VISIBILITY_PUBLIC + }) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewConferencesChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewConferencesChannel.kt new file mode 100644 index 000000000..397e67c6b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewConferencesChannel.kt @@ -0,0 +1,32 @@ +package io.github.wulkanowy.services.sync.channels + +import android.annotation.TargetApi +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import androidx.core.app.NotificationManagerCompat +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import javax.inject.Inject + +@TargetApi(26) +class NewConferencesChannel @Inject constructor( + private val notificationManager: NotificationManagerCompat, + @ApplicationContext private val context: Context +) : Channel { + + companion object { + const val CHANNEL_ID = "new_conferences_channel" + } + + override fun create() { + notificationManager.createNotificationChannel( + NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_new_conference), NotificationManager.IMPORTANCE_HIGH) + .apply { + enableLights(true) + enableVibration(true) + lockscreenVisibility = Notification.VISIBILITY_PUBLIC + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewExamChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewExamChannel.kt new file mode 100644 index 000000000..1bf5f8555 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewExamChannel.kt @@ -0,0 +1,32 @@ +package io.github.wulkanowy.services.sync.channels + +import android.annotation.TargetApi +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import androidx.core.app.NotificationManagerCompat +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import javax.inject.Inject + +@TargetApi(26) +class NewExamChannel @Inject constructor( + private val notificationManager: NotificationManagerCompat, + @ApplicationContext private val context: Context +) : Channel { + + companion object { + const val CHANNEL_ID = "new_exam_channel" + } + + override fun create() { + notificationManager.createNotificationChannel( + NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_new_exam), NotificationManager.IMPORTANCE_HIGH) + .apply { + enableLights(true) + enableVibration(true) + lockscreenVisibility = Notification.VISIBILITY_PUBLIC + }) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewGradesChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewGradesChannel.kt new file mode 100644 index 000000000..5d5727d35 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewGradesChannel.kt @@ -0,0 +1,32 @@ +package io.github.wulkanowy.services.sync.channels + +import android.annotation.TargetApi +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import androidx.core.app.NotificationManagerCompat +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import javax.inject.Inject + +@TargetApi(26) +class NewGradesChannel @Inject constructor( + private val notificationManager: NotificationManagerCompat, + @ApplicationContext private val context: Context +) : Channel { + + companion object { + const val CHANNEL_ID = "new_grade_channel" + } + + override fun create() { + notificationManager.createNotificationChannel( + NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_new_grades), NotificationManager.IMPORTANCE_HIGH) + .apply { + enableLights(true) + enableVibration(true) + lockscreenVisibility = Notification.VISIBILITY_PUBLIC + }) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewHomeworkChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewHomeworkChannel.kt new file mode 100644 index 000000000..4b7dd0e9a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewHomeworkChannel.kt @@ -0,0 +1,32 @@ +package io.github.wulkanowy.services.sync.channels + +import android.annotation.TargetApi +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import androidx.core.app.NotificationManagerCompat +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import javax.inject.Inject + +@TargetApi(26) +class NewHomeworkChannel @Inject constructor( + private val notificationManager: NotificationManagerCompat, + @ApplicationContext private val context: Context +) : Channel { + + companion object { + const val CHANNEL_ID = "new_homework_channel" + } + + override fun create() { + notificationManager.createNotificationChannel( + NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_new_homework), NotificationManager.IMPORTANCE_HIGH) + .apply { + enableLights(true) + enableVibration(true) + lockscreenVisibility = Notification.VISIBILITY_PUBLIC + }) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewMessagesChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewMessagesChannel.kt new file mode 100644 index 000000000..962fbfa24 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewMessagesChannel.kt @@ -0,0 +1,32 @@ +package io.github.wulkanowy.services.sync.channels + +import android.annotation.TargetApi +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import androidx.core.app.NotificationManagerCompat +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import javax.inject.Inject + +@TargetApi(26) +class NewMessagesChannel @Inject constructor( + private val notificationManager: NotificationManagerCompat, + @ApplicationContext private val context: Context +) : Channel { + + companion object { + const val CHANNEL_ID = "new_message_channel" + } + + override fun create() { + notificationManager.createNotificationChannel( + NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_new_message), NotificationManager.IMPORTANCE_HIGH) + .apply { + enableLights(true) + enableVibration(true) + lockscreenVisibility = Notification.VISIBILITY_PUBLIC + }) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewNotesChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewNotesChannel.kt new file mode 100644 index 000000000..c6e79b393 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewNotesChannel.kt @@ -0,0 +1,32 @@ +package io.github.wulkanowy.services.sync.channels + +import android.annotation.TargetApi +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import androidx.core.app.NotificationManagerCompat +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import javax.inject.Inject + +@TargetApi(26) +class NewNotesChannel @Inject constructor( + private val notificationManager: NotificationManagerCompat, + @ApplicationContext private val context: Context +) : Channel { + + companion object { + const val CHANNEL_ID = "new_notes_channel" + } + + override fun create() { + notificationManager.createNotificationChannel( + NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_new_notes), NotificationManager.IMPORTANCE_HIGH) + .apply { + enableLights(true) + enableVibration(true) + lockscreenVisibility = Notification.VISIBILITY_PUBLIC + }) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewSchoolAnnouncementsChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewSchoolAnnouncementsChannel.kt new file mode 100644 index 000000000..2b21ba408 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewSchoolAnnouncementsChannel.kt @@ -0,0 +1,32 @@ +package io.github.wulkanowy.services.sync.channels + +import android.annotation.TargetApi +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import androidx.core.app.NotificationManagerCompat +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import javax.inject.Inject + +@TargetApi(26) +class NewSchoolAnnouncementsChannel @Inject constructor( + private val notificationManager: NotificationManagerCompat, + @ApplicationContext private val context: Context +) : Channel { + + companion object { + const val CHANNEL_ID = "new_school_announcements_channel" + } + + override fun create() { + notificationManager.createNotificationChannel( + NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_new_school_announcement), NotificationManager.IMPORTANCE_HIGH) + .apply { + enableLights(true) + enableVibration(true) + lockscreenVisibility = Notification.VISIBILITY_PUBLIC + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/PushChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/PushChannel.kt new file mode 100644 index 000000000..1d7376b92 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/channels/PushChannel.kt @@ -0,0 +1,33 @@ +package io.github.wulkanowy.services.sync.channels + +import android.annotation.TargetApi +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import androidx.core.app.NotificationManagerCompat +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import javax.inject.Inject + +@TargetApi(26) +class PushChannel @Inject constructor( + private val notificationManager: NotificationManagerCompat, + @ApplicationContext private val context: Context +) : Channel { + + companion object { + const val CHANNEL_ID = "push_channel" + } + + override fun create() { + notificationManager.createNotificationChannel( + NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_push), NotificationManager.IMPORTANCE_HIGH) + .apply { + enableLights(true) + enableVibration(true) + lockscreenVisibility = Notification.VISIBILITY_PUBLIC + } + ) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/TimetableChangeChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/TimetableChangeChannel.kt new file mode 100644 index 000000000..10dd3e004 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/channels/TimetableChangeChannel.kt @@ -0,0 +1,36 @@ +package io.github.wulkanowy.services.sync.channels + +import android.annotation.TargetApi +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import androidx.core.app.NotificationManagerCompat +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import javax.inject.Inject + +@TargetApi(26) +class TimetableChangeChannel @Inject constructor( + private val notificationManager: NotificationManagerCompat, + @ApplicationContext private val context: Context +) : Channel { + + companion object { + const val CHANNEL_ID = "change_timetable_channel" + } + + override fun create() { + notificationManager.createNotificationChannel( + NotificationChannel( + CHANNEL_ID, + context.getString(R.string.channel_change_timetable), + NotificationManager.IMPORTANCE_HIGH + ) + .apply { + enableLights(true) + enableVibration(true) + lockscreenVisibility = Notification.VISIBILITY_PUBLIC + }) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/UpcomingLessonsChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/UpcomingLessonsChannel.kt new file mode 100644 index 000000000..63b3a4f91 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/channels/UpcomingLessonsChannel.kt @@ -0,0 +1,33 @@ +package io.github.wulkanowy.services.sync.channels + +import android.annotation.TargetApi +import android.app.Notification.VISIBILITY_PUBLIC +import android.app.NotificationChannel +import android.app.NotificationManager.IMPORTANCE_DEFAULT +import android.content.Context +import androidx.core.app.NotificationManagerCompat +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import javax.inject.Inject + +@TargetApi(26) +class UpcomingLessonsChannel @Inject constructor( + private val notificationManager: NotificationManagerCompat, + @ApplicationContext private val context: Context +) : Channel { + + companion object { + const val CHANNEL_ID = "upcoming_lesson_channel" + } + + override fun create() { + notificationManager.createNotificationChannel( + NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_upcoming_lessons), IMPORTANCE_DEFAULT).apply { + lockscreenVisibility = VISIBILITY_PUBLIC + setShowBadge(false) + enableVibration(false) + setSound(null, null) + } + ) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/AppNotificationManager.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/AppNotificationManager.kt new file mode 100644 index 000000000..dadb68c50 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/AppNotificationManager.kt @@ -0,0 +1,179 @@ +package io.github.wulkanowy.services.sync.notifications + +import android.annotation.SuppressLint +import android.app.PendingIntent +import android.content.Context +import android.os.Build +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Notification +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.pojos.GroupNotificationData +import io.github.wulkanowy.data.pojos.NotificationData +import io.github.wulkanowy.data.repositories.NotificationRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.modules.splash.SplashActivity +import io.github.wulkanowy.utils.PendingIntentCompat +import io.github.wulkanowy.utils.getCompatBitmap +import io.github.wulkanowy.utils.getCompatColor +import io.github.wulkanowy.utils.nickOrName +import java.time.Instant +import javax.inject.Inject +import kotlin.random.Random + +class AppNotificationManager @Inject constructor( + private val notificationManager: NotificationManagerCompat, + @ApplicationContext private val context: Context, + private val studentRepository: StudentRepository, + private val notificationRepository: NotificationRepository +) { + + @SuppressLint("InlinedApi") + suspend fun sendSingleNotification( + notificationData: NotificationData, + notificationType: NotificationType, + student: Student + ) { + val notification = NotificationCompat.Builder(context, notificationType.channel) + .setLargeIcon(context.getCompatBitmap(notificationType.icon, R.color.colorPrimary)) + .setSmallIcon(R.drawable.ic_stat_all) + .setAutoCancel(true) + .setDefaults(NotificationCompat.DEFAULT_ALL) + .setPriority(NotificationCompat.PRIORITY_HIGH) + .setColor(context.getCompatColor(R.color.colorPrimary)) + .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY) + .setContentIntent( + PendingIntent.getActivity( + context, + Random.nextInt(), + SplashActivity.getStartIntent(context, notificationData.destination), + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE + ) + ) + .setContentTitle(notificationData.title) + .setContentText(notificationData.content) + .setStyle( + NotificationCompat.BigTextStyle() + .bigText(notificationData.content) + .also { builder -> + if (!studentRepository.isOneUniqueStudent()) { + builder.setSummaryText(student.nickOrName) + } + } + ) + .build() + + notificationManager.notify(Random.nextInt(), notification) + saveNotification(notificationData, notificationType, student) + } + + @SuppressLint("InlinedApi") + suspend fun sendMultipleNotifications( + groupNotificationData: GroupNotificationData, + student: Student + ) { + val notificationType = groupNotificationData.type + val groupType = notificationType.channel + val group = "${groupType}_${student.id}" + + sendSummaryNotification(groupNotificationData, group, student) + + groupNotificationData.notificationDataList.forEach { notificationData -> + val notification = NotificationCompat.Builder(context, notificationType.channel) + .setLargeIcon(context.getCompatBitmap(notificationType.icon, R.color.colorPrimary)) + .setSmallIcon(R.drawable.ic_stat_all) + .setAutoCancel(true) + .setDefaults(NotificationCompat.DEFAULT_ALL) + .setPriority(NotificationCompat.PRIORITY_HIGH) + .setColor(context.getCompatColor(R.color.colorPrimary)) + .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY) + .setContentIntent( + PendingIntent.getActivity( + context, + Random.nextInt(), + SplashActivity.getStartIntent(context, notificationData.destination), + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE + ) + ) + .setContentTitle(notificationData.title) + .setContentText(notificationData.content) + .setStyle( + NotificationCompat.BigTextStyle() + .bigText(notificationData.content) + .also { builder -> + if (!studentRepository.isOneUniqueStudent()) { + builder.setSummaryText(student.nickOrName) + } + } + ) + .setGroup(group) + .build() + + notificationManager.notify(Random.nextInt(), notification) + saveNotification(notificationData, groupNotificationData.type, student) + } + } + + private suspend fun sendSummaryNotification( + groupNotificationData: GroupNotificationData, + group: String, + student: Student + ) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return + + val summaryNotification = + NotificationCompat.Builder(context, groupNotificationData.type.channel) + .setContentTitle(groupNotificationData.title) + .setContentText(groupNotificationData.content) + .setSmallIcon(groupNotificationData.type.icon) + .setAutoCancel(true) + .setDefaults(NotificationCompat.DEFAULT_ALL) + .setPriority(NotificationCompat.PRIORITY_HIGH) + .setColor(context.getCompatColor(R.color.colorPrimary)) + .setStyle( + NotificationCompat.InboxStyle() + .also { builder -> + if (!studentRepository.isOneUniqueStudent()) { + builder.setSummaryText(student.nickOrName) + } + groupNotificationData.notificationDataList.forEach { + builder.addLine(it.content) + } + } + ) + .setContentIntent( + PendingIntent.getActivity( + context, + Random.nextInt(), + SplashActivity.getStartIntent(context, groupNotificationData.destination), + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE + ) + ) + .setLocalOnly(true) + .setGroup(group) + .setGroupSummary(true) + .build() + + val groupId = student.id * 100 + groupNotificationData.type.ordinal + notificationManager.notify(groupId.toInt(), summaryNotification) + } + + private suspend fun saveNotification( + notificationData: NotificationData, + notificationType: NotificationType, + student: Student + ) { + val notificationEntity = Notification( + studentId = student.id, + title = notificationData.title, + content = notificationData.content, + destination = notificationData.destination, + type = notificationType, + date = Instant.now(), + ) + + notificationRepository.saveNotification(notificationEntity) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/ChangeTimetableNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/ChangeTimetableNotification.kt new file mode 100644 index 000000000..43ae1fea9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/ChangeTimetableNotification.kt @@ -0,0 +1,120 @@ +package io.github.wulkanowy.services.sync.notifications + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.data.pojos.GroupNotificationData +import io.github.wulkanowy.data.pojos.NotificationData +import io.github.wulkanowy.ui.modules.Destination +import io.github.wulkanowy.utils.getPlural +import io.github.wulkanowy.utils.toFormattedString +import java.time.Instant +import java.time.LocalDate +import javax.inject.Inject + +class ChangeTimetableNotification @Inject constructor( + private val appNotificationManager: AppNotificationManager, + @ApplicationContext private val context: Context, +) { + + suspend fun notify(items: List, student: Student) { + val currentTime = Instant.now() + val changedLessons = items.filter { (it.canceled || it.changes) && it.start > currentTime } + val lessonsByDate = changedLessons.groupBy { it.date } + val notificationDataList = lessonsByDate + .flatMap { (date, lessons) -> + getNotificationContents(date, lessons).map { + NotificationData( + title = context.getPlural( + R.plurals.timetable_notify_new_items_title, + 1 + ), + content = it, + destination = Destination.Timetable(date) + ) + } + } + .ifEmpty { return } + + val groupNotificationData = GroupNotificationData( + notificationDataList = notificationDataList, + title = context.getPlural( + R.plurals.timetable_notify_new_items_title, + changedLessons.size + ), + content = context.getPlural( + R.plurals.timetable_notify_new_items_group, + changedLessons.size, + changedLessons.size + ), + destination = Destination.Timetable(lessonsByDate.toSortedMap().firstKey()), + type = NotificationType.CHANGE_TIMETABLE + ) + + appNotificationManager.sendMultipleNotifications(groupNotificationData, student) + } + + private fun getNotificationContents(date: LocalDate, lessons: List): List { + val formattedDate = date.toFormattedString("EEE dd.MM") + + return if (lessons.size > 2) { + listOf( + context.getPlural( + R.plurals.timetable_notify_new_items, + lessons.size, + formattedDate, + lessons.size, + ) + ) + } else { + lessons.map { + buildString { + append( + context.getString( + R.string.timetable_notify_lesson, + formattedDate, + it.number, + it.subject + ) + ) + if (it.roomOld.isNotBlank()) { + appendLine() + append( + context.getString( + R.string.timetable_notify_change_room, + it.roomOld, + it.room + ) + ) + } + if (it.teacherOld.isNotBlank() && it.teacher != it.teacherOld) { + appendLine() + append( + context.getString( + R.string.timetable_notify_change_teacher, + it.teacherOld, + it.teacher + ) + ) + } + if (it.subjectOld.isNotBlank()) { + appendLine() + append( + context.getString( + R.string.timetable_notify_change_subject, + it.subjectOld, + it.subject + ) + ) + } + if (it.info.isNotBlank()) { + appendLine() + append(it.info) + } + } + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewAttendanceNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewAttendanceNotification.kt new file mode 100644 index 000000000..49842c9a6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewAttendanceNotification.kt @@ -0,0 +1,55 @@ +package io.github.wulkanowy.services.sync.notifications + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Attendance +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.pojos.GroupNotificationData +import io.github.wulkanowy.data.pojos.NotificationData +import io.github.wulkanowy.ui.modules.Destination +import io.github.wulkanowy.ui.modules.splash.SplashActivity +import io.github.wulkanowy.utils.descriptionRes +import io.github.wulkanowy.utils.getPlural +import io.github.wulkanowy.utils.toFormattedString +import javax.inject.Inject + +class NewAttendanceNotification @Inject constructor( + private val appNotificationManager: AppNotificationManager, + @ApplicationContext private val context: Context +) { + + suspend fun notify(items: List, student: Student) { + val lines = items.filterNot { it.presence || it.name == "UNKNOWN" } + .map { + val description = context.getString(it.descriptionRes) + "${it.date.toFormattedString("dd.MM")} - ${it.subject}: $description" + } + .ifEmpty { return } + + val notificationDataList = lines.map { + NotificationData( + title = context.getPlural(R.plurals.attendance_notify_new_items_title, 1), + content = it, + destination = Destination.Attendance + ) + } + + val groupNotificationData = GroupNotificationData( + notificationDataList = notificationDataList, + title = context.getPlural( + R.plurals.attendance_notify_new_items_title, + notificationDataList.size + ), + content = context.getPlural( + R.plurals.attendance_notify_new_items, + notificationDataList.size, + notificationDataList.size + ), + destination = Destination.Attendance, + type = NotificationType.NEW_ATTENDANCE + ) + + appNotificationManager.sendMultipleNotifications(groupNotificationData, student) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewConferenceNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewConferenceNotification.kt new file mode 100644 index 000000000..92977ebb1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewConferenceNotification.kt @@ -0,0 +1,52 @@ +package io.github.wulkanowy.services.sync.notifications + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Conference +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.pojos.GroupNotificationData +import io.github.wulkanowy.data.pojos.NotificationData +import io.github.wulkanowy.ui.modules.Destination +import io.github.wulkanowy.ui.modules.splash.SplashActivity +import io.github.wulkanowy.utils.getPlural +import io.github.wulkanowy.utils.toFormattedString +import java.time.Instant +import javax.inject.Inject + +class NewConferenceNotification @Inject constructor( + private val appNotificationManager: AppNotificationManager, + @ApplicationContext private val context: Context +) { + + suspend fun notify(items: List, student: Student) { + val today = Instant.now() + val lines = items.filter { !it.date.isBefore(today) } + .map { + "${it.date.toFormattedString("dd.MM")} - ${it.title}: ${it.subject}" + } + .ifEmpty { return } + + val notificationDataList = lines.map { + NotificationData( + title = context.getPlural(R.plurals.conference_notify_new_item_title, 1), + content = it, + destination = Destination.Conference + ) + } + + val groupNotificationData = GroupNotificationData( + notificationDataList = notificationDataList, + title = context.getPlural(R.plurals.conference_notify_new_item_title, lines.size), + content = context.getPlural( + R.plurals.conference_notify_new_items, + lines.size, + lines.size + ), + destination = Destination.Conference, + type = NotificationType.NEW_CONFERENCE + ) + + appNotificationManager.sendMultipleNotifications(groupNotificationData, student) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewExamNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewExamNotification.kt new file mode 100644 index 000000000..125bbf92d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewExamNotification.kt @@ -0,0 +1,52 @@ +package io.github.wulkanowy.services.sync.notifications + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Exam +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.pojos.GroupNotificationData +import io.github.wulkanowy.data.pojos.NotificationData +import io.github.wulkanowy.ui.modules.Destination +import io.github.wulkanowy.ui.modules.splash.SplashActivity +import io.github.wulkanowy.utils.getPlural +import io.github.wulkanowy.utils.toFormattedString +import java.time.LocalDate +import javax.inject.Inject + +class NewExamNotification @Inject constructor( + private val appNotificationManager: AppNotificationManager, + @ApplicationContext private val context: Context +) { + + suspend fun notify(items: List, student: Student) { + val today = LocalDate.now() + val lines = items.filter { !it.date.isBefore(today) } + .map { + "${it.date.toFormattedString("dd.MM")} - ${it.subject}: ${it.description}" + } + .ifEmpty { return } + + val notificationDataList = lines.map { + NotificationData( + title = context.getPlural(R.plurals.exam_notify_new_item_title, 1), + content = it, + destination = Destination.Exam, + ) + } + + val groupNotificationData = GroupNotificationData( + notificationDataList = notificationDataList, + title = context.getPlural(R.plurals.exam_notify_new_item_title, lines.size), + content = context.getPlural( + R.plurals.exam_notify_new_item_content, + lines.size, + lines.size + ), + destination = Destination.Exam, + type = NotificationType.NEW_EXAM + ) + + appNotificationManager.sendMultipleNotifications(groupNotificationData, student) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewGradeNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewGradeNotification.kt new file mode 100644 index 000000000..9b49ed178 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewGradeNotification.kt @@ -0,0 +1,91 @@ +package io.github.wulkanowy.services.sync.notifications + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.data.db.entities.GradeSummary +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.pojos.GroupNotificationData +import io.github.wulkanowy.data.pojos.NotificationData +import io.github.wulkanowy.ui.modules.Destination +import io.github.wulkanowy.ui.modules.splash.SplashActivity +import io.github.wulkanowy.utils.getPlural +import javax.inject.Inject + +class NewGradeNotification @Inject constructor( + private val appNotificationManager: AppNotificationManager, + @ApplicationContext private val context: Context +) { + + suspend fun notifyDetails(items: List, student: Student) { + val notificationDataList = items.map { + NotificationData( + title = context.getPlural(R.plurals.grade_new_items, 1), + content = buildString { + append("${it.subject}: ${it.entry}") + if (it.comment.isNotBlank()) append(" (${it.comment})") + }, + destination = Destination.Grade, + ) + } + + val groupNotificationData = GroupNotificationData( + notificationDataList = notificationDataList, + title = context.getPlural(R.plurals.grade_new_items, items.size), + content = context.getPlural(R.plurals.grade_notify_new_items, items.size, items.size), + destination = Destination.Grade, + type = NotificationType.NEW_GRADE_DETAILS + ) + + appNotificationManager.sendMultipleNotifications(groupNotificationData, student) + } + + suspend fun notifyPredicted(items: List, student: Student) { + val notificationDataList = items.map { + NotificationData( + title = context.getPlural(R.plurals.grade_new_items_predicted, 1), + content = "${it.subject}: ${it.predictedGrade}", + destination = Destination.Grade, + ) + } + + val groupNotificationData = GroupNotificationData( + notificationDataList = notificationDataList, + title = context.getPlural(R.plurals.grade_new_items_predicted, items.size), + content = context.getPlural( + R.plurals.grade_notify_new_items_predicted, + items.size, + items.size + ), + destination = Destination.Grade, + type = NotificationType.NEW_GRADE_PREDICTED + ) + + appNotificationManager.sendMultipleNotifications(groupNotificationData, student) + } + + suspend fun notifyFinal(items: List, student: Student) { + val notificationDataList = items.map { + NotificationData( + title = context.getPlural(R.plurals.grade_new_items_final, 1), + content = "${it.subject}: ${it.finalGrade}", + destination = Destination.Grade, + ) + } + + val groupNotificationData = GroupNotificationData( + notificationDataList = notificationDataList, + title = context.getPlural(R.plurals.grade_new_items_final, items.size), + content = context.getPlural( + R.plurals.grade_notify_new_items_final, + items.size, + items.size + ), + destination = Destination.Grade, + type = NotificationType.NEW_GRADE_FINAL + ) + + appNotificationManager.sendMultipleNotifications(groupNotificationData, student) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewHomeworkNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewHomeworkNotification.kt new file mode 100644 index 000000000..856c51581 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewHomeworkNotification.kt @@ -0,0 +1,52 @@ +package io.github.wulkanowy.services.sync.notifications + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.pojos.GroupNotificationData +import io.github.wulkanowy.data.pojos.NotificationData +import io.github.wulkanowy.ui.modules.Destination +import io.github.wulkanowy.ui.modules.splash.SplashActivity +import io.github.wulkanowy.utils.getPlural +import io.github.wulkanowy.utils.toFormattedString +import java.time.LocalDate +import javax.inject.Inject + +class NewHomeworkNotification @Inject constructor( + private val appNotificationManager: AppNotificationManager, + @ApplicationContext private val context: Context +) { + + suspend fun notify(items: List, student: Student) { + val today = LocalDate.now() + val lines = items.filter { !it.date.isBefore(today) } + .map { + "${it.date.toFormattedString("dd.MM")} - ${it.subject}: ${it.content}" + } + .ifEmpty { return } + + val notificationDataList = lines.map { + NotificationData( + title = context.getPlural(R.plurals.homework_notify_new_item_title, 1), + content = it, + destination = Destination.Homework, + ) + } + + val groupNotificationData = GroupNotificationData( + title = context.getPlural(R.plurals.homework_notify_new_item_title, lines.size), + content = context.getPlural( + R.plurals.homework_notify_new_item_content, + lines.size, + lines.size + ), + destination = Destination.Homework, + type = NotificationType.NEW_HOMEWORK, + notificationDataList = notificationDataList + ) + + appNotificationManager.sendMultipleNotifications(groupNotificationData, student) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewLuckyNumberNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewLuckyNumberNotification.kt new file mode 100644 index 000000000..bbe9b8a18 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewLuckyNumberNotification.kt @@ -0,0 +1,34 @@ +package io.github.wulkanowy.services.sync.notifications + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.pojos.NotificationData +import io.github.wulkanowy.ui.modules.Destination +import io.github.wulkanowy.ui.modules.splash.SplashActivity +import javax.inject.Inject + +class NewLuckyNumberNotification @Inject constructor( + private val appNotificationManager: AppNotificationManager, + @ApplicationContext private val context: Context +) { + + suspend fun notify(item: LuckyNumber, student: Student) { + val notificationData = NotificationData( + title = context.getString(R.string.lucky_number_notify_new_item_title), + content = context.getString( + R.string.lucky_number_notify_new_item, + item.luckyNumber.toString() + ), + destination = Destination.LuckyNumber + ) + + appNotificationManager.sendSingleNotification( + notificationData = notificationData, + notificationType = NotificationType.NEW_LUCKY_NUMBER, + student = student + ) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt new file mode 100644 index 000000000..5c3c52c5b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt @@ -0,0 +1,39 @@ +package io.github.wulkanowy.services.sync.notifications + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.pojos.GroupNotificationData +import io.github.wulkanowy.data.pojos.NotificationData +import io.github.wulkanowy.ui.modules.Destination +import io.github.wulkanowy.ui.modules.splash.SplashActivity +import io.github.wulkanowy.utils.getPlural +import javax.inject.Inject + +class NewMessageNotification @Inject constructor( + private val appNotificationManager: AppNotificationManager, + @ApplicationContext private val context: Context +) { + + suspend fun notify(items: List, student: Student) { + val notificationDataList = items.map { + NotificationData( + title = context.getPlural(R.plurals.message_new_items, 1), + content = "${it.sender}: ${it.subject}", + destination = Destination.Message, + ) + } + + val groupNotificationData = GroupNotificationData( + notificationDataList = notificationDataList, + title = context.getPlural(R.plurals.message_new_items, items.size), + content = context.getPlural(R.plurals.message_notify_new_items, items.size, items.size), + destination = Destination.Message, + type = NotificationType.NEW_MESSAGE + ) + + appNotificationManager.sendMultipleNotifications(groupNotificationData, student) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewNoteNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewNoteNotification.kt new file mode 100644 index 000000000..dae7d4330 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewNoteNotification.kt @@ -0,0 +1,46 @@ +package io.github.wulkanowy.services.sync.notifications + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Note +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.pojos.GroupNotificationData +import io.github.wulkanowy.data.pojos.NotificationData +import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory +import io.github.wulkanowy.ui.modules.Destination +import io.github.wulkanowy.ui.modules.splash.SplashActivity +import io.github.wulkanowy.utils.getPlural +import javax.inject.Inject + +class NewNoteNotification @Inject constructor( + private val appNotificationManager: AppNotificationManager, + @ApplicationContext private val context: Context +) { + + suspend fun notify(items: List, student: Student) { + val notificationDataList = items.map { + val titleRes = when (NoteCategory.getByValue(it.categoryType)) { + NoteCategory.POSITIVE -> R.plurals.praise_new_items + NoteCategory.NEUTRAL -> R.plurals.neutral_note_new_items + else -> R.plurals.note_new_items + } + + NotificationData( + title = context.getPlural(titleRes, 1), + content = "${it.teacher}: ${it.category}", + destination = Destination.Note, + ) + } + + val groupNotificationData = GroupNotificationData( + notificationDataList = notificationDataList, + destination = Destination.Note, + title = context.getPlural(R.plurals.note_new_items, items.size), + content = context.getPlural(R.plurals.note_notify_new_items, items.size, items.size), + type = NotificationType.NEW_NOTE + ) + + appNotificationManager.sendMultipleNotifications(groupNotificationData, student) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewSchoolAnnouncementNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewSchoolAnnouncementNotification.kt new file mode 100644 index 000000000..cc7e46564 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewSchoolAnnouncementNotification.kt @@ -0,0 +1,49 @@ +package io.github.wulkanowy.services.sync.notifications + +import android.content.Context +import androidx.core.text.parseAsHtml +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.SchoolAnnouncement +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.pojos.GroupNotificationData +import io.github.wulkanowy.data.pojos.NotificationData +import io.github.wulkanowy.ui.modules.Destination +import io.github.wulkanowy.ui.modules.splash.SplashActivity +import io.github.wulkanowy.utils.getPlural +import javax.inject.Inject + +class NewSchoolAnnouncementNotification @Inject constructor( + private val appNotificationManager: AppNotificationManager, + @ApplicationContext private val context: Context +) { + + suspend fun notify(items: List, student: Student) { + val notificationDataList = items.map { + NotificationData( + destination = Destination.SchoolAnnouncement, + title = context.getPlural( + R.plurals.school_announcement_notify_new_item_title, + 1 + ), + content = "${it.subject}: ${it.content.parseAsHtml()}" + ) + } + val groupNotificationData = GroupNotificationData( + type = NotificationType.NEW_ANNOUNCEMENT, + destination = Destination.SchoolAnnouncement, + title = context.getPlural( + R.plurals.school_announcement_notify_new_item_title, + items.size + ), + content = context.getPlural( + R.plurals.school_announcement_notify_new_items, + items.size, + items.size + ), + notificationDataList = notificationDataList + ) + + appNotificationManager.sendMultipleNotifications(groupNotificationData, student) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NotificationType.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NotificationType.kt new file mode 100644 index 000000000..023ae2e4c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NotificationType.kt @@ -0,0 +1,72 @@ +package io.github.wulkanowy.services.sync.notifications + +import io.github.wulkanowy.R +import io.github.wulkanowy.services.sync.channels.LuckyNumberChannel +import io.github.wulkanowy.services.sync.channels.NewAttendanceChannel +import io.github.wulkanowy.services.sync.channels.NewConferencesChannel +import io.github.wulkanowy.services.sync.channels.NewExamChannel +import io.github.wulkanowy.services.sync.channels.NewGradesChannel +import io.github.wulkanowy.services.sync.channels.NewHomeworkChannel +import io.github.wulkanowy.services.sync.channels.NewMessagesChannel +import io.github.wulkanowy.services.sync.channels.NewNotesChannel +import io.github.wulkanowy.services.sync.channels.NewSchoolAnnouncementsChannel +import io.github.wulkanowy.services.sync.channels.PushChannel +import io.github.wulkanowy.services.sync.channels.TimetableChangeChannel + +enum class NotificationType( + val channel: String, + val icon: Int +) { + NEW_CONFERENCE( + channel = NewConferencesChannel.CHANNEL_ID, + icon = R.drawable.ic_more_conferences, + ), + NEW_EXAM( + channel = NewExamChannel.CHANNEL_ID, + icon = R.drawable.ic_main_exam + ), + NEW_GRADE_DETAILS( + channel = NewGradesChannel.CHANNEL_ID, + icon = R.drawable.ic_stat_grade, + ), + NEW_GRADE_PREDICTED( + channel = NewGradesChannel.CHANNEL_ID, + icon = R.drawable.ic_stat_grade, + ), + NEW_GRADE_FINAL( + channel = NewGradesChannel.CHANNEL_ID, + icon = R.drawable.ic_stat_grade, + ), + NEW_HOMEWORK( + channel = NewHomeworkChannel.CHANNEL_ID, + icon = R.drawable.ic_more_homework, + ), + NEW_LUCKY_NUMBER( + channel = LuckyNumberChannel.CHANNEL_ID, + icon = R.drawable.ic_stat_luckynumber, + ), + NEW_MESSAGE( + channel = NewMessagesChannel.CHANNEL_ID, + icon = R.drawable.ic_stat_message, + ), + NEW_NOTE( + channel = NewNotesChannel.CHANNEL_ID, + icon = R.drawable.ic_stat_note + ), + NEW_ANNOUNCEMENT( + channel = NewSchoolAnnouncementsChannel.CHANNEL_ID, + icon = R.drawable.ic_all_about + ), + CHANGE_TIMETABLE( + channel = TimetableChangeChannel.CHANNEL_ID, + icon = R.drawable.ic_main_timetable + ), + NEW_ATTENDANCE( + channel = NewAttendanceChannel.CHANNEL_ID, + icon = R.drawable.ic_main_attendance + ), + PUSH( + channel = PushChannel.CHANNEL_ID, + icon = R.drawable.ic_stat_all + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt new file mode 100644 index 000000000..55ce7e908 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt @@ -0,0 +1,21 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.AttendanceSummaryRepository +import io.github.wulkanowy.data.waitForResult +import javax.inject.Inject + +class AttendanceSummaryWork @Inject constructor( + private val attendanceSummaryRepository: AttendanceSummaryRepository +) : Work { + + override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + attendanceSummaryRepository.getAttendanceSummary( + student = student, + semester = semester, + subjectId = -1, + forceRefresh = true, + ).waitForResult() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt new file mode 100644 index 000000000..657f69638 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt @@ -0,0 +1,40 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.AttendanceRepository +import io.github.wulkanowy.data.waitForResult +import io.github.wulkanowy.services.sync.notifications.NewAttendanceNotification +import io.github.wulkanowy.utils.previousOrSameSchoolDay +import kotlinx.coroutines.flow.first +import java.time.LocalDate.now +import javax.inject.Inject + +class AttendanceWork @Inject constructor( + private val attendanceRepository: AttendanceRepository, + private val newAttendanceNotification: NewAttendanceNotification, +) : Work { + + override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + attendanceRepository.getAttendance( + student = student, + semester = semester, + start = now().previousOrSameSchoolDay, + end = now().previousOrSameSchoolDay, + forceRefresh = true, + notify = notify, + ) + .waitForResult() + + attendanceRepository.getAttendanceFromDatabase(semester, now().minusDays(7), now()) + .first() + .filterNot { it.isNotified } + .let { + if (it.isNotEmpty()) newAttendanceNotification.notify(it, student) + + attendanceRepository.updateTimetable(it.onEach { attendance -> + attendance.isNotified = true + }) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt new file mode 100644 index 000000000..f898aa04b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt @@ -0,0 +1,25 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.CompletedLessonsRepository +import io.github.wulkanowy.data.waitForResult +import io.github.wulkanowy.utils.monday +import io.github.wulkanowy.utils.sunday +import java.time.LocalDate.now +import javax.inject.Inject + +class CompletedLessonWork @Inject constructor( + private val completedLessonsRepository: CompletedLessonsRepository +) : Work { + + override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + completedLessonsRepository.getCompletedLessons( + student = student, + semester = semester, + start = now().monday, + end = now().sunday, + forceRefresh = true, + ).waitForResult() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/ConferenceWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/ConferenceWork.kt new file mode 100644 index 000000000..c85c00433 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/ConferenceWork.kt @@ -0,0 +1,33 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.ConferenceRepository +import io.github.wulkanowy.data.waitForResult +import io.github.wulkanowy.services.sync.notifications.NewConferenceNotification +import kotlinx.coroutines.flow.first +import javax.inject.Inject + +class ConferenceWork @Inject constructor( + private val conferenceRepository: ConferenceRepository, + private val newConferenceNotification: NewConferenceNotification, +) : Work { + + override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + conferenceRepository.getConferences( + student = student, + semester = semester, + forceRefresh = true, + notify = notify + ).waitForResult() + + conferenceRepository.getConferenceFromDatabase(semester).first() + .filter { !it.isNotified }.let { + if (it.isNotEmpty()) newConferenceNotification.notify(it, student) + + conferenceRepository.updateConference(it.onEach { conference -> + conference.isNotified = true + }) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt new file mode 100644 index 000000000..7071bce20 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt @@ -0,0 +1,34 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.ExamRepository +import io.github.wulkanowy.data.waitForResult +import io.github.wulkanowy.services.sync.notifications.NewExamNotification +import kotlinx.coroutines.flow.first +import java.time.LocalDate.now +import javax.inject.Inject + +class ExamWork @Inject constructor( + private val examRepository: ExamRepository, + private val newExamNotification: NewExamNotification, +) : Work { + + override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + examRepository.getExams( + student = student, + semester = semester, + start = now(), + end = now(), + forceRefresh = true, + notify = notify, + ).waitForResult() + + examRepository.getExamsFromDatabase(semester, now()).first() + .filter { !it.isNotified }.let { + if (it.isNotEmpty()) newExamNotification.notify(it, student) + + examRepository.updateExam(it.onEach { exam -> exam.isNotified = true }) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt new file mode 100644 index 000000000..ac35bc9a8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt @@ -0,0 +1,21 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.GradeStatisticsRepository +import io.github.wulkanowy.data.waitForResult + +import javax.inject.Inject + +class GradeStatisticsWork @Inject constructor( + private val gradeStatisticsRepository: GradeStatisticsRepository +) : Work { + + override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + with(gradeStatisticsRepository) { + getGradesPartialStatistics(student, semester, "Wszystkie", forceRefresh = true).waitForResult() + getGradesSemesterStatistics(student, semester, "Wszystkie", forceRefresh = true).waitForResult() + getGradesPointsStatistics(student, semester, "Wszystkie", forceRefresh = true).waitForResult() + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt new file mode 100644 index 000000000..ba21b8600 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt @@ -0,0 +1,49 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.GradeRepository +import io.github.wulkanowy.data.waitForResult +import io.github.wulkanowy.services.sync.notifications.NewGradeNotification +import kotlinx.coroutines.flow.first +import javax.inject.Inject + +class GradeWork @Inject constructor( + private val gradeRepository: GradeRepository, + private val newGradeNotification: NewGradeNotification, +) : Work { + + override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + gradeRepository.getGrades( + student = student, + semester = semester, + forceRefresh = true, + notify = notify, + ).waitForResult() + + gradeRepository.getGradesFromDatabase(semester).first() + .filter { !it.isNotified }.let { + if (it.isNotEmpty()) newGradeNotification.notifyDetails(it, student) + + gradeRepository.updateGrades(it.onEach { grade -> grade.isNotified = true }) + } + + gradeRepository.getGradesPredictedFromDatabase(semester).first() + .filter { !it.isPredictedGradeNotified }.let { + if (it.isNotEmpty()) newGradeNotification.notifyPredicted(it, student) + + gradeRepository.updateGradesSummary(it.onEach { grade -> + grade.isPredictedGradeNotified = true + }) + } + + gradeRepository.getGradesFinalFromDatabase(semester).first() + .filter { !it.isFinalGradeNotified }.let { + if (it.isNotEmpty()) newGradeNotification.notifyFinal(it, student) + + gradeRepository.updateGradesSummary(it.onEach { grade -> + grade.isFinalGradeNotified = true + }) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt new file mode 100644 index 000000000..4cfe27d0d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt @@ -0,0 +1,37 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.HomeworkRepository +import io.github.wulkanowy.data.waitForResult +import io.github.wulkanowy.services.sync.notifications.NewHomeworkNotification +import io.github.wulkanowy.utils.nextOrSameSchoolDay +import kotlinx.coroutines.flow.first +import java.time.LocalDate.now +import javax.inject.Inject + +class HomeworkWork @Inject constructor( + private val homeworkRepository: HomeworkRepository, + private val newHomeworkNotification: NewHomeworkNotification, +) : Work { + + override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + homeworkRepository.getHomework( + student = student, + semester = semester, + start = now().nextOrSameSchoolDay, + end = now().nextOrSameSchoolDay, + forceRefresh = true, + notify = notify, + ).waitForResult() + + homeworkRepository.getHomeworkFromDatabase(semester, now(), now().plusDays(7)).first() + .filter { !it.isNotified }.let { + if (it.isNotEmpty()) newHomeworkNotification.notify(it, student) + + homeworkRepository.updateHomework(it.onEach { homework -> + homework.isNotified = true + }) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt new file mode 100644 index 000000000..668b1b6b8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt @@ -0,0 +1,27 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.LuckyNumberRepository +import io.github.wulkanowy.data.waitForResult +import io.github.wulkanowy.services.sync.notifications.NewLuckyNumberNotification +import javax.inject.Inject + +class LuckyNumberWork @Inject constructor( + private val luckyNumberRepository: LuckyNumberRepository, + private val newLuckyNumberNotification: NewLuckyNumberNotification, +) : Work { + + override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + luckyNumberRepository.getLuckyNumber( + student = student, + forceRefresh = true, + notify = notify, + ).waitForResult() + + luckyNumberRepository.getNotNotifiedLuckyNumber(student)?.let { + newLuckyNumberNotification.notify(it, student) + luckyNumberRepository.updateLuckyNumber(it.apply { isNotified = true }) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt new file mode 100644 index 000000000..26fac1a2f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt @@ -0,0 +1,32 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED +import io.github.wulkanowy.data.repositories.MessageRepository +import io.github.wulkanowy.data.waitForResult +import io.github.wulkanowy.services.sync.notifications.NewMessageNotification +import kotlinx.coroutines.flow.first +import javax.inject.Inject + +class MessageWork @Inject constructor( + private val messageRepository: MessageRepository, + private val newMessageNotification: NewMessageNotification, +) : Work { + + override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + messageRepository.getMessages( + student = student, + semester = semester, + folder = RECEIVED, + forceRefresh = true, + notify = notify + ).waitForResult() + + messageRepository.getMessagesFromDatabase(student).first() + .filter { !it.isNotified && it.unread }.let { + if (it.isNotEmpty()) newMessageNotification.notify(it, student) + messageRepository.updateMessages(it.onEach { message -> message.isNotified = true }) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt new file mode 100644 index 000000000..df6e2b06b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt @@ -0,0 +1,32 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.NoteRepository +import io.github.wulkanowy.data.waitForResult +import io.github.wulkanowy.services.sync.notifications.NewNoteNotification +import kotlinx.coroutines.flow.first +import javax.inject.Inject + +class NoteWork @Inject constructor( + private val noteRepository: NoteRepository, + private val newNoteNotification: NewNoteNotification, +) : Work { + + override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + noteRepository.getNotes( + student = student, + semester = semester, + forceRefresh = true, + notify = notify, + ).waitForResult() + + noteRepository.getNotesFromDatabase(student).first() + .filter { !it.isNotified }.let { + if (it.isNotEmpty()) newNoteNotification.notify(it, student) + + noteRepository.updateNotes(it.onEach { note -> note.isNotified = true }) + } + } +} + diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt new file mode 100644 index 000000000..425e68b91 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt @@ -0,0 +1,23 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.RecipientRepository +import io.github.wulkanowy.data.repositories.ReportingUnitRepository +import javax.inject.Inject + +class RecipientWork @Inject constructor( + private val reportingUnitRepository: ReportingUnitRepository, + private val recipientRepository: RecipientRepository +) : Work { + + override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + reportingUnitRepository.refreshReportingUnits(student) + + reportingUnitRepository.getReportingUnits(student).let { units -> + units.map { + recipientRepository.refreshRecipients(student, it, 2) + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/SchoolAnnouncementWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/SchoolAnnouncementWork.kt new file mode 100644 index 000000000..805ceb3e4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/SchoolAnnouncementWork.kt @@ -0,0 +1,33 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.SchoolAnnouncementRepository +import io.github.wulkanowy.data.waitForResult +import io.github.wulkanowy.services.sync.notifications.NewSchoolAnnouncementNotification +import kotlinx.coroutines.flow.first +import javax.inject.Inject + +class SchoolAnnouncementWork @Inject constructor( + private val schoolAnnouncementRepository: SchoolAnnouncementRepository, + private val newSchoolAnnouncementNotification: NewSchoolAnnouncementNotification, +) : Work { + + override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + schoolAnnouncementRepository.getSchoolAnnouncements( + student = student, + forceRefresh = true, + notify = notify, + ).waitForResult() + + + schoolAnnouncementRepository.getSchoolAnnouncementFromDatabase(student).first() + .filter { !it.isNotified }.let { + if (it.isNotEmpty()) newSchoolAnnouncementNotification.notify(it, student) + + schoolAnnouncementRepository.updateSchoolAnnouncement(it.onEach { schoolAnnouncement -> + schoolAnnouncement.isNotified = true + }) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/TeacherWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/TeacherWork.kt new file mode 100644 index 000000000..e7c72bf00 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/TeacherWork.kt @@ -0,0 +1,15 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.TeacherRepository +import io.github.wulkanowy.data.waitForResult + +import javax.inject.Inject + +class TeacherWork @Inject constructor(private val teacherRepository: TeacherRepository) : Work { + + override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + teacherRepository.getTeachers(student, semester, true).waitForResult() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt new file mode 100644 index 000000000..29b1f13c7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt @@ -0,0 +1,40 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.TimetableRepository +import io.github.wulkanowy.data.waitForResult +import io.github.wulkanowy.services.sync.notifications.ChangeTimetableNotification +import io.github.wulkanowy.utils.nextOrSameSchoolDay +import kotlinx.coroutines.flow.first +import java.time.LocalDate.now +import javax.inject.Inject + +class TimetableWork @Inject constructor( + private val timetableRepository: TimetableRepository, + private val changeTimetableNotification: ChangeTimetableNotification, +) : Work { + + override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + timetableRepository.getTimetable( + student = student, + semester = semester, + start = now().nextOrSameSchoolDay, + end = now().nextOrSameSchoolDay, + forceRefresh = true, + notify = notify, + ) + .waitForResult() + + timetableRepository.getTimetableFromDatabase(semester, now(), now().plusDays(7)) + .first() + .filterNot { it.isNotified } + .let { + if (it.isNotEmpty()) changeTimetableNotification.notify(it, student) + + timetableRepository.updateTimetable(it.onEach { timetable -> + timetable.isNotified = true + }) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/Work.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/Work.kt new file mode 100644 index 000000000..1c0214cdd --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/Work.kt @@ -0,0 +1,9 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student + +interface Work { + + suspend fun doWork(student: Student, semester: Semester, notify: Boolean) +} diff --git a/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt b/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt new file mode 100644 index 000000000..45cd2b04e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt @@ -0,0 +1,37 @@ +package io.github.wulkanowy.services.widgets + +import android.content.Intent +import android.widget.RemoteViewsService +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.data.db.SharedPrefProvider +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.repositories.TimetableRepository +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetFactory +import timber.log.Timber +import javax.inject.Inject + +@AndroidEntryPoint +class TimetableWidgetService : RemoteViewsService() { + + @Inject + lateinit var timetableRepo: TimetableRepository + + @Inject + lateinit var studentRepo: StudentRepository + + @Inject + lateinit var semesterRepo: SemesterRepository + + @Inject + lateinit var prefRepository: PreferencesRepository + + @Inject + lateinit var sharedPref: SharedPrefProvider + + override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory { + Timber.d("TimetableWidgetFactory created") + return TimetableWidgetFactory(timetableRepo, studentRepo, semesterRepo, prefRepository, sharedPref, applicationContext, intent) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.java b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.java deleted file mode 100644 index c5c66df0c..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.java +++ /dev/null @@ -1,70 +0,0 @@ -package io.github.wulkanowy.ui.base; - -import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.app.AppCompatDelegate; -import android.widget.Toast; - -import butterknife.Unbinder; -import io.github.wulkanowy.R; -import io.github.wulkanowy.WulkanowyApp; -import io.github.wulkanowy.di.component.ActivityComponent; -import io.github.wulkanowy.di.component.DaggerActivityComponent; -import io.github.wulkanowy.di.modules.ActivityModule; -import io.github.wulkanowy.utils.NetworkUtils; - -public abstract class BaseActivity extends AppCompatActivity implements BaseContract.View { - - private ActivityComponent activityComponent; - - private Unbinder unbinder; - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); - - activityComponent = DaggerActivityComponent.builder() - .activityModule(new ActivityModule(this)) - .applicationComponent(((WulkanowyApp) getApplication()).getApplicationComponent()) - .build(); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - if (unbinder != null) { - unbinder.unbind(); - } - } - - @Override - public void onError(int resId) { - onError(getString(resId)); - } - - @Override - public void onError(String message) { - Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); - } - - @Override - public void onNoNetworkError() { - onError(R.string.noInternet_text); - } - - @Override - public boolean isNetworkConnected() { - return NetworkUtils.isOnline(getApplicationContext()); - } - - public ActivityComponent getActivityComponent() { - return activityComponent; - } - - public void setButterKnife(Unbinder unbinder) { - this.unbinder = unbinder; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt new file mode 100644 index 000000000..075557a5c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt @@ -0,0 +1,96 @@ +package io.github.wulkanowy.ui.base + +import android.app.ActivityManager +import android.os.Bundle +import android.view.View +import android.widget.Toast +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import androidx.viewbinding.ViewBinding +import com.google.android.material.snackbar.Snackbar +import com.google.android.material.snackbar.Snackbar.LENGTH_LONG +import io.github.wulkanowy.R +import io.github.wulkanowy.ui.modules.login.LoginActivity +import io.github.wulkanowy.utils.FragmentLifecycleLogger +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.lifecycleAwareVariable +import io.github.wulkanowy.utils.openInternetBrowser +import javax.inject.Inject + +abstract class BaseActivity, VB : ViewBinding> : + AppCompatActivity(), BaseView { + + protected var binding: VB by lifecycleAwareVariable() + + @Inject + lateinit var fragmentLifecycleLogger: FragmentLifecycleLogger + + @Inject + lateinit var themeManager: ThemeManager + + protected var messageContainer: View? = null + + abstract var presenter: T + + override fun onCreate(savedInstanceState: Bundle?) { + inject() + themeManager.applyActivityTheme(this) + super.onCreate(savedInstanceState) + supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleLogger, true) + + @Suppress("DEPRECATION") + setTaskDescription( + ActivityManager.TaskDescription(null, null, getThemeAttrColor(R.attr.colorSurface)) + ) + } + + override fun showError(text: String, error: Throwable) { + if (messageContainer != null) { + Snackbar.make(messageContainer!!, text, LENGTH_LONG) + .setAction(R.string.all_details) { showErrorDetailsDialog(error) } + .show() + } else showMessage(text) + } + + override fun showErrorDetailsDialog(error: Throwable) { + ErrorDialog.newInstance(error).show(supportFragmentManager, error.toString()) + } + + override fun showMessage(text: String) { + if (messageContainer != null) Snackbar.make(messageContainer!!, text, LENGTH_LONG).show() + else Toast.makeText(this, text, Toast.LENGTH_LONG).show() + } + + override fun showExpiredDialog() { + AlertDialog.Builder(this) + .setTitle(R.string.main_session_expired) + .setMessage(R.string.main_session_relogin) + .setPositiveButton(R.string.main_log_in) { _, _ -> presenter.onExpiredLoginSelected() } + .setNegativeButton(android.R.string.cancel) { _, _ -> } + .show() + } + + override fun showChangePasswordSnackbar(redirectUrl: String) { + messageContainer?.let { + Snackbar.make(it, R.string.error_password_change_required, LENGTH_LONG) + .setAction(R.string.all_change) { openInternetBrowser(redirectUrl) } + .show() + } + } + + override fun openClearLoginView() { + startActivity(LoginActivity.getStartIntent(this)) + finishAffinity() + } + + override fun onDestroy() { + super.onDestroy() + invalidateOptionsMenu() + presenter.onDetachView() + } + + //https://github.com/google/dagger/releases/tag/dagger-2.33 + protected open fun inject() { + throw UnsupportedOperationException() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseContract.java b/app/src/main/java/io/github/wulkanowy/ui/base/BaseContract.java deleted file mode 100644 index 2a4dc5696..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseContract.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.wulkanowy.ui.base; - -import android.support.annotation.StringRes; - -import io.github.wulkanowy.di.annotations.PerActivity; - -public interface BaseContract { - - interface View { - - void onError(@StringRes int resId); - - void onError(String message); - - void onNoNetworkError(); - - boolean isNetworkConnected(); - } - - @PerActivity - interface Presenter { - - void onStart(V view); - - void onDestroy(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt new file mode 100644 index 000000000..25a53395d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt @@ -0,0 +1,50 @@ +package io.github.wulkanowy.ui.base + +import android.widget.Toast +import androidx.fragment.app.DialogFragment +import androidx.viewbinding.ViewBinding +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.lifecycleAwareVariable +import javax.inject.Inject + +abstract class BaseDialogFragment : DialogFragment(), BaseView { + + @Inject + lateinit var analyticsHelper: AnalyticsHelper + + protected var binding: VB by lifecycleAwareVariable() + + override fun showError(text: String, error: Throwable) { + showMessage(text) + } + + override fun showMessage(text: String) { + Toast.makeText(context, text, Toast.LENGTH_LONG).show() + } + + override fun showExpiredDialog() { + (activity as? BaseActivity<*, *>)?.showExpiredDialog() + } + + override fun openClearLoginView() { + (activity as? BaseActivity<*, *>)?.openClearLoginView() + } + + override fun showChangePasswordSnackbar(redirectUrl: String) { + (activity as? BaseActivity<*, *>)?.showChangePasswordSnackbar(redirectUrl) + } + + override fun showErrorDetailsDialog(error: Throwable) { + ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) + } + + override fun onResume() { + super.onResume() + analyticsHelper.setCurrentScreen(requireActivity(), this::class.simpleName) + } + + override fun onPause() { + super.onPause() + analyticsHelper.popCurrentScreen(this::class.simpleName) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseExpandableAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseExpandableAdapter.kt new file mode 100644 index 000000000..eee4625c9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseExpandableAdapter.kt @@ -0,0 +1,58 @@ +package io.github.wulkanowy.ui.base + +import android.util.DisplayMetrics +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.LinearSmoothScroller +import androidx.recyclerview.widget.RecyclerView +import kotlin.math.max +import kotlin.math.min + +abstract class BaseExpandableAdapter : RecyclerView.Adapter() { + + companion object { + private const val MILLISECONDS_PER_INCH = 100f + private const val AUTO_SCROLL_DELAY = 150L + } + + private var recyclerView: RecyclerView? = null + + override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { + super.onAttachedToRecyclerView(recyclerView) + this.recyclerView = recyclerView + } + + override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { + super.onDetachedFromRecyclerView(recyclerView) + this.recyclerView = null + } + + // original: https://github.com/davideas/FlexibleAdapter/blob/5.1.0/flexible-adapter/src/main/java/eu/davidea/flexibleadapter/FlexibleAdapter.java#L4984-L5011 + protected fun scrollToHeaderWithSubItems(position: Int, subItemsCount: Int) { + val layoutManager = recyclerView!!.layoutManager as LinearLayoutManager + val firstVisibleItem = layoutManager.findFirstCompletelyVisibleItemPosition() + val lastVisibleItem = layoutManager.findLastCompletelyVisibleItemPosition() + val itemsToShow = position + subItemsCount - lastVisibleItem + if (itemsToShow > 0) { + val scrollMax: Int = position - firstVisibleItem + val scrollMin = max(0, position + subItemsCount - lastVisibleItem) + val scrollBy = min(scrollMax, scrollMin) + val scrollTo = firstVisibleItem + scrollBy + scrollToPosition(scrollTo) + } else if (position < firstVisibleItem) { + scrollToPosition(position) + } + } + + private fun scrollToPosition(position: Int) { + recyclerView?.run { + postDelayed({ + layoutManager?.startSmoothScroll(object : LinearSmoothScroller(context) { + override fun getVerticalSnapPreference() = SNAP_TO_START + override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics) = MILLISECONDS_PER_INCH / displayMetrics.densityDpi + }.apply { + targetPosition = position + }) + }, AUTO_SCROLL_DELAY) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.java b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.java deleted file mode 100644 index efb9d61a9..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.java +++ /dev/null @@ -1,104 +0,0 @@ -package io.github.wulkanowy.ui.base; - -import android.content.Context; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.view.View; - -import butterknife.Unbinder; -import io.github.wulkanowy.R; -import io.github.wulkanowy.WulkanowyApp; -import io.github.wulkanowy.di.component.DaggerFragmentComponent; -import io.github.wulkanowy.di.component.FragmentComponent; -import io.github.wulkanowy.di.modules.FragmentModule; - -public abstract class BaseFragment extends Fragment implements BaseContract.View { - - private BaseActivity activity; - - private Unbinder unbinder; - - private FragmentComponent fragmentComponent; - - @Override - public void onAttach(Context context) { - super.onAttach(context); - if (context instanceof BaseActivity) { - activity = (BaseActivity) context; - } - - fragmentComponent = DaggerFragmentComponent.builder() - .fragmentModule(new FragmentModule(this)) - .applicationComponent(((WulkanowyApp) activity.getApplication()).getApplicationComponent()) - .build(); - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setHasOptionsMenu(false); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setUpOnViewCreated(view); - } - - @Override - public void onDetach() { - activity = null; - super.onDetach(); - } - - @Override - public void onDestroyView() { - if (unbinder != null) { - unbinder.unbind(); - } - super.onDestroyView(); - } - - @Override - public void onError(int resId) { - onError(getString(resId)); - } - - @Override - public void onError(String message) { - if (activity != null) { - activity.onError(message); - } - } - - @Override - public void onNoNetworkError() { - onError(R.string.noInternet_text); - } - - @Override - public boolean isNetworkConnected() { - return activity != null && activity.isNetworkConnected(); - } - - public void setButterKnife(Unbinder unbinder) { - this.unbinder = unbinder; - } - - public void setTitle(String title) { - if (activity != null) { - activity.setTitle(title); - } - } - - public FragmentComponent getFragmentComponent() { - return fragmentComponent; - } - - - protected void setUpOnViewCreated(View fragmentView) { - // do something on view created - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt new file mode 100644 index 000000000..dbc5af3a9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt @@ -0,0 +1,52 @@ +package io.github.wulkanowy.ui.base + +import android.view.View +import androidx.annotation.LayoutRes +import androidx.fragment.app.Fragment +import androidx.viewbinding.ViewBinding +import com.google.android.material.snackbar.Snackbar +import com.google.android.material.snackbar.Snackbar.LENGTH_LONG +import io.github.wulkanowy.R +import io.github.wulkanowy.utils.lifecycleAwareVariable + +abstract class BaseFragment(@LayoutRes layoutId: Int) : Fragment(layoutId), + BaseView { + + protected var binding: VB by lifecycleAwareVariable() + + protected var messageContainer: View? = null + + override fun showError(text: String, error: Throwable) { + if (messageContainer != null) { + Snackbar.make(messageContainer!!, text, LENGTH_LONG) + .setAction(R.string.all_details) { if (isAdded) showErrorDetailsDialog(error) } + .show() + } else { + (activity as? BaseActivity<*, *>)?.showError(text, error) + } + } + + override fun showErrorDetailsDialog(error: Throwable) { + ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) + } + + override fun showMessage(text: String) { + if (messageContainer != null) { + Snackbar.make(messageContainer!!, text, LENGTH_LONG).show() + } else { + (activity as? BaseActivity<*, *>)?.showMessage(text) + } + } + + override fun showExpiredDialog() { + (activity as? BaseActivity<*, *>)?.showExpiredDialog() + } + + override fun openClearLoginView() { + (activity as? BaseActivity<*, *>)?.openClearLoginView() + } + + override fun showChangePasswordSnackbar(redirectUrl: String) { + (activity as? BaseActivity<*, *>)?.showChangePasswordSnackbar(redirectUrl) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt new file mode 100644 index 000000000..6bca87f15 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt @@ -0,0 +1,34 @@ +package io.github.wulkanowy.ui.base + +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.lifecycle.Lifecycle +import androidx.viewpager2.adapter.FragmentStateAdapter +import com.google.android.material.tabs.TabLayout +import com.google.android.material.tabs.TabLayoutMediator + +class BaseFragmentPagerAdapter( + private val fragmentManager: FragmentManager, + private val pagesCount: Int, + lifecycle: Lifecycle, +) : FragmentStateAdapter(fragmentManager, lifecycle), TabLayoutMediator.TabConfigurationStrategy { + + lateinit var itemFactory: (position: Int) -> Fragment + + var titleFactory: (position: Int) -> String? = { "" } + + var containerId = 0 + + fun getFragmentInstance(position: Int): Fragment? { + require(containerId != 0) { "Container id is 0" } + return fragmentManager.findFragmentByTag("f$position") + } + + override fun createFragment(position: Int): Fragment = itemFactory(position) + + override fun getItemCount() = pagesCount + + override fun onConfigureTab(tab: TabLayout.Tab, position: Int) { + tab.text = titleFactory(position) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.java b/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.java deleted file mode 100644 index 611b7a29e..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.wulkanowy.ui.base; - -import javax.inject.Inject; - -import io.github.wulkanowy.data.RepositoryContract; - -public class BasePresenter implements BaseContract.Presenter { - - private final RepositoryContract repository; - - private V view; - - @Inject - public BasePresenter(RepositoryContract repository) { - this.repository = repository; - } - - @Override - public void onStart(V view) { - this.view = view; - } - - @Override - public void onDestroy() { - view = null; - } - - protected boolean isViewAttached() { - return view != null; - } - - public final RepositoryContract getRepository() { - return repository; - } - - public V getView() { - return view; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt new file mode 100644 index 000000000..15c069f54 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt @@ -0,0 +1,76 @@ +package io.github.wulkanowy.ui.base + +import io.github.wulkanowy.data.repositories.StudentRepository +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.launchIn +import timber.log.Timber + +open class BasePresenter( + protected val errorHandler: ErrorHandler, + protected val studentRepository: StudentRepository +) { + private val job = SupervisorJob() + + protected val presenterScope = CoroutineScope(job + Dispatchers.Main) + + private val childrenJobs = mutableMapOf() + + var view: T? = null + + open fun onAttachView(view: T) { + this.view = view + errorHandler.apply { + showErrorMessage = view::showError + onSessionExpired = view::showExpiredDialog + onNoCurrentStudent = view::openClearLoginView + onPasswordChangeRequired = view::showChangePasswordSnackbar + } + } + + fun onExpiredLoginSelected() { + Timber.i("Attempt to switch the student after the session expires") + + presenterScope.launch { + runCatching { + val student = studentRepository.getCurrentStudent(false) + studentRepository.logoutStudent(student) + + val students = studentRepository.getSavedStudents(false) + if (students.isNotEmpty()) { + Timber.i("Switching current student") + studentRepository.switchStudent(students[0]) + } + } + .onFailure { + Timber.i("Switch student result: An exception occurred") + errorHandler.dispatch(it) + } + .onSuccess { + Timber.i("Switch student result: Open login view") + view?.openClearLoginView() + } + } + } + + fun Flow.launch(individualJobTag: String = "load"): Job { + childrenJobs[individualJobTag]?.cancel() + val job = catch { errorHandler.dispatch(it) }.launchIn(presenterScope) + childrenJobs[individualJobTag] = job + Timber.d("Job $individualJobTag launched in ${this@BasePresenter.javaClass.simpleName}: $job") + return job + } + + fun cancelJobs(vararg names: String) { + names.forEach { + childrenJobs[it]?.cancel() + } + } + + open fun onDetachView() { + job.cancelChildren() + errorHandler.clear() + view = null + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt new file mode 100644 index 000000000..d3165ea44 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt @@ -0,0 +1,16 @@ +package io.github.wulkanowy.ui.base + +interface BaseView { + + fun showError(text: String, error: Throwable) + + fun showMessage(text: String) + + fun showExpiredDialog() + + fun openClearLoginView() + + fun showErrorDetailsDialog(error: Throwable) + + fun showChangePasswordSnackbar(redirectUrl: String) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt new file mode 100644 index 000000000..48c003b7e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt @@ -0,0 +1,116 @@ +package io.github.wulkanowy.ui.base + +import android.app.Dialog +import android.content.ClipData +import android.content.ClipboardManager +import android.os.Bundle +import android.view.LayoutInflater +import android.widget.Toast +import android.widget.Toast.LENGTH_LONG +import androidx.appcompat.app.AlertDialog +import androidx.core.content.getSystemService +import androidx.core.os.bundleOf +import androidx.core.view.isGone +import androidx.fragment.app.DialogFragment +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.databinding.DialogErrorBinding +import io.github.wulkanowy.utils.* +import javax.inject.Inject + +@AndroidEntryPoint +class ErrorDialog : DialogFragment() { + + @Inject + lateinit var appInfo: AppInfo + + companion object { + private const val ARGUMENT_KEY = "error" + + fun newInstance(error: Throwable) = ErrorDialog().apply { + arguments = bundleOf(ARGUMENT_KEY to error) + } + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val error = requireArguments().getSerializable(ARGUMENT_KEY) as Throwable + + val binding = DialogErrorBinding.inflate(LayoutInflater.from(context)) + binding.bindErrorDetails(error) + + return getAlertDialog(binding, error).apply { + enableReportButtonIfErrorIsReportable(error) + } + } + + private fun getAlertDialog(binding: DialogErrorBinding, error: Throwable): AlertDialog { + return MaterialAlertDialogBuilder(requireContext()).apply { + val errorStacktrace = error.stackTraceToString() + setTitle(R.string.all_details) + setView(binding.root) + setNeutralButton(R.string.about_feedback) { _, _ -> + openConfirmDialog { openEmailClient(errorStacktrace) } + } + setNegativeButton(android.R.string.cancel) { _, _ -> } + setPositiveButton(android.R.string.copy) { _, _ -> copyErrorToClipboard(errorStacktrace) } + }.create() + } + + private fun DialogErrorBinding.bindErrorDetails(error: Throwable) { + return with(this) { + errorDialogHumanizedMessage.text = resources.getErrorString(error) + errorDialogErrorMessage.text = error.localizedMessage + errorDialogErrorMessage.isGone = error.localizedMessage.isNullOrBlank() + errorDialogContent.text = error.stackTraceToString() + .replace(": ${error.localizedMessage}", "") + } + } + + private fun AlertDialog.enableReportButtonIfErrorIsReportable(error: Throwable) { + setOnShowListener { + getButton(AlertDialog.BUTTON_NEUTRAL).isEnabled = error.isShouldBeReported() + } + } + + private fun copyErrorToClipboard(errorStacktrace: String) { + val clip = ClipData.newPlainText("Error details", errorStacktrace) + requireActivity().getSystemService()?.setPrimaryClip(clip) + Toast.makeText(requireContext(), R.string.all_copied, LENGTH_LONG).show() + } + + private fun openConfirmDialog(callback: () -> Unit) { + AlertDialog.Builder(requireContext()) + .setTitle(R.string.dialog_error_check_update) + .setMessage(R.string.dialog_error_check_update_message) + .setNeutralButton(R.string.about_feedback) { _, _ -> callback() } + .setPositiveButton(R.string.dialog_error_check_update) { _, _ -> + requireContext().openAppInMarket(::showMessage) + } + .show() + } + + private fun openEmailClient(content: String) { + requireContext().openEmailClient( + chooserTitle = getString(R.string.about_feedback), + email = "wulkanowyinc@gmail.com", + subject = "Zgłoszenie błędu", + body = requireContext().getString( + R.string.about_feedback_template, + "${appInfo.systemManufacturer} ${appInfo.systemModel}", + appInfo.systemVersion.toString(), + "${appInfo.versionName}-${appInfo.buildFlavor}" + ) + "\n" + content, + onActivityNotFound = { + requireContext().openInternetBrowser( + "https://github.com/wulkanowy/wulkanowy/issues", + ::showMessage + ) + } + ) + } + + private fun showMessage(text: String) { + Toast.makeText(requireContext(), text, LENGTH_LONG).show() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt new file mode 100644 index 000000000..afe200e9a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt @@ -0,0 +1,43 @@ +package io.github.wulkanowy.ui.base + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.data.exceptions.NoCurrentStudentException +import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException +import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException +import io.github.wulkanowy.utils.getErrorString +import io.github.wulkanowy.utils.security.ScramblerException +import timber.log.Timber +import javax.inject.Inject + +open class ErrorHandler @Inject constructor(@ApplicationContext protected val context: Context) { + + var showErrorMessage: (String, Throwable) -> Unit = { _, _ -> } + + var onSessionExpired: () -> Unit = {} + + var onNoCurrentStudent: () -> Unit = {} + + var onPasswordChangeRequired: (String) -> Unit = {} + + fun dispatch(error: Throwable) { + Timber.e(error, "An exception occurred while the Wulkanowy was running") + proceed(error) + } + + protected open fun proceed(error: Throwable) { + showErrorMessage(context.resources.getErrorString(error), error) + when (error) { + is PasswordChangeRequiredException -> onPasswordChangeRequired(error.redirectUrl) + is ScramblerException, is BadCredentialsException -> onSessionExpired() + is NoCurrentStudentException -> onNoCurrentStudent() + } + } + + open fun clear() { + showErrorMessage = { _, _ -> } + onSessionExpired = {} + onNoCurrentStudent = {} + onPasswordChangeRequired = {} + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt new file mode 100644 index 000000000..2d83bbbf9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt @@ -0,0 +1,55 @@ +package io.github.wulkanowy.ui.base + +import android.content.pm.PackageManager.GET_ACTIVITIES +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.app.AppCompatDelegate +import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM +import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO +import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES +import io.github.wulkanowy.R +import io.github.wulkanowy.data.enums.AppTheme +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.ui.modules.login.LoginActivity +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ThemeManager @Inject constructor(private val preferencesRepository: PreferencesRepository) { + + fun applyActivityTheme(activity: AppCompatActivity) { + if (isThemeApplicable(activity)) { + applyDefaultTheme() + if (preferencesRepository.appTheme == AppTheme.BLACK) { + when (activity) { + is MainActivity -> activity.setTheme(R.style.WulkanowyTheme_Black) + is LoginActivity -> activity.setTheme(R.style.WulkanowyTheme_Login_Black) + is SendMessageActivity -> activity.setTheme(R.style.WulkanowyTheme_MessageSend_Black) + } + } + } + } + + fun applyDefaultTheme() { + AppCompatDelegate.setDefaultNightMode( + when (preferencesRepository.appTheme) { + AppTheme.LIGHT -> MODE_NIGHT_NO + AppTheme.DARK, AppTheme.BLACK -> MODE_NIGHT_YES + AppTheme.SYSTEM -> MODE_NIGHT_FOLLOW_SYSTEM + } + ) + } + + private fun isThemeApplicable(activity: AppCompatActivity) = + activity.packageManager + .getPackageInfo(activity.packageName, GET_ACTIVITIES) + .activities + .singleOrNull { it.name == activity::class.java.canonicalName } + ?.theme + .let { + it == R.style.WulkanowyTheme_Black || it == R.style.WulkanowyTheme_NoActionBar + || it == R.style.WulkanowyTheme_Login || it == R.style.WulkanowyTheme_Login_Black + || it == R.style.WulkanowyTheme_MessageSend || it == R.style.WulkanowyTheme_MessageSend_Black + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/WidgetConfigureAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/WidgetConfigureAdapter.kt new file mode 100644 index 000000000..a43aaffb7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/WidgetConfigureAdapter.kt @@ -0,0 +1,68 @@ +package io.github.wulkanowy.ui.base + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.databinding.ItemAccountBinding +import io.github.wulkanowy.utils.createNameInitialsDrawable +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.nickOrName +import javax.inject.Inject + +class WidgetConfigureAdapter @Inject constructor() : + RecyclerView.Adapter() { + + var items = emptyList() + + var selectedId = -1L + + var onClickListener: (Student) -> Unit = {} + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + ItemAccountBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + @SuppressLint("SetTextI18n") + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + val (student, semesters) = items[position] + val semester = semesters.maxByOrNull { it.semesterId } + val context = holder.binding.root.context + val checkBackgroundColor = context.getThemeAttrColor(R.attr.colorSurface) + val avatar = context.createNameInitialsDrawable(student.nickOrName, student.avatarColor) + val isDuplicatedStudent = items.filter { + val studentToCompare = it.student + + studentToCompare.studentId == student.studentId + && studentToCompare.schoolSymbol == student.schoolSymbol + && studentToCompare.symbol == student.symbol + }.size > 1 + + with(holder.binding) { + accountItemName.text = "${student.nickOrName} ${semester?.diaryName.orEmpty()}" + accountItemSchool.text = student.schoolName + accountItemImage.setImageDrawable(avatar) + + with(accountItemAccountType) { + setText(if (student.isParent) R.string.account_type_parent else R.string.account_type_student) + isVisible = isDuplicatedStudent + } + + with(accountItemCheck) { + isVisible = student.id == selectedId + borderColor = checkBackgroundColor + circleColor = checkBackgroundColor + } + + root.setOnClickListener { onClickListener(student) } + } + } + + class ItemViewHolder(val binding: ItemAccountBinding) : RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/login/LoginActivity.java b/app/src/main/java/io/github/wulkanowy/ui/login/LoginActivity.java deleted file mode 100644 index 95c5548b2..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/login/LoginActivity.java +++ /dev/null @@ -1,221 +0,0 @@ -package io.github.wulkanowy.ui.login; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.support.design.widget.Snackbar; -import android.support.design.widget.TextInputLayout; -import android.view.View; -import android.view.inputmethod.EditorInfo; -import android.widget.ArrayAdapter; -import android.widget.AutoCompleteTextView; -import android.widget.EditText; -import android.widget.TextView; - -import javax.inject.Inject; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import butterknife.OnEditorAction; -import io.github.wulkanowy.R; -import io.github.wulkanowy.ui.base.BaseActivity; -import io.github.wulkanowy.ui.main.MainActivity; -import io.github.wulkanowy.utils.AppConstant; -import io.github.wulkanowy.utils.CommonUtils; -import io.github.wulkanowy.utils.KeyboardUtils; - -public class LoginActivity extends BaseActivity implements LoginContract.View { - - @BindView(R.id.login_activity_email_edit) - EditText emailView; - - @BindView(R.id.login_activity_pass_edit) - EditText passwordView; - - @BindView(R.id.login_activity_symbol_edit) - AutoCompleteTextView symbolView; - - @BindView(R.id.login_activity_form_scroll) - View loginFormView; - - @BindView(R.id.login_activity_progress_container) - View loadingBarView; - - @BindView(R.id.login_activity_progress_text) - TextView loginProgressText; - - @BindView(R.id.login_activity_symbol_text_input) - TextInputLayout symbolLayout; - - @Inject - LoginContract.Presenter presenter; - - private EditText requestedView; - - public static Intent getStartIntent(Context context) { - return new Intent(context, LoginActivity.class); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_login); - - setButterKnife(ButterKnife.bind(this)); - getActivityComponent().inject(this); - - presenter.onStart(this); - - setUpOnCreate(); - - } - - protected void setUpOnCreate() { - symbolView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, - getResources().getStringArray(R.array.symbols))); - } - - @OnClick(R.id.login_activity_sign_button) - void onLoginButtonClick() { - presenter.attemptLogin( - emailView.getText().toString(), - passwordView.getText().toString(), - symbolView.getText().toString()); - } - - @OnEditorAction(value = {R.id.login_activity_symbol_edit, R.id.login_activity_pass_edit}) - boolean onEditorAction(int id) { - if (id == EditorInfo.IME_ACTION_DONE || id == EditorInfo.IME_NULL) { - onLoginButtonClick(); - return true; - } - return false; - } - - @OnClick(R.id.login_activity_create_text) - void onCreateAccountButtonClick() { - CommonUtils.openInternalBrowserViewer(this, AppConstant.VULCAN_CREATE_ACCOUNT_URL); - } - - @OnClick(R.id.login_activity_forgot_text) - void onForgotPasswordButtonClick() { - CommonUtils.openInternalBrowserViewer(this, AppConstant.VULCAN_FORGOT_PASS_URL); - } - - @Override - public void setErrorEmailRequired() { - emailView.requestFocus(); - emailView.setError(getString(R.string.error_field_required)); - requestedView = emailView; - } - - @Override - public void setErrorEmailInvalid() { - emailView.requestFocus(); - emailView.setError(getString(R.string.error_invalid_email)); - requestedView = emailView; - } - - @Override - public void setErrorPassRequired() { - passwordView.requestFocus(); - passwordView.setError(getString(R.string.error_field_required)); - requestedView = passwordView; - } - - @Override - public void setErrorPassInvalid() { - passwordView.requestFocus(); - passwordView.setError(getString(R.string.error_invalid_password)); - requestedView = passwordView; - } - - @Override - public void setErrorPassIncorrect() { - passwordView.requestFocus(); - passwordView.setError(getString(R.string.error_incorrect_password)); - requestedView = passwordView; - } - - @Override - public void setErrorSymbolRequired() { - symbolLayout.setVisibility(View.VISIBLE); - symbolView.setError(getString(R.string.error_bad_account_permission)); - symbolView.requestFocus(); - requestedView = symbolView; - } - - @Override - public void resetViewErrors() { - emailView.setError(null); - passwordView.setError(null); - } - - @Override - public void showSoftInput() { - KeyboardUtils.showSoftInput(requestedView, this); - } - - @Override - public void hideSoftInput() { - KeyboardUtils.hideSoftInput(this); - } - - @Override - public void onError(String message) { - Snackbar.make(findViewById(R.id.login_activity_container), message, - Snackbar.LENGTH_LONG).show(); - } - - @Override - public void setStepOneLoginProgress() { - onLoginProgressUpdate("1", getString(R.string.step_login)); - } - - @Override - public void setStepTwoLoginProgress() { - onLoginProgressUpdate("2", getString(R.string.step_synchronization)); - } - - @Override - public void openMainActivity() { - startActivity(MainActivity.getStartIntent(this)); - finish(); - } - - @Override - public void showLoginProgress(final boolean show) { - int animTime = getResources().getInteger(android.R.integer.config_shortAnimTime); - - loginFormView.setVisibility(show ? View.GONE : View.VISIBLE); - loginFormView.animate().setDuration(animTime).alpha( - show ? 0 : 1).setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - loginFormView.setVisibility(show ? View.GONE : View.VISIBLE); - } - }); - - loadingBarView.setVisibility(show ? View.VISIBLE : View.GONE); - loadingBarView.animate().setDuration(animTime).alpha( - show ? 1 : 0).setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - loadingBarView.setVisibility(show ? View.VISIBLE : View.GONE); - } - }); - } - - @Override - public void onDestroy() { - super.onDestroy(); - presenter.onDestroy(); - } - - private void onLoginProgressUpdate(String step, String message) { - loginProgressText.setText(String.format("%1$s/2 - %2$s...", step, message)); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/login/LoginContract.java b/app/src/main/java/io/github/wulkanowy/ui/login/LoginContract.java deleted file mode 100644 index debfbbde1..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/login/LoginContract.java +++ /dev/null @@ -1,55 +0,0 @@ -package io.github.wulkanowy.ui.login; - -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.di.annotations.PerActivity; -import io.github.wulkanowy.ui.base.BaseContract; - -public interface LoginContract { - interface View extends BaseContract.View { - - void setErrorEmailRequired(); - - void setErrorPassRequired(); - - void setErrorSymbolRequired(); - - void setErrorEmailInvalid(); - - void setErrorPassInvalid(); - - void setErrorPassIncorrect(); - - void resetViewErrors(); - - void setStepOneLoginProgress(); - - void setStepTwoLoginProgress(); - - void openMainActivity(); - - void showLoginProgress(boolean show); - - void showSoftInput(); - - void hideSoftInput(); - - } - - @PerActivity - interface Presenter extends BaseContract.Presenter { - - void attemptLogin(String email, String password, String symbol); - - void onStartAsync(); - - void onDoInBackground(int stepNumber) throws Exception; - - void onLoginProgress(int step); - - void onEndAsync(boolean success, Exception exception); - - void onCanceledAsync(); - - RepositoryContract getRepository(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/login/LoginPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/login/LoginPresenter.java deleted file mode 100644 index 2b6d173d0..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/login/LoginPresenter.java +++ /dev/null @@ -1,165 +0,0 @@ -package io.github.wulkanowy.ui.login; - -import android.text.TextUtils; - -import java.util.LinkedHashMap; - -import javax.inject.Inject; - -import io.github.wulkanowy.api.login.AccountPermissionException; -import io.github.wulkanowy.api.login.BadCredentialsException; -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.ui.base.BasePresenter; -import io.github.wulkanowy.utils.AppConstant; - -public class LoginPresenter extends BasePresenter - implements LoginContract.Presenter { - - private LoginTask loginAsync; - - private String email; - - private String password; - - private String symbol; - - @Inject - LoginPresenter(RepositoryContract repository) { - super(repository); - } - - @Override - public void attemptLogin(String email, String password, String symbol) { - getView().resetViewErrors(); - - this.email = email; - this.password = password; - this.symbol = getNormalizedSymbol(symbol); - - if (!isAllFieldCorrect(password, email)) { - getView().showSoftInput(); - return; - } - - if (getView().isNetworkConnected()) { - loginAsync = new LoginTask(this); - loginAsync.execute(); - - } else { - getView().onNoNetworkError(); - } - - getView().hideSoftInput(); - } - - @Override - public void onStartAsync() { - if (isViewAttached()) { - getView().showLoginProgress(true); - } - } - - @Override - public void onDoInBackground(int stepNumber) throws Exception { - switch (stepNumber) { - case 1: - getRepository().registerUser(email, password, symbol); - break; - case 2: - getRepository().syncAll(); - break; - } - } - - @Override - public void onLoginProgress(int step) { - if (step == 1) { - getView().setStepOneLoginProgress(); - } else if (step == 2) { - getView().setStepTwoLoginProgress(); - } - } - - @Override - public void onEndAsync(boolean success, Exception exception) { - if (success) { - getView().openMainActivity(); - } else if (exception instanceof BadCredentialsException) { - getView().setErrorPassIncorrect(); - getView().showSoftInput(); - getView().showLoginProgress(false); - } else if (exception instanceof AccountPermissionException) { - getView().setErrorSymbolRequired(); - getView().showSoftInput(); - getView().showLoginProgress(false); - } else { - getView().onError(getRepository().getErrorLoginMessage(exception)); - getView().showLoginProgress(false); - } - - } - - @Override - public void onCanceledAsync() { - if (isViewAttached()) { - getView().showLoginProgress(false); - } - } - - private boolean isEmailValid(String email) { - return email.contains("@") || email.contains("\\\\"); - } - - private boolean isPasswordValid(String password) { - return password.length() > 4; - } - - private String getNormalizedSymbol(String symbol) { - if (TextUtils.isEmpty(symbol)) { - return AppConstant.DEFAULT_SYMBOL; - } - - String[] keys = getRepository().getSymbolsKeysArray(); - String[] values = getRepository().getSymbolsValuesArray(); - LinkedHashMap map = new LinkedHashMap<>(); - - for (int i = 0; i < Math.min(keys.length, values.length); ++i) { - map.put(keys[i], values[i]); - } - - if (map.containsKey(symbol)) { - return map.get(symbol); - } - return AppConstant.DEFAULT_SYMBOL; - } - - private boolean isAllFieldCorrect(String password, String email) { - boolean correct = true; - - if (TextUtils.isEmpty(password)) { - getView().setErrorPassRequired(); - correct = false; - } else if (!isPasswordValid(password)) { - getView().setErrorPassInvalid(); - correct = false; - } - - if (TextUtils.isEmpty(email)) { - getView().setErrorEmailRequired(); - correct = false; - } else if (!isEmailValid(email)) { - getView().setErrorEmailInvalid(); - correct = false; - } - return correct; - } - - @Override - public void onDestroy() { - if (loginAsync != null) { - loginAsync.cancel(true); - loginAsync = null; - } - super.onDestroy(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/login/LoginTask.java b/app/src/main/java/io/github/wulkanowy/ui/login/LoginTask.java deleted file mode 100644 index b22194da9..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/login/LoginTask.java +++ /dev/null @@ -1,49 +0,0 @@ -package io.github.wulkanowy.ui.login; - -import android.os.AsyncTask; - -public class LoginTask extends AsyncTask { - - private LoginContract.Presenter presenter; - - private Exception exception; - - LoginTask(LoginContract.Presenter presenter) { - this.presenter = presenter; - } - - @Override - protected void onPreExecute() { - presenter.onStartAsync(); - } - - @Override - protected Boolean doInBackground(Void... params) { - try { - publishProgress(1); - presenter.onDoInBackground(1); - - publishProgress(2); - presenter.onDoInBackground(2); - } catch (Exception e) { - exception = e; - return false; - } - return true; - } - - @Override - protected void onProgressUpdate(Integer... progress) { - presenter.onLoginProgress(progress[0]); - } - - @Override - protected void onPostExecute(Boolean success) { - presenter.onEndAsync(success, exception); - } - - @Override - protected void onCancelled() { - presenter.onCanceledAsync(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/MainActivity.java b/app/src/main/java/io/github/wulkanowy/ui/main/MainActivity.java deleted file mode 100644 index cc9b1dc8a..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/MainActivity.java +++ /dev/null @@ -1,154 +0,0 @@ -package io.github.wulkanowy.ui.main; - -import android.content.Context; -import android.content.Intent; -import android.graphics.Color; -import android.os.Bundle; -import android.support.v7.app.ActionBar; -import android.view.View; - -import com.aurelhubert.ahbottomnavigation.AHBottomNavigation; -import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem; -import com.aurelhubert.ahbottomnavigation.AHBottomNavigationViewPager; - -import javax.inject.Inject; - -import butterknife.BindView; -import butterknife.ButterKnife; -import io.github.wulkanowy.R; -import io.github.wulkanowy.services.SyncJob; -import io.github.wulkanowy.ui.base.BaseActivity; -import io.github.wulkanowy.ui.main.attendance.AttendanceFragment; -import io.github.wulkanowy.ui.main.dashboard.DashboardFragment; -import io.github.wulkanowy.ui.main.grades.GradesFragment; -import io.github.wulkanowy.ui.main.timetable.TimetableFragment; - -public class MainActivity extends BaseActivity implements MainContract.View, - AHBottomNavigation.OnTabSelectedListener, OnFragmentIsReadyListener { - - private int initTabPosition = 0; - - @BindView(R.id.main_activity_nav) - AHBottomNavigation bottomNavigation; - - @BindView(R.id.main_activity_view_pager) - AHBottomNavigationViewPager viewPager; - - @BindView(R.id.main_activity_progress_bar) - View progressBar; - - @Inject - MainPagerAdapter pagerAdapter; - - @Inject - MainContract.Presenter presenter; - - public static Intent getStartIntent(Context context) { - return new Intent(context, MainActivity.class); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - initTabPosition = getIntent().getIntExtra(SyncJob.EXTRA_INTENT_KEY, initTabPosition); - - getActivityComponent().inject(this); - setButterKnife(ButterKnife.bind(this)); - - presenter.onStart(this); - - initiationViewPager(); - initiationBottomNav(); - } - - @Override - public void showProgressBar(boolean show) { - progressBar.setVisibility(show ? View.VISIBLE : View.INVISIBLE); - viewPager.setVisibility(show ? View.INVISIBLE : View.VISIBLE); - bottomNavigation.setVisibility(show ? View.INVISIBLE : View.VISIBLE); - } - - @Override - public void showActionBar() { - ActionBar actionBar = getSupportActionBar(); - if (actionBar != null) { - actionBar.show(); - } - } - - @Override - public void hideActionBar() { - ActionBar actionBar = getSupportActionBar(); - if (actionBar != null) { - actionBar.hide(); - } - } - - @Override - public boolean onTabSelected(int position, boolean wasSelected) { - presenter.onTabSelected(position, wasSelected); - return true; - } - - @Override - public void setCurrentPage(int position) { - viewPager.setCurrentItem(position, false); - } - - @Override - public void onFragmentIsReady() { - presenter.onFragmentIsReady(); - } - - private void initiationBottomNav() { - bottomNavigation.addItem(new AHBottomNavigationItem( - getString(R.string.grades_text), - getResources().getDrawable(R.drawable.ic_menu_grade_26dp) - )); - bottomNavigation.addItem(new AHBottomNavigationItem( - getString(R.string.attendance_text), - getResources().getDrawable(R.drawable.ic_menu_attendance_24dp) - )); - bottomNavigation.addItem(new AHBottomNavigationItem( - getString(R.string.dashboard_text), - getResources().getDrawable(R.drawable.ic_menu_dashboard_24dp) - )); - bottomNavigation.addItem(new AHBottomNavigationItem( - getString(R.string.lessonplan_text), - getResources().getDrawable(R.drawable.ic_menu_timetable_24dp) - )); - bottomNavigation.addItem(new AHBottomNavigationItem( - getString(R.string.settings_text), - getResources().getDrawable(R.drawable.ic_menu_other_24dp) - )); - - bottomNavigation.setAccentColor(getResources().getColor(R.color.colorPrimary)); - bottomNavigation.setInactiveColor(Color.BLACK); - bottomNavigation.setBackgroundColor(getResources().getColor(R.color.colorBackgroundBottomNav)); - bottomNavigation.setTitleState(AHBottomNavigation.TitleState.ALWAYS_SHOW); - bottomNavigation.setOnTabSelectedListener(this); - bottomNavigation.setCurrentItem(initTabPosition); - bottomNavigation.setBehaviorTranslationEnabled(false); - } - - private void initiationViewPager() { - pagerAdapter.addFragment(new GradesFragment()); - pagerAdapter.addFragment(new AttendanceFragment()); - pagerAdapter.addFragment(new DashboardFragment()); - pagerAdapter.addFragment(new TimetableFragment()); - pagerAdapter.addFragment(new DashboardFragment()); - - viewPager.setPagingEnabled(false); - viewPager.setAdapter(pagerAdapter); - viewPager.setOffscreenPageLimit(4); - viewPager.setCurrentItem(initTabPosition, false); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - presenter.onDestroy(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/MainContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/MainContract.java deleted file mode 100644 index cb1d5d7c7..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/MainContract.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.ui.main; - -import io.github.wulkanowy.di.annotations.PerActivity; -import io.github.wulkanowy.ui.base.BaseContract; - -public interface MainContract { - - interface View extends BaseContract.View { - - void setCurrentPage(int position); - - void showProgressBar(boolean show); - - void showActionBar(); - - void hideActionBar(); - } - - @PerActivity - interface Presenter extends BaseContract.Presenter { - - void onTabSelected(int position, boolean wasSelected); - - void onFragmentIsReady(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/MainPagerAdapter.java b/app/src/main/java/io/github/wulkanowy/ui/main/MainPagerAdapter.java deleted file mode 100644 index fcc51fb54..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/MainPagerAdapter.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.github.wulkanowy.ui.main; - -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentStatePagerAdapter; - -import java.util.ArrayList; -import java.util.List; - -public class MainPagerAdapter extends FragmentStatePagerAdapter { - - private List fragmentList = new ArrayList<>(); - - public MainPagerAdapter(FragmentManager fragmentManager) { - super(fragmentManager); - } - - void addFragment(Fragment fragment) { - fragmentList.add(fragment); - } - - @Override - public Fragment getItem(int position) { - return fragmentList.get(position); - } - - @Override - public int getCount() { - return fragmentList.size(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/MainPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/MainPresenter.java deleted file mode 100644 index 149e49e2f..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/MainPresenter.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.github.wulkanowy.ui.main; - - -import javax.inject.Inject; - -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.ui.base.BasePresenter; - -public class MainPresenter extends BasePresenter - implements MainContract.Presenter { - - private int fragmentCount = 0; - - @Inject - MainPresenter(RepositoryContract repository) { - super(repository); - } - - @Override - public void onStart(MainContract.View view) { - super.onStart(view); - getView().showProgressBar(true); - getView().hideActionBar(); - } - - @Override - public void onTabSelected(int position, boolean wasSelected) { - if (!wasSelected) { - getView().setCurrentPage(position); - } - } - - @Override - public void onFragmentIsReady() { - if (fragmentCount < 5) { - fragmentCount++; - } - - if (fragmentCount == 5) { - getView().showActionBar(); - getView().showProgressBar(false); - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/OnFragmentIsReadyListener.java b/app/src/main/java/io/github/wulkanowy/ui/main/OnFragmentIsReadyListener.java deleted file mode 100644 index 21e3ba12f..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/OnFragmentIsReadyListener.java +++ /dev/null @@ -1,6 +0,0 @@ -package io.github.wulkanowy.ui.main; - -public interface OnFragmentIsReadyListener { - - void onFragmentIsReady(); -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/TabsData.java b/app/src/main/java/io/github/wulkanowy/ui/main/TabsData.java deleted file mode 100644 index 3af29534b..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/TabsData.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.wulkanowy.ui.main; - -import android.support.v4.app.Fragment; - -import java.util.ArrayList; -import java.util.List; - -public class TabsData { - - private List fragments = new ArrayList<>(); - - private List titles = new ArrayList<>(); - - public Fragment getFragment(int index) { - return fragments.get(index); - } - - public void addFragment(Fragment fragment) { - if (fragment != null) { - fragments.add(fragment); - } - } - - public int getFragmentsCount() { - return fragments.size(); - } - - public String getTitle(int index) { - return titles.get(index); - } - - public void addTitle(String title) { - if (title != null) { - titles.add(title); - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java deleted file mode 100644 index 545e300ea..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.wulkanowy.ui.main.attendance; - -import io.github.wulkanowy.di.annotations.PerActivity; -import io.github.wulkanowy.ui.base.BaseContract; -import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; -import io.github.wulkanowy.ui.main.TabsData; - -public interface AttendanceContract { - - interface View extends BaseContract.View { - - void setActivityTitle(); - - void scrollViewPagerToPosition(int position); - - void setTabDataToAdapter(TabsData tabsData); - - void setAdapterWithTabLayout(); - - boolean isMenuVisible(); - } - - @PerActivity - interface Presenter extends BaseContract.Presenter { - - void onFragmentActivated(boolean isVisible); - - void onStart(View view, OnFragmentIsReadyListener listener); - - void setRestoredPosition(int position); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialogFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialogFragment.java deleted file mode 100644 index f5bbcb414..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialogFragment.java +++ /dev/null @@ -1,91 +0,0 @@ -package io.github.wulkanowy.ui.main.attendance; - -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.DialogFragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import io.github.wulkanowy.R; -import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; - -public class AttendanceDialogFragment extends DialogFragment { - - private static final String ARGUMENT_KEY = "Item"; - - private AttendanceLesson lesson; - - @BindView(R.id.attendance_dialog_subject_value) - TextView subject; - - @BindView(R.id.attendance_dialog_date_value) - TextView date; - - @BindView(R.id.attendance_dialog_number_value) - TextView number; - - @BindView(R.id.attendance_dialog_description_value) - TextView description; - - public AttendanceDialogFragment() { - //empty constructor for fragment - } - - public static AttendanceDialogFragment newInstance(AttendanceLesson lesson) { - AttendanceDialogFragment dialogFragment = new AttendanceDialogFragment(); - - Bundle bundle = new Bundle(); - bundle.putSerializable(ARGUMENT_KEY, lesson); - - dialogFragment.setArguments(bundle); - - return dialogFragment; - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (getArguments() != null) { - lesson = (AttendanceLesson) getArguments().getSerializable(ARGUMENT_KEY); - } - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.attendance_dialog, container, false); - - ButterKnife.bind(this, view); - - if (!lesson.getSubject().isEmpty()) { - subject.setText(lesson.getSubject()); - } - - if (!lesson.getDate().isEmpty()) { - date.setText(lesson.getDate()); - } - - if (0 != lesson.getNumber()) { - number.setText(String.valueOf(lesson.getNumber())); - } - - description.setText(lesson.getDescription()); - - if (lesson.getIsAbsenceUnexcused()) { - description.setTextColor(getResources().getColor(R.color.colorPrimaryDark)); - } - - return view; - } - - @OnClick(R.id.attendance_dialog_close) - void onClickCloseButton() { - dismiss(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.java deleted file mode 100644 index 4dd179b15..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.java +++ /dev/null @@ -1,107 +0,0 @@ -package io.github.wulkanowy.ui.main.attendance; - -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.design.widget.Snackbar; -import android.support.design.widget.TabLayout; -import android.support.v4.view.ViewPager; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import javax.inject.Inject; - -import butterknife.BindView; -import butterknife.ButterKnife; -import io.github.wulkanowy.R; -import io.github.wulkanowy.di.component.FragmentComponent; -import io.github.wulkanowy.ui.base.BaseFragment; -import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; -import io.github.wulkanowy.ui.main.TabsData; - -public class AttendanceFragment extends BaseFragment implements AttendanceContract.View { - - private static final String CURRENT_ITEM_KEY = "CurrentItem"; - - @BindView(R.id.attendance_fragment_viewpager) - ViewPager viewPager; - - @BindView(R.id.attendance_fragment_tab_layout) - TabLayout tabLayout; - - @Inject - AttendancePagerAdapter pagerAdapter; - - @Inject - AttendanceContract.Presenter presenter; - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_attendance, container, false); - - FragmentComponent component = getFragmentComponent(); - if (component != null) { - component.inject(this); - setButterKnife(ButterKnife.bind(this, view)); - presenter.onStart(this, (OnFragmentIsReadyListener) getActivity()); - - if (savedInstanceState != null) { - presenter.setRestoredPosition(savedInstanceState.getInt(CURRENT_ITEM_KEY)); - } - } - - return view; - } - - @Override - public void setMenuVisibility(boolean menuVisible) { - super.setMenuVisibility(menuVisible); - if (presenter != null) { - presenter.onFragmentActivated(menuVisible); - } - } - - @Override - public void setTabDataToAdapter(TabsData tabsData) { - pagerAdapter.setTabsData(tabsData); - } - - @Override - public void setAdapterWithTabLayout() { - viewPager.setAdapter(pagerAdapter); - tabLayout.setupWithViewPager(viewPager); - } - - @Override - public void scrollViewPagerToPosition(int position) { - viewPager.setCurrentItem(position, false); - } - - @Override - public void setActivityTitle() { - setTitle(getString(R.string.attendance_text)); - } - - @Override - public void onError(String message) { - if (getActivity() != null) { - Snackbar.make(getActivity().findViewById(R.id.main_activity_view_pager), - message, Snackbar.LENGTH_LONG).show(); - } - } - - @Override - public void onSaveInstanceState(Bundle outState) { - outState.putInt(CURRENT_ITEM_KEY, viewPager.getCurrentItem()); - super.onSaveInstanceState(outState); - } - - @Override - public void onDestroyView() { - presenter.onDestroy(); - super.onDestroyView(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceHeaderItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceHeaderItem.java deleted file mode 100644 index a055156d6..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceHeaderItem.java +++ /dev/null @@ -1,161 +0,0 @@ -package io.github.wulkanowy.ui.main.attendance; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.drawable.Drawable; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.TextView; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; - -import java.util.List; - -import butterknife.BindColor; -import butterknife.BindView; -import butterknife.ButterKnife; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem; -import eu.davidea.flexibleadapter.items.IFlexible; -import eu.davidea.viewholders.ExpandableViewHolder; -import io.github.wulkanowy.R; -import io.github.wulkanowy.data.db.dao.entities.Day; - -public class AttendanceHeaderItem - extends AbstractExpandableHeaderItem { - - private Day day; - - AttendanceHeaderItem(Day day) { - this.day = day; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - - if (o == null || getClass() != o.getClass()) return false; - - AttendanceHeaderItem that = (AttendanceHeaderItem) o; - - return new EqualsBuilder() - .append(day, that.day) - .isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder(17, 37) - .append(day) - .toHashCode(); - } - - @Override - public int getLayoutRes() { - return R.layout.attendance_header; - } - - @Override - public HeaderViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new HeaderViewHolder(view, adapter); - } - - @Override - public void bindViewHolder(FlexibleAdapter adapter, HeaderViewHolder holder, - int position, List payloads) { - holder.onBind(day, getSubItems()); - } - - static class HeaderViewHolder extends ExpandableViewHolder { - - @BindView(R.id.attendance_header_day) - TextView dayName; - - @BindView(R.id.attendance_header_date) - TextView date; - - @BindView(R.id.attendance_header_description) - TextView description; - - @BindView(R.id.attendance_header_alert_image) - ImageView alert; - - @BindView(R.id.attendance_header_free_name) - TextView freeName; - - @BindColor(R.color.secondary_text) - int secondaryColor; - - @BindColor(R.color.free_day) - int backgroundFreeDay; - - @BindColor(android.R.color.black) - int black; - - private Context context; - - HeaderViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - view.setOnClickListener(this); - ButterKnife.bind(this, view); - context = view.getContext(); - } - - void onBind(Day item, List subItems) { - dayName.setText(StringUtils.capitalize(item.getDayName())); - date.setText(item.getDate()); - - int numberOfHours = countNotPresentHours(subItems); - description.setText((getContentView().getResources().getQuantityString(R.plurals.numberOfAbsences, - numberOfHours, numberOfHours))); - description.setVisibility(numberOfHours > 0 ? View.VISIBLE : View.INVISIBLE); - alert.setVisibility(isSubItemsHasChanges(subItems) ? View.VISIBLE : View.INVISIBLE); - freeName.setVisibility(subItems.isEmpty() ? View.VISIBLE : View.INVISIBLE); - setInactiveHeader(item.getAttendanceLessons().isEmpty()); - } - - - private void setInactiveHeader(boolean inactive) { - ((FrameLayout) getContentView()).setForeground(inactive ? null : getSelectableDrawable()); - dayName.setTextColor(inactive ? secondaryColor : black); - - if (inactive) { - getContentView().setBackgroundColor(backgroundFreeDay); - } else { - getContentView().setBackgroundDrawable(context.getResources() - .getDrawable(R.drawable.ic_border)); - } - } - - private Drawable getSelectableDrawable() { - int[] attrs = new int[]{R.attr.selectableItemBackground}; - TypedArray typedArray = context.obtainStyledAttributes(attrs); - Drawable drawable = typedArray.getDrawable(0); - typedArray.recycle(); - return drawable; - } - - private int countNotPresentHours(List subItems) { - int i = 0; - for (AttendanceSubItem subItem : subItems) { - if (subItem.getLesson().getIsAbsenceUnexcused()) { - i++; - } - } - return i; - } - - private boolean isSubItemsHasChanges(List subItems) { - for (AttendanceSubItem subItem : subItems) { - if (subItem.getLesson().getIsAbsenceUnexcused() || subItem.getLesson() - .getIsUnexcusedLateness()) { - return true; - } - } - return false; - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePagerAdapter.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePagerAdapter.java deleted file mode 100644 index 3cabff71d..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePagerAdapter.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.wulkanowy.ui.main.attendance; - -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentStatePagerAdapter; - -import io.github.wulkanowy.ui.main.TabsData; - -public class AttendancePagerAdapter extends FragmentStatePagerAdapter { - - private TabsData tabsData; - - public AttendancePagerAdapter(FragmentManager fragmentManager) { - super(fragmentManager); - } - - void setTabsData(TabsData tabsData) { - this.tabsData = tabsData; - } - - @Override - public Fragment getItem(int position) { - return tabsData.getFragment(position); - } - - - @Override - public int getCount() { - return tabsData.getFragmentsCount(); - } - - @Nullable - @Override - public CharSequence getPageTitle(int position) { - return tabsData.getTitle(position); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.java deleted file mode 100644 index bad68e166..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.java +++ /dev/null @@ -1,109 +0,0 @@ -package io.github.wulkanowy.ui.main.attendance; - -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.ui.base.BasePresenter; -import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; -import io.github.wulkanowy.ui.main.TabsData; -import io.github.wulkanowy.utils.TimeUtils; -import io.github.wulkanowy.utils.async.AbstractTask; -import io.github.wulkanowy.utils.async.AsyncListeners; - -public class AttendancePresenter extends BasePresenter - implements AttendanceContract.Presenter, AsyncListeners.OnFirstLoadingListener { - - private AbstractTask loadingTask; - - private List dates = new ArrayList<>(); - - private TabsData tabsData = new TabsData(); - - private OnFragmentIsReadyListener listener; - - private int positionToScroll = 0; - - private boolean isFirstSight = false; - - @Inject - AttendancePresenter(RepositoryContract repository) { - super(repository); - } - - @Override - public void onStart(AttendanceContract.View view, OnFragmentIsReadyListener listener) { - super.onStart(view); - this.listener = listener; - - if (getView().isMenuVisible()) { - getView().setActivityTitle(); - } - - if (dates.isEmpty()) { - dates = TimeUtils.getMondaysFromCurrentSchoolYear(); - } - - if (positionToScroll == 0) { - positionToScroll = dates.indexOf(TimeUtils.getDateOfCurrentMonday(true)); - } - - if (!isFirstSight) { - isFirstSight = true; - - loadingTask = new AbstractTask(); - loadingTask.setOnFirstLoadingListener(this); - loadingTask.execute(); - } - } - - @Override - public void onFragmentActivated(boolean isVisible) { - if (isVisible) { - getView().setActivityTitle(); - } - } - - @Override - public void onDoInBackgroundLoading() throws Exception { - for (String date : dates) { - tabsData.addTitle(date); - tabsData.addFragment(AttendanceTabFragment.newInstance(date)); - } - } - - @Override - public void onCanceledLoadingAsync() { - //do nothing - - } - - @Override - public void onEndLoadingAsync(boolean result, Exception exception) { - if (result) { - getView().setTabDataToAdapter(tabsData); - getView().setAdapterWithTabLayout(); - getView().scrollViewPagerToPosition(positionToScroll); - listener.onFragmentIsReady(); - } - } - - @Override - public void setRestoredPosition(int position) { - this.positionToScroll = position; - } - - @Override - public void onDestroy() { - isFirstSight = false; - - if (loadingTask != null) { - loadingTask.cancel(true); - loadingTask = null; - } - - super.onDestroy(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceSubItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceSubItem.java deleted file mode 100644 index 8249a5139..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceSubItem.java +++ /dev/null @@ -1,121 +0,0 @@ -package io.github.wulkanowy.ui.main.attendance; - -import android.content.Context; -import android.support.v4.app.DialogFragment; -import android.support.v4.app.FragmentActivity; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; - -import java.util.List; - -import butterknife.BindView; -import butterknife.ButterKnife; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.items.AbstractSectionableItem; -import eu.davidea.flexibleadapter.items.IFlexible; -import eu.davidea.viewholders.FlexibleViewHolder; -import io.github.wulkanowy.R; -import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; - -class AttendanceSubItem - extends AbstractSectionableItem { - - private AttendanceLesson lesson; - - AttendanceSubItem(AttendanceHeaderItem header, AttendanceLesson lesson) { - super(header); - this.lesson = lesson; - } - - AttendanceLesson getLesson() { - return lesson; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - - if (o == null || getClass() != o.getClass()) return false; - - AttendanceSubItem that = (AttendanceSubItem) o; - - return new EqualsBuilder() - .append(lesson, that.lesson) - .isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder(17, 37) - .append(lesson) - .toHashCode(); - } - - @Override - public int getLayoutRes() { - return R.layout.attendance_subitem; - } - - @Override - public SubItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new SubItemViewHolder(view, adapter); - } - - @Override - public void bindViewHolder(FlexibleAdapter adapter, SubItemViewHolder holder, - int position, List payloads) { - holder.onBind(lesson); - } - - static class SubItemViewHolder extends FlexibleViewHolder { - - @BindView(R.id.attendance_subItem_lesson) - TextView lessonName; - - @BindView(R.id.attendance_subItem_number) - TextView lessonNumber; - - @BindView(R.id.attendance_subItem_description) - TextView lessonDescription; - - @BindView(R.id.attendance_subItem_alert_image) - ImageView alert; - - private Context context; - - private AttendanceLesson item; - - SubItemViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - ButterKnife.bind(this, view); - context = view.getContext(); - view.setOnClickListener(this); - } - - void onBind(AttendanceLesson lesson) { - item = lesson; - - lessonName.setText(lesson.getSubject()); - lessonNumber.setText((String.valueOf(lesson.getNumber()))); - lessonDescription.setText(lesson.getDescription()); - alert.setVisibility(lesson.getIsAbsenceUnexcused() || lesson.getIsUnexcusedLateness() - ? View.VISIBLE : View.INVISIBLE); - } - - @Override - public void onClick(View view) { - super.onClick(view); - showDialog(); - } - - private void showDialog() { - AttendanceDialogFragment dialogFragment = AttendanceDialogFragment.newInstance(item); - dialogFragment.setStyle(DialogFragment.STYLE_NO_TITLE, 0); - dialogFragment.show(((FragmentActivity) context).getSupportFragmentManager(), item.toString()); - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabContract.java deleted file mode 100644 index dcb16bf88..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabContract.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.wulkanowy.ui.main.attendance; - -import java.util.List; - -import io.github.wulkanowy.ui.base.BaseContract; - -public interface AttendanceTabContract { - - interface View extends BaseContract.View { - - void updateAdapterList(List headerItems); - - void onRefreshSuccess(); - - void hideRefreshingBar(); - - void showNoItem(boolean show); - - void showProgressBar(boolean show); - } - - interface Presenter extends BaseContract.Presenter { - - void onFragmentActivated(boolean isSelected); - - void setArgumentDate(String date); - - void onRefresh(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabFragment.java deleted file mode 100644 index b635da2b3..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabFragment.java +++ /dev/null @@ -1,146 +0,0 @@ -package io.github.wulkanowy.ui.main.attendance; - -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.design.widget.Snackbar; -import android.support.v4.widget.SwipeRefreshLayout; -import android.support.v7.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import java.util.List; - -import javax.inject.Inject; - -import butterknife.BindView; -import butterknife.ButterKnife; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager; -import io.github.wulkanowy.R; -import io.github.wulkanowy.di.component.FragmentComponent; -import io.github.wulkanowy.ui.base.BaseFragment; - -public class AttendanceTabFragment extends BaseFragment implements AttendanceTabContract.View, - SwipeRefreshLayout.OnRefreshListener { - - private static final String ARGUMENT_KEY = "date"; - - @BindView(R.id.attendance_tab_fragment_recycler) - RecyclerView recyclerView; - - @BindView(R.id.attendance_tab_fragment_swipe_refresh) - SwipeRefreshLayout refreshLayout; - - @BindView(R.id.attendance_tab_fragment_progress_bar) - View progressBar; - - @BindView(R.id.attendance_tab_fragment_no_item_container) - View noItemView; - - @Inject - AttendanceTabContract.Presenter presenter; - - @Inject - FlexibleAdapter adapter; - - private boolean isFragmentVisible = false; - - public static AttendanceTabFragment newInstance(String date) { - AttendanceTabFragment fragmentTab = new AttendanceTabFragment(); - - Bundle bundle = new Bundle(); - bundle.putString(ARGUMENT_KEY, date); - fragmentTab.setArguments(bundle); - - return fragmentTab; - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_attendance_tab, container, false); - - FragmentComponent component = getFragmentComponent(); - if (component != null) { - component.inject(this); - setButterKnife(ButterKnife.bind(this, view)); - - if (getArguments() != null) { - presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY)); - } - - presenter.onStart(this); - presenter.onFragmentActivated(isFragmentVisible); - } - return view; - } - - @Override - protected void setUpOnViewCreated(View fragmentView) { - adapter.setAutoCollapseOnExpand(true); - adapter.setAutoScrollOnExpand(true); - adapter.expandItemsAtStartUp(); - - recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(fragmentView.getContext())); - recyclerView.setAdapter(adapter); - - refreshLayout.setColorSchemeResources(android.R.color.black); - refreshLayout.setOnRefreshListener(this); - - } - - @Override - public void updateAdapterList(List headerItems) { - adapter.updateDataSet(headerItems); - } - - @Override - public void setMenuVisibility(boolean menuVisible) { - super.setMenuVisibility(menuVisible); - isFragmentVisible = menuVisible; - if (presenter != null) { - presenter.onFragmentActivated(menuVisible); - } - } - - @Override - public void onRefresh() { - presenter.onRefresh(); - } - - @Override - public void onRefreshSuccess() { - onError(R.string.timetable_refresh_success); - } - - @Override - public void hideRefreshingBar() { - refreshLayout.setRefreshing(false); - } - - @Override - public void showProgressBar(boolean show) { - progressBar.setVisibility(show ? View.VISIBLE : View.INVISIBLE); - } - - @Override - public void showNoItem(boolean show) { - noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE); - } - - @Override - public void onError(String message) { - if (getActivity() != null) { - Snackbar.make(getActivity().findViewById(R.id.main_activity_view_pager), - message, Snackbar.LENGTH_LONG).show(); - } - } - - @Override - public void onDestroyView() { - presenter.onDestroy(); - super.onDestroyView(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabPresenter.java deleted file mode 100644 index 1db29cb1e..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabPresenter.java +++ /dev/null @@ -1,178 +0,0 @@ -package io.github.wulkanowy.ui.main.attendance; - -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; -import io.github.wulkanowy.data.db.dao.entities.Day; -import io.github.wulkanowy.data.db.dao.entities.Week; -import io.github.wulkanowy.ui.base.BasePresenter; -import io.github.wulkanowy.utils.async.AbstractTask; -import io.github.wulkanowy.utils.async.AsyncListeners; - -public class AttendanceTabPresenter extends BasePresenter - implements AttendanceTabContract.Presenter, AsyncListeners.OnRefreshListener, - AsyncListeners.OnFirstLoadingListener { - - private AbstractTask refreshTask; - - private AbstractTask loadingTask; - - private List headerItems = new ArrayList<>(); - - private String date; - - private boolean isFirstSight = false; - - @Inject - AttendanceTabPresenter(RepositoryContract repository) { - super(repository); - } - - @Override - public void onStart(AttendanceTabContract.View view) { - super.onStart(view); - getView().showProgressBar(true); - getView().showNoItem(false); - } - - @Override - public void onFragmentActivated(boolean isSelected) { - if (!isFirstSight && isSelected && isViewAttached()) { - isFirstSight = true; - - loadingTask = new AbstractTask(); - loadingTask.setOnFirstLoadingListener(this); - loadingTask.execute(); - } else if (!isSelected) { - cancelAsyncTasks(); - } - } - - @Override - public void onRefresh() { - if (getView().isNetworkConnected()) { - refreshTask = new AbstractTask(); - refreshTask.setOnRefreshListener(this); - refreshTask.execute(); - } else { - getView().onNoNetworkError(); - getView().hideRefreshingBar(); - } - } - - @Override - public void onDoInBackgroundRefresh() throws Exception { - syncData(); - } - - @Override - public void onCanceledRefreshAsync() { - if (isViewAttached()) { - getView().hideRefreshingBar(); - } - } - - @Override - public void onEndRefreshAsync(boolean result, Exception exception) { - if (result) { - loadingTask = new AbstractTask(); - loadingTask.setOnFirstLoadingListener(this); - loadingTask.execute(); - - getView().onRefreshSuccess(); - } else { - getView().onError(getRepository().getErrorLoginMessage(exception)); - } - getView().hideRefreshingBar(); - } - - @Override - public void onDoInBackgroundLoading() throws Exception { - Week week = getRepository().getWeek(date); - - if (week == null || !week.getIsAttendanceSynced()) { - syncData(); - week = getRepository().getWeek(date); - } - - List dayList = week.getDayList(); - - headerItems = new ArrayList<>(); - - boolean isEmptyWeek = true; - - for (Day day : dayList) { - day.resetAttendanceLessons(); - AttendanceHeaderItem headerItem = new AttendanceHeaderItem(day); - - if (isEmptyWeek) { - isEmptyWeek = day.getAttendanceLessons().isEmpty(); - } - - List lessonList = day.getAttendanceLessons(); - - List subItems = new ArrayList<>(); - - for (AttendanceLesson lesson : lessonList) { - lesson.setDescription(getRepository().getAttendanceLessonDescription(lesson)); - subItems.add(new AttendanceSubItem(headerItem, lesson)); - } - - headerItem.setSubItems(subItems); - headerItem.setExpanded(false); - headerItems.add(headerItem); - } - - if (isEmptyWeek) { - headerItems = new ArrayList<>(); - } - } - - @Override - public void onCanceledLoadingAsync() { - // do nothing - } - - @Override - public void onEndLoadingAsync(boolean result, Exception exception) { - if (headerItems.isEmpty()) { - getView().showNoItem(true); - getView().updateAdapterList(null); - } else { - getView().updateAdapterList(headerItems); - getView().showNoItem(false); - } - getView().showProgressBar(false); - } - - @Override - public void setArgumentDate(String date) { - this.date = date; - } - - private void syncData() throws Exception { - getRepository().syncAttendance(date); - } - - private void cancelAsyncTasks() { - if (refreshTask != null) { - refreshTask.cancel(true); - refreshTask = null; - } - if (loadingTask != null) { - loadingTask.cancel(true); - loadingTask = null; - } - } - - @Override - public void onDestroy() { - cancelAsyncTasks(); - isFirstSight = false; - super.onDestroy(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/dashboard/DashboardContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/dashboard/DashboardContract.java deleted file mode 100644 index 2488eb95c..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/dashboard/DashboardContract.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.wulkanowy.ui.main.dashboard; - -import io.github.wulkanowy.di.annotations.PerActivity; -import io.github.wulkanowy.ui.base.BaseContract; -import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; - -public interface DashboardContract { - - interface View extends BaseContract.View { - - void setActivityTitle(); - - boolean isMenuVisible(); - } - - @PerActivity - interface Presenter extends BaseContract.Presenter { - - void onStart(View view, OnFragmentIsReadyListener listener); - - void onFragmentVisible(boolean isVisible); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/dashboard/DashboardFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/dashboard/DashboardFragment.java deleted file mode 100644 index 0832b5fb7..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/dashboard/DashboardFragment.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.github.wulkanowy.ui.main.dashboard; - -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import javax.inject.Inject; - -import butterknife.ButterKnife; -import io.github.wulkanowy.R; -import io.github.wulkanowy.di.component.FragmentComponent; -import io.github.wulkanowy.ui.base.BaseFragment; -import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; - -public class DashboardFragment extends BaseFragment implements DashboardContract.View { - - @Inject - DashboardContract.Presenter presenter; - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_board, container, false); - - FragmentComponent component = getFragmentComponent(); - if (component != null) { - component.inject(this); - setButterKnife(ButterKnife.bind(this, view)); - presenter.onStart(this, (OnFragmentIsReadyListener) getActivity()); - } - return view; - } - - @Override - public void setMenuVisibility(boolean menuVisible) { - super.setMenuVisibility(menuVisible); - if (presenter != null) { - presenter.onFragmentVisible(menuVisible); - } - } - - @Override - public void setActivityTitle() { - setTitle(getString(R.string.dashboard_text)); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/dashboard/DashboardPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/dashboard/DashboardPresenter.java deleted file mode 100644 index 58d89b399..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/dashboard/DashboardPresenter.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.wulkanowy.ui.main.dashboard; - -import javax.inject.Inject; - -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.ui.base.BasePresenter; -import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; - -public class DashboardPresenter extends BasePresenter - implements DashboardContract.Presenter { - - private OnFragmentIsReadyListener listener; - - @Inject - DashboardPresenter(RepositoryContract repository) { - super(repository); - } - - @Override - public void onStart(DashboardContract.View view, OnFragmentIsReadyListener listener) { - super.onStart(view); - this.listener = listener; - - if (getView().isMenuVisible()) { - getView().setActivityTitle(); - } - - this.listener.onFragmentIsReady(); - } - - @Override - public void onFragmentVisible(boolean isVisible) { - if (isVisible) { - getView().setActivityTitle(); - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradeHeaderItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradeHeaderItem.java deleted file mode 100644 index a66be29b9..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradeHeaderItem.java +++ /dev/null @@ -1,118 +0,0 @@ -package io.github.wulkanowy.ui.main.grades; - - -import android.content.res.Resources; -import android.view.View; -import android.widget.TextView; - -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; - -import java.util.List; - -import butterknife.BindView; -import butterknife.ButterKnife; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem; -import eu.davidea.viewholders.ExpandableViewHolder; -import io.github.wulkanowy.R; -import io.github.wulkanowy.data.db.dao.entities.Subject; -import io.github.wulkanowy.utils.AverageCalculator; - -public class GradeHeaderItem - extends AbstractExpandableHeaderItem { - - private Subject subject; - - GradeHeaderItem(Subject subject) { - this.subject = subject; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - - if (o == null || getClass() != o.getClass()) return false; - - GradeHeaderItem that = (GradeHeaderItem) o; - - return new EqualsBuilder() - .append(subject, that.subject) - .isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder(17, 37) - .append(subject) - .toHashCode(); - } - - @Override - public int getLayoutRes() { - return R.layout.grade_header; - } - - @Override - public HeaderViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new HeaderViewHolder(view, adapter); - } - - @Override - public void bindViewHolder(FlexibleAdapter adapter, HeaderViewHolder holder, int position, List payloads) { - holder.onBind(subject, getSubItems()); - } - - static class HeaderViewHolder extends ExpandableViewHolder { - - @BindView(R.id.grade_header_subject_text) - TextView subjectName; - - @BindView(R.id.grade_header_average_text) - TextView averageText; - - @BindView(R.id.grade_header_number_of_grade_text) - TextView numberText; - - @BindView(R.id.grade_header_alert_image) - View alertImage; - - Resources resources; - - HeaderViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - ButterKnife.bind(this, view); - resources = view.getResources(); - view.setOnClickListener(this); - } - - void onBind(Subject item, List subItems) { - subjectName.setText(item.getName()); - numberText.setText(resources.getQuantityString(R.plurals.numberOfGradesPlurals, - subItems.size(), subItems.size())); - averageText.setText(getGradesAverageString(item)); - alertImage.setVisibility(isSubItemsReadAndSaveAlertView(subItems) - ? View.INVISIBLE : View.VISIBLE); - } - - private boolean isSubItemsReadAndSaveAlertView(List subItems) { - boolean isRead = true; - - for (GradesSubItem item : subItems) { - isRead = item.getGrade().getRead(); - item.setSubjectAlertImage(alertImage); - } - return isRead; - } - - private String getGradesAverageString(Subject item) { - float average = AverageCalculator.calculate(item.getGradeList()); - - if (average < 0) { - return resources.getString(R.string.info_no_average); - } else { - return resources.getString(R.string.info_average_grades, average); - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesContract.java deleted file mode 100644 index 7deabb5e7..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesContract.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.wulkanowy.ui.main.grades; - -import android.support.v4.widget.SwipeRefreshLayout; - -import java.util.List; - -import io.github.wulkanowy.di.annotations.PerActivity; -import io.github.wulkanowy.ui.base.BaseContract; -import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; - -public interface GradesContract { - - interface View extends BaseContract.View, SwipeRefreshLayout.OnRefreshListener { - - void updateAdapterList(List headerItems); - - void showNoItem(boolean show); - - void onRefreshSuccessNoGrade(); - - void onRefreshSuccess(int number); - - void hideRefreshingBar(); - - void setActivityTitle(); - - boolean isMenuVisible(); - - } - - @PerActivity - interface Presenter extends BaseContract.Presenter { - - void onFragmentVisible(boolean isVisible); - - void onRefresh(); - - void onStart(View view, OnFragmentIsReadyListener listener); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesDialogFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesDialogFragment.java deleted file mode 100644 index 0d9a699f5..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesDialogFragment.java +++ /dev/null @@ -1,116 +0,0 @@ -package io.github.wulkanowy.ui.main.grades; - - -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.DialogFragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import io.github.wulkanowy.R; -import io.github.wulkanowy.data.db.dao.entities.Grade; -import io.github.wulkanowy.utils.CommonUtils; - -public class GradesDialogFragment extends DialogFragment { - - private static final String ARGUMENT_KEY = "Item"; - - private Grade grade; - - @BindView(R.id.grade_dialog_value) - TextView value; - - @BindView(R.id.grade_dialog_subject) - TextView subject; - - @BindView(R.id.grade_dialog_description_value) - TextView description; - - @BindView(R.id.grade_dialog_weight_value) - TextView weight; - - @BindView(R.id.grade_dialog_teacher_value) - TextView teacher; - - @BindView(R.id.grade_dialog_color_value) - TextView color; - - @BindView(R.id.grade_dialog_date_value) - TextView date; - - public GradesDialogFragment() { - //empty constructor for fragment - } - - public static GradesDialogFragment newInstance(Grade item) { - GradesDialogFragment dialogFragment = new GradesDialogFragment(); - - Bundle bundle = new Bundle(); - bundle.putSerializable(ARGUMENT_KEY, item); - - dialogFragment.setArguments(bundle); - - return dialogFragment; - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (getArguments() != null) { - grade = (Grade) getArguments().getSerializable(ARGUMENT_KEY); - } - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.grade_dialog, container, false); - - ButterKnife.bind(this, view); - - subject.setText(grade.getSubject()); - value.setText(grade.getValue()); - value.setBackgroundResource(grade.getValueColor()); - weight.setText(grade.getWeight()); - date.setText(grade.getDate()); - color.setText(CommonUtils.colorHexToColorName(grade.getColor())); - teacher.setText(getTeacherString()); - description.setText(getDescriptionString()); - - - return view; - } - - @OnClick(R.id.grade_dialog_close_button) - void onClickClose() { - dismiss(); - } - - private String getDescriptionString() { - if ("".equals(grade.getDescription())) { - if (!"".equals(grade.getSymbol())) { - return grade.getSymbol(); - } else { - return getString(R.string.noDescription_text); - } - } else if (!"".equals(grade.getSymbol())) { - return String.format("%1$s - %2$s", grade.getSymbol(), grade.getDescription()); - } else { - return grade.getDescription(); - } - } - - private String getTeacherString() { - if (grade.getTeacher() != null && !"".equals(grade.getTeacher())) { - return grade.getTeacher(); - } else { - return getString(R.string.generic_app_no_data); - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesFragment.java deleted file mode 100644 index bd44f2983..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesFragment.java +++ /dev/null @@ -1,133 +0,0 @@ -package io.github.wulkanowy.ui.main.grades; - -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.design.widget.Snackbar; -import android.support.v4.widget.SwipeRefreshLayout; -import android.support.v7.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import java.util.List; - -import javax.inject.Inject; - -import butterknife.BindView; -import butterknife.ButterKnife; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager; -import io.github.wulkanowy.R; -import io.github.wulkanowy.di.component.FragmentComponent; -import io.github.wulkanowy.ui.base.BaseFragment; -import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; - -public class GradesFragment extends BaseFragment implements GradesContract.View { - - @BindView(R.id.grade_fragment_recycler) - RecyclerView recyclerView; - - @BindView(R.id.grade_fragment_no_item_container) - View noItemView; - - @BindView(R.id.grade_fragment_swipe_refresh) - SwipeRefreshLayout refreshLayout; - - @Inject - FlexibleAdapter adapter; - - @Inject - GradesContract.Presenter presenter; - - public GradesFragment() { - // empty constructor for fragment - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_grades, container, false); - - FragmentComponent component = getFragmentComponent(); - if (component != null) { - component.inject(this); - setButterKnife(ButterKnife.bind(this, view)); - presenter.onStart(this, (OnFragmentIsReadyListener) getActivity()); - } - - return view; - } - - @Override - protected void setUpOnViewCreated(View fragmentView) { - noItemView.setVisibility(View.GONE); - - adapter.setAutoCollapseOnExpand(true); - adapter.setAutoScrollOnExpand(true); - adapter.expandItemsAtStartUp(); - - recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(fragmentView.getContext())); - recyclerView.setAdapter(adapter); - - refreshLayout.setColorSchemeResources(android.R.color.black); - refreshLayout.setOnRefreshListener(this); - } - - @Override - public void setMenuVisibility(boolean menuVisible) { - super.setMenuVisibility(menuVisible); - if (presenter != null) { - presenter.onFragmentVisible(menuVisible); - } - } - - @Override - public void setActivityTitle() { - setTitle(getString(R.string.grades_text)); - } - - @Override - public void onRefresh() { - presenter.onRefresh(); - } - - @Override - public void showNoItem(boolean show) { - noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE); - } - - @Override - public void hideRefreshingBar() { - refreshLayout.setRefreshing(false); - } - - @Override - public void updateAdapterList(List headerItems) { - adapter.updateDataSet(headerItems); - } - - @Override - public void onRefreshSuccessNoGrade() { - onError(R.string.snackbar_no_grades); - } - - @Override - public void onRefreshSuccess(int number) { - onError(getString(R.string.snackbar_new_grade, number)); - } - - @Override - public void onError(String message) { - if (getActivity() != null) { - Snackbar.make(getActivity().findViewById(R.id.main_activity_view_pager), - message, Snackbar.LENGTH_LONG).show(); - } - } - - @Override - public void onDestroyView() { - presenter.onDestroy(); - super.onDestroyView(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesPresenter.java deleted file mode 100644 index 4ea950ded..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesPresenter.java +++ /dev/null @@ -1,161 +0,0 @@ -package io.github.wulkanowy.ui.main.grades; - -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.data.db.dao.entities.Grade; -import io.github.wulkanowy.data.db.dao.entities.Subject; -import io.github.wulkanowy.ui.base.BasePresenter; -import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; -import io.github.wulkanowy.utils.async.AbstractTask; -import io.github.wulkanowy.utils.async.AsyncListeners; - -public class GradesPresenter extends BasePresenter - implements GradesContract.Presenter, AsyncListeners.OnRefreshListener, - AsyncListeners.OnFirstLoadingListener { - - private AbstractTask refreshTask; - - private AbstractTask loadingTask; - - private OnFragmentIsReadyListener listener; - - private List headerItems = new ArrayList<>(); - - private boolean isFirstSight = false; - - @Inject - GradesPresenter(RepositoryContract repository) { - super(repository); - } - - @Override - public void onStart(GradesContract.View view, OnFragmentIsReadyListener listener) { - super.onStart(view); - this.listener = listener; - - if (getView().isMenuVisible()) { - getView().setActivityTitle(); - } - - if (!isFirstSight) { - isFirstSight = true; - - loadingTask = new AbstractTask(); - loadingTask.setOnFirstLoadingListener(this); - loadingTask.execute(); - } - } - - @Override - public void onFragmentVisible(boolean isVisible) { - if (isVisible) { - getView().setActivityTitle(); - } - } - - @Override - public void onRefresh() { - if (getView().isNetworkConnected()) { - refreshTask = new AbstractTask(); - refreshTask.setOnRefreshListener(this); - refreshTask.execute(); - } else { - getView().onNoNetworkError(); - getView().hideRefreshingBar(); - } - } - - @Override - public void onDoInBackgroundRefresh() throws Exception { - getRepository().syncSubjects(); - getRepository().syncGrades(); - } - - @Override - public void onCanceledRefreshAsync() { - if (isViewAttached()) { - getView().hideRefreshingBar(); - } - } - - @Override - public void onEndRefreshAsync(boolean success, Exception exception) { - if (success) { - loadingTask = new AbstractTask(); - loadingTask.setOnFirstLoadingListener(this); - loadingTask.execute(); - - int numberOfNewGrades = getRepository().getNewGrades().size(); - - if (numberOfNewGrades <= 0) { - getView().onRefreshSuccessNoGrade(); - } else { - getView().onRefreshSuccess(numberOfNewGrades); - } - } else { - getView().onError(getRepository().getErrorLoginMessage(exception)); - } - getView().hideRefreshingBar(); - } - - @Override - public void onDoInBackgroundLoading() throws Exception { - List subjectList = getRepository().getCurrentUser().getSubjectList(); - - headerItems = new ArrayList<>(); - - for (Subject subject : subjectList) { - subject.resetGradeList(); - List gradeList = subject.getGradeList(); - - if (!gradeList.isEmpty()) { - GradeHeaderItem headerItem = new GradeHeaderItem(subject); - - List subItems = new ArrayList<>(); - - for (Grade grade : gradeList) { - subItems.add(new GradesSubItem(headerItem, grade)); - } - - headerItem.setSubItems(subItems); - headerItem.setExpanded(false); - headerItems.add(headerItem); - } - } - } - - @Override - public void onCanceledLoadingAsync() { - // do nothing - } - - @Override - public void onEndLoadingAsync(boolean result, Exception exception) { - if (headerItems.isEmpty()) { - getView().showNoItem(true); - } else { - getView().updateAdapterList(headerItems); - getView().showNoItem(false); - } - listener.onFragmentIsReady(); - } - - @Override - public void onDestroy() { - isFirstSight = false; - - if (refreshTask != null) { - refreshTask.cancel(true); - refreshTask = null; - } - if (loadingTask != null) { - loadingTask.cancel(true); - loadingTask = null; - } - super.onDestroy(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSubItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSubItem.java deleted file mode 100644 index e8bd71ca8..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSubItem.java +++ /dev/null @@ -1,157 +0,0 @@ -package io.github.wulkanowy.ui.main.grades; - -import android.content.Context; -import android.support.v4.app.DialogFragment; -import android.support.v4.app.FragmentActivity; -import android.view.View; -import android.widget.TextView; - -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; - -import java.util.List; - -import butterknife.BindView; -import butterknife.ButterKnife; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.items.AbstractSectionableItem; -import eu.davidea.viewholders.FlexibleViewHolder; -import io.github.wulkanowy.R; -import io.github.wulkanowy.data.db.dao.entities.Grade; - -public class GradesSubItem - extends AbstractSectionableItem { - - private Grade grade; - - private static int numberOfNotReadGrade; - - private View subjectAlertImage; - - GradesSubItem(GradeHeaderItem header, Grade grade) { - super(header); - this.grade = grade; - } - - public Grade getGrade() { - return grade; - } - - void setSubjectAlertImage(View subjectAlertImage) { - this.subjectAlertImage = subjectAlertImage; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - - if (o == null || getClass() != o.getClass()) return false; - - GradesSubItem that = (GradesSubItem) o; - - return new EqualsBuilder() - .append(grade, that.grade) - .isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder(17, 37) - .append(grade) - .toHashCode(); - } - - @Override - public int getLayoutRes() { - return R.layout.grade_subitem; - } - - @Override - public SubItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new SubItemViewHolder(view, adapter); - } - - @Override - public void bindViewHolder(FlexibleAdapter adapter, SubItemViewHolder holder, int position, List payloads) { - holder.onBind(grade, subjectAlertImage); - } - - static class SubItemViewHolder extends FlexibleViewHolder { - - @BindView(R.id.grade_subitem_value) - TextView value; - - @BindView(R.id.grade_subitem_description) - TextView description; - - @BindView(R.id.grade_subitem_date) - TextView date; - - @BindView(R.id.grade_subitem_alert_image) - View alert; - - private View subjectAlertImage; - - private Context context; - - private Grade item; - - SubItemViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - ButterKnife.bind(this, view); - context = view.getContext(); - view.setOnClickListener(this); - } - - void onBind(Grade item, View subjectAlertImage) { - this.item = item; - this.subjectAlertImage = subjectAlertImage; - - value.setText(item.getValue()); - value.setBackgroundResource(item.getValueColor()); - date.setText(item.getDate()); - description.setText(getDescriptionString()); - alert.setVisibility(item.getRead() ? View.INVISIBLE : View.VISIBLE); - - if (!item.getRead()) { - numberOfNotReadGrade++; - } - } - - @Override - public void onClick(View view) { - super.onClick(view); - showDialog(); - - if (!item.getRead()) { - numberOfNotReadGrade--; - - if (numberOfNotReadGrade == 0) { - subjectAlertImage.setVisibility(View.INVISIBLE); - } - item.setIsNew(false); - item.setRead(true); - item.update(); - alert.setVisibility(View.INVISIBLE); - } - } - - private String getDescriptionString() { - if (item.getDescription() == null || "".equals(item.getDescription())) { - if (!"".equals(item.getSymbol())) { - return item.getSymbol(); - } else { - return context.getString(R.string.noDescription_text); - } - } else { - return item.getDescription(); - } - } - - private void showDialog() { - GradesDialogFragment dialogFragment = GradesDialogFragment.newInstance(item); - dialogFragment.setStyle(DialogFragment.STYLE_NO_TITLE, 0); - dialogFragment.show(((FragmentActivity) context).getSupportFragmentManager(), item.toString()); - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableContract.java deleted file mode 100644 index aa4576bf4..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableContract.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.wulkanowy.ui.main.timetable; - -import io.github.wulkanowy.di.annotations.PerActivity; -import io.github.wulkanowy.ui.base.BaseContract; -import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; -import io.github.wulkanowy.ui.main.TabsData; - -public interface TimetableContract { - - interface View extends BaseContract.View { - - void setActivityTitle(); - - void scrollViewPagerToPosition(int position); - - void setTabDataToAdapter(TabsData tabsData); - - void setAdapterWithTabLayout(); - - boolean isMenuVisible(); - } - - @PerActivity - interface Presenter extends BaseContract.Presenter { - - void onFragmentActivated(boolean isVisible); - - void onStart(View view, OnFragmentIsReadyListener listener); - - void setRestoredPosition(int position); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableDialogFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableDialogFragment.java deleted file mode 100644 index b93901953..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableDialogFragment.java +++ /dev/null @@ -1,127 +0,0 @@ -package io.github.wulkanowy.ui.main.timetable; - -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.DialogFragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import org.apache.commons.lang3.StringUtils; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import io.github.wulkanowy.R; -import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; - -public class TimetableDialogFragment extends DialogFragment { - - private static final String ARGUMENT_KEY = "Item"; - - private TimetableLesson lesson; - - @BindView(R.id.timetable_dialog_lesson_value) - TextView lessonName; - - @BindView(R.id.timetable_dialog_teacher_value) - TextView teacher; - - @BindView(R.id.timetable_dialog_group_value) - TextView group; - - @BindView(R.id.timetable_dialog_room_value) - TextView room; - - @BindView(R.id.timetable_dialog_time_value) - TextView time; - - @BindView(R.id.timetable_dialog_description_value) - TextView description; - - @BindView(R.id.timetable_dialog_description) - View descriptionLabel; - - @BindView(R.id.timetable_dialog_teacher) - View teacherLabel; - - @BindView(R.id.timetable_dialog_group) - View groupLabel; - - public TimetableDialogFragment() { - //empty constructor for fragment - } - - public static TimetableDialogFragment newInstance(TimetableLesson lesson) { - TimetableDialogFragment dialogFragment = new TimetableDialogFragment(); - - Bundle bundle = new Bundle(); - bundle.putSerializable(ARGUMENT_KEY, lesson); - - dialogFragment.setArguments(bundle); - - return dialogFragment; - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (getArguments() != null) { - lesson = (TimetableLesson) getArguments().getSerializable(ARGUMENT_KEY); - } - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.timetable_dialog, container, false); - - ButterKnife.bind(this, view); - - if (!lesson.getSubject().isEmpty()) { - lessonName.setText(lesson.getSubject()); - } - - if (!lesson.getTeacher().isEmpty()) { - teacher.setText(lesson.getTeacher()); - } else { - teacher.setVisibility(View.GONE); - teacherLabel.setVisibility(View.GONE); - } - - if (!lesson.getGroupName().isEmpty()) { - group.setText(lesson.getGroupName()); - } else { - group.setVisibility(View.GONE); - groupLabel.setVisibility(View.GONE); - } - - if (!lesson.getRoom().isEmpty()) { - room.setText(lesson.getRoom()); - } - - if (!lesson.getEndTime().isEmpty() && !lesson.getStartTime().isEmpty()) { - time.setText(getTimeString()); - } - - if (!lesson.getDescription().isEmpty()) { - description.setText(StringUtils.capitalize(lesson.getDescription())); - } else { - description.setVisibility(View.GONE); - descriptionLabel.setVisibility(View.GONE); - } - - return view; - } - - private String getTimeString() { - return String.format("%1$s - %2$s", lesson.getStartTime(), lesson.getEndTime()); - } - - @OnClick(R.id.timetable_dialog_close) - void onClickCloseButton() { - dismiss(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableFragment.java deleted file mode 100644 index 2df28df2c..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableFragment.java +++ /dev/null @@ -1,105 +0,0 @@ -package io.github.wulkanowy.ui.main.timetable; - -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.design.widget.Snackbar; -import android.support.design.widget.TabLayout; -import android.support.v4.view.ViewPager; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import javax.inject.Inject; - -import butterknife.BindView; -import butterknife.ButterKnife; -import io.github.wulkanowy.R; -import io.github.wulkanowy.di.component.FragmentComponent; -import io.github.wulkanowy.ui.base.BaseFragment; -import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; -import io.github.wulkanowy.ui.main.TabsData; - -public class TimetableFragment extends BaseFragment implements TimetableContract.View { - - private static final String CURRENT_ITEM_KEY = "CurrentItem"; - - @BindView(R.id.timetable_fragment_viewpager) - ViewPager viewPager; - - @BindView(R.id.timetable_fragment_tab_layout) - TabLayout tabLayout; - - @Inject - TimetablePagerAdapter pagerAdapter; - - @Inject - TimetableContract.Presenter presenter; - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_timetable, container, false); - - FragmentComponent component = getFragmentComponent(); - if (component != null) { - component.inject(this); - setButterKnife(ButterKnife.bind(this, view)); - presenter.onStart(this, (OnFragmentIsReadyListener) getActivity()); - - if (savedInstanceState != null) { - presenter.setRestoredPosition(savedInstanceState.getInt(CURRENT_ITEM_KEY)); - } - } - return view; - } - - @Override - public void setMenuVisibility(boolean menuVisible) { - super.setMenuVisibility(menuVisible); - if (presenter != null) { - presenter.onFragmentActivated(menuVisible); - } - } - - @Override - public void setTabDataToAdapter(TabsData tabsData) { - pagerAdapter.setTabsData(tabsData); - } - - @Override - public void setAdapterWithTabLayout() { - viewPager.setAdapter(pagerAdapter); - tabLayout.setupWithViewPager(viewPager); - } - - @Override - public void scrollViewPagerToPosition(int position) { - viewPager.setCurrentItem(position, false); - } - - @Override - public void setActivityTitle() { - setTitle(getString(R.string.lessonplan_text)); - } - - @Override - public void onError(String message) { - if (getActivity() != null) { - Snackbar.make(getActivity().findViewById(R.id.main_activity_view_pager), - message, Snackbar.LENGTH_LONG).show(); - } - } - - @Override - public void onSaveInstanceState(Bundle outState) { - outState.putInt(CURRENT_ITEM_KEY, viewPager.getCurrentItem()); - super.onSaveInstanceState(outState); - } - - @Override - public void onDestroyView() { - presenter.onDestroy(); - super.onDestroyView(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableHeaderItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableHeaderItem.java deleted file mode 100644 index c904d9317..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableHeaderItem.java +++ /dev/null @@ -1,145 +0,0 @@ -package io.github.wulkanowy.ui.main.timetable; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.drawable.Drawable; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.TextView; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; - -import java.util.List; - -import butterknife.BindColor; -import butterknife.BindView; -import butterknife.ButterKnife; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem; -import eu.davidea.flexibleadapter.items.IFlexible; -import eu.davidea.viewholders.ExpandableViewHolder; -import io.github.wulkanowy.R; -import io.github.wulkanowy.data.db.dao.entities.Day; - -public class TimetableHeaderItem - extends AbstractExpandableHeaderItem { - - private Day day; - - TimetableHeaderItem(Day day) { - this.day = day; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - - if (o == null || getClass() != o.getClass()) return false; - - TimetableHeaderItem that = (TimetableHeaderItem) o; - - return new EqualsBuilder() - .append(day, that.day) - .isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder(17, 37) - .append(day) - .toHashCode(); - } - - @Override - public int getLayoutRes() { - return R.layout.timetable_header; - } - - @Override - public HeaderViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new HeaderViewHolder(view, adapter); - } - - @Override - public void bindViewHolder(FlexibleAdapter adapter, HeaderViewHolder holder, - int position, List payloads) { - holder.onBind(day, getSubItems()); - } - - static class HeaderViewHolder extends ExpandableViewHolder { - - @BindView(R.id.timetable_header_day) - TextView dayName; - - @BindView(R.id.timetable_header_date) - TextView date; - - @BindView(R.id.timetable_header_alert_image) - ImageView alert; - - @BindView(R.id.timetable_header_free_name) - TextView freeName; - - @BindColor(R.color.secondary_text) - int secondaryColor; - - @BindColor(R.color.free_day) - int backgroundFreeDay; - - @BindColor(android.R.color.black) - int black; - - private Context context; - - HeaderViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - view.setOnClickListener(this); - ButterKnife.bind(this, view); - context = view.getContext(); - } - - void onBind(Day item, List subItems) { - dayName.setText(StringUtils.capitalize(item.getDayName())); - date.setText(item.getDate()); - alert.setVisibility(isSubItemNewMovedInOrChanged(subItems) ? View.VISIBLE : View.INVISIBLE); - freeName.setVisibility(item.getIsFreeDay() ? View.VISIBLE : View.INVISIBLE); - freeName.setText(item.getFreeDayName()); - setInactiveHeader(item.getIsFreeDay()); - } - - private void setInactiveHeader(boolean inactive) { - ((FrameLayout) getContentView()).setForeground(inactive ? null : getSelectableDrawable()); - dayName.setTextColor(inactive ? secondaryColor : black); - - if (inactive) { - getContentView().setBackgroundColor(backgroundFreeDay); - } else { - getContentView().setBackgroundDrawable(context.getResources() - .getDrawable(R.drawable.ic_border)); - } - } - - private Drawable getSelectableDrawable() { - int[] attrs = new int[]{R.attr.selectableItemBackground}; - TypedArray typedArray = context.obtainStyledAttributes(attrs); - Drawable drawable = typedArray.getDrawable(0); - typedArray.recycle(); - return drawable; - } - - private boolean isSubItemNewMovedInOrChanged(List subItems) { - boolean isAlertActive = false; - - for (TimetableSubItem subItem : subItems) { - if (subItem.getLesson().getIsMovedOrCanceled() || - subItem.getLesson().getIsNewMovedInOrChanged()) { - isAlertActive = true; - } - } - return isAlertActive; - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePagerAdapter.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePagerAdapter.java deleted file mode 100644 index 0b8523420..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePagerAdapter.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.wulkanowy.ui.main.timetable; - -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentStatePagerAdapter; - -import io.github.wulkanowy.ui.main.TabsData; - -public class TimetablePagerAdapter extends FragmentStatePagerAdapter { - - private TabsData tabsData; - - public TimetablePagerAdapter(FragmentManager fragmentManager) { - super(fragmentManager); - } - - void setTabsData(TabsData tabsData) { - this.tabsData = tabsData; - } - - @Override - public Fragment getItem(int position) { - return tabsData.getFragment(position); - } - - - @Override - public int getCount() { - return tabsData.getFragmentsCount(); - } - - @Nullable - @Override - public CharSequence getPageTitle(int position) { - return tabsData.getTitle(position); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePresenter.java deleted file mode 100644 index 080ec90ba..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePresenter.java +++ /dev/null @@ -1,107 +0,0 @@ -package io.github.wulkanowy.ui.main.timetable; - -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.ui.base.BasePresenter; -import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; -import io.github.wulkanowy.ui.main.TabsData; -import io.github.wulkanowy.utils.TimeUtils; -import io.github.wulkanowy.utils.async.AbstractTask; -import io.github.wulkanowy.utils.async.AsyncListeners; - -public class TimetablePresenter extends BasePresenter - implements TimetableContract.Presenter, AsyncListeners.OnFirstLoadingListener { - - private AbstractTask loadingTask; - - private List dates = new ArrayList<>(); - - private TabsData tabsData = new TabsData(); - - private OnFragmentIsReadyListener listener; - - private int positionToScroll = 0; - - private boolean isFirstSight = false; - - @Inject - TimetablePresenter(RepositoryContract repository) { - super(repository); - } - - @Override - public void onStart(TimetableContract.View view, OnFragmentIsReadyListener listener) { - super.onStart(view); - this.listener = listener; - - if (getView().isMenuVisible()) { - getView().setActivityTitle(); - } - - if (dates.isEmpty()) { - dates = TimeUtils.getMondaysFromCurrentSchoolYear(); - } - - if (positionToScroll == 0) { - positionToScroll = dates.indexOf(TimeUtils.getDateOfCurrentMonday(true)); - } - - if (!isFirstSight) { - isFirstSight = true; - - loadingTask = new AbstractTask(); - loadingTask.setOnFirstLoadingListener(this); - loadingTask.execute(); - } - } - - @Override - public void onFragmentActivated(boolean isVisible) { - if (isVisible) { - getView().setActivityTitle(); - } - } - - @Override - public void onDoInBackgroundLoading() throws Exception { - for (String date : dates) { - tabsData.addTitle(date); - tabsData.addFragment(TimetableTabFragment.newInstance(date)); - } - } - - @Override - public void onCanceledLoadingAsync() { - //do nothing - } - - @Override - public void onEndLoadingAsync(boolean result, Exception exception) { - if (result) { - getView().setTabDataToAdapter(tabsData); - getView().setAdapterWithTabLayout(); - getView().scrollViewPagerToPosition(positionToScroll); - listener.onFragmentIsReady(); - } - } - - @Override - public void setRestoredPosition(int position) { - this.positionToScroll = position; - } - - @Override - public void onDestroy() { - isFirstSight = false; - - if (loadingTask != null) { - loadingTask.cancel(true); - loadingTask = null; - } - super.onDestroy(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableSubItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableSubItem.java deleted file mode 100644 index 3d85772a6..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableSubItem.java +++ /dev/null @@ -1,143 +0,0 @@ -package io.github.wulkanowy.ui.main.timetable; - -import android.content.Context; -import android.graphics.Paint; -import android.support.v4.app.DialogFragment; -import android.support.v4.app.FragmentActivity; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; - -import java.util.List; - -import butterknife.BindView; -import butterknife.ButterKnife; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.items.AbstractSectionableItem; -import eu.davidea.flexibleadapter.items.IFlexible; -import eu.davidea.viewholders.FlexibleViewHolder; -import io.github.wulkanowy.R; -import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; - - -public class TimetableSubItem - extends AbstractSectionableItem { - - private TimetableLesson lesson; - - TimetableSubItem(TimetableHeaderItem header, TimetableLesson lesson) { - super(header); - this.lesson = lesson; - } - - public TimetableLesson getLesson() { - return lesson; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - - if (o == null || getClass() != o.getClass()) return false; - - TimetableSubItem that = (TimetableSubItem) o; - - return new EqualsBuilder() - .append(lesson, that.lesson) - .isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder(17, 37) - .append(lesson) - .toHashCode(); - } - - @Override - public int getLayoutRes() { - return R.layout.timetable_subitem; - } - - @Override - public SubItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new SubItemViewHolder(view, adapter); - } - - @Override - public void bindViewHolder(FlexibleAdapter adapter, SubItemViewHolder holder, - int position, List payloads) { - holder.onBind(lesson); - } - - static class SubItemViewHolder extends FlexibleViewHolder { - - @BindView(R.id.timetable_subItem_lesson) - TextView lessonName; - - @BindView(R.id.timetable_subItem_number_of_lesson) - TextView numberOfLesson; - - @BindView(R.id.timetable_subItem_time) - TextView lessonTime; - - @BindView(R.id.timetable_subItem_room) - TextView room; - - @BindView(R.id.timetable_subItem_alert_image) - ImageView alert; - - private Context context; - - private TimetableLesson item; - - SubItemViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - ButterKnife.bind(this, view); - context = view.getContext(); - view.setOnClickListener(this); - } - - void onBind(TimetableLesson lesson) { - item = lesson; - - lessonName.setText(lesson.getSubject()); - lessonTime.setText(getLessonTimeString()); - numberOfLesson.setText(lesson.getNumber()); - room.setText(getRoomString()); - alert.setVisibility(lesson.getIsMovedOrCanceled() || lesson.getIsNewMovedInOrChanged() - ? View.VISIBLE : View.INVISIBLE); - lessonName.setPaintFlags(lesson.getIsMovedOrCanceled() ? lessonName.getPaintFlags() - | Paint.STRIKE_THRU_TEXT_FLAG : - lessonName.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG)); - room.setText(getRoomString()); - } - - @Override - public void onClick(View view) { - super.onClick(view); - showDialog(); - } - - private String getLessonTimeString() { - return String.format("%1$s - %2$s", item.getStartTime(), item.getEndTime()); - } - - private String getRoomString() { - if (!item.getRoom().isEmpty()) { - return context.getString(R.string.timetable_subitem_room, item.getRoom()); - } else { - return item.getRoom(); - } - } - - private void showDialog() { - TimetableDialogFragment dialogFragment = TimetableDialogFragment.newInstance(item); - dialogFragment.setStyle(DialogFragment.STYLE_NO_TITLE, 0); - dialogFragment.show(((FragmentActivity) context).getSupportFragmentManager(), item.toString()); - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabContract.java deleted file mode 100644 index 0d6bdf2a6..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabContract.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.wulkanowy.ui.main.timetable; - -import java.util.List; - -import io.github.wulkanowy.ui.base.BaseContract; - -public interface TimetableTabContract { - - interface View extends BaseContract.View { - - void updateAdapterList(List headerItems); - - void onRefreshSuccess(); - - void hideRefreshingBar(); - - void showNoItem(boolean show); - - void showProgressBar(boolean show); - - void setFreeWeekName(String text); - } - - interface Presenter extends BaseContract.Presenter { - - void onFragmentActivated(boolean isSelected); - - void setArgumentDate(String date); - - void onRefresh(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabFragment.java deleted file mode 100644 index 9cbca0452..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabFragment.java +++ /dev/null @@ -1,154 +0,0 @@ -package io.github.wulkanowy.ui.main.timetable; - -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.design.widget.Snackbar; -import android.support.v4.widget.SwipeRefreshLayout; -import android.support.v7.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import java.util.List; - -import javax.inject.Inject; - -import butterknife.BindView; -import butterknife.ButterKnife; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager; -import io.github.wulkanowy.R; -import io.github.wulkanowy.di.component.FragmentComponent; -import io.github.wulkanowy.ui.base.BaseFragment; - -public class TimetableTabFragment extends BaseFragment implements TimetableTabContract.View, - SwipeRefreshLayout.OnRefreshListener { - - private static final String ARGUMENT_KEY = "date"; - - @BindView(R.id.timetable_tab_fragment_recycler) - RecyclerView recyclerView; - - @BindView(R.id.timetable_tab_fragment_swipe_refresh) - SwipeRefreshLayout refreshLayout; - - @BindView(R.id.timetable_tab_fragment_progress_bar) - View progressBar; - - @BindView(R.id.timetable_tab_fragment_no_item_container) - View noItemView; - - @BindView(R.id.timetable_tab_fragment_no_item_name) - TextView noItemName; - - @Inject - TimetableTabContract.Presenter presenter; - - @Inject - FlexibleAdapter adapter; - - private boolean isFragmentVisible = false; - - public static TimetableTabFragment newInstance(String date) { - TimetableTabFragment fragmentTab = new TimetableTabFragment(); - - Bundle bundle = new Bundle(); - bundle.putString(ARGUMENT_KEY, date); - fragmentTab.setArguments(bundle); - - return fragmentTab; - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_timetable_tab, container, false); - - FragmentComponent component = getFragmentComponent(); - if (component != null) { - component.inject(this); - setButterKnife(ButterKnife.bind(this, view)); - - if (getArguments() != null) { - presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY)); - } - presenter.onStart(this); - presenter.onFragmentActivated(isFragmentVisible); - } - return view; - } - - @Override - protected void setUpOnViewCreated(View fragmentView) { - adapter.setAutoCollapseOnExpand(true); - adapter.setAutoScrollOnExpand(true); - adapter.expandItemsAtStartUp(); - - recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(fragmentView.getContext())); - recyclerView.setAdapter(adapter); - - refreshLayout.setColorSchemeResources(android.R.color.black); - refreshLayout.setOnRefreshListener(this); - - } - - @Override - public void updateAdapterList(List headerItems) { - adapter.updateDataSet(headerItems); - } - - @Override - public void setMenuVisibility(boolean menuVisible) { - super.setMenuVisibility(menuVisible); - isFragmentVisible = menuVisible; - if (presenter != null) { - presenter.onFragmentActivated(menuVisible); - } - } - - @Override - public void setFreeWeekName(String text) { - noItemName.setText(text); - } - - @Override - public void onRefresh() { - presenter.onRefresh(); - } - - @Override - public void onRefreshSuccess() { - onError(R.string.timetable_refresh_success); - } - - @Override - public void hideRefreshingBar() { - refreshLayout.setRefreshing(false); - } - - @Override - public void showProgressBar(boolean show) { - progressBar.setVisibility(show ? View.VISIBLE : View.INVISIBLE); - } - - @Override - public void showNoItem(boolean show) { - noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE); - } - - @Override - public void onError(String message) { - if (getActivity() != null) { - Snackbar.make(getActivity().findViewById(R.id.main_activity_view_pager), - message, Snackbar.LENGTH_LONG).show(); - } - } - - @Override - public void onDestroyView() { - presenter.onDestroy(); - super.onDestroyView(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabPresenter.java deleted file mode 100644 index dcfe2612c..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabPresenter.java +++ /dev/null @@ -1,182 +0,0 @@ -package io.github.wulkanowy.ui.main.timetable; - - -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.data.db.dao.entities.Day; -import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; -import io.github.wulkanowy.data.db.dao.entities.Week; -import io.github.wulkanowy.ui.base.BasePresenter; -import io.github.wulkanowy.utils.async.AbstractTask; -import io.github.wulkanowy.utils.async.AsyncListeners; - -public class TimetableTabPresenter extends BasePresenter - implements TimetableTabContract.Presenter, AsyncListeners.OnRefreshListener, - AsyncListeners.OnFirstLoadingListener { - - private AbstractTask refreshTask; - - private AbstractTask loadingTask; - - private List headerItems = new ArrayList<>(); - - private String date; - - private String freeWeekName; - - private boolean isFirstSight = false; - - @Inject - TimetableTabPresenter(RepositoryContract repository) { - super(repository); - } - - @Override - public void onStart(TimetableTabContract.View view) { - super.onStart(view); - getView().showProgressBar(true); - getView().showNoItem(false); - } - - @Override - public void onFragmentActivated(boolean isSelected) { - if (!isFirstSight && isSelected && isViewAttached()) { - isFirstSight = true; - - loadingTask = new AbstractTask(); - loadingTask.setOnFirstLoadingListener(this); - loadingTask.execute(); - } else if (!isSelected) { - cancelAsyncTasks(); - } - } - - @Override - public void onRefresh() { - if (getView().isNetworkConnected()) { - refreshTask = new AbstractTask(); - refreshTask.setOnRefreshListener(this); - refreshTask.execute(); - } else { - getView().onNoNetworkError(); - getView().hideRefreshingBar(); - } - } - - @Override - public void onDoInBackgroundRefresh() throws Exception { - syncData(); - } - - @Override - public void onCanceledRefreshAsync() { - if (isViewAttached()) { - getView().hideRefreshingBar(); - } - } - - @Override - public void onEndRefreshAsync(boolean result, Exception exception) { - if (result) { - loadingTask = new AbstractTask(); - loadingTask.setOnFirstLoadingListener(this); - loadingTask.execute(); - - getView().onRefreshSuccess(); - } else { - getView().onError(getRepository().getErrorLoginMessage(exception)); - } - getView().hideRefreshingBar(); - } - - @Override - public void onDoInBackgroundLoading() throws Exception { - Week week = getRepository().getWeek(date); - - if (week == null || !week.getIsTimetableSynced()) { - syncData(); - week = getRepository().getWeek(date); - } - - List dayList = week.getDayList(); - - headerItems = new ArrayList<>(); - - boolean isFreeWeek = true; - - for (Day day : dayList) { - day.resetTimetableLessons(); - TimetableHeaderItem headerItem = new TimetableHeaderItem(day); - - if (isFreeWeek) { - isFreeWeek = day.getIsFreeDay(); - } - - List lessonList = day.getTimetableLessons(); - - List subItems = new ArrayList<>(); - - for (TimetableLesson lesson : lessonList) { - subItems.add(new TimetableSubItem(headerItem, lesson)); - } - - headerItem.setSubItems(subItems); - headerItem.setExpanded(false); - headerItems.add(headerItem); - } - - if (isFreeWeek) { - freeWeekName = dayList.get(4).getFreeDayName(); - headerItems = new ArrayList<>(); - } - } - - @Override - public void onCanceledLoadingAsync() { - // do nothing - } - - @Override - public void onEndLoadingAsync(boolean result, Exception exception) { - if (headerItems.isEmpty()) { - getView().showNoItem(true); - getView().setFreeWeekName(freeWeekName); - getView().updateAdapterList(null); - } else { - getView().updateAdapterList(headerItems); - getView().showNoItem(false); - } - getView().showProgressBar(false); - } - - @Override - public void setArgumentDate(String date) { - this.date = date; - } - - private void syncData() throws Exception { - getRepository().syncTimetable(date); - } - - private void cancelAsyncTasks() { - if (refreshTask != null) { - refreshTask.cancel(true); - refreshTask = null; - } - if (loadingTask != null) { - loadingTask.cancel(true); - loadingTask = null; - } - } - - @Override - public void onDestroy() { - isFirstSight = false; - cancelAsyncTasks(); - super.onDestroy(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt new file mode 100644 index 000000000..561419a05 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt @@ -0,0 +1,128 @@ +package io.github.wulkanowy.ui.modules + +import androidx.fragment.app.Fragment +import io.github.wulkanowy.data.serializers.LocalDateSerializer +import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment +import io.github.wulkanowy.ui.modules.conference.ConferenceFragment +import io.github.wulkanowy.ui.modules.dashboard.DashboardFragment +import io.github.wulkanowy.ui.modules.exam.ExamFragment +import io.github.wulkanowy.ui.modules.grade.GradeFragment +import io.github.wulkanowy.ui.modules.homework.HomeworkFragment +import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment +import io.github.wulkanowy.ui.modules.message.MessageFragment +import io.github.wulkanowy.ui.modules.more.MoreFragment +import io.github.wulkanowy.ui.modules.note.NoteFragment +import io.github.wulkanowy.ui.modules.schoolandteachers.school.SchoolFragment +import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment +import io.github.wulkanowy.ui.modules.timetable.TimetableFragment +import kotlinx.serialization.Serializable +import java.time.LocalDate + +@Serializable +sealed class Destination { + + /* + Type in children classes have to be as getter to avoid null in enums + https://stackoverflow.com/questions/68866453/kotlin-enum-val-is-returning-null-despite-being-set-at-compile-time + */ + abstract val destinationType: Type + + abstract val destinationFragment: Fragment + + enum class Type(val defaultDestination: Destination) { + DASHBOARD(Dashboard), + GRADE(Grade), + ATTENDANCE(Attendance), + EXAM(Exam), + TIMETABLE(Timetable()), + HOMEWORK(Homework), + NOTE(Note), + CONFERENCE(Conference), + SCHOOL_ANNOUNCEMENT(SchoolAnnouncement), + SCHOOL(School), + LUCKY_NUMBER(More), + MORE(More), + MESSAGE(Message); + } + + @Serializable + object Dashboard : Destination() { + override val destinationType get() = Type.DASHBOARD + override val destinationFragment get() = DashboardFragment.newInstance() + } + + @Serializable + object Grade : Destination() { + override val destinationType get() = Type.GRADE + override val destinationFragment get() = GradeFragment.newInstance() + } + + @Serializable + object Attendance : Destination() { + override val destinationType get() = Type.ATTENDANCE + override val destinationFragment get() = AttendanceFragment.newInstance() + } + + @Serializable + object Exam : Destination() { + override val destinationType get() = Type.EXAM + override val destinationFragment get() = ExamFragment.newInstance() + } + + @Serializable + data class Timetable( + @Serializable(with = LocalDateSerializer::class) + private val date: LocalDate? = null + ) : Destination() { + override val destinationType get() = Type.TIMETABLE + override val destinationFragment get() = TimetableFragment.newInstance(date) + } + + @Serializable + object Homework : Destination() { + override val destinationType get() = Type.HOMEWORK + override val destinationFragment get() = HomeworkFragment.newInstance() + } + + @Serializable + object Note : Destination() { + override val destinationType get() = Type.NOTE + override val destinationFragment get() = NoteFragment.newInstance() + } + + @Serializable + object Conference : Destination() { + override val destinationType get() = Type.CONFERENCE + override val destinationFragment get() = ConferenceFragment.newInstance() + } + + @Serializable + object SchoolAnnouncement : Destination() { + override val destinationType get() = Type.SCHOOL_ANNOUNCEMENT + override val destinationFragment get() = SchoolAnnouncementFragment.newInstance() + } + + @Serializable + object School : Destination() { + override val destinationType get() = Type.SCHOOL + override val destinationFragment get() = SchoolFragment.newInstance() + } + + @Serializable + object LuckyNumber : Destination() { + override val destinationType get() = Type.LUCKY_NUMBER + override val destinationFragment get() = LuckyNumberFragment.newInstance() + } + + @Serializable + object More : Destination() { + override val destinationType get() = Type.MORE + override val destinationFragment get() = MoreFragment.newInstance() + } + + @Serializable + object Message : Destination() { + override val destinationType get() = Type.MESSAGE + override val destinationFragment get() = MessageFragment.newInstance() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutAdapter.kt new file mode 100644 index 000000000..35dec3b4f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutAdapter.kt @@ -0,0 +1,72 @@ +package io.github.wulkanowy.ui.modules.about + +import android.graphics.drawable.Drawable +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.content.res.ResourcesCompat +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.databinding.ItemAboutBinding +import io.github.wulkanowy.databinding.ScrollableHeaderAboutBinding +import javax.inject.Inject + +class AboutAdapter @Inject constructor() : RecyclerView.Adapter() { + + private enum class ViewType(val id: Int) { + ITEM_HEADER(1), + ITEM_ELEMENT(2) + } + + var items = emptyList>() + + var onClickListener: (name: String) -> Unit = {} + + override fun getItemCount() = items.size + 1 + + override fun getItemViewType(position: Int) = when (position) { + 0 -> ViewType.ITEM_HEADER.id + else -> ViewType.ITEM_ELEMENT.id + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + + return when (viewType) { + ViewType.ITEM_HEADER.id -> HeaderViewHolder(ScrollableHeaderAboutBinding.inflate(inflater, parent, false)) + ViewType.ITEM_ELEMENT.id -> ItemViewHolder(ItemAboutBinding.inflate(inflater, parent, false)) + else -> throw IllegalStateException() + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is HeaderViewHolder -> bindHeaderViewHolder(holder.binding) + is ItemViewHolder -> bindItemViewHolder(holder.binding, position - 1) + } + } + + private fun bindHeaderViewHolder(binding: ScrollableHeaderAboutBinding) { + with(binding.aboutScrollableHeaderIcon) { + setImageDrawable(ResourcesCompat.getDrawableForDensity( + context.resources, context.applicationInfo.icon, 640, null) + ) + } + } + + private fun bindItemViewHolder(binding: ItemAboutBinding, position: Int) { + val (title, summary, image) = items[position] + + with(binding) { + aboutItemImage.setImageDrawable(image) + aboutItemTitle.text = title + aboutItemSummary.text = summary + + root.setOnClickListener { onClickListener(title) } + } + } + + private class HeaderViewHolder(val binding: ScrollableHeaderAboutBinding) : + RecyclerView.ViewHolder(binding.root) + + private class ItemViewHolder(val binding: ItemAboutBinding) : + RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt new file mode 100644 index 000000000..701656b55 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt @@ -0,0 +1,225 @@ +package io.github.wulkanowy.ui.modules.about + +import android.graphics.drawable.Drawable +import android.os.Bundle +import android.view.View +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.databinding.FragmentAboutBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.about.contributor.ContributorFragment +import io.github.wulkanowy.ui.modules.about.license.LicenseFragment +import io.github.wulkanowy.ui.modules.debug.DebugFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.* +import java.time.Instant +import javax.inject.Inject + +@AndroidEntryPoint +class AboutFragment : BaseFragment(R.layout.fragment_about), AboutView, + MainView.TitledView { + + @Inject + lateinit var presenter: AboutPresenter + + @Inject + lateinit var aboutAdapter: AboutAdapter + + @Inject + lateinit var appInfo: AppInfo + + override val versionRes: Triple? + get() = context?.run { + val buildTimestamp = + Instant.ofEpochMilli(appInfo.buildTimestamp).toFormattedString("yyyy-MM-dd") + val versionSignature = + "${appInfo.versionName}-${appInfo.buildFlavor} (${appInfo.versionCode}), $buildTimestamp" + Triple( + getString(R.string.about_version), + versionSignature, + getCompatDrawable(R.drawable.ic_all_about) + ) + } + + override val creatorsRes: Triple? + get() = context?.run { + Triple( + getString(R.string.about_contributor), + getString(R.string.about_contributor_summary), + getCompatDrawable(R.drawable.ic_about_creator) + ) + } + + override val feedbackRes: Triple? + get() = context?.run { + Triple( + getString(R.string.about_feedback), + getString(R.string.about_feedback_summary), + getCompatDrawable(R.drawable.ic_about_feedback) + ) + } + + override val faqRes: Triple? + get() = context?.run { + Triple( + getString(R.string.about_faq), + getString(R.string.about_faq_summary), + getCompatDrawable(R.drawable.ic_about_faq) + ) + } + + override val discordRes: Triple? + get() = context?.run { + Triple( + getString(R.string.about_discord), + getString(R.string.about_discord_summary), + getCompatDrawable(R.drawable.ic_about_discord) + ) + } + + override val facebookRes: Triple? + get() = context?.run { + Triple( + getString(R.string.about_facebook), + getString(R.string.about_facebook_summary), + getCompatDrawable(R.drawable.ic_about_facebook) + ) + } + + override val twitterRes: Triple? + get() = context?.run { + Triple( + getString(R.string.about_twitter), + getString(R.string.about_twitter_summary), + getCompatDrawable(R.drawable.ic_about_twitter) + ) + } + + override val homepageRes: Triple? + get() = context?.run { + Triple( + getString(R.string.about_homepage), + getString(R.string.about_homepage_summary), + getCompatDrawable(R.drawable.ic_all_home) + ) + } + + override val licensesRes: Triple? + get() = context?.run { + Triple( + getString(R.string.about_licenses), + getString(R.string.about_licenses_summary), + getCompatDrawable(R.drawable.ic_about_licenses) + ) + } + + override val privacyRes: Triple? + get() = context?.run { + Triple( + getString(R.string.about_privacy), + getString(R.string.about_privacy_summary), + getCompatDrawable(R.drawable.ic_about_privacy) + ) + } + + override val titleStringId get() = R.string.about_title + + companion object { + fun newInstance() = AboutFragment() + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentAboutBinding.bind(view) + presenter.onAttachView(this) + } + + override fun initView() { + aboutAdapter.onClickListener = presenter::onItemSelected + + with(binding.aboutRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = aboutAdapter + } + } + + override fun updateData(data: List>) { + with(aboutAdapter) { + items = data + notifyDataSetChanged() + } + } + + override fun openAppInMarket() { + context?.openAppInMarket(::showMessage) + } + + override fun openDebugScreen() { + (activity as? MainActivity)?.pushView(DebugFragment.newInstance()) + } + + override fun openDiscordInvite() { + context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage) + } + + override fun openFacebookPage() { + context?.openInternetBrowser("https://www.facebook.com/wulkanowy", ::showMessage) + } + + override fun openTwitterPage() { + context?.openInternetBrowser("https://twitter.com/wulkanowy", ::showMessage) + } + + override fun openHomepage() { + context?.openInternetBrowser("https://wulkanowy.github.io/", ::showMessage) + } + + override fun openEmailClient() { + requireContext().openEmailClient( + chooserTitle = getString(R.string.about_feedback), + email = "wulkanowyinc@gmail.com", + subject = "Zgłoszenie błędu", + body = getString( + R.string.about_feedback_template, + "${appInfo.systemManufacturer} ${appInfo.systemModel}", + appInfo.systemVersion.toString(), + "${appInfo.versionName}-${appInfo.buildFlavor}" + ), + onActivityNotFound = { + requireContext().openInternetBrowser( + "https://github.com/wulkanowy/wulkanowy/issues", + ::showMessage + ) + } + ) + } + + override fun openFaqPage() { + context?.openInternetBrowser( + "https://wulkanowy.github.io/czesto-zadawane-pytania", + ::showMessage + ) + } + + override fun openLicenses() { + (activity as? MainActivity)?.pushView(LicenseFragment.newInstance()) + } + + override fun openCreators() { + (activity as? MainActivity)?.pushView(ContributorFragment.newInstance()) + } + + override fun openPrivacyPolicy() { + context?.openInternetBrowser( + "https://wulkanowy.github.io/polityka-prywatnosci.html", + ::showMessage + ) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt new file mode 100644 index 000000000..552749349 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt @@ -0,0 +1,101 @@ +package io.github.wulkanowy.ui.modules.about + +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.AppInfo +import timber.log.Timber +import javax.inject.Inject + +class AboutPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val appInfo: AppInfo, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + override fun onAttachView(view: AboutView) { + super.onAttachView(view) + view.initView() + Timber.i("About view was initialized") + loadData() + } + + fun onItemSelected(name: String) { + view?.run { + when (name) { + versionRes?.first -> { + Timber.i("Opening debug screen") + if (appInfo.isDebug) openDebugScreen() + else openAppInMarket() + analytics.logEvent("about_open", "name" to "debug_screen") + } + feedbackRes?.first -> { + Timber.i("Opening email client") + openEmailClient() + analytics.logEvent("about_open", "name" to "feedback") + } + faqRes?.first -> { + Timber.i("Opening faq page") + openFaqPage() + analytics.logEvent("about_open", "name" to "faq") + } + discordRes?.first -> { + Timber.i("Opening discord") + openDiscordInvite() + analytics.logEvent("about_open", "name" to "discord") + } + facebookRes?.first -> { + Timber.i("Opening facebook") + openFacebookPage() + analytics.logEvent("about_open", "name" to "facebook") + } + twitterRes?.first -> { + Timber.i("Opening twitter") + openTwitterPage() + analytics.logEvent("about_open", "name" to "twitter") + } + homepageRes?.first -> { + Timber.i("Opening homepage") + openHomepage() + analytics.logEvent("about_open", "name" to "homepage") + } + licensesRes?.first -> { + Timber.i("Opening licenses view") + openLicenses() + analytics.logEvent("about_open", "name" to "licenses") + } + creatorsRes?.first -> { + Timber.i("Opening creators view") + openCreators() + analytics.logEvent("about_open", "name" to "creators") + } + privacyRes?.first -> { + Timber.i("Opening privacy page ") + openPrivacyPolicy() + analytics.logEvent("about_open", "name" to "privacy") + } + } + } + } + + private fun loadData() { + view?.run { + updateData( + listOfNotNull( + versionRes, + creatorsRes, + feedbackRes, + faqRes, + discordRes, + facebookRes, + twitterRes, + homepageRes, + licensesRes, + privacyRes + ) + ) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt new file mode 100644 index 000000000..5c286828b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt @@ -0,0 +1,53 @@ +package io.github.wulkanowy.ui.modules.about + +import android.graphics.drawable.Drawable +import io.github.wulkanowy.ui.base.BaseView + +interface AboutView : BaseView { + + val versionRes: Triple? + + val creatorsRes: Triple? + + val feedbackRes: Triple? + + val faqRes: Triple? + + val discordRes: Triple? + + val twitterRes: Triple? + + val facebookRes: Triple? + + val homepageRes: Triple? + + val licensesRes: Triple? + + val privacyRes: Triple? + + fun initView() + + fun updateData(data: List>) + + fun openAppInMarket() + + fun openDebugScreen() + + fun openDiscordInvite() + + fun openFacebookPage() + + fun openTwitterPage() + + fun openEmailClient() + + fun openFaqPage() + + fun openHomepage() + + fun openLicenses() + + fun openCreators() + + fun openPrivacyPolicy() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorAdapter.kt new file mode 100644 index 000000000..b6755c6a7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorAdapter.kt @@ -0,0 +1,41 @@ +package io.github.wulkanowy.ui.modules.about.contributor + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import coil.load +import coil.transform.RoundedCornersTransformation +import io.github.wulkanowy.data.pojos.Contributor +import io.github.wulkanowy.databinding.ItemContributorBinding +import javax.inject.Inject + +class ContributorAdapter @Inject constructor() : + RecyclerView.Adapter() { + + var items = emptyList() + + var onClickListener: (Contributor) -> Unit = {} + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + ItemContributorBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + val item = items[position] + + with(holder.binding) { + creatorItemName.text = item.displayName + creatorItemAvatar.load("https://github.com/${item.githubUsername}.png") { + transformations(RoundedCornersTransformation(8f)) + crossfade(true) + } + + root.setOnClickListener { onClickListener(item) } + } + } + + class ItemViewHolder(val binding: ItemContributorBinding) : + RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorFragment.kt new file mode 100644 index 000000000..5d7e076c7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorFragment.kt @@ -0,0 +1,73 @@ +package io.github.wulkanowy.ui.modules.about.contributor + +import android.os.Bundle +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.pojos.Contributor +import io.github.wulkanowy.databinding.FragmentContributorBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.widgets.DividerItemDecoration +import io.github.wulkanowy.utils.openInternetBrowser +import javax.inject.Inject + +@AndroidEntryPoint +class ContributorFragment : BaseFragment(R.layout.fragment_contributor), + ContributorView, MainView.TitledView { + + @Inject + lateinit var presenter: ContributorPresenter + + @Inject + lateinit var creatorsAdapter: ContributorAdapter + + override val titleStringId get() = R.string.contributors_title + + companion object { + fun newInstance() = ContributorFragment() + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentContributorBinding.bind(view) + presenter.onAttachView(this) + } + + override fun initView() { + with(binding.creatorRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = creatorsAdapter + addItemDecoration(DividerItemDecoration(context)) + } + creatorsAdapter.onClickListener = presenter::onItemSelected + binding.creatorSeeMore.setOnClickListener { presenter.onSeeMoreClick() } + } + + override fun updateData(data: List) { + with(creatorsAdapter) { + items = data + notifyDataSetChanged() + } + } + + override fun openUserGithubPage(username: String) { + context?.openInternetBrowser("https://github.com/${username}", ::showMessage) + } + + override fun openGithubContributorsPage() { + context?.openInternetBrowser("https://github.com/wulkanowy/wulkanowy/graphs/contributors", ::showMessage) + } + + override fun showProgress(show: Boolean) { + binding.creatorProgress.visibility = if (show) VISIBLE else GONE + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorPresenter.kt new file mode 100644 index 000000000..126bb2b48 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorPresenter.kt @@ -0,0 +1,39 @@ +package io.github.wulkanowy.ui.modules.about.contributor + +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.pojos.Contributor +import io.github.wulkanowy.data.repositories.AppCreatorRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import javax.inject.Inject + +class ContributorPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val appCreatorRepository: AppCreatorRepository +) : BasePresenter(errorHandler, studentRepository) { + + override fun onAttachView(view: ContributorView) { + super.onAttachView(view) + view.initView() + loadData() + } + + fun onItemSelected(contributor: Contributor) { + view?.openUserGithubPage(contributor.githubUsername) + } + + fun onSeeMoreClick() { + view?.openGithubContributorsPage() + } + + private fun loadData() { + resourceFlow { appCreatorRepository.getAppCreators() } + .onResourceLoading { view?.showProgress(true) } + .onResourceSuccess { view?.updateData(it) } + .onResourceNotLoading { view?.showProgress(false) } + .onResourceError { errorHandler.dispatch(it) } + .launch() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorView.kt new file mode 100644 index 000000000..8007e4e3f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorView.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.ui.modules.about.contributor + +import io.github.wulkanowy.data.pojos.Contributor +import io.github.wulkanowy.ui.base.BaseView + +interface ContributorView : BaseView { + + fun initView() + + fun updateData(data: List) + + fun openUserGithubPage(username: String) + + fun openGithubContributorsPage() + + fun showProgress(show: Boolean) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseAdapter.kt new file mode 100644 index 000000000..6ae06bbe7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseAdapter.kt @@ -0,0 +1,34 @@ +package io.github.wulkanowy.ui.modules.about.license + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.mikepenz.aboutlibraries.entity.Library +import io.github.wulkanowy.databinding.ItemLicenseBinding +import javax.inject.Inject + +class LicenseAdapter @Inject constructor() : RecyclerView.Adapter() { + + var items = emptyList() + + var onClickListener: (Library) -> Unit = {} + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + ItemLicenseBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + val item = items[position] + + with(holder.binding) { + licenseItemName.text = item.libraryName + licenseItemSummary.text = item.licenses?.firstOrNull()?.licenseName?.takeIf { it.isNotBlank() } ?: item.libraryVersion + + root.setOnClickListener { onClickListener(item) } + } + } + + class ItemViewHolder(val binding: ItemLicenseBinding) : RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseFragment.kt new file mode 100644 index 000000000..4c3b608aa --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseFragment.kt @@ -0,0 +1,78 @@ +package io.github.wulkanowy.ui.modules.about.license + +import android.os.Bundle +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import androidx.core.text.parseAsHtml +import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.mikepenz.aboutlibraries.Libs +import com.mikepenz.aboutlibraries.entity.Library +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.databinding.FragmentLicenseBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainView +import javax.inject.Inject + +@AndroidEntryPoint +class LicenseFragment : BaseFragment(R.layout.fragment_license), + LicenseView, MainView.TitledView { + + @Inject + lateinit var presenter: LicensePresenter + + @Inject + lateinit var licenseAdapter: LicenseAdapter + + override val titleStringId get() = R.string.license_title + + override val appLibraries by lazy { Libs(requireContext()).libraries } + + companion object { + fun newInstance() = LicenseFragment() + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentLicenseBinding.bind(view) + presenter.onAttachView(this) + } + + override fun initView() { + licenseAdapter.onClickListener = presenter::onItemSelected + + with(binding.licenseRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = licenseAdapter + } + } + + override fun updateData(data: List) { + with(licenseAdapter) { + items = data + notifyDataSetChanged() + } + } + + override fun openLicense(licenseHtml: String) { + context?.let { + MaterialAlertDialogBuilder(it).apply { + setTitle(R.string.license_dialog_title) + setMessage(licenseHtml.parseAsHtml()) + setPositiveButton(android.R.string.ok) { _, _ -> } + show() + } + } + } + + override fun showProgress(show: Boolean) { + binding.licenseProgress.visibility = if (show) VISIBLE else GONE + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt new file mode 100644 index 000000000..5aa12a575 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt @@ -0,0 +1,41 @@ +package io.github.wulkanowy.ui.modules.about.license + +import com.mikepenz.aboutlibraries.entity.Library +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.DispatchersProvider +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import javax.inject.Inject + +class LicensePresenter @Inject constructor( + private val dispatchers: DispatchersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository +) : BasePresenter(errorHandler, studentRepository) { + + override fun onAttachView(view: LicenseView) { + super.onAttachView(view) + view.initView() + loadData() + } + + fun onItemSelected(library: Library) { + view?.run { library.licenses?.firstOrNull()?.licenseDescription?.let { openLicense(it) } } + } + + private fun loadData() { + presenterScope.launch { + runCatching { + withContext(dispatchers.io) { + view?.appLibraries.orEmpty() + } + } + .onFailure { errorHandler.dispatch(it) } + .onSuccess { view?.updateData(it) } + + view?.showProgress(false) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseView.kt new file mode 100644 index 000000000..6c97d8759 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseView.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.ui.modules.about.license + +import com.mikepenz.aboutlibraries.entity.Library +import io.github.wulkanowy.ui.base.BaseView + +interface LicenseView : BaseView { + + val appLibraries: List + + fun initView() + + fun updateData(data: List) + + fun openLicense(licenseHtml: String) + + fun showProgress(show: Boolean) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/Account.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/Account.kt new file mode 100644 index 000000000..dbcb499e8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/Account.kt @@ -0,0 +1,3 @@ +package io.github.wulkanowy.ui.modules.account + +data class Account(val email: String, val isParent: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountAdapter.kt new file mode 100644 index 000000000..523fbbd79 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountAdapter.kt @@ -0,0 +1,115 @@ +package io.github.wulkanowy.ui.modules.account + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.View.GONE +import android.view.View.VISIBLE +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.databinding.HeaderAccountBinding +import io.github.wulkanowy.databinding.ItemAccountBinding +import io.github.wulkanowy.utils.createNameInitialsDrawable +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.nickOrName +import javax.inject.Inject + +class AccountAdapter @Inject constructor() : RecyclerView.Adapter() { + + var isAccountQuickDialogMode = false + + var items = emptyList>() + + var onClickListener: (StudentWithSemesters) -> Unit = {} + + override fun getItemCount() = items.size + + override fun getItemViewType(position: Int) = items[position].viewType.id + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + + return when (viewType) { + AccountItem.ViewType.HEADER.id -> HeaderViewHolder( + HeaderAccountBinding.inflate(inflater, parent, false) + ) + AccountItem.ViewType.ITEM.id -> ItemViewHolder( + ItemAccountBinding.inflate(inflater, parent, false) + ) + else -> throw IllegalStateException() + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is HeaderViewHolder -> bindHeaderViewHolder( + holder.binding, + items[position].value as Account, + position + ) + is ItemViewHolder -> bindItemViewHolder( + holder.binding, + items[position].value as StudentWithSemesters + ) + } + } + + private fun bindHeaderViewHolder( + binding: HeaderAccountBinding, + account: Account, + position: Int + ) { + with(binding) { + accountHeaderDivider.visibility = if (position == 0) GONE else VISIBLE + accountHeaderEmail.text = account.email + accountHeaderType.setText(if (account.isParent) R.string.account_type_parent else R.string.account_type_student) + } + } + + @SuppressLint("SetTextI18n") + private fun bindItemViewHolder( + binding: ItemAccountBinding, + studentWithSemesters: StudentWithSemesters + ) { + val context = binding.root.context + val (student, semesters) = studentWithSemesters + val semester = semesters.maxByOrNull { it.semesterId } + val avatar = context.createNameInitialsDrawable(student.nickOrName, student.avatarColor) + val checkBackgroundColor = + context.getThemeAttrColor(if (isAccountQuickDialogMode) R.attr.colorBackgroundFloating else R.attr.colorSurface) + val isDuplicatedStudent = items.filter { + if (it.value !is StudentWithSemesters) return@filter false + val studentToCompare = it.value.student + + studentToCompare.studentId == student.studentId + && studentToCompare.schoolSymbol == student.schoolSymbol + && studentToCompare.symbol == student.symbol + }.size > 1 && isAccountQuickDialogMode + + with(binding) { + accountItemName.text = "${student.nickOrName} ${semester?.diaryName.orEmpty()}" + accountItemSchool.text = studentWithSemesters.student.schoolName + accountItemImage.setImageDrawable(avatar) + + with(accountItemAccountType) { + setText(if (student.isParent) R.string.account_type_parent else R.string.account_type_student) + isVisible = isDuplicatedStudent + } + + with(accountItemCheck) { + isVisible = student.isCurrent + borderColor = checkBackgroundColor + circleColor = checkBackgroundColor + } + + root.setOnClickListener { onClickListener(studentWithSemesters) } + } + } + + class HeaderViewHolder(val binding: HeaderAccountBinding) : + RecyclerView.ViewHolder(binding.root) + + class ItemViewHolder(val binding: ItemAccountBinding) : RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountFragment.kt new file mode 100644 index 000000000..051c93c95 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountFragment.kt @@ -0,0 +1,81 @@ +package io.github.wulkanowy.ui.modules.account + +import android.os.Bundle +import android.view.Menu +import android.view.MenuInflater +import android.view.View +import androidx.core.view.get +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.databinding.FragmentAccountBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsFragment +import io.github.wulkanowy.ui.modules.login.LoginActivity +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import javax.inject.Inject + +@AndroidEntryPoint +class AccountFragment : BaseFragment(R.layout.fragment_account), + AccountView, MainView.TitledView { + + @Inject + lateinit var presenter: AccountPresenter + + @Inject + lateinit var accountAdapter: AccountAdapter + + companion object { + + fun newInstance() = AccountFragment() + } + + override val titleStringId = R.string.account_title + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } + + @Suppress("UNCHECKED_CAST") + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding = FragmentAccountBinding.bind(view) + presenter.onAttachView(this) + } + + override fun initView() { + binding.accountRecycler.apply { + layoutManager = LinearLayoutManager(context) + adapter = accountAdapter + } + + accountAdapter.onClickListener = presenter::onItemSelected + + binding.accountAdd.setOnClickListener { presenter.onAddSelected() } + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + menu[0].isVisible = false + } + + override fun updateData(data: List>) { + with(accountAdapter) { + items = data + notifyDataSetChanged() + } + } + + override fun openLoginView() { + activity?.let { + startActivity(LoginActivity.getStartIntent(it)) + } + } + + override fun openAccountDetailsView(student: Student) { + (activity as? MainActivity)?.pushView(AccountDetailsFragment.newInstance(student)) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt new file mode 100644 index 000000000..05a4a69ce --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt @@ -0,0 +1,9 @@ +package io.github.wulkanowy.ui.modules.account + +data class AccountItem(val value: T, val viewType: ViewType) { + + enum class ViewType(val id: Int) { + HEADER(1), + ITEM(2) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt new file mode 100644 index 000000000..77c1ffe64 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt @@ -0,0 +1,56 @@ +package io.github.wulkanowy.ui.modules.account + +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.data.logResourceStatus +import io.github.wulkanowy.data.onResourceError +import io.github.wulkanowy.data.onResourceSuccess +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.resourceFlow +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import timber.log.Timber +import javax.inject.Inject + +class AccountPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, +) : BasePresenter(errorHandler, studentRepository) { + + override fun onAttachView(view: AccountView) { + super.onAttachView(view) + view.initView() + Timber.i("Account view was initialized") + loadData() + } + + fun onAddSelected() { + Timber.i("Select add account") + view?.openLoginView() + } + + fun onItemSelected(studentWithSemesters: StudentWithSemesters) { + view?.openAccountDetailsView(studentWithSemesters.student) + } + + private fun loadData() { + resourceFlow { studentRepository.getSavedStudents(false) } + .logResourceStatus("load account data") + .onResourceSuccess { view?.updateData(createAccountItems(it)) } + .onResourceError(errorHandler::dispatch) + .launch("load") + } + + private fun createAccountItems(items: List): List> { + return items.groupBy { + Account("${it.student.userName} (${it.student.email})", it.student.isParent) + } + .map { (account, students) -> + listOf( + AccountItem(account, AccountItem.ViewType.HEADER) + ) + students.map { student -> + AccountItem(student, AccountItem.ViewType.ITEM) + } + } + .flatten() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt new file mode 100644 index 000000000..56fcb0a35 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt @@ -0,0 +1,15 @@ +package io.github.wulkanowy.ui.modules.account + +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.ui.base.BaseView + +interface AccountView : BaseView { + + fun initView() + + fun updateData(data: List>) + + fun openLoginView() + + fun openAccountDetailsView(student: Student) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt new file mode 100644 index 000000000..c3137ec58 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt @@ -0,0 +1,168 @@ +package io.github.wulkanowy.ui.modules.account.accountdetails + +import android.os.Bundle +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import androidx.appcompat.app.AlertDialog +import androidx.core.view.get +import androidx.core.view.isVisible +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.databinding.FragmentAccountDetailsBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.account.accountedit.AccountEditDialog +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoFragment +import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView +import io.github.wulkanowy.utils.createNameInitialsDrawable +import io.github.wulkanowy.utils.nickOrName +import javax.inject.Inject + +@AndroidEntryPoint +class AccountDetailsFragment : + BaseFragment(R.layout.fragment_account_details), + AccountDetailsView, MainView.TitledView { + + @Inject + lateinit var presenter: AccountDetailsPresenter + + override val titleStringId = R.string.account_details_title + + companion object { + + private const val ARGUMENT_KEY = "Data" + + fun newInstance(student: Student) = + AccountDetailsFragment().apply { + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, student) } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentAccountDetailsBinding.bind(view) + presenter.onAttachView(this, requireArguments()[ARGUMENT_KEY] as Student) + } + + override fun initView() { + binding.accountDetailsErrorRetry.setOnClickListener { presenter.onRetry() } + binding.accountDetailsErrorDetails.setOnClickListener { presenter.onDetailsClick() } + binding.accountDetailsLogout.setOnClickListener { presenter.onRemoveSelected() } + binding.accountDetailsSelect.setOnClickListener { presenter.onStudentSelect() } + + binding.accountDetailsPersonalData.setOnClickListener { + presenter.onStudentInfoSelected(StudentInfoView.Type.PERSONAL) + } + binding.accountDetailsAddressData.setOnClickListener { + presenter.onStudentInfoSelected(StudentInfoView.Type.ADDRESS) + } + binding.accountDetailsContactData.setOnClickListener { + presenter.onStudentInfoSelected(StudentInfoView.Type.CONTACT) + } + binding.accountDetailsFamilyData.setOnClickListener { + presenter.onStudentInfoSelected(StudentInfoView.Type.FAMILY) + } + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + menu[0].isVisible = false + inflater.inflate(R.menu.action_menu_account_details, menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return if (item.itemId == R.id.accountDetailsMenuEdit) { + presenter.onAccountEditSelected() + true + } else false + } + + override fun showAccountData(student: Student) { + with(binding) { + accountDetailsCheck.isVisible = student.isCurrent + accountDetailsName.text = student.nickOrName + accountDetailsSchool.text = student.schoolName + accountDetailsAvatar.setImageDrawable( + requireContext().createNameInitialsDrawable( + student.nickOrName, + student.avatarColor + ) + ) + } + } + + override fun enableSelectStudentButton(enable: Boolean) { + binding.accountDetailsSelect.isEnabled = enable + } + + override fun showAccountEditDetailsDialog(student: Student) { + (requireActivity() as MainActivity).showDialogFragment( + AccountEditDialog.newInstance(student) + ) + } + + override fun showLogoutConfirmDialog() { + context?.let { + AlertDialog.Builder(it) + .setTitle(R.string.account_logout_student) + .setMessage(R.string.account_confirm) + .setPositiveButton(R.string.account_logout) { _, _ -> presenter.onLogoutConfirm() } + .setNegativeButton(android.R.string.cancel) { _, _ -> } + .show() + } + } + + override fun popViewToMain() { + (requireActivity() as MainActivity).popView(2) + } + + override fun popViewToAccounts() { + (requireActivity() as MainActivity).popView(1) + } + + override fun recreateMainView() { + requireActivity().recreate() + } + + override fun openStudentInfoView( + infoType: StudentInfoView.Type, + studentWithSemesters: StudentWithSemesters + ) { + (requireActivity() as MainActivity).pushView( + StudentInfoFragment.newInstance( + infoType, + studentWithSemesters + ) + ) + } + + override fun showErrorView(show: Boolean) { + binding.accountDetailsError.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun setErrorDetails(message: String) { + binding.accountDetailsErrorMessage.text = message + } + + override fun showProgress(show: Boolean) { + binding.accountDetailsProgress.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showContent(show: Boolean) { + binding.accountDetailsContent.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsPresenter.kt new file mode 100644 index 000000000..5d68ff2e1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsPresenter.kt @@ -0,0 +1,156 @@ +package io.github.wulkanowy.ui.modules.account.accountdetails + +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.services.sync.SyncManager +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView +import timber.log.Timber +import javax.inject.Inject + +class AccountDetailsPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val syncManager: SyncManager +) : BasePresenter(errorHandler, studentRepository) { + + private var studentWithSemesters: StudentWithSemesters? = null + + private lateinit var lastError: Throwable + + private var studentId: Long? = null + + fun onAttachView(view: AccountDetailsView, student: Student) { + super.onAttachView(view) + studentId = student.id + + view.initView() + errorHandler.showErrorMessage = ::showErrorViewOnError + Timber.i("Account details view was initialized") + loadData() + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData() + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + private fun loadData() { + resourceFlow { studentRepository.getSavedStudentById(studentId ?: -1) } + .logResourceStatus("loading account details view") + .onResourceLoading { + view?.run { + showProgress(true) + showContent(false) + } + } + .onResourceSuccess { + studentWithSemesters = it + view?.run { + showAccountData(studentWithSemesters!!.student) + enableSelectStudentButton(!studentWithSemesters!!.student.isCurrent) + showContent(true) + showErrorView(false) + } + } + .onResourceNotLoading { view?.showProgress(false) } + .onResourceError(errorHandler::dispatch) + .launch() + } + + fun onAccountEditSelected() { + studentWithSemesters?.let { + view?.showAccountEditDetailsDialog(it.student) + } + } + + fun onStudentInfoSelected(infoType: StudentInfoView.Type) { + studentWithSemesters?.let { + view?.openStudentInfoView(infoType, it) + } + } + + fun onStudentSelect() { + if (studentWithSemesters == null) return + + Timber.i("Select student ${studentWithSemesters!!.student.id}") + + resourceFlow { studentRepository.switchStudent(studentWithSemesters!!) } + .logResourceStatus("change student") + .onResourceSuccess { view?.recreateMainView() } + .onResourceNotLoading { view?.popViewToMain() } + .onResourceError(errorHandler::dispatch) + .launch("switch") + } + + fun onRemoveSelected() { + Timber.i("Select remove account") + view?.showLogoutConfirmDialog() + } + + fun onLogoutConfirm() { + if (studentWithSemesters == null) return + + resourceFlow { + val studentToLogout = studentWithSemesters!!.student + + studentRepository.logoutStudent(studentToLogout) + val students = studentRepository.getSavedStudents(false) + + if (studentToLogout.isCurrent && students.isNotEmpty()) { + studentRepository.switchStudent(students[0]) + } + + students + } + .logResourceStatus("logout user") + .onResourceSuccess { + view?.run { + when { + it.isEmpty() -> { + Timber.i("Logout result: Open login view") + syncManager.stopSyncWorker() + openClearLoginView() + } + studentWithSemesters?.student?.isCurrent == true -> { + Timber.i("Logout result: Logout student and switch to another") + recreateMainView() + } + else -> { + Timber.i("Logout result: Logout student") + recreateMainView() + } + } + } + } + .onResourceNotLoading { + if (studentWithSemesters?.student?.isCurrent == true) { + view?.popViewToMain() + } else { + view?.popViewToAccounts() + } + } + .onResourceError(errorHandler::dispatch) + .launch("logout") + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + lastError = error + setErrorDetails(message) + showErrorView(true) + showContent(false) + showProgress(false) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsView.kt new file mode 100644 index 000000000..aeb743fa5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsView.kt @@ -0,0 +1,38 @@ +package io.github.wulkanowy.ui.modules.account.accountdetails + +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.ui.base.BaseView +import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView + +interface AccountDetailsView : BaseView { + + fun initView() + + fun showAccountData(student: Student) + + fun showAccountEditDetailsDialog(student: Student) + + fun showLogoutConfirmDialog() + + fun popViewToMain() + + fun popViewToAccounts() + + fun recreateMainView() + + fun enableSelectStudentButton(enable: Boolean) + + fun openStudentInfoView( + infoType: StudentInfoView.Type, + studentWithSemesters: StudentWithSemesters + ) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun showProgress(show: Boolean) + + fun showContent(show: Boolean) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditColorAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditColorAdapter.kt new file mode 100644 index 000000000..66e39fc72 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditColorAdapter.kt @@ -0,0 +1,70 @@ +package io.github.wulkanowy.ui.modules.account.accountedit + +import android.annotation.SuppressLint +import android.content.res.ColorStateList +import android.graphics.Color +import android.graphics.drawable.Drawable +import android.graphics.drawable.GradientDrawable +import android.graphics.drawable.RippleDrawable +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.databinding.ItemAccountEditColorBinding +import javax.inject.Inject + +class AccountEditColorAdapter @Inject constructor() : + RecyclerView.Adapter() { + + var items = listOf() + + var selectedColor = 0 + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( + ItemAccountEditColorBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + @SuppressLint("RestrictedApi", "NewApi") + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val item = items[position] + + with(holder.binding) { + accountEditItemColor.setImageDrawable(GradientDrawable().apply { + shape = GradientDrawable.OVAL + setColor(item) + }) + + accountEditItemColorContainer.foreground = item.createForegroundDrawable() + accountEditCheck.isVisible = selectedColor == item + + root.setOnClickListener { + val oldSelectedPosition = items.indexOf(selectedColor) + selectedColor = item + + notifyItemChanged(oldSelectedPosition) + notifyItemChanged(position) + } + } + } + + private fun Int.createForegroundDrawable(): Drawable { + val mask = GradientDrawable().apply { + shape = GradientDrawable.OVAL + setColor(Color.BLACK) + } + return RippleDrawable(ColorStateList.valueOf(this.rippleColor), null, mask) + } + + private inline val Int.rippleColor: Int + get() { + val hsv = FloatArray(3) + Color.colorToHSV(this, hsv) + hsv[2] = hsv[2] * 0.5f + return Color.HSVToColor(hsv) + } + + class ViewHolder(val binding: ItemAccountEditColorBinding) : + RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditDialog.kt new file mode 100644 index 000000000..21a7a492d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditDialog.kt @@ -0,0 +1,98 @@ +package io.github.wulkanowy.ui.modules.account.accountedit + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.GridLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.databinding.DialogAccountEditBinding +import io.github.wulkanowy.ui.base.BaseDialogFragment +import javax.inject.Inject + +@AndroidEntryPoint +class AccountEditDialog : BaseDialogFragment(), AccountEditView { + + @Inject + lateinit var presenter: AccountEditPresenter + + @Inject + lateinit var accountEditColorAdapter: AccountEditColorAdapter + + companion object { + + private const val ARGUMENT_KEY = "student_with_semesters" + + fun newInstance(student: Student) = + AccountEditDialog().apply { + arguments = Bundle().apply { + putSerializable(ARGUMENT_KEY, student) + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View = DialogAccountEditBinding.inflate(inflater).apply { binding = this }.root + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + presenter.onAttachView(this, requireArguments()[ARGUMENT_KEY] as Student) + } + + override fun initView() { + with(binding) { + accountEditDetailsCancel.setOnClickListener { dismiss() } + accountEditDetailsSave.setOnClickListener { + presenter.changeStudentNickAndAvatar( + binding.accountEditDetailsNickText.text.toString(), + accountEditColorAdapter.selectedColor + ) + } + + with(binding.accountEditColors) { + layoutManager = GridLayoutManager(context, 4) + adapter = accountEditColorAdapter + } + } + } + + override fun updateSelectedColorData(color: Int) { + with(accountEditColorAdapter) { + selectedColor = color + notifyDataSetChanged() + } + } + + override fun updateColorsData(colors: List) { + with(accountEditColorAdapter) { + items = colors + notifyDataSetChanged() + } + } + + override fun showCurrentNick(nick: String) { + binding.accountEditDetailsNickText.setText(nick) + } + + override fun popView() { + dismiss() + } + + override fun recreateMainView() { + activity?.recreate() + } + + override fun onDestroyView() { + super.onDestroyView() + presenter.onDetachView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditPresenter.kt new file mode 100644 index 000000000..c401158ea --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditPresenter.kt @@ -0,0 +1,60 @@ +package io.github.wulkanowy.ui.modules.account.accountedit + +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AppInfo +import timber.log.Timber +import javax.inject.Inject + +class AccountEditPresenter @Inject constructor( + appInfo: AppInfo, + errorHandler: ErrorHandler, + studentRepository: StudentRepository +) : BasePresenter(errorHandler, studentRepository) { + + lateinit var student: Student + + private val colors = appInfo.defaultColorsForAvatar.map { it.toInt() } + + fun onAttachView(view: AccountEditView, student: Student) { + super.onAttachView(view) + this.student = student + + with(view) { + initView() + showCurrentNick(student.nick.trim()) + } + Timber.i("Account edit dialog view was initialized") + loadData() + + view.updateColorsData(colors) + } + + private fun loadData() { + resourceFlow { studentRepository.getStudentById(student.id, false).avatarColor } + .logResourceStatus("load student") + .onResourceSuccess { view?.updateSelectedColorData(it.toInt()) } + .onResourceError(errorHandler::dispatch) + .launch("load_data") + } + + fun changeStudentNickAndAvatar(nick: String, avatarColor: Int) { + resourceFlow { + val studentNick = StudentNickAndAvatar( + nick = nick.trim(), + avatarColor = avatarColor.toLong() + ).apply { id = student.id } + + studentRepository.updateStudentNickAndAvatar(studentNick) + } + .logResourceStatus("change student nick and avatar") + .onResourceSuccess { view?.recreateMainView() } + .onResourceNotLoading { view?.popView() } + .onResourceError(errorHandler::dispatch) + .launch("update_student") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditView.kt new file mode 100644 index 000000000..517492de1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditView.kt @@ -0,0 +1,18 @@ +package io.github.wulkanowy.ui.modules.account.accountedit + +import io.github.wulkanowy.ui.base.BaseView + +interface AccountEditView : BaseView { + + fun initView() + + fun popView() + + fun recreateMainView() + + fun showCurrentNick(nick: String) + + fun updateSelectedColorData(color: Int) + + fun updateColorsData(colors: List) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickDialog.kt new file mode 100644 index 000000000..4279102e1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickDialog.kt @@ -0,0 +1,95 @@ +package io.github.wulkanowy.ui.modules.account.accountquick + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.databinding.DialogAccountQuickBinding +import io.github.wulkanowy.ui.base.BaseDialogFragment +import io.github.wulkanowy.ui.modules.account.AccountAdapter +import io.github.wulkanowy.ui.modules.account.AccountFragment +import io.github.wulkanowy.ui.modules.account.AccountItem +import io.github.wulkanowy.ui.modules.main.MainActivity +import javax.inject.Inject + +@AndroidEntryPoint +class AccountQuickDialog : BaseDialogFragment(), AccountQuickView { + + @Inject + lateinit var accountAdapter: AccountAdapter + + @Inject + lateinit var presenter: AccountQuickPresenter + + companion object { + + private const val STUDENTS_ARGUMENT_KEY = "students" + + fun newInstance(studentsWithSemesters: List) = + AccountQuickDialog().apply { + arguments = Bundle().apply { + putSerializable(STUDENTS_ARGUMENT_KEY, studentsWithSemesters.toTypedArray()) + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogAccountQuickBinding.inflate(inflater).apply { binding = this }.root + + @Suppress("UNCHECKED_CAST") + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + val studentsWithSemesters = + (requireArguments()[STUDENTS_ARGUMENT_KEY] as Array).toList() + + presenter.onAttachView(this, studentsWithSemesters) + } + + override fun initView() { + binding.accountQuickDialogManger.setOnClickListener { presenter.onManagerSelected() } + + with(accountAdapter) { + isAccountQuickDialogMode = true + onClickListener = presenter::onStudentSelect + } + + with(binding.accountQuickDialogRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = accountAdapter + } + } + + override fun updateData(data: List>) { + with(accountAdapter) { + items = data + notifyDataSetChanged() + } + } + + override fun popView() { + dismiss() + } + + override fun recreateMainView() { + activity?.recreate() + } + + override fun openAccountView() { + (requireActivity() as MainActivity).pushView(AccountFragment.newInstance()) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickPresenter.kt new file mode 100644 index 000000000..32c07f80a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickPresenter.kt @@ -0,0 +1,54 @@ +package io.github.wulkanowy.ui.modules.account.accountquick + +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.ui.modules.account.AccountItem +import timber.log.Timber +import javax.inject.Inject + +class AccountQuickPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository +) : BasePresenter(errorHandler, studentRepository) { + + private lateinit var studentsWithSemesters: List + + fun onAttachView(view: AccountQuickView, studentsWithSemesters: List) { + super.onAttachView(view) + this.studentsWithSemesters = studentsWithSemesters + + view.initView() + Timber.i("Account quick dialog view was initialized") + view.updateData(createAccountItems(studentsWithSemesters)) + } + + fun onManagerSelected() { + view?.run { + openAccountView() + popView() + } + } + + fun onStudentSelect(studentWithSemesters: StudentWithSemesters) { + Timber.i("Select student ${studentWithSemesters.student.id}") + + if (studentWithSemesters.student.isCurrent) { + view?.popView() + return + } + + resourceFlow { studentRepository.switchStudent(studentWithSemesters) } + .logResourceStatus("change student") + .onResourceSuccess { view?.recreateMainView() } + .onResourceNotLoading { view?.popView() } + .onResourceError(errorHandler::dispatch) + .launch("switch") + } + + private fun createAccountItems(items: List) = items.map { + AccountItem(it, AccountItem.ViewType.ITEM) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickView.kt new file mode 100644 index 000000000..4a9420d99 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickView.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.ui.modules.account.accountquick + +import io.github.wulkanowy.ui.base.BaseView +import io.github.wulkanowy.ui.modules.account.AccountItem + +interface AccountQuickView : BaseView { + + fun initView() + + fun updateData(data: List>) + + fun recreateMainView() + + fun popView() + + fun openAccountView() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt new file mode 100644 index 000000000..5d5ed504c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt @@ -0,0 +1,82 @@ +package io.github.wulkanowy.ui.modules.attendance + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Attendance +import io.github.wulkanowy.data.enums.SentExcuseStatus +import io.github.wulkanowy.databinding.ItemAttendanceBinding +import io.github.wulkanowy.utils.descriptionRes +import io.github.wulkanowy.utils.isExcusableOrNotExcused +import javax.inject.Inject + +class AttendanceAdapter @Inject constructor() : + RecyclerView.Adapter() { + + var items = emptyList() + + var excuseActionMode: Boolean = false + + var onClickListener: (Attendance) -> Unit = {} + + var onExcuseCheckboxSelect: (attendanceItem: Attendance, checked: Boolean) -> Unit = { _, _ -> } + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + ItemAttendanceBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + val item = items[position] + + with(holder.binding) { + attendanceItemNumber.text = item.number.toString() + attendanceItemSubject.text = item.subject + attendanceItemDescription.setText(item.descriptionRes) + attendanceItemAlert.visibility = item.run { if (absence && !excused) View.VISIBLE else View.INVISIBLE } + attendanceItemNumber.visibility = View.GONE + attendanceItemExcuseInfo.visibility = View.GONE + attendanceItemExcuseCheckbox.visibility = View.GONE + attendanceItemExcuseCheckbox.isChecked = false + attendanceItemExcuseCheckbox.setOnCheckedChangeListener { _, checked -> + onExcuseCheckboxSelect(item, checked) + } + + when (item.excuseStatus?.let { SentExcuseStatus.valueOf(it)}) { + SentExcuseStatus.WAITING -> { + attendanceItemExcuseInfo.setImageResource(R.drawable.ic_excuse_waiting) + attendanceItemExcuseInfo.visibility = View.VISIBLE + attendanceItemAlert.visibility = View.INVISIBLE + } + SentExcuseStatus.DENIED -> { + attendanceItemExcuseInfo.setImageResource(R.drawable.ic_excuse_denied) + attendanceItemExcuseInfo.visibility = View.VISIBLE + } + else -> { + if (item.isExcusableOrNotExcused && excuseActionMode) { + attendanceItemNumber.visibility = View.GONE + attendanceItemExcuseCheckbox.visibility = View.VISIBLE + } else { + attendanceItemNumber.visibility = View.VISIBLE + attendanceItemExcuseCheckbox.visibility = View.GONE + } + } + } + root.setOnClickListener { + onClickListener(item) + + with(attendanceItemExcuseCheckbox) { + if (excuseActionMode && isVisible) { + isChecked = !isChecked + } + } + } + } + } + + class ItemViewHolder(val binding: ItemAttendanceBinding) : RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt new file mode 100644 index 000000000..9b5c63e4c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt @@ -0,0 +1,54 @@ +package io.github.wulkanowy.ui.modules.attendance + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import io.github.wulkanowy.data.db.entities.Attendance +import io.github.wulkanowy.databinding.DialogAttendanceBinding +import io.github.wulkanowy.utils.descriptionRes +import io.github.wulkanowy.utils.lifecycleAwareVariable +import io.github.wulkanowy.utils.toFormattedString + +class AttendanceDialog : DialogFragment() { + + private var binding: DialogAttendanceBinding by lifecycleAwareVariable() + + private lateinit var attendance: Attendance + + companion object { + + private const val ARGUMENT_KEY = "Item" + + fun newInstance(exam: Attendance) = AttendanceDialog().apply { + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + attendance = getSerializable(ARGUMENT_KEY) as Attendance + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogAttendanceBinding.inflate(inflater).apply { binding = this }.root + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + with(binding) { + attendanceDialogSubjectValue.text = attendance.subject + attendanceDialogDescriptionValue.setText(attendance.descriptionRes) + attendanceDialogDateValue.text = attendance.date.toFormattedString() + attendanceDialogNumberValue.text = attendance.number.toString() + attendanceDialogClose.setOnClickListener { dismiss() } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt new file mode 100644 index 000000000..6354b5e04 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt @@ -0,0 +1,290 @@ +package io.github.wulkanowy.ui.modules.attendance + +import android.content.DialogInterface.BUTTON_POSITIVE +import android.os.Bundle +import android.view.* +import android.view.View.* +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.view.ActionMode +import androidx.core.view.isVisible +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Attendance +import io.github.wulkanowy.databinding.DialogExcuseBinding +import io.github.wulkanowy.databinding.FragmentAttendanceBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity +import io.github.wulkanowy.ui.widgets.DividerItemDecoration +import io.github.wulkanowy.utils.dpToPx +import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.openMaterialDatePicker +import java.time.LocalDate +import javax.inject.Inject + +@AndroidEntryPoint +class AttendanceFragment : BaseFragment(R.layout.fragment_attendance), + AttendanceView, MainView.MainChildView, + MainView.TitledView { + + @Inject + lateinit var presenter: AttendancePresenter + + @Inject + lateinit var attendanceAdapter: AttendanceAdapter + + override val excuseSuccessString: String + get() = getString(R.string.attendance_excuse_success) + + override val excuseNoSelectionString: String + get() = getString(R.string.attendance_excuse_no_selection) + + companion object { + private const val SAVED_DATE_KEY = "CURRENT_DATE" + + fun newInstance() = AttendanceFragment() + } + + override val titleStringId get() = R.string.attendance_title + + override val isViewEmpty get() = attendanceAdapter.items.isEmpty() + + override val currentStackSize get() = (activity as? MainActivity)?.currentStackSize + + override val excuseActionMode: Boolean get() = attendanceAdapter.excuseActionMode + + private var actionMode: ActionMode? = null + + private val actionModeCallback = object : ActionMode.Callback { + override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { + val inflater = mode.menuInflater + inflater.inflate(R.menu.context_menu_attendance, menu) + return true + } + + override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { + mode.title = getString(R.string.attendance_excuse_title) + return presenter.onPrepareActionMode() + } + + override fun onDestroyActionMode(mode: ActionMode) { + presenter.onDestroyActionMode() + actionMode = null + } + + override fun onActionItemClicked(mode: ActionMode, menu: MenuItem): Boolean { + return when (menu.itemId) { + R.id.excuseMenuSubmit -> presenter.onExcuseSubmitButtonClick() + else -> false + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentAttendanceBinding.bind(view) + messageContainer = binding.attendanceRecycler + presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY)) + } + + override fun initView() { + with(attendanceAdapter) { + onClickListener = presenter::onAttendanceItemSelected + onExcuseCheckboxSelect = presenter::onExcuseCheckboxSelect + } + + with(binding.attendanceRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = attendanceAdapter + addItemDecoration(DividerItemDecoration(context)) + } + + with(binding) { + attendanceSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + attendanceSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) + attendanceSwipe.setProgressBackgroundColorSchemeColor( + requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh) + ) + attendanceErrorRetry.setOnClickListener { presenter.onRetry() } + attendanceErrorDetails.setOnClickListener { presenter.onDetailsClick() } + + attendancePreviousButton.setOnClickListener { presenter.onPreviousDay() } + attendanceNavDate.setOnClickListener { presenter.onPickDate() } + attendanceNextButton.setOnClickListener { presenter.onNextDay() } + + attendanceExcuseButton.setOnClickListener { presenter.onExcuseButtonClick() } + + attendanceNavContainer.elevation = requireContext().dpToPx(8f) + } + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.action_menu_attendance, menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return if (item.itemId == R.id.attendanceMenuSummary) presenter.onSummarySwitchSelected() + else false + } + + override fun updateData(data: List) { + with(attendanceAdapter) { + items = data + notifyDataSetChanged() + } + } + + override fun updateNavigationDay(date: String) { + binding.attendanceNavDate.text = date + } + + override fun clearData() { + with(attendanceAdapter) { + items = emptyList() + notifyDataSetChanged() + } + } + + override fun resetView() { + binding.attendanceRecycler.smoothScrollToPosition(0) + } + + override fun onFragmentReselected() { + if (::presenter.isInitialized) presenter.onViewReselected() + } + + override fun onFragmentChanged() { + if (::presenter.isInitialized) presenter.onMainViewChanged() + } + + override fun popView() { + (activity as? MainActivity)?.popView() + } + + override fun showEmpty(show: Boolean) { + binding.attendanceEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showErrorView(show: Boolean) { + binding.attendanceError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + binding.attendanceErrorMessage.text = message + } + + override fun showProgress(show: Boolean) { + binding.attendanceProgress.visibility = if (show) VISIBLE else GONE + } + + override fun enableSwipe(enable: Boolean) { + binding.attendanceSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + binding.attendanceRecycler.visibility = if (show) VISIBLE else GONE + } + + override fun showRefresh(show: Boolean) { + binding.attendanceSwipe.isRefreshing = show + } + + override fun showPreButton(show: Boolean) { + binding.attendancePreviousButton.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showNextButton(show: Boolean) { + binding.attendanceNextButton.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showExcuseButton(show: Boolean) { + binding.attendanceExcuseButton.isVisible = show + } + + override fun showAttendanceDialog(lesson: Attendance) { + (activity as? MainActivity)?.showDialogFragment(AttendanceDialog.newInstance(lesson)) + } + + override fun showDatePickerDialog(selectedDate: LocalDate) { + openMaterialDatePicker( + selected = selectedDate, + rangeStart = selectedDate.firstSchoolDayInSchoolYear, + rangeEnd = LocalDate.now().plusWeeks(1), + onDateSelected = { + presenter.onDateSet(it.year, it.monthValue, it.dayOfMonth) + } + ) + } + + override fun showExcuseDialog() { + val dialogBinding = DialogExcuseBinding.inflate(LayoutInflater.from(context)) + AlertDialog.Builder(requireContext()) + .setTitle(R.string.attendance_excuse_title) + .setView(dialogBinding.root) + .setNegativeButton(android.R.string.cancel) { _, _ -> } + .create() + .apply { + setButton( + BUTTON_POSITIVE, + getString(R.string.attendance_excuse_dialog_submit) + ) { _, _ -> + presenter.onExcuseDialogSubmit( + dialogBinding.excuseReason.text?.toString().orEmpty() + ) + } + }.show() + } + + override fun openSummaryView() { + (activity as? MainActivity)?.pushView(AttendanceSummaryFragment.newInstance()) + } + + override fun startActionMode() { + actionMode = (activity as MainActivity?)?.startSupportActionMode(actionModeCallback) + } + + override fun startSendMessageIntent(date: LocalDate, numbers: String, reason: String) { + val reasonFullText = getString( + R.string.attendance_excuse_formula, + date, + numbers, + if (reason.isNotBlank()) " ${getString(R.string.attendance_excuse_reason)} " else "", + reason.ifBlank { "" } + ) + startActivity(SendMessageActivity.getStartIntent(requireContext(), reasonFullText)) + } + + override fun showExcuseCheckboxes(show: Boolean) { + with(attendanceAdapter) { + excuseActionMode = show + notifyDataSetChanged() + } + } + + override fun showDayNavigation(show: Boolean) { + binding.attendanceNavContainer.isVisible = show + } + + override fun finishActionMode() { + actionMode?.finish() + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt new file mode 100644 index 000000000..7fcbd002e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt @@ -0,0 +1,331 @@ +package io.github.wulkanowy.ui.modules.attendance + +import android.annotation.SuppressLint +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.db.entities.Attendance +import io.github.wulkanowy.data.repositories.AttendanceRepository +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.* +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.onEach +import timber.log.Timber +import java.time.LocalDate +import java.time.LocalDate.now +import java.time.LocalDate.ofEpochDay +import javax.inject.Inject + +class AttendancePresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val attendanceRepository: AttendanceRepository, + private val semesterRepository: SemesterRepository, + private val prefRepository: PreferencesRepository, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + private var baseDate: LocalDate = now().previousOrSameSchoolDay + + lateinit var currentDate: LocalDate + private set + + private lateinit var lastError: Throwable + + private val attendanceToExcuseList = mutableListOf() + + private var isVulcanExcusedFunctionEnabled = false + + fun onAttachView(view: AttendanceView, date: Long?) { + super.onAttachView(view) + view.initView() + Timber.i("Attendance view was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError + reloadView(ofEpochDay(date ?: baseDate.toEpochDay())) + loadData() + if (currentDate.isHolidays) setBaseDateOnHolidays() + } + + fun onPreviousDay() { + view?.finishActionMode() + attendanceToExcuseList.clear() + reloadView(currentDate.previousSchoolDay) + loadData() + } + + fun onNextDay() { + view?.finishActionMode() + attendanceToExcuseList.clear() + reloadView(currentDate.nextSchoolDay) + loadData() + } + + fun onPickDate() { + view?.showDatePickerDialog(currentDate) + } + + fun onDateSet(year: Int, month: Int, day: Int) { + reloadView(LocalDate.of(year, month, day)) + loadData() + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the attendance") + loadData(true) + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + fun onViewReselected() { + Timber.i("Attendance view is reselected") + view?.also { view -> + if (view.currentStackSize == 1) { + baseDate.also { + if (currentDate != it) { + reloadView(it) + loadData() + } else if (!view.isViewEmpty) view.resetView() + } + } else view.popView() + } + } + + fun onMainViewChanged() { + view?.finishActionMode() + } + + fun onAttendanceItemSelected(attendance: Attendance) { + view?.apply { + if (!excuseActionMode) { + Timber.i("Select attendance item ${attendance.id}") + showAttendanceDialog(attendance) + } + } + } + + fun onExcuseButtonClick() { + view?.startActionMode() + } + + fun onExcuseCheckboxSelect(attendanceItem: Attendance, checked: Boolean) { + if (checked) attendanceToExcuseList.add(attendanceItem) + else attendanceToExcuseList.remove(attendanceItem) + } + + fun onExcuseSubmitButtonClick(): Boolean { + view?.apply { + return if (attendanceToExcuseList.isNotEmpty()) { + showExcuseDialog() + true + } else { + showMessage(excuseNoSelectionString) + false + } + } + return false + } + + fun onExcuseDialogSubmit(reason: String) { + view?.finishActionMode() + + if (attendanceToExcuseList.isEmpty()) return + + if (isVulcanExcusedFunctionEnabled) { + excuseAbsence( + reason = reason.takeIf { it.isNotBlank() }, + toExcuseList = attendanceToExcuseList.toList() + ) + } else { + val attendanceToExcuseNumbers = attendanceToExcuseList.map { it.number } + + view?.startSendMessageIntent( + date = attendanceToExcuseList[0].date, + numbers = attendanceToExcuseNumbers.joinToString(", "), + reason = reason + ) + } + } + + fun onPrepareActionMode(): Boolean { + view?.apply { + showExcuseCheckboxes(true) + showExcuseButton(false) + enableSwipe(false) + showDayNavigation(false) + } + attendanceToExcuseList.clear() + return true + } + + fun onDestroyActionMode() { + view?.apply { + showExcuseCheckboxes(false) + showExcuseButton(true) + enableSwipe(true) + showDayNavigation(true) + } + } + + fun onSummarySwitchSelected(): Boolean { + view?.openSummaryView() + return true + } + + private fun setBaseDateOnHolidays() { + flow { + val student = studentRepository.getCurrentStudent() + emit(semesterRepository.getCurrentSemester(student)) + }.catch { + Timber.i("Loading semester result: An exception occurred") + }.onEach { + baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear) + currentDate = baseDate + reloadNavigation() + }.launch("holidays") + } + + private fun loadData(forceRefresh: Boolean = false) { + Timber.i("Loading attendance data started") + + var isParent = false + + flatResourceFlow { + val student = studentRepository.getCurrentStudent() + isParent = student.isParent + + val semester = semesterRepository.getCurrentSemester(student) + attendanceRepository.getAttendance( + student = student, + semester = semester, + start = currentDate, + end = currentDate, + forceRefresh = forceRefresh + ) + } + .logResourceStatus("load attendance") + .onResourceLoading { + view?.showExcuseButton(false) + } + .mapResourceData { + if (prefRepository.isShowPresent) { + it + } else { + it.filter { item -> !item.presence } + }.sortedBy { item -> item.number } + } + .onResourceData { + view?.run { + enableSwipe(true) + showProgress(false) + showErrorView(false) + showEmpty(it.isEmpty()) + showContent(it.isNotEmpty()) + updateData(it) + } + } + .onResourceIntermediate { view?.showRefresh(true) } + .onResourceSuccess { + isVulcanExcusedFunctionEnabled = it.any { item -> item.excusable } + val anyExcusables = it.any { it.isExcusableOrNotExcused } + view?.showExcuseButton(anyExcusables && (isParent || isVulcanExcusedFunctionEnabled)) + + analytics.logEvent( + "load_data", + "type" to "attendance", + "items" to it.size + ) + } + .onResourceNotLoading { + view?.run { + showRefresh(false) + showProgress(false) + enableSwipe(true) + } + } + .onResourceError(errorHandler::dispatch) + .launch() + } + + private fun excuseAbsence(reason: String?, toExcuseList: List) { + resourceFlow { + val student = studentRepository.getCurrentStudent() + val semester = semesterRepository.getCurrentSemester(student) + attendanceRepository.excuseForAbsence(student, semester, toExcuseList, reason) + }.onEach { + when (it) { + is Resource.Loading -> view?.run { + Timber.i("Excusing absence started") + showProgress(true) + showContent(false) + showExcuseButton(false) + } + is Resource.Success -> { + Timber.i("Excusing for absence result: Success") + analytics.logEvent("excuse_absence", "items" to attendanceToExcuseList.size) + attendanceToExcuseList.clear() + view?.run { + showExcuseButton(false) + showMessage(excuseSuccessString) + showContent(true) + showProgress(false) + } + loadData(forceRefresh = true) + } + is Resource.Error -> { + Timber.i("Excusing for absence result: An exception occurred") + errorHandler.dispatch(it.error) + loadData() + } + } + }.launch("excuse") + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + + private fun reloadView(date: LocalDate) { + currentDate = date + + Timber.i("Reload attendance view with the date ${currentDate.toFormattedString()}") + view?.apply { + showProgress(true) + enableSwipe(false) + showRefresh(false) + showContent(false) + showEmpty(false) + showErrorView(false) + clearData() + reloadNavigation() + } + } + + @SuppressLint("DefaultLocale") + private fun reloadNavigation() { + view?.apply { + showPreButton(!currentDate.minusDays(1).isHolidays) + showNextButton(!currentDate.plusDays(1).isHolidays) + updateNavigationDay(currentDate.toFormattedString("EEEE, dd.MM").capitalise()) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt new file mode 100644 index 000000000..b0123065a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt @@ -0,0 +1,68 @@ +package io.github.wulkanowy.ui.modules.attendance + +import io.github.wulkanowy.data.db.entities.Attendance +import io.github.wulkanowy.ui.base.BaseView +import java.time.LocalDate + +interface AttendanceView : BaseView { + + val isViewEmpty: Boolean + + val currentStackSize: Int? + + val excuseSuccessString: String + + val excuseNoSelectionString: String + + val excuseActionMode: Boolean + + fun initView() + + fun updateData(data: List) + + fun updateNavigationDay(date: String) + + fun clearData() + + fun showRefresh(show: Boolean) + + fun resetView() + + fun showEmpty(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun showPreButton(show: Boolean) + + fun showNextButton(show: Boolean) + + fun showExcuseButton(show: Boolean) + + fun showAttendanceDialog(lesson: Attendance) + + fun showDatePickerDialog(selectedDate: LocalDate) + + fun showExcuseDialog() + + fun openSummaryView() + + fun startSendMessageIntent(date: LocalDate, numbers: String, reason: String) + + fun startActionMode() + + fun showExcuseCheckboxes(show: Boolean) + + fun showDayNavigation(show: Boolean) + + fun finishActionMode() + + fun popView() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryAdapter.kt new file mode 100644 index 000000000..a2b6abfb8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryAdapter.kt @@ -0,0 +1,101 @@ +package io.github.wulkanowy.ui.modules.attendance.summary + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.AttendanceSummary +import io.github.wulkanowy.databinding.ItemAttendanceSummaryBinding +import io.github.wulkanowy.databinding.ScrollableHeaderAttendanceSummaryBinding +import io.github.wulkanowy.utils.calculatePercentage +import io.github.wulkanowy.utils.getFormattedName +import java.time.Month +import java.util.Locale +import javax.inject.Inject + +class AttendanceSummaryAdapter @Inject constructor() : + RecyclerView.Adapter() { + + private enum class ViewType(val id: Int) { + HEADER(1), + ITEM(2) + } + + var items = emptyList() + + override fun getItemCount() = if (items.isNotEmpty()) items.size + 2 else 0 + + override fun getItemViewType(position: Int) = when (position) { + 0 -> ViewType.HEADER.id + else -> ViewType.ITEM.id + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + + return when (viewType) { + ViewType.HEADER.id -> HeaderViewHolder(ScrollableHeaderAttendanceSummaryBinding.inflate(inflater, parent, false)) + ViewType.ITEM.id -> ItemViewHolder(ItemAttendanceSummaryBinding.inflate(inflater, parent, false)) + else -> throw IllegalStateException() + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is HeaderViewHolder -> bindHeaderViewHolder(holder.binding) + is ItemViewHolder -> bindItemViewHolder(holder.binding, position - 2) + } + } + + private fun bindHeaderViewHolder(binding: ScrollableHeaderAttendanceSummaryBinding) { + binding.attendanceSummaryScrollableHeaderPercentage.text = formatPercentage(items.calculatePercentage()) + } + + private fun bindItemViewHolder(binding: ItemAttendanceSummaryBinding, position: Int) { + val item = if (position == -1) getTotalItem() else items[position] + + with(binding) { + attendanceSummaryMonth.text = when (position) { + -1 -> root.context.getString(R.string.attendance_summary_total) + else -> item.month.getFormattedName() + } + attendanceSummaryPercentage.text = when (position) { + -1 -> formatPercentage(items.calculatePercentage()) + else -> formatPercentage(item.calculatePercentage()) + } + + attendanceSummaryPresent.text = item.presence.toString() + attendanceSummaryAbsenceUnexcused.text = item.absence.toString() + attendanceSummaryAbsenceExcused.text = item.absenceExcused.toString() + attendanceSummaryAbsenceSchool.text = item.absenceForSchoolReasons.toString() + attendanceSummaryExemption.text = item.exemption.toString() + attendanceSummaryLatenessUnexcused.text = item.lateness.toString() + attendanceSummaryLatenessExcused.text = item.latenessExcused.toString() + } + } + + private fun getTotalItem() = AttendanceSummary( + month = Month.APRIL, + presence = items.sumOf { it.presence }, + absence = items.sumOf { it.absence }, + absenceExcused = items.sumOf { it.absenceExcused }, + absenceForSchoolReasons = items.sumOf { it.absenceForSchoolReasons }, + exemption = items.sumOf { it.exemption }, + lateness = items.sumOf { it.lateness }, + latenessExcused = items.sumOf { it.latenessExcused }, + diaryId = -1, + studentId = -1, + subjectId = -1 + ) + + private fun formatPercentage(percentage: Double): String { + return if (percentage == 0.0) "0%" + else "${String.format(Locale.FRANCE, "%.2f", percentage)}%" + } + + class HeaderViewHolder(val binding: ScrollableHeaderAttendanceSummaryBinding) : + RecyclerView.ViewHolder(binding.root) + + class ItemViewHolder(val binding: ItemAttendanceSummaryBinding) : + RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt new file mode 100644 index 000000000..e750b8d57 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt @@ -0,0 +1,140 @@ +package io.github.wulkanowy.ui.modules.attendance.summary + +import android.os.Bundle +import android.view.View +import android.view.View.GONE +import android.view.View.INVISIBLE +import android.view.View.VISIBLE +import android.widget.ArrayAdapter +import android.widget.TextView +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.AttendanceSummary +import io.github.wulkanowy.databinding.FragmentAttendanceSummaryBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.dpToPx +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.setOnItemSelectedListener +import javax.inject.Inject + +@AndroidEntryPoint +class AttendanceSummaryFragment : + BaseFragment(R.layout.fragment_attendance_summary), + AttendanceSummaryView, MainView.TitledView { + + @Inject + lateinit var presenter: AttendanceSummaryPresenter + + @Inject + lateinit var attendanceSummaryAdapter: AttendanceSummaryAdapter + + private lateinit var subjectsAdapter: ArrayAdapter + + companion object { + private const val SAVED_SUBJECT_KEY = "CURRENT_SUBJECT" + + fun newInstance() = AttendanceSummaryFragment() + } + + override val titleStringId get() = R.string.attendance_title + + override val isViewEmpty get() = attendanceSummaryAdapter.items.isEmpty() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentAttendanceSummaryBinding.bind(view) + messageContainer = binding.attendanceSummaryRecycler + presenter.onAttachView(this, savedInstanceState?.getInt(SAVED_SUBJECT_KEY)) + } + + override fun initView() { + with(binding.attendanceSummaryRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = attendanceSummaryAdapter + } + + with(binding) { + attendanceSummarySwipe.setOnRefreshListener(presenter::onSwipeRefresh) + attendanceSummarySwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) + attendanceSummarySwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh)) + attendanceSummaryErrorRetry.setOnClickListener { presenter.onRetry() } + attendanceSummaryErrorDetails.setOnClickListener { presenter.onDetailsClick() } + } + + subjectsAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, mutableListOf()) + subjectsAdapter.setDropDownViewResource(R.layout.item_attendance_summary_subject) + + with(binding.attendanceSummarySubjects) { + adapter = subjectsAdapter + setOnItemSelectedListener { presenter.onSubjectSelected(it?.text?.toString()) } + } + + binding.attendanceSummarySubjectsContainer.elevation = requireContext().dpToPx(1f) + } + + override fun updateSubjects(data: Collection) { + with(subjectsAdapter) { + clear() + addAll(data) + notifyDataSetChanged() + } + } + + override fun updateDataSet(data: List) { + with(attendanceSummaryAdapter) { + items = data + notifyDataSetChanged() + } + } + + override fun clearView() { + with(attendanceSummaryAdapter) { + items = emptyList() + notifyDataSetChanged() + } + } + + override fun showEmpty(show: Boolean) { + binding.attendanceSummaryEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showErrorView(show: Boolean) { + binding.attendanceSummaryError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + binding.attendanceSummaryErrorMessage.text = message + } + + override fun showProgress(show: Boolean) { + binding.attendanceSummaryProgress.visibility = if (show) VISIBLE else GONE + } + + override fun enableSwipe(enable: Boolean) { + binding.attendanceSummarySwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + binding.attendanceSummaryRecycler.visibility = if (show) VISIBLE else GONE + } + + override fun showSubjects(show: Boolean) { + binding.attendanceSummarySubjectsContainer.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showRefresh(show: Boolean) { + binding.attendanceSummarySwipe.isRefreshing = show + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putInt(SAVED_SUBJECT_KEY, presenter.currentSubjectId) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt new file mode 100644 index 000000000..281999176 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt @@ -0,0 +1,152 @@ +package io.github.wulkanowy.ui.modules.attendance.summary + +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.db.entities.AttendanceSummary +import io.github.wulkanowy.data.db.entities.Subject +import io.github.wulkanowy.data.repositories.AttendanceSummaryRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.repositories.SubjectRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import timber.log.Timber +import java.time.Month +import javax.inject.Inject + +class AttendanceSummaryPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val attendanceSummaryRepository: AttendanceSummaryRepository, + private val subjectRepository: SubjectRepository, + private val semesterRepository: SemesterRepository, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + private var subjects = emptyList() + + var currentSubjectId = -1 + private set + + private lateinit var lastError: Throwable + + fun onAttachView(view: AttendanceSummaryView, subjectId: Int?) { + super.onAttachView(view) + view.initView() + Timber.i("Attendance summary view was initialized with subject id ${subjectId ?: -1}") + errorHandler.showErrorMessage = ::showErrorViewOnError + loadData(subjectId ?: -1) + loadSubjects() + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the attendance summary") + loadData(currentSubjectId, true) + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(currentSubjectId, true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + fun onSubjectSelected(name: String?) { + Timber.i("Select attendance summary subject $name") + view?.run { + showContent(false) + showProgress(true) + enableSwipe(false) + showEmpty(false) + showErrorView(false) + clearView() + } + (subjects.singleOrNull { it.name == name }?.realId ?: -1).let { + if (it != currentSubjectId) loadData(it) + } + } + + private fun loadData(subjectId: Int, forceRefresh: Boolean = false) { + currentSubjectId = subjectId + + flatResourceFlow { + val student = studentRepository.getCurrentStudent() + val semester = semesterRepository.getCurrentSemester(student) + + attendanceSummaryRepository.getAttendanceSummary( + student = student, + semester = semester, + subjectId = subjectId, + forceRefresh = forceRefresh + ) + } + .logResourceStatus("load attendance summary") + .mapResourceData(this::sortItems) + .onResourceData { + view?.run { + enableSwipe(true) + showProgress(false) + showErrorView(false) + showContent(it.isNotEmpty()) + showEmpty(it.isEmpty()) + updateDataSet(it) + } + } + .onResourceIntermediate { view?.showRefresh(true) } + .onResourceSuccess { + analytics.logEvent( + "load_data", + "type" to "attendance_summary", + "items" to it.size, + "item_id" to subjectId + ) + } + .onResourceNotLoading { + view?.run { + showProgress(false) + showRefresh(false) + enableSwipe(true) + } + } + .onResourceError(errorHandler::dispatch) + .launch() + } + + private fun sortItems(items: List) = items.sortedByDescending { item -> + if (item.month.value <= Month.JUNE.value) item.month.value + 12 else item.month.value + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + + private fun loadSubjects() { + flatResourceFlow { + val student = studentRepository.getCurrentStudent() + val semester = semesterRepository.getCurrentSemester(student) + subjectRepository.getSubjects(student, semester) + } + .logResourceStatus("load attendance summary subjects") + .onResourceData { + subjects = it + view?.run { + view?.updateSubjects(it.map { subject -> subject.name }.toList()) + showSubjects(true) + } + } + .onResourceError(errorHandler::dispatch) + .launch("subjects") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt new file mode 100644 index 000000000..99192f185 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt @@ -0,0 +1,33 @@ +package io.github.wulkanowy.ui.modules.attendance.summary + +import io.github.wulkanowy.data.db.entities.AttendanceSummary +import io.github.wulkanowy.ui.base.BaseView + +interface AttendanceSummaryView : BaseView { + + val isViewEmpty: Boolean + + fun initView() + + fun showRefresh(show: Boolean) + + fun showContent(show: Boolean) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showEmpty(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun updateDataSet(data: List) + + fun updateSubjects(data: Collection) + + fun showSubjects(show: Boolean) + + fun clearView() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceAdapter.kt new file mode 100644 index 000000000..f63b293cf --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceAdapter.kt @@ -0,0 +1,41 @@ +package io.github.wulkanowy.ui.modules.conference + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.data.db.entities.Conference +import io.github.wulkanowy.databinding.ItemConferenceBinding +import io.github.wulkanowy.utils.toFormattedString +import javax.inject.Inject + +class ConferenceAdapter @Inject constructor() : + RecyclerView.Adapter() { + + var items = emptyList() + + var onItemClickListener: (Conference) -> Unit = {} + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + ItemConferenceBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + val item = items[position] + with(holder.binding) { + conferenceItemDate.text = item.date.toFormattedString("dd.MM.yyyy HH:mm") + conferenceItemName.text = item.presentOnConference + conferenceItemTitle.text = item.title + conferenceItemSubject.text = item.subject + conferenceItemContent.text = item.agenda + conferenceItemContent.visibility = + if (item.agenda.isBlank()) View.GONE else View.VISIBLE + + root.setOnClickListener { onItemClickListener(item) } + } + } + + class ItemViewHolder(val binding: ItemConferenceBinding) : RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceDialog.kt new file mode 100644 index 000000000..477b762b9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceDialog.kt @@ -0,0 +1,60 @@ +package io.github.wulkanowy.ui.modules.conference + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.fragment.app.DialogFragment +import io.github.wulkanowy.data.db.entities.Conference +import io.github.wulkanowy.databinding.DialogConferenceBinding +import io.github.wulkanowy.utils.lifecycleAwareVariable +import io.github.wulkanowy.utils.toFormattedString + +class ConferenceDialog : DialogFragment() { + + private var binding: DialogConferenceBinding by lifecycleAwareVariable() + + private lateinit var conference: Conference + + companion object { + + private const val ARGUMENT_KEY = "item" + + fun newInstance(conference: Conference) = ConferenceDialog().apply { + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, conference) } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) + arguments?.let { + conference = it.getSerializable(ARGUMENT_KEY) as Conference + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogConferenceBinding.inflate(inflater).also { binding = it }.root + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + with(binding) { + conferenceDialogClose.setOnClickListener { dismiss() } + + conferenceDialogSubjectValue.text = conference.subject + conferenceDialogDateValue.text = conference.date.toFormattedString("dd.MM.yyyy HH:mm") + conferenceDialogHeaderValue.text = conference.title + conferenceDialogAgendaValue.text = conference.agenda + conferenceDialogPresentValue.text = conference.presentOnConference + conferenceDialogPresentValue.isVisible = conference.presentOnConference.isNotBlank() + conferenceDialogPresentTitle.isVisible = conference.presentOnConference.isNotBlank() + conferenceDialogAgendaValue.isVisible = conference.agenda.isNotBlank() + conferenceDialogAgendaTitle.isVisible = conference.agenda.isNotBlank() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceFragment.kt new file mode 100644 index 000000000..b9642b1c7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceFragment.kt @@ -0,0 +1,116 @@ +package io.github.wulkanowy.ui.modules.conference + +import android.os.Bundle +import android.view.View +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Conference +import io.github.wulkanowy.databinding.FragmentConferenceBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.widgets.DividerItemDecoration +import io.github.wulkanowy.utils.getThemeAttrColor +import javax.inject.Inject + +@AndroidEntryPoint +class ConferenceFragment : BaseFragment(R.layout.fragment_conference), + ConferenceView, MainView.TitledView { + + @Inject + lateinit var presenter: ConferencePresenter + + @Inject + lateinit var conferencesAdapter: ConferenceAdapter + + companion object { + fun newInstance() = ConferenceFragment() + } + + override val isViewEmpty: Boolean + get() = conferencesAdapter.items.isEmpty() + + override val titleStringId: Int + get() = R.string.conferences_title + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentConferenceBinding.bind(view) + messageContainer = binding.conferenceRecycler + presenter.onAttachView(this) + } + + override fun initView() { + conferencesAdapter.onItemClickListener = presenter::onItemSelected + + with(binding.conferenceRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = conferencesAdapter + addItemDecoration(DividerItemDecoration(context)) + } + + with(binding) { + conferenceSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + conferenceSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) + conferenceSwipe.setProgressBackgroundColorSchemeColor( + requireContext().getThemeAttrColor( + R.attr.colorSwipeRefresh + ) + ) + conferenceErrorRetry.setOnClickListener { presenter.onRetry() } + conferenceErrorDetails.setOnClickListener { presenter.onDetailsClick() } + } + } + + override fun updateData(data: List) { + with(conferencesAdapter) { + items = data + notifyDataSetChanged() + } + } + + override fun clearData() { + with(conferencesAdapter) { + items = emptyList() + notifyDataSetChanged() + } + } + + override fun showRefresh(show: Boolean) { + binding.conferenceSwipe.isRefreshing = show + } + + override fun showProgress(show: Boolean) { + binding.conferenceProgress.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showEmpty(show: Boolean) { + binding.conferenceEmpty.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showErrorView(show: Boolean) { + binding.conferenceError.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun setErrorDetails(message: String) { + binding.conferenceErrorMessage.text = message + } + + override fun enableSwipe(enable: Boolean) { + binding.conferenceSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + binding.conferenceRecycler.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun openConferenceDialog(conference: Conference) { + (activity as? MainActivity)?.showDialogFragment(ConferenceDialog.newInstance(conference)) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt new file mode 100644 index 000000000..f53648930 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt @@ -0,0 +1,99 @@ +package io.github.wulkanowy.ui.modules.conference + +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.db.entities.Conference +import io.github.wulkanowy.data.repositories.ConferenceRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import timber.log.Timber +import javax.inject.Inject + +class ConferencePresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val semesterRepository: SemesterRepository, + private val conferenceRepository: ConferenceRepository, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + private lateinit var lastError: Throwable + + override fun onAttachView(view: ConferenceView) { + super.onAttachView(view) + view.initView() + Timber.i("Conferences view was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError + loadData() + } + + fun onSwipeRefresh() { + loadData(true) + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(true) + } + + fun onItemSelected(conference: Conference) { + view?.openConferenceDialog(conference) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + + private fun loadData(forceRefresh: Boolean = false) { + flatResourceFlow { + val student = studentRepository.getCurrentStudent() + val semester = semesterRepository.getCurrentSemester(student) + conferenceRepository.getConferences(student, semester, forceRefresh) + } + .logResourceStatus("load conference data") + .mapResourceData { it.sortedByDescending { conference -> conference.date } } + .onResourceData { + view?.run { + enableSwipe(true) + showProgress(false) + showErrorView(false) + showContent(it.isNotEmpty()) + showEmpty(it.isEmpty()) + updateData(it) + } + } + .onResourceIntermediate { view?.showRefresh(true) } + .onResourceSuccess { + analytics.logEvent( + "load_data", + "type" to "conferences", + "items" to it.size + ) + } + .onResourceNotLoading { + view?.run { + enableSwipe(true) + showProgress(false) + showRefresh(false) + } + } + .onResourceError(errorHandler::dispatch) + .launch() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceView.kt new file mode 100644 index 000000000..4f73394df --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceView.kt @@ -0,0 +1,31 @@ +package io.github.wulkanowy.ui.modules.conference + +import io.github.wulkanowy.data.db.entities.Conference +import io.github.wulkanowy.ui.base.BaseView + +interface ConferenceView : BaseView { + + val isViewEmpty: Boolean + + fun initView() + + fun updateData(data: List) + + fun clearData() + + fun showRefresh(show: Boolean) + + fun showEmpty(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun openConferenceDialog(conference: Conference) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt new file mode 100644 index 000000000..9191d43c9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt @@ -0,0 +1,817 @@ +package io.github.wulkanowy.ui.modules.dashboard + +import android.annotation.SuppressLint +import android.content.res.ColorStateList +import android.graphics.Color +import android.graphics.Typeface +import android.os.Handler +import android.os.Looper +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.text.parseAsHtml +import androidx.core.view.isVisible +import androidx.core.view.updateLayoutParams +import androidx.core.view.updateMarginsRelative +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.AdminMessage +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.data.db.entities.TimetableHeader +import io.github.wulkanowy.data.enums.GradeColorTheme +import io.github.wulkanowy.databinding.ItemDashboardAccountBinding +import io.github.wulkanowy.databinding.ItemDashboardAdminMessageBinding +import io.github.wulkanowy.databinding.ItemDashboardAnnouncementsBinding +import io.github.wulkanowy.databinding.ItemDashboardConferencesBinding +import io.github.wulkanowy.databinding.ItemDashboardExamsBinding +import io.github.wulkanowy.databinding.ItemDashboardGradesBinding +import io.github.wulkanowy.databinding.ItemDashboardHomeworkBinding +import io.github.wulkanowy.databinding.ItemDashboardHorizontalGroupBinding +import io.github.wulkanowy.databinding.ItemDashboardLessonsBinding +import io.github.wulkanowy.utils.createNameInitialsDrawable +import io.github.wulkanowy.utils.dpToPx +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.left +import io.github.wulkanowy.utils.nickOrName +import io.github.wulkanowy.utils.toFormattedString +import timber.log.Timber +import java.time.* +import java.util.Timer +import javax.inject.Inject +import kotlin.concurrent.timer + +class DashboardAdapter @Inject constructor() : RecyclerView.Adapter() { + + private var lessonsTimer: Timer? = null + + var onAccountTileClickListener: (Student) -> Unit = {} + + var onLuckyNumberTileClickListener: () -> Unit = {} + + var onMessageTileClickListener: () -> Unit = {} + + var onGradeTileClickListener: () -> Unit = {} + + var onAttendanceTileClickListener: () -> Unit = {} + + var onLessonsTileClickListener: (LocalDate) -> Unit = {} + + var onHomeworkTileClickListener: () -> Unit = {} + + var onAnnouncementsTileClickListener: () -> Unit = {} + + var onExamsTileClickListener: () -> Unit = {} + + var onConferencesTileClickListener: () -> Unit = {} + + var onAdminMessageClickListener: (String?) -> Unit = {} + + var onAdminMessageDismissClickListener: (AdminMessage) -> Unit = {} + + val items = mutableListOf() + + fun submitList(newItems: List) { + val diffResult = + DiffUtil.calculateDiff(DiffCallback(newItems, items.toMutableList())) + + with(items) { + clear() + addAll(newItems) + } + + diffResult.dispatchUpdatesTo(this) + } + + override fun getItemCount() = items.size + + override fun getItemViewType(position: Int) = items[position].type.ordinal + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + + return when (viewType) { + DashboardItem.Type.ACCOUNT.ordinal -> AccountViewHolder( + ItemDashboardAccountBinding.inflate(inflater, parent, false) + ) + DashboardItem.Type.HORIZONTAL_GROUP.ordinal -> HorizontalGroupViewHolder( + ItemDashboardHorizontalGroupBinding.inflate(inflater, parent, false) + ) + DashboardItem.Type.GRADES.ordinal -> GradesViewHolder( + ItemDashboardGradesBinding.inflate(inflater, parent, false) + ) + DashboardItem.Type.LESSONS.ordinal -> LessonsViewHolder( + ItemDashboardLessonsBinding.inflate(inflater, parent, false) + ) + DashboardItem.Type.HOMEWORK.ordinal -> HomeworkViewHolder( + ItemDashboardHomeworkBinding.inflate(inflater, parent, false) + ) + DashboardItem.Type.ANNOUNCEMENTS.ordinal -> AnnouncementsViewHolder( + ItemDashboardAnnouncementsBinding.inflate(inflater, parent, false) + ) + DashboardItem.Type.EXAMS.ordinal -> ExamsViewHolder( + ItemDashboardExamsBinding.inflate(inflater, parent, false) + ) + DashboardItem.Type.CONFERENCES.ordinal -> ConferencesViewHolder( + ItemDashboardConferencesBinding.inflate(inflater, parent, false) + ) + DashboardItem.Type.ADMIN_MESSAGE.ordinal -> AdminMessageViewHolder( + ItemDashboardAdminMessageBinding.inflate(inflater, parent, false) + ) + else -> throw IllegalArgumentException() + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is AccountViewHolder -> bindAccountViewHolder(holder, position) + is HorizontalGroupViewHolder -> bindHorizontalGroupViewHolder(holder, position) + is GradesViewHolder -> bindGradesViewHolder(holder, position) + is LessonsViewHolder -> bindLessonsViewHolder(holder, position) + is HomeworkViewHolder -> bindHomeworkViewHolder(holder, position) + is AnnouncementsViewHolder -> bindAnnouncementsViewHolder(holder, position) + is ExamsViewHolder -> bindExamsViewHolder(holder, position) + is ConferencesViewHolder -> bindConferencesViewHolder(holder, position) + is AdminMessageViewHolder -> bindAdminMessage(holder, position) + } + } + + fun clearTimers() { + lessonsTimer?.let { + it.cancel() + it.purge() + } + lessonsTimer = null + } + + private fun bindAccountViewHolder(accountViewHolder: AccountViewHolder, position: Int) { + val item = items[position] as DashboardItem.Account + val student = item.student + val isLoading = item.isLoading + + val avatar = student?.let { + accountViewHolder.binding.root.context.createNameInitialsDrawable( + text = it.nickOrName, + backgroundColor = it.avatarColor + ) + } + + with(accountViewHolder.binding) { + dashboardAccountItemContent.isVisible = !isLoading + dashboardAccountItemProgress.isVisible = isLoading + + dashboardAccountItemAvatar.setImageDrawable(avatar) + dashboardAccountItemName.text = student?.nickOrName.orEmpty() + dashboardAccountItemSchoolName.text = student?.schoolName.orEmpty() + + root.setOnClickListener { student?.let(onAccountTileClickListener) } + } + } + + @SuppressLint("SetTextI18n") + private fun bindHorizontalGroupViewHolder( + horizontalGroupViewHolder: HorizontalGroupViewHolder, + position: Int + ) { + val item = items[position] as DashboardItem.HorizontalGroup + val unreadMessagesCount = item.unreadMessagesCount + val attendancePercentage = item.attendancePercentage + val luckyNumber = item.luckyNumber + val error = item.error + val isLoading = item.isLoading + val binding = horizontalGroupViewHolder.binding + val context = binding.root.context + val isLoadingVisible = + (isLoading && !item.isDataLoaded) || (isLoading && !item.isFullDataLoaded) + val attendanceColor = when { + attendancePercentage == null || attendancePercentage == .0 -> { + context.getThemeAttrColor(R.attr.colorOnSurface) + } + attendancePercentage <= ATTENDANCE_SECOND_WARNING_THRESHOLD -> { + context.getThemeAttrColor(R.attr.colorPrimary) + } + attendancePercentage <= ATTENDANCE_FIRST_WARNING_THRESHOLD -> { + context.getThemeAttrColor(R.attr.colorTimetableChange) + } + else -> context.getThemeAttrColor(R.attr.colorOnSurface) + } + val attendanceString = if (attendancePercentage == null || attendancePercentage == .0) { + context.getString(R.string.dashboard_horizontal_group_no_data) + } else { + "%.2f%%".format(attendancePercentage) + } + + with(binding.dashboardHorizontalGroupItemAttendanceValue) { + text = attendanceString + setTextColor(attendanceColor) + } + + with(binding) { + dashboardHorizontalGroupItemMessageValue.text = unreadMessagesCount.toString() + dashboardHorizontalGroupItemLuckyValue.text = if (luckyNumber == 0) { + context.getString(R.string.dashboard_horizontal_group_no_data) + } else luckyNumber?.toString() + + dashboardHorizontalGroupItemInfoContainer.isVisible = error != null || isLoadingVisible + dashboardHorizontalGroupItemInfoProgress.isVisible = isLoadingVisible + dashboardHorizontalGroupItemInfoErrorText.isVisible = error != null + + with(dashboardHorizontalGroupItemLuckyContainer) { + isVisible = luckyNumber != null && luckyNumber != -1 && !isLoadingVisible + setOnClickListener { onLuckyNumberTileClickListener() } + + updateLayoutParams { + updateMarginsRelative( + end = if (attendancePercentage == null && unreadMessagesCount == null && luckyNumber != null) { + 0 + } else { + context.dpToPx(8f).toInt() + } + ) + } + } + + with(dashboardHorizontalGroupItemAttendanceContainer) { + isVisible = + attendancePercentage != null && attendancePercentage != -1.0 && !isLoadingVisible + updateLayoutParams { + matchConstraintPercentWidth = when { + luckyNumber == null && unreadMessagesCount == null -> 1.0f + luckyNumber == null || unreadMessagesCount == null -> 0.5f + else -> 0.4f + } + } + setOnClickListener { onAttendanceTileClickListener() } + } + + with(dashboardHorizontalGroupItemMessageContainer) { + isVisible = + unreadMessagesCount != null && unreadMessagesCount != -1 && !isLoadingVisible + setOnClickListener { onMessageTileClickListener() } + } + } + } + + private fun bindGradesViewHolder(gradesViewHolder: GradesViewHolder, position: Int) { + val item = items[position] as DashboardItem.Grades + val subjectWithGrades = item.subjectWithGrades.orEmpty() + val gradeTheme = item.gradeTheme + val error = item.error + val isLoading = item.isLoading + val dashboardGradesAdapter = gradesViewHolder.adapter.apply { + this.items = subjectWithGrades.toList() + this.gradeColorTheme = gradeTheme ?: GradeColorTheme.VULCAN + } + + with(gradesViewHolder.binding) { + dashboardGradesItemEmpty.isVisible = + subjectWithGrades.isEmpty() && error == null && !isLoading + dashboardGradesItemError.isVisible = error != null && !isLoading + dashboardGradesItemProgress.isVisible = + isLoading && error == null && subjectWithGrades.isEmpty() + + with(dashboardGradesItemRecycler) { + adapter = dashboardGradesAdapter + layoutManager = LinearLayoutManager(context) + isVisible = subjectWithGrades.isNotEmpty() && error == null + suppressLayout(true) + } + + root.setOnClickListener { onGradeTileClickListener() } + } + } + + private fun bindLessonsViewHolder(lessonsViewHolder: LessonsViewHolder, position: Int) { + val item = items[position] as DashboardItem.Lessons + val timetableFull = item.lessons + val binding = lessonsViewHolder.binding + var dateToNavigate = LocalDate.now() + + fun updateLessonState() { + val currentDateTime = Instant.now() + val currentDate = LocalDate.now() + val tomorrowDate = currentDate.plusDays(1) + + val currentTimetable = timetableFull?.lessons + .orEmpty() + .filter { it.date == currentDate } + .filter { it.end.isAfter(currentDateTime) } + .filterNot { it.canceled } + val currentDayHeader = + timetableFull?.headers.orEmpty().singleOrNull { it.date == currentDate } + + val tomorrowTimetable = timetableFull?.lessons + .orEmpty() + .filter { it.date == currentDate.plusDays(1) } + .filterNot { it.canceled } + val tomorrowDayHeader = + timetableFull?.headers.orEmpty().singleOrNull { it.date == currentDate.plusDays(1) } + + when { + currentTimetable.isNotEmpty() -> { + dateToNavigate = currentDate + updateLessonView(item, currentTimetable, binding) + binding.dashboardLessonsItemTitleTomorrow.isVisible = false + binding.dashboardLessonsItemTitleTodayAndTomorrow.isVisible = false + } + tomorrowTimetable.isNotEmpty() -> { + dateToNavigate = tomorrowDate + updateLessonView(item, tomorrowTimetable, binding) + binding.dashboardLessonsItemTitleTomorrow.isVisible = true + binding.dashboardLessonsItemTitleTodayAndTomorrow.isVisible = false + } + currentDayHeader != null && currentDayHeader.content.isNotBlank() -> { + dateToNavigate = currentDate + updateLessonView(item, emptyList(), binding, currentDayHeader) + binding.dashboardLessonsItemTitleTomorrow.isVisible = false + binding.dashboardLessonsItemTitleTodayAndTomorrow.isVisible = false + } + tomorrowDayHeader != null && tomorrowDayHeader.content.isNotBlank() -> { + dateToNavigate = tomorrowDate + updateLessonView(item, emptyList(), binding, tomorrowDayHeader) + binding.dashboardLessonsItemTitleTomorrow.isVisible = true + binding.dashboardLessonsItemTitleTodayAndTomorrow.isVisible = false + } + else -> { + dateToNavigate = currentDate + updateLessonView(item, emptyList(), binding) + binding.dashboardLessonsItemTitleTomorrow.isVisible = false + binding.dashboardLessonsItemTitleTodayAndTomorrow.isVisible = + !(item.isLoading && item.error == null) + } + } + } + + updateLessonState() + + lessonsTimer?.cancel() + lessonsTimer = timer(period = 1000) { + Handler(Looper.getMainLooper()).post { updateLessonState() } + } + + binding.root.setOnClickListener { onLessonsTileClickListener(dateToNavigate) } + } + + private fun updateLessonView( + item: DashboardItem.Lessons, + timetableToShow: List, + binding: ItemDashboardLessonsBinding, + header: TimetableHeader? = null, + ) { + val currentDateTime = Instant.now() + val nextLessons = timetableToShow.filter { it.end.isAfter(currentDateTime) } + .sortedBy { it.start } + + with(binding) { + dashboardLessonsItemEmpty.isVisible = + (timetableToShow.isEmpty() || nextLessons.isEmpty()) && item.error == null && header == null && !item.isLoading + dashboardLessonsItemError.isVisible = item.error != null && !item.isLoading + dashboardLessonsItemProgress.isVisible = + item.isLoading && (timetableToShow.isEmpty() || nextLessons.isEmpty()) && item.error == null && header == null + + val secondLesson = nextLessons.getOrNull(1) + val firstLesson = nextLessons.getOrNull(0) + + updateFirstLessonView(binding, firstLesson, currentDateTime) + updateSecondLesson(binding, firstLesson, secondLesson) + updateLessonSummary(binding, nextLessons) + updateLessonHeader(binding, header) + } + } + + @SuppressLint("SetTextI18n") + private fun updateFirstLessonView( + binding: ItemDashboardLessonsBinding, + firstLesson: Timetable?, + currentDateTime: Instant + ) { + val context = binding.root.context + val sansSerifFont = Typeface.create("sans-serif", Typeface.NORMAL) + val sansSerifMediumFont = Typeface.create("sans-serif-medium", Typeface.NORMAL) + + with(binding) { + dashboardLessonsItemFirstTitle.isVisible = firstLesson != null + dashboardLessonsItemFirstTime.isVisible = firstLesson != null + dashboardLessonsItemFirstTimeRange.isVisible = firstLesson != null + dashboardLessonsItemFirstValue.isVisible = firstLesson != null + } + + firstLesson ?: return + + val minutesToStartLesson = + Duration.between(currentDateTime, firstLesson.start).toMinutes() + 1 + val isFirstTimeVisible: Boolean + val isFirstTimeRangeVisible: Boolean + val firstTimeText: String + val firstTimeRangeText: String + val firstTitleText: String + val firstTitleAndValueTextColor: Int + val firstTitleAndValueTextFont: Typeface + + if (currentDateTime < firstLesson.start) { + if (minutesToStartLesson > 60) { + val formattedStartTime = firstLesson.start.toFormattedString("HH:mm") + val formattedEndTime = firstLesson.end.toFormattedString("HH:mm") + + firstTimeRangeText = "$formattedStartTime - $formattedEndTime" + firstTimeText = "" + + isFirstTimeRangeVisible = true + isFirstTimeVisible = false + } else { + firstTimeText = context.getString( + R.string.timetable_time_until, + context.getString( + R.string.timetable_minutes, + minutesToStartLesson.toString() + ) + ) + firstTimeRangeText = "" + + isFirstTimeRangeVisible = false + isFirstTimeVisible = true + } + + when { + minutesToStartLesson < 60 -> { + firstTitleAndValueTextColor = context.getThemeAttrColor(R.attr.colorPrimary) + firstTitleAndValueTextFont = sansSerifMediumFont + firstTitleText = + context.getString(R.string.dashboard_timetable_first_lesson_title_moment) + } + minutesToStartLesson < 240 -> { + firstTitleAndValueTextColor = + context.getThemeAttrColor(R.attr.colorOnSurface) + firstTitleAndValueTextFont = sansSerifFont + firstTitleText = + context.getString(R.string.dashboard_timetable_first_lesson_title_soon) + } + else -> { + firstTitleAndValueTextColor = + context.getThemeAttrColor(R.attr.colorOnSurface) + firstTitleAndValueTextFont = sansSerifFont + firstTitleText = + context.getString(R.string.dashboard_timetable_first_lesson_title_first) + } + } + } else { + val minutesToEndLesson = firstLesson.left?.toMinutes()?.plus(1) ?: run { + Timber.e(IllegalArgumentException("Lesson left is null. START ${firstLesson.start} ; END ${firstLesson.end} ; CURRENT ${LocalDateTime.now()}")) + 0 + } + + firstTimeText = context.getString( + R.string.timetable_time_left, + context.getString( + R.string.timetable_minutes, + minutesToEndLesson.toString() + ) + ) + firstTimeRangeText = "" + + isFirstTimeRangeVisible = false + isFirstTimeVisible = true + + firstTitleAndValueTextColor = context.getThemeAttrColor(R.attr.colorPrimary) + firstTitleAndValueTextFont = sansSerifMediumFont + firstTitleText = context.getString(R.string.dashboard_timetable_first_lesson_title_now) + } + + with(binding.dashboardLessonsItemFirstTime) { + isVisible = isFirstTimeVisible + text = firstTimeText + } + with(binding.dashboardLessonsItemFirstTimeRange) { + isVisible = isFirstTimeRangeVisible + text = firstTimeRangeText + } + with(binding.dashboardLessonsItemFirstTitle) { + setTextColor(firstTitleAndValueTextColor) + typeface = firstTitleAndValueTextFont + text = firstTitleText + } + with(binding.dashboardLessonsItemFirstValue) { + setTextColor(firstTitleAndValueTextColor) + typeface = firstTitleAndValueTextFont + text = + "${firstLesson.subject} ${if (firstLesson.room.isNotBlank()) "(${firstLesson.room})" else ""}" + } + } + + private fun updateSecondLesson( + binding: ItemDashboardLessonsBinding, + firstLesson: Timetable?, + secondLesson: Timetable? + ) { + val context = binding.root.context + + val formattedStartTime = secondLesson?.start?.toFormattedString("HH:mm") + val formattedEndTime = secondLesson?.end?.toFormattedString("HH:mm") + + val secondTimeText = "$formattedStartTime - $formattedEndTime" + val secondValueText = if (secondLesson != null) { + val roomString = if (secondLesson.room.isNotBlank()) "(${secondLesson.room})" else "" + + "${secondLesson.subject} $roomString" + } else { + context.getString(R.string.dashboard_timetable_second_lesson_value_end) + } + + with(binding.dashboardLessonsItemSecondTime) { + isVisible = secondLesson != null + text = secondTimeText + } + with(binding.dashboardLessonsItemSecondValue) { + isVisible = !(secondLesson == null && firstLesson == null) + text = secondValueText + } + binding.dashboardLessonsItemSecondTitle.isVisible = + !(secondLesson == null && firstLesson == null) + } + + private fun updateLessonSummary( + binding: ItemDashboardLessonsBinding, + nextLessons: List + ) { + val context = binding.root.context + val formattedEndTime = nextLessons.lastOrNull()?.end?.toFormattedString("HH:mm") + + with(binding) { + dashboardLessonsItemThirdTime.isVisible = + nextLessons.size > LESSON_SUMMARY_VISIBILITY_THRESHOLD + dashboardLessonsItemThirdTitle.isVisible = + nextLessons.size > LESSON_SUMMARY_VISIBILITY_THRESHOLD + dashboardLessonsItemThirdValue.isVisible = + nextLessons.size > LESSON_SUMMARY_VISIBILITY_THRESHOLD + dashboardLessonsItemDivider.isVisible = + nextLessons.size > LESSON_SUMMARY_VISIBILITY_THRESHOLD + + dashboardLessonsItemThirdValue.text = context.resources.getQuantityString( + R.plurals.dashboard_timetable_third_value, + nextLessons.size - LESSON_SUMMARY_VISIBILITY_THRESHOLD, + nextLessons.size - LESSON_SUMMARY_VISIBILITY_THRESHOLD + ) + dashboardLessonsItemThirdTime.text = + context.getString(R.string.dashboard_timetable_third_time, formattedEndTime) + } + } + + private fun updateLessonHeader( + binding: ItemDashboardLessonsBinding, + header: TimetableHeader? + ) { + with(binding.dashboardLessonsItemDayHeader) { + isVisible = header != null + text = header?.content?.parseAsHtml() + } + } + + private fun bindHomeworkViewHolder(homeworkViewHolder: HomeworkViewHolder, position: Int) { + val item = items[position] as DashboardItem.Homework + val homeworkList = item.homework.orEmpty() + val error = item.error + val isLoading = item.isLoading + val context = homeworkViewHolder.binding.root.context + val homeworkAdapter = homeworkViewHolder.adapter.apply { + this.items = homeworkList.take(MAX_VISIBLE_LIST_ITEMS) + } + + with(homeworkViewHolder.binding) { + dashboardHomeworkItemEmpty.isVisible = + homeworkList.isEmpty() && error == null && !isLoading + dashboardHomeworkItemError.isVisible = error != null && !isLoading + dashboardHomeworkItemProgress.isVisible = + isLoading && error == null && homeworkList.isEmpty() + dashboardHomeworkItemDivider.isVisible = homeworkList.size > MAX_VISIBLE_LIST_ITEMS + dashboardHomeworkItemMore.isVisible = homeworkList.size > MAX_VISIBLE_LIST_ITEMS + dashboardHomeworkItemMore.text = context.resources.getQuantityString( + R.plurals.dashboard_homework_more, + homeworkList.size - MAX_VISIBLE_LIST_ITEMS, + homeworkList.size - MAX_VISIBLE_LIST_ITEMS + ) + + with(dashboardHomeworkItemRecycler) { + adapter = homeworkAdapter + layoutManager = LinearLayoutManager(context) + isVisible = homeworkList.isNotEmpty() && error == null + suppressLayout(true) + } + + root.setOnClickListener { onHomeworkTileClickListener() } + } + } + + private fun bindAnnouncementsViewHolder( + announcementsViewHolder: AnnouncementsViewHolder, + position: Int + ) { + val item = items[position] as DashboardItem.Announcements + val schoolAnnouncementList = item.announcement.orEmpty() + val error = item.error + val isLoading = item.isLoading + val context = announcementsViewHolder.binding.root.context + val schoolAnnouncementsAdapter = announcementsViewHolder.adapter.apply { + this.items = schoolAnnouncementList.take(MAX_VISIBLE_LIST_ITEMS) + } + + with(announcementsViewHolder.binding) { + dashboardAnnouncementsItemEmpty.isVisible = + schoolAnnouncementList.isEmpty() && error == null && !isLoading + dashboardAnnouncementsItemError.isVisible = error != null && !isLoading + dashboardAnnouncementsItemProgress.isVisible = + isLoading && error == null && schoolAnnouncementList.isEmpty() + dashboardAnnouncementsItemDivider.isVisible = + schoolAnnouncementList.size > MAX_VISIBLE_LIST_ITEMS + dashboardAnnouncementsItemMore.isVisible = + schoolAnnouncementList.size > MAX_VISIBLE_LIST_ITEMS + dashboardAnnouncementsItemMore.text = context.resources.getQuantityString( + R.plurals.dashboard_announcements_more, + schoolAnnouncementList.size - MAX_VISIBLE_LIST_ITEMS, + schoolAnnouncementList.size - MAX_VISIBLE_LIST_ITEMS + ) + + with(dashboardAnnouncementsItemRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = schoolAnnouncementsAdapter + isVisible = schoolAnnouncementList.isNotEmpty() && error == null + suppressLayout(true) + } + + root.setOnClickListener { onAnnouncementsTileClickListener() } + } + } + + private fun bindExamsViewHolder(examsViewHolder: ExamsViewHolder, position: Int) { + val item = items[position] as DashboardItem.Exams + val exams = item.exams.orEmpty() + val error = item.error + val isLoading = item.isLoading + val context = examsViewHolder.binding.root.context + val examAdapter = examsViewHolder.adapter.apply { + this.items = exams.take(MAX_VISIBLE_LIST_ITEMS) + } + + with(examsViewHolder.binding) { + dashboardExamsItemEmpty.isVisible = exams.isEmpty() && error == null && !isLoading + dashboardExamsItemError.isVisible = error != null && !isLoading + dashboardExamsItemProgress.isVisible = isLoading && error == null && exams.isEmpty() + dashboardExamsItemDivider.isVisible = exams.size > MAX_VISIBLE_LIST_ITEMS + dashboardExamsItemMore.isVisible = exams.size > MAX_VISIBLE_LIST_ITEMS + dashboardExamsItemMore.text = context.resources.getQuantityString( + R.plurals.dashboard_exams_more, + exams.size - MAX_VISIBLE_LIST_ITEMS, + exams.size - MAX_VISIBLE_LIST_ITEMS + ) + + with(dashboardExamsItemRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = examAdapter + isVisible = exams.isNotEmpty() && error == null + suppressLayout(true) + } + + root.setOnClickListener { onExamsTileClickListener() } + } + } + + private fun bindConferencesViewHolder( + conferencesViewHolder: ConferencesViewHolder, + position: Int + ) { + val item = items[position] as DashboardItem.Conferences + val conferences = item.conferences.orEmpty() + val error = item.error + val isLoading = item.isLoading + val context = conferencesViewHolder.binding.root.context + val conferenceAdapter = conferencesViewHolder.adapter.apply { + this.items = conferences.take(MAX_VISIBLE_LIST_ITEMS) + } + + with(conferencesViewHolder.binding) { + dashboardConferencesItemEmpty.isVisible = + conferences.isEmpty() && error == null && !isLoading + dashboardConferencesItemError.isVisible = error != null && !isLoading + dashboardConferencesItemProgress.isVisible = + isLoading && error == null && conferences.isEmpty() + dashboardConferencesItemDivider.isVisible = conferences.size > MAX_VISIBLE_LIST_ITEMS + dashboardConferencesItemMore.isVisible = conferences.size > MAX_VISIBLE_LIST_ITEMS + dashboardConferencesItemMore.text = context.resources.getQuantityString( + R.plurals.dashboard_conference_more, + conferences.size - MAX_VISIBLE_LIST_ITEMS, + conferences.size - MAX_VISIBLE_LIST_ITEMS + ) + + with(dashboardConferencesItemRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = conferenceAdapter + isVisible = conferences.isNotEmpty() && error == null + suppressLayout(true) + } + + root.setOnClickListener { onConferencesTileClickListener() } + } + } + + private fun bindAdminMessage(adminMessageViewHolder: AdminMessageViewHolder, position: Int) { + val item = (items[position] as DashboardItem.AdminMessages).adminMessage ?: return + val context = adminMessageViewHolder.binding.root.context + val (backgroundColor, textColor) = when (item.priority) { + "HIGH" -> { + context.getThemeAttrColor(R.attr.colorPrimary) to + context.getThemeAttrColor(R.attr.colorOnPrimary) + } + "MEDIUM" -> { + context.getThemeAttrColor(R.attr.colorMessageMedium) to Color.BLACK + } + else -> null to context.getThemeAttrColor(R.attr.colorOnSurface) + } + + with(adminMessageViewHolder.binding) { + dashboardAdminMessageItemTitle.text = item.title + dashboardAdminMessageItemTitle.setTextColor(textColor) + dashboardAdminMessageItemDescription.text = item.content + dashboardAdminMessageItemDescription.setTextColor(textColor) + dashboardAdminMessageItemIcon.setColorFilter(textColor) + dashboardAdminMessageItemDismiss.isVisible = item.isDismissible + dashboardAdminMessageItemDismiss.setOnClickListener { + onAdminMessageDismissClickListener(item) + } + + root.setCardBackgroundColor(backgroundColor?.let { ColorStateList.valueOf(it) }) + item.destinationUrl?.let { url -> + root.setOnClickListener { onAdminMessageClickListener(url) } + } + } + } + + class AccountViewHolder(val binding: ItemDashboardAccountBinding) : + RecyclerView.ViewHolder(binding.root) + + class HorizontalGroupViewHolder(val binding: ItemDashboardHorizontalGroupBinding) : + RecyclerView.ViewHolder(binding.root) + + class GradesViewHolder(val binding: ItemDashboardGradesBinding) : + RecyclerView.ViewHolder(binding.root) { + + val adapter by lazy { DashboardGradesAdapter() } + } + + class LessonsViewHolder(val binding: ItemDashboardLessonsBinding) : + RecyclerView.ViewHolder(binding.root) + + class HomeworkViewHolder(val binding: ItemDashboardHomeworkBinding) : + RecyclerView.ViewHolder(binding.root) { + + val adapter by lazy { DashboardHomeworkAdapter() } + } + + class AnnouncementsViewHolder(val binding: ItemDashboardAnnouncementsBinding) : + RecyclerView.ViewHolder(binding.root) { + + val adapter by lazy { DashboardAnnouncementsAdapter() } + } + + class ExamsViewHolder(val binding: ItemDashboardExamsBinding) : + RecyclerView.ViewHolder(binding.root) { + + val adapter by lazy { DashboardExamsAdapter() } + } + + class ConferencesViewHolder(val binding: ItemDashboardConferencesBinding) : + RecyclerView.ViewHolder(binding.root) { + + val adapter by lazy { DashboardConferencesAdapter() } + } + + class AdminMessageViewHolder(val binding: ItemDashboardAdminMessageBinding) : + RecyclerView.ViewHolder(binding.root) + + private class DiffCallback( + private val newList: List, + private val oldList: List + ) : DiffUtil.Callback() { + + override fun getNewListSize() = newList.size + + override fun getOldListSize() = oldList.size + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) = + newList[newItemPosition] == oldList[oldItemPosition] + + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) = + newList[newItemPosition].type == oldList[oldItemPosition].type + } + + private companion object { + + private const val LESSON_SUMMARY_VISIBILITY_THRESHOLD = 2 + + private const val MAX_VISIBLE_LIST_ITEMS = 5 + + private const val ATTENDANCE_FIRST_WARNING_THRESHOLD = 75.0 + + private const val ATTENDANCE_SECOND_WARNING_THRESHOLD = 50.0 + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAnnouncementsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAnnouncementsAdapter.kt new file mode 100644 index 000000000..7a4c2b257 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAnnouncementsAdapter.kt @@ -0,0 +1,36 @@ +package io.github.wulkanowy.ui.modules.dashboard + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.data.db.entities.SchoolAnnouncement +import io.github.wulkanowy.databinding.SubitemDashboardAnnouncementsBinding +import io.github.wulkanowy.utils.toFormattedString + +class DashboardAnnouncementsAdapter : + RecyclerView.Adapter() { + + var items = emptyList() + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( + SubitemDashboardAnnouncementsBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val item = items[position] + + with(holder.binding) { + dashboardHomeworkSubitemTime.text = item.date.toFormattedString() + dashboardHomeworkSubitemTitle.text = item.subject + } + } + + class ViewHolder(val binding: SubitemDashboardAnnouncementsBinding) : + RecyclerView.ViewHolder(binding.root) +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardConferencesAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardConferencesAdapter.kt new file mode 100644 index 000000000..64cf599c8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardConferencesAdapter.kt @@ -0,0 +1,36 @@ +package io.github.wulkanowy.ui.modules.dashboard + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.data.db.entities.Conference +import io.github.wulkanowy.databinding.SubitemDashboardConferencesBinding +import io.github.wulkanowy.utils.toFormattedString + +class DashboardConferencesAdapter : + RecyclerView.Adapter() { + + var items = emptyList() + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( + SubitemDashboardConferencesBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val item = items[position] + + with(holder.binding) { + dashboardHomeworkSubitemTime.text = item.date.toFormattedString("HH:mm dd.MM.yyyy") + dashboardHomeworkSubitemTitle.text = item.title + } + } + + class ViewHolder(val binding: SubitemDashboardConferencesBinding) : + RecyclerView.ViewHolder(binding.root) +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardExamsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardExamsAdapter.kt new file mode 100644 index 000000000..060f224b3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardExamsAdapter.kt @@ -0,0 +1,59 @@ +package io.github.wulkanowy.ui.modules.dashboard + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Exam +import io.github.wulkanowy.databinding.SubitemDashboardExamsBinding +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.toFormattedString +import java.time.LocalDate + +class DashboardExamsAdapter : + RecyclerView.Adapter() { + + var items = emptyList() + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( + SubitemDashboardExamsBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + + @SuppressLint("SetTextI18n") + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val item = items[position] + val context = holder.binding.root.context + val primaryWarningTextColor = context.getThemeAttrColor( + if (item.date == LocalDate.now()) { + R.attr.colorPrimary + } else { + android.R.attr.textColorPrimary + } + ) + val secondaryWarningTextColor = context.getThemeAttrColor( + if (item.date == LocalDate.now()) { + R.attr.colorPrimary + } else { + android.R.attr.textColorSecondary + } + ) + + with(holder.binding) { + dashboardHomeworkSubitemTime.text = item.date.toFormattedString("dd.MM") + dashboardHomeworkSubitemTime.setTextColor(secondaryWarningTextColor) + + dashboardHomeworkSubitemTitle.text = "${item.type} - ${item.subject}" + dashboardHomeworkSubitemTitle.setTextColor(primaryWarningTextColor) + } + } + + class ViewHolder(val binding: SubitemDashboardExamsBinding) : + RecyclerView.ViewHolder(binding.root) +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt new file mode 100644 index 000000000..65832bdb1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt @@ -0,0 +1,207 @@ +package io.github.wulkanowy.ui.modules.dashboard + +import android.os.Bundle +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import androidx.appcompat.app.AlertDialog +import androidx.core.view.isVisible +import androidx.recyclerview.widget.DefaultItemAnimator +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.databinding.FragmentDashboardBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsFragment +import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment +import io.github.wulkanowy.ui.modules.conference.ConferenceFragment +import io.github.wulkanowy.ui.modules.exam.ExamFragment +import io.github.wulkanowy.ui.modules.grade.GradeFragment +import io.github.wulkanowy.ui.modules.homework.HomeworkFragment +import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.modules.message.MessageFragment +import io.github.wulkanowy.ui.modules.notificationscenter.NotificationsCenterFragment +import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment +import io.github.wulkanowy.ui.modules.timetable.TimetableFragment +import io.github.wulkanowy.utils.* +import java.time.LocalDate +import javax.inject.Inject + +@AndroidEntryPoint +class DashboardFragment : BaseFragment(R.layout.fragment_dashboard), + DashboardView, MainView.TitledView, MainView.MainChildView { + + @Inject + lateinit var presenter: DashboardPresenter + + @Inject + lateinit var dashboardAdapter: DashboardAdapter + + override val titleStringId get() = R.string.dashboard_title + + override var subtitleString = + LocalDate.now().toFormattedString("EEEE, d MMMM yyyy").capitalise() + + companion object { + + fun newInstance() = DashboardFragment() + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentDashboardBinding.bind(view) + presenter.onAttachView(this) + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.action_menu_dashboard, menu) + } + + override fun initView() { + val mainActivity = requireActivity() as MainActivity + val itemTouchHelper = ItemTouchHelper( + DashboardItemMoveCallback(dashboardAdapter, presenter::onDragAndDropEnd) + ) + + dashboardAdapter.apply { + onAccountTileClickListener = { + mainActivity.pushView(AccountDetailsFragment.newInstance(it)) + } + onLuckyNumberTileClickListener = { + mainActivity.pushView(LuckyNumberFragment.newInstance()) + } + onMessageTileClickListener = { mainActivity.pushView(MessageFragment.newInstance()) } + onAttendanceTileClickListener = { + mainActivity.pushView(AttendanceSummaryFragment.newInstance()) + } + onLessonsTileClickListener = { + mainActivity.pushView(TimetableFragment.newInstance(it)) + } + onGradeTileClickListener = { mainActivity.pushView(GradeFragment.newInstance()) } + onHomeworkTileClickListener = { mainActivity.pushView(HomeworkFragment.newInstance()) } + onAnnouncementsTileClickListener = { + mainActivity.pushView(SchoolAnnouncementFragment.newInstance()) + } + onExamsTileClickListener = { mainActivity.pushView(ExamFragment.newInstance()) } + onConferencesTileClickListener = { + mainActivity.pushView(ConferenceFragment.newInstance()) + } + onAdminMessageClickListener = presenter::onAdminMessageSelected + onAdminMessageDismissClickListener = presenter::onAdminMessageDismissed + + registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { + override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { + binding.dashboardRecycler.scrollToPosition(0) + } + }) + } + + with(binding) { + dashboardErrorRetry.setOnClickListener { presenter.onRetry() } + dashboardErrorDetails.setOnClickListener { presenter.onDetailsClick() } + dashboardSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + dashboardSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) + dashboardSwipe.setProgressBackgroundColorSchemeColor( + requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh) + ) + + with(dashboardRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = dashboardAdapter + (itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false + } + + itemTouchHelper.attachToRecyclerView(dashboardRecycler) + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.dashboard_menu_tiles -> presenter.onDashboardTileSettingsSelected() + R.id.dashboard_menu_notifaction_list -> presenter.onNotificationsCenterSelected() + else -> false + } + } + + override fun showDashboardTileSettings(selectedItems: List) { + val entries = requireContext().resources.getStringArray(R.array.dashboard_tile_entries) + val values = requireContext().resources.getStringArray(R.array.dashboard_tile_values) + val selectedItemsState = values.map { value -> selectedItems.any { it.name == value } } + + AlertDialog.Builder(requireContext()) + .setTitle(R.string.pref_dashboard_appearance_tiles_title) + .setMultiChoiceItems(entries, selectedItemsState.toBooleanArray()) { _, _, _ -> } + .setPositiveButton(android.R.string.ok) { dialog, _ -> + val selectedState = (dialog as AlertDialog).listView.checkedItemPositions + val selectedValues = values.filterIndexed { index, _ -> selectedState[index] } + + presenter.onDashboardTileSettingSelected(selectedValues) + } + .setNegativeButton(android.R.string.cancel) { _, _ -> } + .show() + } + + override fun updateData(data: List) { + dashboardAdapter.submitList(data.toMutableList()) + } + + override fun showMessage(text: String) { + //Empty function to avoid message flood + } + + override fun showRefresh(show: Boolean) { + binding.dashboardSwipe.isRefreshing = show + } + + override fun showProgress(show: Boolean) { + binding.dashboardProgress.isVisible = show + } + + override fun showContent(show: Boolean) { + binding.dashboardRecycler.isVisible = show + } + + override fun showErrorView(show: Boolean) { + binding.dashboardErrorContainer.isVisible = show + } + + override fun setErrorDetails(error: Throwable) { + binding.dashboardErrorMessage.text = requireContext().resources.getErrorString(error) + } + + override fun resetView() { + binding.dashboardRecycler.smoothScrollToPosition(0) + } + + override fun popViewToRoot() { + (requireActivity() as MainActivity).popView(20) + } + + override fun onFragmentReselected() { + if (::presenter.isInitialized) presenter.onViewReselected() + } + + override fun openNotificationsCenterView() { + (requireActivity() as MainActivity).pushView(NotificationsCenterFragment.newInstance()) + } + + override fun openInternetBrowser(url: String) { + requireContext().openInternetBrowser(url) + } + + override fun onDestroyView() { + dashboardAdapter.clearTimers() + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardGradesAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardGradesAdapter.kt new file mode 100644 index 000000000..afffcc511 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardGradesAdapter.kt @@ -0,0 +1,50 @@ +package io.github.wulkanowy.ui.modules.dashboard + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.data.enums.GradeColorTheme +import io.github.wulkanowy.databinding.SubitemDashboardGradesBinding +import io.github.wulkanowy.databinding.SubitemDashboardSmallGradeBinding +import io.github.wulkanowy.utils.getBackgroundColor + +class DashboardGradesAdapter : RecyclerView.Adapter() { + + var items = listOf>>() + + lateinit var gradeColorTheme: GradeColorTheme + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( + SubitemDashboardGradesBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val (subject, grades) = items[position] + val context = holder.binding.root.context + + with(holder.binding) { + dashboardGradesSubitemTitle.text = subject + + grades.forEach { + val subitemBinding = SubitemDashboardSmallGradeBinding.inflate( + LayoutInflater.from(context), + dashboardGradesSubitemGradeContainer, + false + ) + + with(subitemBinding.dashboardSmallGradeSubitemValue) { + text = it.entry + setBackgroundResource(it.getBackgroundColor(gradeColorTheme)) + } + + dashboardGradesSubitemGradeContainer.addView(subitemBinding.root) + } + } + } + + class ViewHolder(val binding: SubitemDashboardGradesBinding) : + RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardHomeworkAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardHomeworkAdapter.kt new file mode 100644 index 000000000..55ec90294 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardHomeworkAdapter.kt @@ -0,0 +1,56 @@ +package io.github.wulkanowy.ui.modules.dashboard + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.databinding.SubitemDashboardHomeworkBinding +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.toFormattedString +import java.time.LocalDate + +class DashboardHomeworkAdapter : RecyclerView.Adapter() { + + var items = emptyList() + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( + SubitemDashboardHomeworkBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + @SuppressLint("SetTextI18n") + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val item = items[position] + val context = holder.binding.root.context + val formattedDate = item.date.toFormattedString("dd.MM") + val primaryWarningTextColor = context.getThemeAttrColor( + if (item.date == LocalDate.now()) { + R.attr.colorPrimary + } else { + android.R.attr.textColorPrimary + } + ) + val secondaryWarningTextColor = context.getThemeAttrColor( + if (item.date == LocalDate.now()) { + R.attr.colorPrimary + } else { + android.R.attr.textColorSecondary + } + ) + + with(holder.binding) { + dashboardHomeworkSubitemTitle.text = "${item.subject} - ${item.content}" + dashboardHomeworkSubitemTitle.setTextColor(primaryWarningTextColor) + + dashboardHomeworkSubitemTime.text = + context.getString(R.string.dashboard_homework_time, formattedDate) + dashboardHomeworkSubitemTime.setTextColor(secondaryWarningTextColor) + } + } + + class ViewHolder(val binding: SubitemDashboardHomeworkBinding) : + RecyclerView.ViewHolder(binding.root) +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt new file mode 100644 index 000000000..c20bae7fa --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt @@ -0,0 +1,151 @@ +package io.github.wulkanowy.ui.modules.dashboard + +import io.github.wulkanowy.data.db.entities.AdminMessage +import io.github.wulkanowy.data.db.entities.Conference +import io.github.wulkanowy.data.db.entities.Exam +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.data.db.entities.SchoolAnnouncement +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.enums.GradeColorTheme +import io.github.wulkanowy.data.pojos.TimetableFull +import io.github.wulkanowy.data.db.entities.Homework as EntitiesHomework + +sealed class DashboardItem(val type: Type) { + + abstract val error: Throwable? + + abstract val isLoading: Boolean + + abstract val isDataLoaded: Boolean + + data class AdminMessages( + val adminMessage: AdminMessage? = null, + override val error: Throwable? = null, + override val isLoading: Boolean = false + ) : DashboardItem(Type.ADMIN_MESSAGE) { + + override val isDataLoaded get() = adminMessage != null + } + + data class Account( + val student: Student? = null, + override val error: Throwable? = null, + override val isLoading: Boolean = false + ) : DashboardItem(Type.ACCOUNT) { + + override val isDataLoaded get() = student != null + } + + data class HorizontalGroup( + val unreadMessagesCount: Int? = null, + val attendancePercentage: Double? = null, + val luckyNumber: Int? = null, + override val error: Throwable? = null, + override val isLoading: Boolean = false + ) : DashboardItem(Type.HORIZONTAL_GROUP) { + + override val isDataLoaded + get() = unreadMessagesCount != null || attendancePercentage != null || luckyNumber != null + + val isFullDataLoaded + get() = luckyNumber != -1 && attendancePercentage != -1.0 && unreadMessagesCount != -1 + } + + data class Grades( + val subjectWithGrades: Map>? = null, + val gradeTheme: GradeColorTheme? = null, + override val error: Throwable? = null, + override val isLoading: Boolean = false + ) : DashboardItem(Type.GRADES) { + + override val isDataLoaded get() = subjectWithGrades != null + } + + data class Lessons( + val lessons: TimetableFull? = null, + override val error: Throwable? = null, + override val isLoading: Boolean = false + ) : DashboardItem(Type.LESSONS) { + + override val isDataLoaded get() = lessons != null + } + + data class Homework( + val homework: List? = null, + override val error: Throwable? = null, + override val isLoading: Boolean = false + ) : DashboardItem(Type.HOMEWORK) { + + override val isDataLoaded get() = homework != null + } + + data class Announcements( + val announcement: List? = null, + override val error: Throwable? = null, + override val isLoading: Boolean = false + ) : DashboardItem(Type.ANNOUNCEMENTS) { + + override val isDataLoaded get() = announcement != null + } + + data class Exams( + val exams: List? = null, + override val error: Throwable? = null, + override val isLoading: Boolean = false + ) : DashboardItem(Type.EXAMS) { + + override val isDataLoaded get() = exams != null + } + + data class Conferences( + val conferences: List? = null, + override val error: Throwable? = null, + override val isLoading: Boolean = false + ) : DashboardItem(Type.CONFERENCES) { + + override val isDataLoaded get() = conferences != null + } + + enum class Type { + ADMIN_MESSAGE, + ACCOUNT, + HORIZONTAL_GROUP, + LESSONS, + GRADES, + HOMEWORK, + ANNOUNCEMENTS, + EXAMS, + CONFERENCES, + ADS + } + + enum class Tile { + ADMIN_MESSAGE, + ACCOUNT, + LUCKY_NUMBER, + MESSAGES, + ATTENDANCE, + LESSONS, + GRADES, + HOMEWORK, + ANNOUNCEMENTS, + EXAMS, + CONFERENCES, + ADS + } +} + +fun DashboardItem.Tile.toDashboardItemType() = when (this) { + DashboardItem.Tile.ADMIN_MESSAGE -> DashboardItem.Type.ADMIN_MESSAGE + DashboardItem.Tile.ACCOUNT -> DashboardItem.Type.ACCOUNT + DashboardItem.Tile.LUCKY_NUMBER -> DashboardItem.Type.HORIZONTAL_GROUP + DashboardItem.Tile.MESSAGES -> DashboardItem.Type.HORIZONTAL_GROUP + DashboardItem.Tile.ATTENDANCE -> DashboardItem.Type.HORIZONTAL_GROUP + DashboardItem.Tile.LESSONS -> DashboardItem.Type.LESSONS + DashboardItem.Tile.GRADES -> DashboardItem.Type.GRADES + DashboardItem.Tile.HOMEWORK -> DashboardItem.Type.HOMEWORK + DashboardItem.Tile.ANNOUNCEMENTS -> DashboardItem.Type.ANNOUNCEMENTS + DashboardItem.Tile.EXAMS -> DashboardItem.Type.EXAMS + DashboardItem.Tile.CONFERENCES -> DashboardItem.Type.CONFERENCES + DashboardItem.Tile.ADS -> DashboardItem.Type.ADS +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItemMoveCallback.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItemMoveCallback.kt new file mode 100644 index 000000000..b9625570f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItemMoveCallback.kt @@ -0,0 +1,58 @@ +package io.github.wulkanowy.ui.modules.dashboard + +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.RecyclerView +import java.util.Collections + +class DashboardItemMoveCallback( + private val dashboardAdapter: DashboardAdapter, + private var onUserInteractionEndListener: (List) -> Unit = {} +) : ItemTouchHelper.Callback() { + + override fun isLongPressDragEnabled() = true + + override fun isItemViewSwipeEnabled() = false + + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { + //Not implemented + } + + override fun getMovementFlags( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder + ): Int { + val dragFlags = if (!viewHolder.isAdminMessageOrAccountItem) { + ItemTouchHelper.UP or ItemTouchHelper.DOWN + } else 0 + + return makeMovementFlags(dragFlags, 0) + } + + override fun canDropOver( + recyclerView: RecyclerView, + current: RecyclerView.ViewHolder, + target: RecyclerView.ViewHolder + ) = !target.isAdminMessageOrAccountItem + + override fun onMove( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder, + target: RecyclerView.ViewHolder + ): Boolean { + val list = dashboardAdapter.items.toMutableList() + + Collections.swap(list, viewHolder.bindingAdapterPosition, target.bindingAdapterPosition) + + dashboardAdapter.submitList(list) + return true + } + + override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) { + super.clearView(recyclerView, viewHolder) + + onUserInteractionEndListener(dashboardAdapter.items.toList()) + } + + private val RecyclerView.ViewHolder.isAdminMessageOrAccountItem: Boolean + get() = this is DashboardAdapter.AdminMessageViewHolder || this is DashboardAdapter.AccountViewHolder +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt new file mode 100644 index 000000000..c33955bc7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt @@ -0,0 +1,748 @@ +package io.github.wulkanowy.ui.modules.dashboard + +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.db.entities.AdminMessage +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.enums.MessageFolder +import io.github.wulkanowy.data.repositories.* +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.calculatePercentage +import io.github.wulkanowy.utils.nextOrSameSchoolDay +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch +import timber.log.Timber +import java.time.Instant +import java.time.LocalDate +import javax.inject.Inject + +class DashboardPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val luckyNumberRepository: LuckyNumberRepository, + private val gradeRepository: GradeRepository, + private val semesterRepository: SemesterRepository, + private val messageRepository: MessageRepository, + private val attendanceSummaryRepository: AttendanceSummaryRepository, + private val timetableRepository: TimetableRepository, + private val homeworkRepository: HomeworkRepository, + private val examRepository: ExamRepository, + private val conferenceRepository: ConferenceRepository, + private val preferencesRepository: PreferencesRepository, + private val schoolAnnouncementRepository: SchoolAnnouncementRepository, + private val adminMessageRepository: AdminMessageRepository +) : BasePresenter(errorHandler, studentRepository) { + + private val dashboardItemLoadedList = mutableListOf() + + private val dashboardItemRefreshLoadedList = mutableListOf() + + private var dashboardItemsToLoad = emptySet() + + private var dashboardTileLoadedList = emptySet() + + private val firstLoadedItemList = mutableListOf() + + private lateinit var lastError: Throwable + + override fun onAttachView(view: DashboardView) { + super.onAttachView(view) + + with(view) { + initView() + showProgress(true) + showContent(false) + } + + preferencesRepository.selectedDashboardTilesFlow + .onEach { loadData(tilesToLoad = it) } + .launch("dashboard_pref") + } + + fun onAdminMessageDismissed(adminMessage: AdminMessage) { + preferencesRepository.dismissedAdminMessageIds += adminMessage.id + + loadData(preferencesRepository.selectedDashboardTiles) + } + + fun onDragAndDropEnd(list: List) { + with(dashboardItemLoadedList) { + clear() + addAll(list) + } + + val positionList = + list.mapIndexed { index, dashboardItem -> Pair(dashboardItem.type, index) }.toMap() + + preferencesRepository.dashboardItemsPosition = positionList + } + + fun loadData( + tilesToLoad: Set, + forceRefresh: Boolean = false, + ) { + val oldDashboardTileLoadedList = dashboardTileLoadedList + dashboardItemsToLoad = tilesToLoad.map { it.toDashboardItemType() }.toSet() + dashboardTileLoadedList = tilesToLoad + + val itemsToLoad = generateDashboardTileListToLoad( + dashboardTilesToLoad = tilesToLoad, + dashboardLoadedTiles = oldDashboardTileLoadedList, + forceRefresh = forceRefresh + ).map { it.toDashboardItemType() } + + removeUnselectedTiles(tilesToLoad.toList()) + loadTiles(tileList = itemsToLoad, forceRefresh = forceRefresh) + } + + private fun generateDashboardTileListToLoad( + dashboardTilesToLoad: Set, + dashboardLoadedTiles: Set, + forceRefresh: Boolean + ) = dashboardTilesToLoad.filter { newItemToLoad -> + dashboardLoadedTiles.none { it == newItemToLoad } || forceRefresh + || newItemToLoad == DashboardItem.Tile.ADMIN_MESSAGE + } + + private fun removeUnselectedTiles(tilesToLoad: List) { + dashboardItemLoadedList.removeAll { loadedTile -> dashboardItemsToLoad.none { it == loadedTile.type } } + + val horizontalGroup = + dashboardItemLoadedList.find { it is DashboardItem.HorizontalGroup } as DashboardItem.HorizontalGroup? + + if (horizontalGroup != null) { + val isLuckyNumberToLoad = DashboardItem.Tile.LUCKY_NUMBER in tilesToLoad + val isMessagesToLoad = DashboardItem.Tile.MESSAGES in tilesToLoad + val isAttendanceToLoad = DashboardItem.Tile.ATTENDANCE in tilesToLoad + + val horizontalGroupIndex = dashboardItemLoadedList.indexOf(horizontalGroup) + + val newHorizontalGroup = horizontalGroup.copy( + attendancePercentage = horizontalGroup.attendancePercentage.takeIf { isAttendanceToLoad }, + unreadMessagesCount = horizontalGroup.unreadMessagesCount.takeIf { isMessagesToLoad }, + luckyNumber = horizontalGroup.luckyNumber.takeIf { isLuckyNumberToLoad } + ) + + with(dashboardItemLoadedList) { + removeAt(horizontalGroupIndex) + add(horizontalGroupIndex, newHorizontalGroup) + } + } + + view?.updateData(dashboardItemLoadedList) + } + + private fun loadTiles( + tileList: List, + forceRefresh: Boolean + ) { + presenterScope.launch { + Timber.i("Loading dashboard account data started") + val student = runCatching { studentRepository.getCurrentStudent(true) } + .onFailure { + Timber.i("Loading dashboard account result: An exception occurred") + errorHandler.dispatch(it) + updateData(DashboardItem.Account(error = it), forceRefresh) + } + .onSuccess { Timber.i("Loading dashboard account result: Success") } + .getOrNull() ?: return@launch + + tileList.forEach { + when (it) { + DashboardItem.Type.ACCOUNT -> { + updateData(DashboardItem.Account(student), forceRefresh) + } + DashboardItem.Type.HORIZONTAL_GROUP -> { + loadHorizontalGroup(student, forceRefresh) + } + DashboardItem.Type.LESSONS -> loadLessons(student, forceRefresh) + DashboardItem.Type.GRADES -> loadGrades(student, forceRefresh) + DashboardItem.Type.HOMEWORK -> loadHomework(student, forceRefresh) + DashboardItem.Type.ANNOUNCEMENTS -> { + loadSchoolAnnouncements(student, forceRefresh) + } + DashboardItem.Type.EXAMS -> loadExams(student, forceRefresh) + DashboardItem.Type.CONFERENCES -> { + loadConferences(student, forceRefresh) + } + DashboardItem.Type.ADS -> TODO() + DashboardItem.Type.ADMIN_MESSAGE -> loadAdminMessage(student, forceRefresh) + } + } + } + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the dashboard") + loadData(preferencesRepository.selectedDashboardTiles, forceRefresh = true) + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(preferencesRepository.selectedDashboardTiles, forceRefresh = true) + } + + fun onViewReselected() { + Timber.i("Dashboard view is reselected") + view?.run { + resetView() + popViewToRoot() + } + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + fun onNotificationsCenterSelected(): Boolean { + view?.openNotificationsCenterView() + return true + } + + fun onDashboardTileSettingsSelected(): Boolean { + view?.showDashboardTileSettings(preferencesRepository.selectedDashboardTiles.toList()) + return true + } + + fun onDashboardTileSettingSelected(selectedItems: List) { + preferencesRepository.selectedDashboardTiles = selectedItems.map { + DashboardItem.Tile.valueOf(it) + }.toSet() + } + + fun onAdminMessageSelected(url: String?) { + url?.let { view?.openInternetBrowser(it) } + } + + private fun loadHorizontalGroup(student: Student, forceRefresh: Boolean) { + flow { + val semester = semesterRepository.getCurrentSemester(student) + val selectedTiles = preferencesRepository.selectedDashboardTiles + + val flowSuccess = flowOf(Resource.Success(null)) + val luckyNumberFlow = luckyNumberRepository.getLuckyNumber(student, forceRefresh) + .mapResourceData { + it ?: LuckyNumber(0, LocalDate.now(), 0) + } + .takeIf { DashboardItem.Tile.LUCKY_NUMBER in selectedTiles } ?: flowSuccess + + val messageFLow = messageRepository.getMessages( + student = student, + semester = semester, + folder = MessageFolder.RECEIVED, + forceRefresh = forceRefresh + ).takeIf { DashboardItem.Tile.MESSAGES in selectedTiles } ?: flowSuccess + + val attendanceFlow = attendanceSummaryRepository.getAttendanceSummary( + student = student, + semester = semester, + subjectId = -1, + forceRefresh = forceRefresh + ).takeIf { DashboardItem.Tile.ATTENDANCE in selectedTiles } ?: flowSuccess + + emitAll( + combine( + luckyNumberFlow, + messageFLow, + attendanceFlow + ) { luckyNumberResource, messageResource, attendanceResource -> + val resList = listOf(luckyNumberResource, messageResource, attendanceResource) + resList.firstNotNullOfOrNull { it.errorOrNull }?.let { throw it } + val isLoading = resList.any { it is Resource.Loading } + + val luckyNumber = luckyNumberResource.dataOrNull?.luckyNumber + val messageCount = messageResource.dataOrNull?.count { it.unread } + val attendancePercentage = attendanceResource.dataOrNull?.calculatePercentage() + + DashboardItem.HorizontalGroup( + isLoading = isLoading, + attendancePercentage = if (attendancePercentage == 0.0 && isLoading) -1.0 else attendancePercentage, + unreadMessagesCount = if (messageCount == 0 && isLoading) -1 else messageCount, + luckyNumber = if (luckyNumber == 0 && isLoading) -1 else luckyNumber + ) + }) + } + .filterNot { it.isLoading && forceRefresh } + .distinctUntilChanged() + .onEach { + updateData(it, forceRefresh) + + if (it.isLoading) { + Timber.i("Loading horizontal group data started") + + if (it.isFullDataLoaded) { + firstLoadedItemList += DashboardItem.Type.HORIZONTAL_GROUP + } + } else { + Timber.i("Loading horizontal group result: Success") + } + } + .catch { + Timber.i("Loading horizontal group result: An exception occurred") + updateData( + DashboardItem.HorizontalGroup(error = it), + forceRefresh, + ) + errorHandler.dispatch(it) + } + .launch("horizontal_group ${if (forceRefresh) "-forceRefresh" else ""}") + } + + private fun loadGrades(student: Student, forceRefresh: Boolean) { + flatResourceFlow { + val semester = semesterRepository.getCurrentSemester(student) + + gradeRepository.getGrades(student, semester, forceRefresh) + } + .mapResourceData { (details, _) -> + val filteredSubjectWithGrades = details + .filter { it.date >= LocalDate.now().minusDays(7) } + .groupBy { it.subject } + .mapValues { entry -> + entry.value + .take(5) + .sortedByDescending { it.date } + } + .toList() + .sortedByDescending { (_, grades) -> grades[0].date } + .toMap() + + filteredSubjectWithGrades + } + .onEach { + when (it) { + is Resource.Loading -> { + Timber.i("Loading dashboard grades data started") + if (forceRefresh) return@onEach + updateData( + DashboardItem.Grades( + subjectWithGrades = it.dataOrNull, + gradeTheme = preferencesRepository.gradeColorTheme, + isLoading = true + ), forceRefresh + ) + + if (!it.dataOrNull.isNullOrEmpty()) { + firstLoadedItemList += DashboardItem.Type.GRADES + } + } + is Resource.Success -> { + Timber.i("Loading dashboard grades result: Success") + updateData( + DashboardItem.Grades( + subjectWithGrades = it.data, + gradeTheme = preferencesRepository.gradeColorTheme + ), + forceRefresh + ) + } + is Resource.Error -> { + Timber.i("Loading dashboard grades result: An exception occurred") + errorHandler.dispatch(it.error) + updateData(DashboardItem.Grades(error = it.error), forceRefresh) + } + } + } + .launchWithUniqueRefreshJob("dashboard_grades", forceRefresh) + } + + private fun loadLessons(student: Student, forceRefresh: Boolean) { + flatResourceFlow { + val semester = semesterRepository.getCurrentSemester(student) + val date = LocalDate.now().nextOrSameSchoolDay + + timetableRepository.getTimetable( + student = student, + semester = semester, + start = date, + end = date.plusDays(1), + forceRefresh = forceRefresh + ) + } + .onEach { + when (it) { + is Resource.Loading -> { + Timber.i("Loading dashboard lessons data started") + if (forceRefresh) return@onEach + updateData( + DashboardItem.Lessons(it.dataOrNull, isLoading = true), + forceRefresh + ) + + if (!it.dataOrNull?.lessons.isNullOrEmpty()) { + firstLoadedItemList += DashboardItem.Type.LESSONS + } + } + is Resource.Success -> { + Timber.i("Loading dashboard lessons result: Success") + updateData( + DashboardItem.Lessons(it.data), forceRefresh + ) + } + is Resource.Error -> { + Timber.i("Loading dashboard lessons result: An exception occurred") + errorHandler.dispatch(it.error) + updateData( + DashboardItem.Lessons(error = it.error), forceRefresh + ) + } + } + } + .launchWithUniqueRefreshJob("dashboard_lessons", forceRefresh) + } + + private fun loadHomework(student: Student, forceRefresh: Boolean) { + flatResourceFlow { + val semester = semesterRepository.getCurrentSemester(student) + val date = LocalDate.now().nextOrSameSchoolDay + + homeworkRepository.getHomework( + student = student, + semester = semester, + start = date, + end = date, + forceRefresh = forceRefresh + ) + } + .mapResourceData { homework -> + val currentDate = LocalDate.now() + + val filteredHomework = homework.filter { + (it.date.isAfter(currentDate) || it.date == currentDate) && !it.isDone + }.sortedBy { it.date } + + filteredHomework + } + .onEach { + when (it) { + is Resource.Loading -> { + Timber.i("Loading dashboard homework data started") + if (forceRefresh) return@onEach + val data = it.dataOrNull.orEmpty() + updateData( + DashboardItem.Homework(data, isLoading = true), + forceRefresh + ) + + if (data.isNotEmpty()) { + firstLoadedItemList += DashboardItem.Type.HOMEWORK + } + } + is Resource.Success -> { + Timber.i("Loading dashboard homework result: Success") + updateData(DashboardItem.Homework(it.data), forceRefresh) + } + is Resource.Error -> { + Timber.i("Loading dashboard homework result: An exception occurred") + errorHandler.dispatch(it.error) + updateData(DashboardItem.Homework(error = it.error), forceRefresh) + } + } + } + .launchWithUniqueRefreshJob("dashboard_homework", forceRefresh) + } + + private fun loadSchoolAnnouncements(student: Student, forceRefresh: Boolean) { + flatResourceFlow { + schoolAnnouncementRepository.getSchoolAnnouncements(student, forceRefresh) + } + .onEach { + when (it) { + is Resource.Loading -> { + Timber.i("Loading dashboard announcements data started") + if (forceRefresh) return@onEach + updateData( + DashboardItem.Announcements(it.dataOrNull.orEmpty(), isLoading = true), + forceRefresh + ) + + if (!it.dataOrNull.isNullOrEmpty()) { + firstLoadedItemList += DashboardItem.Type.ANNOUNCEMENTS + } + } + is Resource.Success -> { + Timber.i("Loading dashboard announcements result: Success") + updateData(DashboardItem.Announcements(it.data), forceRefresh) + } + is Resource.Error -> { + Timber.i("Loading dashboard announcements result: An exception occurred") + errorHandler.dispatch(it.error) + updateData(DashboardItem.Announcements(error = it.error), forceRefresh) + } + } + } + .launchWithUniqueRefreshJob("dashboard_announcements", forceRefresh) + } + + private fun loadExams(student: Student, forceRefresh: Boolean) { + flatResourceFlow { + val semester = semesterRepository.getCurrentSemester(student) + + examRepository.getExams( + student = student, + semester = semester, + start = LocalDate.now(), + end = LocalDate.now().plusDays(7), + forceRefresh = forceRefresh + ) + } + .mapResourceData { exams -> exams.sortedBy { exam -> exam.date } } + .onEach { + when (it) { + is Resource.Loading -> { + Timber.i("Loading dashboard exams data started") + if (forceRefresh) return@onEach + updateData( + DashboardItem.Exams(it.dataOrNull.orEmpty(), isLoading = true), + forceRefresh + ) + + if (!it.dataOrNull.isNullOrEmpty()) { + firstLoadedItemList += DashboardItem.Type.EXAMS + } + } + is Resource.Success -> { + Timber.i("Loading dashboard exams result: Success") + updateData(DashboardItem.Exams(it.data), forceRefresh) + } + is Resource.Error -> { + Timber.i("Loading dashboard exams result: An exception occurred") + errorHandler.dispatch(it.error) + updateData(DashboardItem.Exams(error = it.error), forceRefresh) + } + } + } + .launchWithUniqueRefreshJob("dashboard_exams", forceRefresh) + } + + private fun loadConferences(student: Student, forceRefresh: Boolean) { + flatResourceFlow { + val semester = semesterRepository.getCurrentSemester(student) + + conferenceRepository.getConferences( + student = student, + semester = semester, + forceRefresh = forceRefresh, + startDate = Instant.now(), + ) + } + .onEach { + when (it) { + is Resource.Loading -> { + Timber.i("Loading dashboard conferences data started") + if (forceRefresh) return@onEach + updateData( + DashboardItem.Conferences(it.dataOrNull.orEmpty(), isLoading = true), + forceRefresh + ) + + if (!it.dataOrNull.isNullOrEmpty()) { + firstLoadedItemList += DashboardItem.Type.CONFERENCES + } + } + is Resource.Success -> { + Timber.i("Loading dashboard conferences result: Success") + updateData(DashboardItem.Conferences(it.data), forceRefresh) + } + is Resource.Error -> { + Timber.i("Loading dashboard conferences result: An exception occurred") + errorHandler.dispatch(it.error) + updateData(DashboardItem.Conferences(error = it.error), forceRefresh) + } + } + } + .launchWithUniqueRefreshJob("dashboard_conferences", forceRefresh) + } + + private fun loadAdminMessage(student: Student, forceRefresh: Boolean) { + flatResourceFlow { adminMessageRepository.getAdminMessages(student) } + .filter { + val data = it.dataOrNull ?: return@filter true + val isDismissed = data.id in preferencesRepository.dismissedAdminMessageIds + !isDismissed + } + .onEach { + when (it) { + is Resource.Loading -> { + Timber.i("Loading dashboard admin message data started") + if (forceRefresh) return@onEach + updateData(DashboardItem.AdminMessages(), forceRefresh) + } + is Resource.Success -> { + Timber.i("Loading dashboard admin message result: Success") + updateData( + dashboardItem = DashboardItem.AdminMessages(adminMessage = it.data), + forceRefresh = forceRefresh + ) + } + is Resource.Error -> { + Timber.i("Loading dashboard admin message result: An exception occurred") + errorHandler.dispatch(it.error) + updateData( + dashboardItem = DashboardItem.AdminMessages( + adminMessage = null, + error = it.error + ), + forceRefresh = forceRefresh + ) + } + } + } + .launchWithUniqueRefreshJob("dashboard_admin_messages", forceRefresh) + } + + private fun updateData(dashboardItem: DashboardItem, forceRefresh: Boolean) { + val isForceRefreshError = forceRefresh && dashboardItem.error != null + val isFirstRunDataLoadedError = + dashboardItem.type in firstLoadedItemList && dashboardItem.error != null + + with(dashboardItemLoadedList) { + removeAll { it.type == dashboardItem.type && !isForceRefreshError && !isFirstRunDataLoadedError } + if (!isForceRefreshError && !isFirstRunDataLoadedError) add(dashboardItem) + } + + sortDashboardItems() + + if (dashboardItem is DashboardItem.AdminMessages) { + if (!dashboardItem.isDataLoaded) { + dashboardItemsToLoad = dashboardItemsToLoad - DashboardItem.Type.ADMIN_MESSAGE + dashboardTileLoadedList = dashboardTileLoadedList - DashboardItem.Tile.ADMIN_MESSAGE + + dashboardItemLoadedList.removeAll { it.type == DashboardItem.Type.ADMIN_MESSAGE } + } else { + dashboardItemsToLoad = dashboardItemsToLoad + DashboardItem.Type.ADMIN_MESSAGE + dashboardTileLoadedList = dashboardTileLoadedList + DashboardItem.Tile.ADMIN_MESSAGE + } + } + + if (forceRefresh) { + updateForceRefreshData(dashboardItem) + } else { + updateNormalData() + } + } + + private fun updateNormalData() { + val isItemsLoaded = + dashboardItemsToLoad.all { type -> dashboardItemLoadedList.any { it.type == type } } + val isItemsDataLoaded = isItemsLoaded && dashboardItemLoadedList.all { + it.isDataLoaded || it.error != null + } + + if (isItemsDataLoaded) { + view?.run { + showProgress(false) + showErrorView(false) + showContent(true) + updateData(dashboardItemLoadedList.toList()) + } + } + + showErrorIfExists( + isItemsLoaded = isItemsLoaded, + itemsLoadedList = dashboardItemLoadedList, + forceRefresh = false + ) + } + + private fun updateForceRefreshData(dashboardItem: DashboardItem) { + val isNotLoadedAdminMessage = + dashboardItem is DashboardItem.AdminMessages && !dashboardItem.isDataLoaded + + with(dashboardItemRefreshLoadedList) { + removeAll { it.type == dashboardItem.type } + if (!isNotLoadedAdminMessage) add(dashboardItem) + } + + val isRefreshItemLoaded = + dashboardItemsToLoad.all { type -> dashboardItemRefreshLoadedList.any { it.type == type } } + val isRefreshItemsDataLoaded = isRefreshItemLoaded && dashboardItemRefreshLoadedList.all { + it.isDataLoaded || it.error != null + } + + if (isRefreshItemsDataLoaded) { + view?.run { + showRefresh(false) + showErrorView(false) + showContent(true) + updateData(dashboardItemLoadedList.toList()) + } + } + + showErrorIfExists( + isItemsLoaded = isRefreshItemLoaded, + itemsLoadedList = dashboardItemRefreshLoadedList, + forceRefresh = true + ) + + if (isRefreshItemsDataLoaded) dashboardItemRefreshLoadedList.clear() + } + + private fun showErrorIfExists( + isItemsLoaded: Boolean, + itemsLoadedList: List, + forceRefresh: Boolean + ) { + val filteredItems = itemsLoadedList.filterNot { + it.type == DashboardItem.Type.ACCOUNT || it.type == DashboardItem.Type.ADMIN_MESSAGE + } + val isAccountItemError = + itemsLoadedList.find { it.type == DashboardItem.Type.ACCOUNT }?.error != null + val isGeneralError = + filteredItems.none { it.error == null } && filteredItems.isNotEmpty() || isAccountItemError + val firstError = itemsLoadedList.mapNotNull { it.error }.firstOrNull() + + val filteredOriginalLoadedList = + dashboardItemLoadedList.filterNot { it.type == DashboardItem.Type.ACCOUNT } + val wasAccountItemError = + dashboardItemLoadedList.find { it.type == DashboardItem.Type.ACCOUNT }?.error != null + val wasGeneralError = + filteredOriginalLoadedList.none { it.error == null } && filteredOriginalLoadedList.isNotEmpty() || wasAccountItemError + + if (isGeneralError && isItemsLoaded) { + lastError = requireNotNull(firstError) + + view?.run { + showProgress(false) + showRefresh(false) + if ((forceRefresh && wasGeneralError) || !forceRefresh) { + showContent(false) + showErrorView(true) + setErrorDetails(lastError) + } + } + } + } + + private fun sortDashboardItems() { + val dashboardItemsPosition = preferencesRepository.dashboardItemsPosition + + dashboardItemLoadedList.sortBy { tile -> + val defaultPosition = if (tile is DashboardItem.AdminMessages) { + -1 + } else { + tile.type.ordinal + 100 + } + + dashboardItemsPosition?.getOrDefault(tile.type, defaultPosition) ?: tile.type.ordinal + } + } + + private fun Flow>.launchWithUniqueRefreshJob(name: String, forceRefresh: Boolean) { + val jobName = if (forceRefresh) "$name-forceRefresh" else name + + if (forceRefresh) { + onEach { + if (it is Resource.Success) { + cancelJobs(jobName) + } + }.launch(jobName) + } else { + launch(jobName) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardView.kt new file mode 100644 index 000000000..2cc2f1d2d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardView.kt @@ -0,0 +1,30 @@ +package io.github.wulkanowy.ui.modules.dashboard + +import io.github.wulkanowy.ui.base.BaseView + +interface DashboardView : BaseView { + + fun initView() + + fun updateData(data: List) + + fun showDashboardTileSettings(selectedItems: List) + + fun showProgress(show: Boolean) + + fun showContent(show: Boolean) + + fun showRefresh(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(error: Throwable) + + fun resetView() + + fun popViewToRoot() + + fun openNotificationsCenterView() + + fun openInternetBrowser(url: String) +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/DebugAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/DebugAdapter.kt new file mode 100644 index 000000000..36f99ca30 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/DebugAdapter.kt @@ -0,0 +1,32 @@ +package io.github.wulkanowy.ui.modules.debug + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.databinding.ItemDebugBinding + +class DebugAdapter : RecyclerView.Adapter() { + + var items = emptyList() + + var onItemClickListener: (DebugItem) -> Unit = {} + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + ItemDebugBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + val item = items[position] + with(holder.binding) { + debugItemName.setText(item.title) + + root.setOnClickListener { + onItemClickListener(item) + } + } + } + + class ItemViewHolder(val binding: ItemDebugBinding) : RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/DebugFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/DebugFragment.kt new file mode 100644 index 000000000..000916b17 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/DebugFragment.kt @@ -0,0 +1,65 @@ +package io.github.wulkanowy.ui.modules.debug + +import android.os.Bundle +import android.view.View +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.databinding.FragmentDebugBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.debug.logviewer.LogViewerFragment +import io.github.wulkanowy.ui.modules.debug.notification.NotificationDebugFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import javax.inject.Inject + +@AndroidEntryPoint +class DebugFragment : BaseFragment(R.layout.fragment_debug), DebugView, + MainView.TitledView { + + @Inject + lateinit var presenter: DebugPresenter + + private val debugAdapter = DebugAdapter() + + override val titleStringId: Int + get() = R.string.debug_title + + companion object { + fun newInstance() = DebugFragment() + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentDebugBinding.bind(view) + presenter.onAttachView(this) + } + + override fun initView() { + debugAdapter.onItemClickListener = presenter::onItemSelect + with(binding.debugRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = debugAdapter + } + } + + override fun setItems(itemList: List) { + with(debugAdapter) { + items = itemList + notifyDataSetChanged() + } + } + + override fun openLogViewer() { + (activity as? MainActivity)?.pushView(LogViewerFragment.newInstance()) + } + + override fun openNotificationsDebug() { + (activity as? MainActivity)?.pushView(NotificationDebugFragment.newInstance()) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/DebugItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/DebugItem.kt new file mode 100644 index 000000000..2273294ef --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/DebugItem.kt @@ -0,0 +1,7 @@ +package io.github.wulkanowy.ui.modules.debug + +import androidx.annotation.StringRes + +data class DebugItem( + @StringRes val title: Int, +) \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/DebugPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/DebugPresenter.kt new file mode 100644 index 000000000..67ac88861 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/DebugPresenter.kt @@ -0,0 +1,37 @@ +package io.github.wulkanowy.ui.modules.debug + +import io.github.wulkanowy.R +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import timber.log.Timber +import javax.inject.Inject + +class DebugPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, +) : BasePresenter(errorHandler, studentRepository) { + + val items = listOf( + DebugItem(R.string.logviewer_title), + DebugItem(R.string.notification_debug_title), + ) + + override fun onAttachView(view: DebugView) { + super.onAttachView(view) + Timber.i("Debug view was initialized") + + with(view) { + initView() + setItems(items) + } + } + + fun onItemSelect(item: DebugItem) { + when (item.title) { + R.string.logviewer_title -> view?.openLogViewer() + R.string.notification_debug_title -> view?.openNotificationsDebug() + else -> Timber.d("Unknown debug item: $item") + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/DebugView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/DebugView.kt new file mode 100644 index 000000000..9396ec6ac --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/DebugView.kt @@ -0,0 +1,14 @@ +package io.github.wulkanowy.ui.modules.debug + +import io.github.wulkanowy.ui.base.BaseView + +interface DebugView : BaseView { + + fun initView() + + fun setItems(itemList: List) + + fun openLogViewer() + + fun openNotificationsDebug() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerAdapter.kt new file mode 100644 index 000000000..5a0e21046 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerAdapter.kt @@ -0,0 +1,22 @@ +package io.github.wulkanowy.ui.modules.debug.logviewer + +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView + +class LogViewerAdapter : RecyclerView.Adapter() { + + var lines = emptyList() + + class ViewHolder(val textView: TextView) : RecyclerView.ViewHolder(textView) + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder(TextView(parent.context)) + } + + override fun getItemCount() = lines.size + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.textView.text = lines[position] + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerFragment.kt new file mode 100644 index 000000000..1e11c874b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerFragment.kt @@ -0,0 +1,92 @@ +package io.github.wulkanowy.ui.modules.debug.logviewer + +import android.content.Intent +import android.content.Intent.EXTRA_EMAIL +import android.content.Intent.EXTRA_STREAM +import android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION +import android.os.Bundle +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import androidx.core.content.FileProvider +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.BuildConfig.APPLICATION_ID +import io.github.wulkanowy.R +import io.github.wulkanowy.databinding.FragmentLogviewerBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainView +import java.io.File +import javax.inject.Inject + +@AndroidEntryPoint +class LogViewerFragment : BaseFragment(R.layout.fragment_logviewer), + LogViewerView, MainView.TitledView { + + @Inject + lateinit var presenter: LogViewerPresenter + + private val logAdapter = LogViewerAdapter() + + override val titleStringId: Int + get() = R.string.logviewer_title + + companion object { + fun newInstance() = LogViewerFragment() + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentLogviewerBinding.bind(view) + messageContainer = binding.logViewerRecycler + presenter.onAttachView(this) + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.action_menu_logviewer, menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return if (item.itemId == R.id.logViewerMenuShare) presenter.onShareLogsSelected() + else false + } + + override fun initView() { + with(binding.logViewerRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = logAdapter + } + + binding.logViewRefreshButton.setOnClickListener { presenter.onRefreshClick() } + } + + override fun setLines(lines: List) { + logAdapter.lines = lines + logAdapter.notifyDataSetChanged() + binding.logViewerRecycler.scrollToPosition(lines.size - 1) + } + + override fun shareLogs(files: List) { + val intent = Intent(Intent.ACTION_SEND_MULTIPLE).apply { + type = "text/plain" + putExtra(EXTRA_EMAIL, arrayOf("wulkanowyinc@gmail.com")) + addFlags(FLAG_GRANT_READ_URI_PERMISSION) + putParcelableArrayListExtra(EXTRA_STREAM, ArrayList(files.map { + FileProvider.getUriForFile(requireContext(), "$APPLICATION_ID.fileprovider", it) + })) + } + + startActivity(Intent.createChooser(intent, getString(R.string.logviewer_share))) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerPresenter.kt new file mode 100644 index 000000000..7adb56b8c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerPresenter.kt @@ -0,0 +1,65 @@ +package io.github.wulkanowy.ui.modules.debug.logviewer + +import io.github.wulkanowy.data.Resource +import io.github.wulkanowy.data.repositories.LoggerRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.resourceFlow +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import kotlinx.coroutines.flow.onEach +import timber.log.Timber +import javax.inject.Inject + +class LogViewerPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val loggerRepository: LoggerRepository +) : BasePresenter(errorHandler, studentRepository) { + + override fun onAttachView(view: LogViewerView) { + super.onAttachView(view) + view.initView() + loadLogFile() + } + + fun onShareLogsSelected(): Boolean { + resourceFlow { loggerRepository.getLogFiles() } + .onEach { + when (it) { + is Resource.Loading -> Timber.d("Loading logs files started") + is Resource.Success -> { + Timber.i("Loading logs files result: ${it.data.joinToString { file -> file.name }}") + view?.shareLogs(it.data) + } + is Resource.Error -> { + Timber.i("Loading logs files result: An exception occurred") + errorHandler.dispatch(it.error) + } + } + } + .launch("share") + return true + } + + fun onRefreshClick() { + loadLogFile() + } + + private fun loadLogFile() { + resourceFlow { loggerRepository.getLastLogLines() } + .onEach { + when (it) { + is Resource.Loading -> Timber.d("Loading last log file started") + is Resource.Success -> { + Timber.i("Loading last log file result: load ${it.data.size} lines") + view?.setLines(it.data) + } + is Resource.Error -> { + Timber.i("Loading last log file result: An exception occurred") + errorHandler.dispatch(it.error) + } + } + } + .launch("file") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerView.kt new file mode 100644 index 000000000..3d138ecec --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerView.kt @@ -0,0 +1,13 @@ +package io.github.wulkanowy.ui.modules.debug.logviewer + +import io.github.wulkanowy.ui.base.BaseView +import java.io.File + +interface LogViewerView : BaseView { + + fun initView() + + fun setLines(lines: List) + + fun shareLogs(files: List) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugAdapter.kt new file mode 100644 index 000000000..78107d436 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugAdapter.kt @@ -0,0 +1,31 @@ +package io.github.wulkanowy.ui.modules.debug.notification + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.databinding.ItemDebugNotificationsBinding + +class NotificationDebugAdapter : RecyclerView.Adapter() { + + var items = emptyList() + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + ItemDebugNotificationsBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + val item = items[position] + + with(holder.binding) { + title.setText(item.title) + button1.setOnClickListener { item.onClickCallback(1) } + button2.setOnClickListener { item.onClickCallback(3) } + button3.setOnClickListener { item.onClickCallback(10) } + } + } + + class ItemViewHolder(val binding: ItemDebugNotificationsBinding) : + RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugFragment.kt new file mode 100644 index 000000000..c5c797d0b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugFragment.kt @@ -0,0 +1,54 @@ +package io.github.wulkanowy.ui.modules.debug.notification + +import android.os.Bundle +import android.view.View +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.databinding.FragmentDebugNotificationsBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainView +import javax.inject.Inject + +@AndroidEntryPoint +class NotificationDebugFragment : + BaseFragment(R.layout.fragment_debug_notifications), + NotificationDebugView, MainView.TitledView { + + @Inject + lateinit var presenter: NotificationDebugPresenter + + private val notificationDebugAdapter = NotificationDebugAdapter() + + override val titleStringId: Int + get() = R.string.notification_debug_title + + companion object { + fun newInstance() = NotificationDebugFragment() + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentDebugNotificationsBinding.bind(view) + presenter.onAttachView(this) + } + + override fun initView() { + with(binding.recyclerView) { + adapter = notificationDebugAdapter + layoutManager = LinearLayoutManager(requireContext()) + } + } + + override fun setItems(notificationDebugs: List) { + with(notificationDebugAdapter) { + items = notificationDebugs + notifyDataSetChanged() + } + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugItem.kt new file mode 100644 index 000000000..5a5287873 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugItem.kt @@ -0,0 +1,8 @@ +package io.github.wulkanowy.ui.modules.debug.notification + +import androidx.annotation.StringRes + +data class NotificationDebugItem( + @StringRes val title: Int, + val onClickCallback: (numberOfNotifications: Int) -> Unit, +) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugPresenter.kt new file mode 100644 index 000000000..d0dfcd696 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugPresenter.kt @@ -0,0 +1,107 @@ +package io.github.wulkanowy.ui.modules.debug.notification + +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.services.sync.notifications.ChangeTimetableNotification +import io.github.wulkanowy.services.sync.notifications.NewAttendanceNotification +import io.github.wulkanowy.services.sync.notifications.NewConferenceNotification +import io.github.wulkanowy.services.sync.notifications.NewExamNotification +import io.github.wulkanowy.services.sync.notifications.NewGradeNotification +import io.github.wulkanowy.services.sync.notifications.NewHomeworkNotification +import io.github.wulkanowy.services.sync.notifications.NewLuckyNumberNotification +import io.github.wulkanowy.services.sync.notifications.NewMessageNotification +import io.github.wulkanowy.services.sync.notifications.NewNoteNotification +import io.github.wulkanowy.services.sync.notifications.NewSchoolAnnouncementNotification +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.ui.modules.debug.notification.mock.debugAttendanceItems +import io.github.wulkanowy.ui.modules.debug.notification.mock.debugConferenceItems +import io.github.wulkanowy.ui.modules.debug.notification.mock.debugExamItems +import io.github.wulkanowy.ui.modules.debug.notification.mock.debugGradeDetailsItems +import io.github.wulkanowy.ui.modules.debug.notification.mock.debugGradeSummaryItems +import io.github.wulkanowy.ui.modules.debug.notification.mock.debugHomeworkItems +import io.github.wulkanowy.ui.modules.debug.notification.mock.debugLuckyNumber +import io.github.wulkanowy.ui.modules.debug.notification.mock.debugMessageItems +import io.github.wulkanowy.ui.modules.debug.notification.mock.debugNoteItems +import io.github.wulkanowy.ui.modules.debug.notification.mock.debugSchoolAnnouncementItems +import io.github.wulkanowy.ui.modules.debug.notification.mock.debugTimetableItems +import kotlinx.coroutines.launch +import timber.log.Timber +import javax.inject.Inject + +class NotificationDebugPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val newGradeNotification: NewGradeNotification, + private val newHomeworkNotification: NewHomeworkNotification, + private val newConferenceNotification: NewConferenceNotification, + private val newExamNotification: NewExamNotification, + private val newMessageNotification: NewMessageNotification, + private val newNoteNotification: NewNoteNotification, + private val newSchoolAnnouncementNotification: NewSchoolAnnouncementNotification, + private val newLuckyNumberNotification: NewLuckyNumberNotification, + private val changeTimetableNotification: ChangeTimetableNotification, + private val newAttendanceNotification: NewAttendanceNotification, +) : BasePresenter(errorHandler, studentRepository) { + + private val items = listOf( + NotificationDebugItem(R.string.grade_title) { n -> + withStudent { newGradeNotification.notifyDetails(debugGradeDetailsItems.take(n), it) } + }, + NotificationDebugItem(R.string.grade_summary_predicted_grade) { n -> + withStudent { newGradeNotification.notifyPredicted(debugGradeSummaryItems.take(n), it) } + }, + NotificationDebugItem(R.string.grade_summary_final_grade) { n -> + withStudent { newGradeNotification.notifyFinal(debugGradeSummaryItems.take(n), it) } + }, + NotificationDebugItem(R.string.homework_title) { n -> + withStudent { newHomeworkNotification.notify(debugHomeworkItems.take(n), it) } + }, + NotificationDebugItem(R.string.conferences_title) { n -> + withStudent { newConferenceNotification.notify(debugConferenceItems.take(n), it) } + }, + NotificationDebugItem(R.string.exam_title) { n -> + withStudent { newExamNotification.notify(debugExamItems.take(n), it) } + }, + NotificationDebugItem(R.string.message_title) { n -> + withStudent { newMessageNotification.notify(debugMessageItems.take(n), it) } + }, + NotificationDebugItem(R.string.note_title) { n -> + withStudent { newNoteNotification.notify(debugNoteItems.take(n), it) } + }, + NotificationDebugItem(R.string.attendance_title) { n -> + withStudent { newAttendanceNotification.notify(debugAttendanceItems.take(n), it) } + }, + NotificationDebugItem(R.string.timetable_title) { n -> + withStudent { changeTimetableNotification.notify(debugTimetableItems.take(n), it) } + }, + NotificationDebugItem(R.string.school_announcement_title) { n -> + withStudent { + newSchoolAnnouncementNotification.notify(debugSchoolAnnouncementItems.take(n), it) + } + }, + NotificationDebugItem(R.string.lucky_number_title) { n -> + withStudent { + repeat(n) { _ -> + newLuckyNumberNotification.notify(debugLuckyNumber, it) + } + } + }, + ) + + override fun onAttachView(view: NotificationDebugView) { + super.onAttachView(view) + Timber.i("Notification debug view was initialized") + with(view) { + initView() + setItems(items) + } + } + + private fun withStudent(block: suspend (Student) -> Unit) { + presenterScope.launch { + block(studentRepository.getCurrentStudent(false)) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugView.kt new file mode 100644 index 000000000..e2c043c3b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugView.kt @@ -0,0 +1,10 @@ +package io.github.wulkanowy.ui.modules.debug.notification + +import io.github.wulkanowy.ui.base.BaseView + +interface NotificationDebugView : BaseView { + + fun initView() + + fun setItems(notificationDebugs: List) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/attendance.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/attendance.kt new file mode 100644 index 000000000..042cf07e7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/attendance.kt @@ -0,0 +1,35 @@ +package io.github.wulkanowy.ui.modules.debug.notification.mock + +import io.github.wulkanowy.data.db.entities.Attendance +import java.time.LocalDate + +val debugAttendanceItems = listOf( + generateAttendance("Matematyka", "PRESENCE"), + generateAttendance("Język angielski", "UNEXCUSED_LATENESS"), + generateAttendance("Geografia", "ABSENCE_UNEXCUSED"), + generateAttendance("Sieci komputerowe", "ABSENCE_EXCUSED"), + generateAttendance("Systemy operacyjne", "EXCUSED_LATENESS"), + generateAttendance("Język niemiecki", "ABSENCE_UNEXCUSED"), + generateAttendance("Biologia", "ABSENCE_UNEXCUSED"), + generateAttendance("Chemia", "ABSENCE_EXCUSED"), + generateAttendance("Fizyka", "ABSENCE_UNEXCUSED"), + generateAttendance("Matematyka", "ABSENCE_EXCUSED"), +) + +private fun generateAttendance(subject: String, name: String) = Attendance( + subject = subject, + studentId = 0, + diaryId = 0, + date = LocalDate.now(), + timeId = 0, + number = 1, + name = name, + presence = false, + absence = false, + exemption = false, + lateness = false, + excused = false, + deleted = false, + excusable = false, + excuseStatus = "" +) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/conference.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/conference.kt new file mode 100644 index 000000000..625ff4c9d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/conference.kt @@ -0,0 +1,59 @@ +package io.github.wulkanowy.ui.modules.debug.notification.mock + +import io.github.wulkanowy.data.db.entities.Conference +import java.time.Duration +import java.time.Instant + +val debugConferenceItems = listOf( + generateConference( + title = "Spotkanie z rodzicami/opiekunami", + subject = "Podsumowanie I semestru - średnia klasy, oceny, frekwencja, zachowanie" + ), + generateConference( + title = "ZSW", + subject = "Pierwsze - organizacyjne zebranie z rodzicami klas pierwszych" + ), + generateConference( + title = "Spotkanie z rodzicami w sprawie bójki", + subject = "Pierwsze - i miejmy nadzieję ostatnie - zebranie w takiej sprawie" + ), + generateConference( + title = "Spotkanie z rodzicami w sprawie kolejnej bójki", + subject = "Kolejne - ale miejmy jeszcze nadzieję, że ostatnie - zebranie w takiej sprawie" + ), + generateConference( + title = "Spotkanie z rodzicami w sprawie jeszcze jednej bójki", + subject = "Proszę państwa, proszę uspokoić swoje dzieci" + ), + generateConference( + title = "Spotkanie w sprawie wydalenia części uczniów", + subject = "Proszę państwa, to jest krok ostateczny, którego nikt nie chciał się podjąć, ale ktoś musi" + ), + generateConference( + title = "Spotkanie organizacyjne w drugim semestrze", + subject = "Prezentacja na temat projektu 'Spokojnej szkoły'" + ), + generateConference( + title = "Spotkanie z pierwszakami", + subject = "Mamy sobie do pogadania" + ), + generateConference( + title = "Spotkanie z rodzicami szóstoklaistów", + subject = "Musimy przygotować dzieci do ważnej uroczystości" + ), + generateConference( + title = "Spotkanie podsumowujące pracę w ciągu ostatniego roku szkolnego", + subject = "Proszę państwa, zapraszam serdecznie na spotkanie" + ), +) + +private fun generateConference(title: String, subject: String) = Conference( + title = title, + subject = subject, + studentId = 0, + diaryId = 0, + agenda = "", + conferenceId = 0, + date = Instant.now().plus(Duration.ofMinutes(10)), + presentOnConference = "", +) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/exam.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/exam.kt new file mode 100644 index 000000000..45700c29a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/exam.kt @@ -0,0 +1,30 @@ +package io.github.wulkanowy.ui.modules.debug.notification.mock + +import io.github.wulkanowy.data.db.entities.Exam +import java.time.LocalDate + +val debugExamItems = listOf( + generateExam("Matematyka", "Figury na płaszczyźnie"), + generateExam("Język angielski", "czasowniki nieregularne 1 część"), + generateExam("Geografia", "Opolszczyzna - mapa"), + generateExam("Sieci komputerowe", "Zaciskanie erjotek"), + generateExam("Systemy operacyjne", "Instalacja ubuntu 16.04"), + generateExam("Język niemiecki", "oral exam"), + generateExam("Biologia", "Budowa koniczyny"), + generateExam("Chemia", "synteza płynnego zaliczenia"), + generateExam("Fizyka", "telekineza"), + generateExam("Matematyka", "Liczby zespolone i pochodne piątego rzędu"), +) + +private fun generateExam(subject: String, description: String) = Exam( + subject = subject, + description = description, + studentId = 0, + diaryId = 0, + date = LocalDate.now(), + entryDate = LocalDate.now(), + group = "", + type = "", + teacher = "", + teacherSymbol = "" +) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/gradeDetails.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/gradeDetails.kt new file mode 100644 index 000000000..77b60188b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/gradeDetails.kt @@ -0,0 +1,35 @@ +package io.github.wulkanowy.ui.modules.debug.notification.mock + +import io.github.wulkanowy.data.db.entities.Grade +import java.time.LocalDate + +val debugGradeDetailsItems = listOf( + generateGrade("Matematyka", "+"), + generateGrade("Matematyka", "120", comment = "%"), + generateGrade("Fizyka", "-"), + generateGrade("Geografia", "4+"), + generateGrade("Sieci komputerowe", "1"), + generateGrade("Systemy operacyjne", "3+"), + generateGrade("Język polski", "2-"), + generateGrade("Język angielski", "4+"), + generateGrade("Religia", "6"), + generateGrade("Język niemiecki", "1!"), + generateGrade("Wychowanie fizyczne", "5"), +) + +private fun generateGrade(subject: String, entry: String, comment: String = "") = Grade( + subject = subject, + entry = entry, + semesterId = 0, + studentId = 0, + value = 0.0, + modifier = 0.0, + comment = comment, + color = "", + gradeSymbol = "", + description = "", + weight = "", + weightValue = 0.0, + date = LocalDate.now(), + teacher = "" +) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/gradeSummary.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/gradeSummary.kt new file mode 100644 index 000000000..c452204b9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/gradeSummary.kt @@ -0,0 +1,30 @@ +package io.github.wulkanowy.ui.modules.debug.notification.mock + +import io.github.wulkanowy.data.db.entities.GradeSummary + +val debugGradeSummaryItems = listOf( + generateSummary("Matematyka", "2-", "2"), + generateSummary("Fizyka", "1", "2"), + generateSummary("Geografia", "4+", "5"), + generateSummary("Sieci komputerowe", "2", "5"), + generateSummary("Systemy operacyjne", "3", "4"), + generateSummary("Język polski", "1", "3"), + generateSummary("Język angielski", "4", "3"), + generateSummary("Religia", "5", "6"), + generateSummary("Język niemiecki", "2", "2"), + generateSummary("Wychowanie fizyczne", "5", "5"), + generateSummary("Biologia", "4", "4"), +) + +private fun generateSummary(subject: String, predicted: String, final: String) = GradeSummary( + semesterId = 0, + studentId = 0, + position = 0, + subject = subject, + predictedGrade = predicted, + finalGrade = final, + proposedPoints = "", + finalPoints = "", + pointsSum = "", + average = .0 +) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/homework.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/homework.kt new file mode 100644 index 000000000..6c7d7fac6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/homework.kt @@ -0,0 +1,30 @@ +package io.github.wulkanowy.ui.modules.debug.notification.mock + +import io.github.wulkanowy.data.db.entities.Homework +import java.time.LocalDate + +val debugHomeworkItems = listOf( + generateHomework("Chemia", "Test diagnozujący i Rozdział I do 30.10"), + generateHomework("Etyka", "Notatka własna do zajęć o ks. Jerzym Popiełuszko"), + generateHomework("Język angielski", "Zadania egzaminacyjne"), + generateHomework("Metodologia programowania", "Wszystkie instrukcje"), + generateHomework("Język polski", "Notatka własna na temat Wokulskiego z lektury Lalka"), + generateHomework("Systemy operacyjne", "Sprawozdanie z wykonania ćwiczenia nr 21.137"), + generateHomework("Matematyka", "Zadania od strony 1 do 128"), + generateHomework("Język niemiecki", "Opis swoich wakacji - dialog z kolegą"), + generateHomework("Język angielski", "Opis swoich wakacji - dialog z kolegą"), + generateHomework("Wychowanie fizyczne", "Notatka na temat skoku w dald"), + generateHomework("Biologia", "Notatka na temat grzechotnika"), +) + +private fun generateHomework(subject: String, content: String) = Homework( + subject = subject, + content = content, + semesterId = 0, + studentId = 0, + date = LocalDate.now(), + entryDate = LocalDate.now(), + teacher = "", + teacherSymbol = "", + attachments = listOf(), +) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/luckyNumber.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/luckyNumber.kt new file mode 100644 index 000000000..e929a0904 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/luckyNumber.kt @@ -0,0 +1,12 @@ +package io.github.wulkanowy.ui.modules.debug.notification.mock + +import io.github.wulkanowy.data.db.entities.LuckyNumber +import java.time.LocalDate +import kotlin.random.Random + +val debugLuckyNumber + get() = LuckyNumber( + studentId = 0, + date = LocalDate.now(), + luckyNumber = Random.nextInt(1, 128), + ) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt new file mode 100644 index 000000000..53d439612 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt @@ -0,0 +1,32 @@ +package io.github.wulkanowy.ui.modules.debug.notification.mock + +import io.github.wulkanowy.data.db.entities.Message +import java.time.Instant + +val debugMessageItems = listOf( + generateMessage("Kowalski Jan", "Tytuł"), + generateMessage("Nazwisko Imię", "Tytuł wiadomości"), + generateMessage("Malinowski Kazimierz", "Nakrętki"), + generateMessage("Jastębowszki Orest", "Prośba do uczniów o pomoc przy projekcie"), + generateMessage("Metylowy Oranż", "Pozew o plagiat"), + generateMessage("VULCAN", "Uwaga na nieautoryzowane aplikacje"), + generateMessage("Mama", "Zacznij się w końcu uczyć do matury!!!11"), + generateMessage("Tata", "Kupisz mi coś w sklepie?"), + generateMessage("Wychowawca", "Upomnienie od wychowawcy za nieobecności"), + generateMessage("Kochanowska Joanna", "Poprawa rozprawki - termin"), +) + +private fun generateMessage(sender: String, subject: String) = Message( + sender = sender, + subject = subject, + studentId = 0, + realId = 0, + messageId = 0, + senderId = 0, + recipient = "", + date = Instant.now(), + folderId = 0, + unread = true, + removed = false, + hasAttachments = false +) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/note.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/note.kt new file mode 100644 index 000000000..b287a508b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/note.kt @@ -0,0 +1,29 @@ +package io.github.wulkanowy.ui.modules.debug.notification.mock + +import io.github.wulkanowy.data.db.entities.Note +import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory +import java.time.LocalDate + +val debugNoteItems = listOf( + generateNote("Aleksadra Krajewska", "Przeszkadzanie na lekcjach", NoteCategory.NEGATIVE), + generateNote("Zofia Czerwińska", "Udział w konkursie szkolnym", NoteCategory.POSITIVE), + generateNote("Stanisław Krupa", "Kultura języka", NoteCategory.NEUTRAL), + generateNote("Karolina Kowalska", "Wypełnianie obowiązków ucznia", NoteCategory.NEUTRAL), + generateNote("Joanna Krupa", "Umycie tablicy cifem", NoteCategory.POSITIVE), + generateNote("Duchowicz Maksymilian", "Reprezentowanie szkoły", NoteCategory.POSITIVE), + generateNote("Michał Mazur", "Przeszkadzanie na lekcji", NoteCategory.NEGATIVE), + generateNote("Karolina Kowalska", "Wypełnianie obowiązków ucznia", NoteCategory.NEGATIVE), + generateNote("Aleksandra Krajewska", "Wysadzenie klasy w powietrze", NoteCategory.NEGATIVE), +) + +private fun generateNote(teacher: String, category: String, type: NoteCategory) = Note( + teacher = teacher, + category = category, + categoryType = type.id, + studentId = 0, + date = LocalDate.now(), + teacherSymbol = "", + isPointsShow = false, + points = 0, + content = "" +) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/schoolAnnouncement.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/schoolAnnouncement.kt new file mode 100644 index 000000000..9b21f08e6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/schoolAnnouncement.kt @@ -0,0 +1,24 @@ +package io.github.wulkanowy.ui.modules.debug.notification.mock + +import io.github.wulkanowy.data.db.entities.SchoolAnnouncement +import java.time.LocalDate + +val debugSchoolAnnouncementItems = listOf( + generateAnnouncement("Dzień wolny od zajęć dydaktycznych", "Dzień wolny od zajęć dydaktycznych
03.05.2021 – poniedziałek"), + generateAnnouncement("Zasady bezpieczeństwa", "Wszyscy uczniowie są zobowiązani do noszenia maseczek"), + generateAnnouncement("Święto szkoły", "W najbliższych dniach obchodzimy święto szkoły, podczas którego..."), + generateAnnouncement("Rocznica odzyskania przez szkołę sztandaru", "Juz niedługo, bo za tydzień, a dokładnie za 8 dni..."), + generateAnnouncement("Ogłoszenie w sprawie otwarcia stołówki", "Wszyscy uczniowie zainteresowani obiadami w szkole..."), + generateAnnouncement("Uczniowie proszeni do sekretariatu", "Kuba i Jacek z klasy czwartej proszeni do dyrektora w trybie pilnym"), + generateAnnouncement("Dzień wolny od zajęć dydaktycznych", "Dzień wolny od zajęć dydaktycznych
21.06.2021 – poniedziałek"), + generateAnnouncement("Zasady bezpieczeństwa", "Wszyscy uczniowie są zobowiązani do zdjęcia maseczek"), + generateAnnouncement("Święto państwowe", "W najbliższych dniach obchodzimy święto państwowe, podczas którego..."), + generateAnnouncement("Uczniowie proszeni do sekretariatu", "Kuba i Jacek z klasy czwartej proszeni do dyrektora w trybie wolnym"), +) + +private fun generateAnnouncement(subject: String, content: String) = SchoolAnnouncement( + subject = subject, + content = content, + studentId = 0, + date = LocalDate.now() +) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/timetable.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/timetable.kt new file mode 100644 index 000000000..ff968654d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/timetable.kt @@ -0,0 +1,40 @@ +package io.github.wulkanowy.ui.modules.debug.notification.mock + +import io.github.wulkanowy.data.db.entities.Timetable +import java.time.Duration +import java.time.Instant +import java.time.LocalDate +import kotlin.random.Random + +val debugTimetableItems = listOf( + generateTimetable("Matematyka", "12", "01"), + generateTimetable("Język angielski", "23", "12"), + generateTimetable("Geografia", "34", "23"), + generateTimetable("Sieci komputerowe", "45", "34"), + generateTimetable("Systemy operacyjne", "56", "45"), + generateTimetable("Język niemiecki", "67", "56"), + generateTimetable("Biologia", "78", "67"), + generateTimetable("Chemia", "89", "78"), + generateTimetable("Fizyka", "90", "89"), + generateTimetable("Matematyka", "01", "90"), +) + +private fun generateTimetable(subject: String, room: String, roomOld: String) = Timetable( + subject = subject, + studentId = 0, + diaryId = 0, + date = LocalDate.now().minusDays(Random.nextLong(0, 8)), + number = 1, + start = Instant.now().plus(Duration.ofHours(1)), + end = Instant.now(), + subjectOld = "", + group = "", + room = room, + roomOld = roomOld, + teacher = "Wtorkowska Renata", + teacherOld = "", + info = "", + isStudentPlan = true, + changes = true, + canceled = true +) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamAdapter.kt new file mode 100644 index 000000000..4dc8afd30 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamAdapter.kt @@ -0,0 +1,66 @@ +package io.github.wulkanowy.ui.modules.exam + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.data.db.entities.Exam +import io.github.wulkanowy.databinding.HeaderExamBinding +import io.github.wulkanowy.databinding.ItemExamBinding +import io.github.wulkanowy.utils.capitalise +import io.github.wulkanowy.utils.toFormattedString +import io.github.wulkanowy.utils.weekDayName +import java.time.LocalDate +import javax.inject.Inject + +class ExamAdapter @Inject constructor() : RecyclerView.Adapter() { + + var items = emptyList>() + + var onClickListener: (Exam) -> Unit = {} + + override fun getItemCount() = items.size + + override fun getItemViewType(position: Int) = items[position].viewType.id + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + + return when (viewType) { + ExamItem.ViewType.HEADER.id -> HeaderViewHolder(HeaderExamBinding.inflate(inflater, parent, false)) + ExamItem.ViewType.ITEM.id -> ItemViewHolder(ItemExamBinding.inflate(inflater, parent, false)) + else -> throw IllegalStateException() + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is HeaderViewHolder -> bindHeaderViewHolder(holder.binding, items[position].value as LocalDate) + is ItemViewHolder -> bindItemViewHolder(holder.binding, items[position].value as Exam) + } + } + + @SuppressLint("DefaultLocale") + private fun bindHeaderViewHolder(binding: HeaderExamBinding, date: LocalDate) { + with(binding) { + examHeaderDay.text = date.weekDayName.capitalise() + examHeaderDate.text = date.toFormattedString() + } + } + + private fun bindItemViewHolder(binding: ItemExamBinding, exam: Exam) { + with(binding) { + examItemSubject.text = exam.subject + examItemTeacher.text = exam.teacher + examItemType.text = exam.type + + root.setOnClickListener { onClickListener(exam) } + } + } + + private class HeaderViewHolder(val binding: HeaderExamBinding) : + RecyclerView.ViewHolder(binding.root) + + private class ItemViewHolder(val binding: ItemExamBinding) : + RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt new file mode 100644 index 000000000..41adc008a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt @@ -0,0 +1,69 @@ +package io.github.wulkanowy.ui.modules.exam + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Exam +import io.github.wulkanowy.databinding.DialogExamBinding +import io.github.wulkanowy.utils.lifecycleAwareVariable +import io.github.wulkanowy.utils.openCalendarEventAdd +import io.github.wulkanowy.utils.toFormattedString +import java.time.LocalTime + +class ExamDialog : DialogFragment() { + + private var binding: DialogExamBinding by lifecycleAwareVariable() + + private lateinit var exam: Exam + + companion object { + + private const val ARGUMENT_KEY = "Item" + + fun newInstance(exam: Exam) = ExamDialog().apply { + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + exam = getSerializable(ARGUMENT_KEY) as Exam + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogExamBinding.inflate(inflater).apply { binding = this }.root + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + with(binding) { + examDialogSubjectValue.text = exam.subject + examDialogTypeValue.text = exam.type + examDialogTeacherValue.text = exam.teacher + examDialogEntryDateValue.text = exam.entryDate.toFormattedString() + examDialogDeadlineDateValue.text = exam.date.toFormattedString() + examDialogDescriptionValue.text = exam.description.ifBlank { + getString(R.string.all_no_data) + } + + examDialogClose.setOnClickListener { dismiss() } + examDialogAddToCalendar.setOnClickListener { + requireContext().openCalendarEventAdd( + title = "${exam.subject} - ${exam.type}", + description = exam.description, + start = exam.date.atTime(LocalTime.of(8, 0)), + end = exam.date.atTime(LocalTime.of(8, 45)), + ) + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt new file mode 100644 index 000000000..ddd0e4a19 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt @@ -0,0 +1,138 @@ +package io.github.wulkanowy.ui.modules.exam + +import android.os.Bundle +import android.view.View +import android.view.View.GONE +import android.view.View.INVISIBLE +import android.view.View.VISIBLE +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Exam +import io.github.wulkanowy.databinding.FragmentExamBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.widgets.DividerItemDecoration +import io.github.wulkanowy.utils.dpToPx +import io.github.wulkanowy.utils.getThemeAttrColor +import javax.inject.Inject + +@AndroidEntryPoint +class ExamFragment : BaseFragment(R.layout.fragment_exam), ExamView, + MainView.TitledView { + + @Inject + lateinit var presenter: ExamPresenter + + @Inject + lateinit var examAdapter: ExamAdapter + + companion object { + private const val SAVED_DATE_KEY = "CURRENT_DATE" + + fun newInstance() = ExamFragment() + } + + override val titleStringId get() = R.string.exam_title + + override val isViewEmpty get() = examAdapter.items.isEmpty() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentExamBinding.bind(view) + messageContainer = binding.examRecycler + presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY)) + } + + override fun initView() { + examAdapter.onClickListener = presenter::onExamItemSelected + + with(binding.examRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = examAdapter + addItemDecoration(DividerItemDecoration(context)) + } + + with(binding) { + examSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + examSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) + examSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh)) + examErrorRetry.setOnClickListener { presenter.onRetry() } + examErrorDetails.setOnClickListener { presenter.onDetailsClick() } + + examPreviousButton.setOnClickListener { presenter.onPreviousWeek() } + examNextButton.setOnClickListener { presenter.onNextWeek() } + + examNavContainer.elevation = requireContext().dpToPx(8f) + } + } + + override fun showRefresh(show: Boolean) { + binding.examSwipe.isRefreshing = show + } + + override fun updateData(data: List>) { + with(examAdapter) { + items = data + notifyDataSetChanged() + } + } + + override fun updateNavigationWeek(date: String) { + binding.examNavDate.text = date + } + + override fun clearData() { + with(examAdapter) { + items = emptyList() + notifyDataSetChanged() + } + } + + override fun showEmpty(show: Boolean) { + binding.examEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showErrorView(show: Boolean) { + binding.examError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + binding.examErrorMessage.text = message + } + + override fun showProgress(show: Boolean) { + binding.examProgress.visibility = if (show) VISIBLE else GONE + } + + override fun enableSwipe(enable: Boolean) { + binding.examSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + binding.examRecycler.visibility = if (show) VISIBLE else GONE + } + + override fun showPreButton(show: Boolean) { + binding.examPreviousButton.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showNextButton(show: Boolean) { + binding.examNextButton.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showExamDialog(exam: Exam) { + (activity as? MainActivity)?.showDialogFragment(ExamDialog.newInstance(exam)) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamItem.kt new file mode 100644 index 000000000..579e37203 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamItem.kt @@ -0,0 +1,9 @@ +package io.github.wulkanowy.ui.modules.exam + +data class ExamItem(val value: T, val viewType: ViewType) { + + enum class ViewType(val id: Int) { + HEADER(1), + ITEM(2) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt new file mode 100644 index 000000000..99b0bcb87 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt @@ -0,0 +1,178 @@ +package io.github.wulkanowy.ui.modules.exam + +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.db.entities.Exam +import io.github.wulkanowy.data.repositories.ExamRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.* +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.onEach +import timber.log.Timber +import java.time.LocalDate +import java.time.LocalDate.now +import java.time.LocalDate.ofEpochDay +import javax.inject.Inject + +class ExamPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val examRepository: ExamRepository, + private val semesterRepository: SemesterRepository, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + private var baseDate: LocalDate = now().nextOrSameSchoolDay + + lateinit var currentDate: LocalDate + private set + + private lateinit var lastError: Throwable + + fun onAttachView(view: ExamView, date: Long?) { + super.onAttachView(view) + view.initView() + Timber.i("Exam view was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError + reloadView(ofEpochDay(date ?: baseDate.toEpochDay())) + loadData() + if (currentDate.isHolidays) setBaseDateOnHolidays() + } + + fun onPreviousWeek() { + reloadView(currentDate.minusDays(7)) + loadData() + } + + fun onNextWeek() { + reloadView(currentDate.plusDays(7)) + loadData() + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the exam") + loadData(true) + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + fun onExamItemSelected(exam: Exam) { + Timber.i("Select exam item ${exam.id}") + view?.showExamDialog(exam) + } + + private fun setBaseDateOnHolidays() { + flow { + val student = studentRepository.getCurrentStudent() + emit(semesterRepository.getCurrentSemester(student)) + } + .catch { Timber.i("Loading semester result: An exception occurred") } + .onEach { + baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear) + currentDate = baseDate + reloadNavigation() + } + .launch("holidays") + } + + private fun loadData(forceRefresh: Boolean = false) { + flatResourceFlow { + val student = studentRepository.getCurrentStudent() + val semester = semesterRepository.getCurrentSemester(student) + examRepository.getExams( + student = student, + semester = semester, + start = currentDate.monday, + end = currentDate.sunday, + forceRefresh = forceRefresh + ) + } + .logResourceStatus("load exam data") + .mapResourceData { createExamItems(it) } + .onResourceData { + view?.run { + enableSwipe(true) + showProgress(false) + showErrorView(false) + showContent(it.isNotEmpty()) + showEmpty(it.isEmpty()) + updateData(it) + } + } + .onResourceIntermediate { view?.showRefresh(true) } + .onResourceSuccess { + analytics.logEvent( + "load_data", + "type" to "exam", + "items" to it.size + ) + } + .onResourceNotLoading { + view?.run { + enableSwipe(true) + showProgress(false) + showRefresh(false) + } + } + .onResourceError(errorHandler::dispatch) + .launch() + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + + private fun createExamItems(items: List): List> { + return items.groupBy { it.date }.toSortedMap().map { (date, exams) -> + listOf(ExamItem(date, ExamItem.ViewType.HEADER)) + exams.reversed().map { exam -> + ExamItem(exam, ExamItem.ViewType.ITEM) + } + }.flatten() + } + + private fun reloadView(date: LocalDate) { + currentDate = date + + Timber.i("Reload exam view with the date ${currentDate.toFormattedString()}") + view?.apply { + showProgress(true) + enableSwipe(false) + showContent(false) + showEmpty(false) + showErrorView(false) + clearData() + reloadNavigation() + } + } + + private fun reloadNavigation() { + view?.apply { + showPreButton(!currentDate.minusDays(7).isHolidays) + showNextButton(!currentDate.plusDays(7).isHolidays) + updateNavigationWeek( + "${currentDate.monday.toFormattedString("dd.MM")} - " + + currentDate.sunday.toFormattedString("dd.MM") + ) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt new file mode 100644 index 000000000..45b9e788c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt @@ -0,0 +1,37 @@ +package io.github.wulkanowy.ui.modules.exam + +import io.github.wulkanowy.data.db.entities.Exam +import io.github.wulkanowy.ui.base.BaseView + +interface ExamView : BaseView { + + val isViewEmpty: Boolean + + fun initView() + + fun updateData(data: List>) + + fun updateNavigationWeek(date: String) + + fun clearData() + + fun showRefresh(show: Boolean) + + fun showEmpty(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun showNextButton(show: Boolean) + + fun showPreButton(show: Boolean) + + fun showExamDialog(exam: Exam) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageMode.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageMode.kt new file mode 100644 index 000000000..1960c3df7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageMode.kt @@ -0,0 +1,11 @@ +package io.github.wulkanowy.ui.modules.grade + +enum class GradeAverageMode(val value: String) { + ALL_YEAR("all_year"), + ONE_SEMESTER("only_one_semester"), + BOTH_SEMESTERS("both_semesters"); + + companion object { + fun getByValue(value: String) = values().firstOrNull { it.value == value } ?: ONE_SEMESTER + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt new file mode 100644 index 000000000..b6733d4f2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt @@ -0,0 +1,225 @@ +package io.github.wulkanowy.ui.modules.grade + +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.data.db.entities.GradeSummary +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.GradeRepository +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.* +import io.github.wulkanowy.utils.calcAverage +import io.github.wulkanowy.utils.changeModifier +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import javax.inject.Inject + +@OptIn(FlowPreview::class) +class GradeAverageProvider @Inject constructor( + private val semesterRepository: SemesterRepository, + private val gradeRepository: GradeRepository, + private val preferencesRepository: PreferencesRepository +) { + + private val plusModifier get() = preferencesRepository.gradePlusModifier + + private val minusModifier get() = preferencesRepository.gradeMinusModifier + + private val isOptionalArithmeticAverage get() = preferencesRepository.isOptionalArithmeticAverage + + fun getGradesDetailsWithAverage(student: Student, semesterId: Int, forceRefresh: Boolean) = + flatResourceFlow { + val semesters = semesterRepository.getSemesters(student) + + when (preferencesRepository.gradeAverageMode) { + ONE_SEMESTER -> getGradeSubjects( + student = student, + semester = semesters.single { it.semesterId == semesterId }, + forceRefresh = forceRefresh + ) + BOTH_SEMESTERS -> calculateCombinedAverage( + student = student, + semesters = semesters, + semesterId = semesterId, + forceRefresh = forceRefresh, + averageMode = BOTH_SEMESTERS + ) + ALL_YEAR -> calculateCombinedAverage( + student = student, + semesters = semesters, + semesterId = semesterId, + forceRefresh = forceRefresh, + averageMode = ALL_YEAR + ) + } + }.distinctUntilChanged() + + private fun calculateCombinedAverage( + student: Student, + semesters: List, + semesterId: Int, + forceRefresh: Boolean, + averageMode: GradeAverageMode + ): Flow>> { + val isGradeAverageForceCalc = preferencesRepository.gradeAverageForceCalc + val selectedSemester = semesters.single { it.semesterId == semesterId } + val firstSemester = + semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 } + + val selectedSemesterGradeSubjects = + getGradeSubjects(student, selectedSemester, forceRefresh) + + if (selectedSemester == firstSemester) return selectedSemesterGradeSubjects + + val firstSemesterGradeSubjects = getGradeSubjects(student, firstSemester, forceRefresh) + + return selectedSemesterGradeSubjects.combine(firstSemesterGradeSubjects) { secondSemesterGradeSubject, firstSemesterGradeSubject -> + if (firstSemesterGradeSubject.errorOrNull != null) { + return@combine firstSemesterGradeSubject + } + + val isAnyVulcanAverageInFirstSemester = + firstSemesterGradeSubject.dataOrNull.orEmpty().any { it.isVulcanAverage } + val isAnyVulcanAverageInSecondSemester = + secondSemesterGradeSubject.dataOrNull.orEmpty().any { it.isVulcanAverage } + + val updatedData = secondSemesterGradeSubject.dataOrNull?.map { secondSemesterSubject -> + val firstSemesterSubject = firstSemesterGradeSubject.dataOrNull.orEmpty() + .singleOrNull { it.subject == secondSemesterSubject.subject } + + val updatedAverage = if (averageMode == ALL_YEAR) { + calculateAllYearAverage( + student = student, + isAnyVulcanAverage = isAnyVulcanAverageInFirstSemester || isAnyVulcanAverageInSecondSemester, + isGradeAverageForceCalc = isGradeAverageForceCalc, + secondSemesterSubject = secondSemesterSubject, + firstSemesterSubject = firstSemesterSubject + ) + } else { + calculateBothSemestersAverage( + student = student, + isAnyVulcanAverage = isAnyVulcanAverageInFirstSemester || isAnyVulcanAverageInSecondSemester, + isGradeAverageForceCalc = isGradeAverageForceCalc, + secondSemesterSubject = secondSemesterSubject, + firstSemesterSubject = firstSemesterSubject + ) + } + secondSemesterSubject.copy(average = updatedAverage) + } + secondSemesterGradeSubject.mapData { updatedData!! } + } + } + + private fun calculateAllYearAverage( + student: Student, + isAnyVulcanAverage: Boolean, + isGradeAverageForceCalc: Boolean, + secondSemesterSubject: GradeSubject, + firstSemesterSubject: GradeSubject? + ) = if (!isAnyVulcanAverage || isGradeAverageForceCalc) { + val updatedSecondSemesterGrades = + secondSemesterSubject.grades.updateModifiers(student) + val updatedFirstSemesterGrades = + firstSemesterSubject?.grades?.updateModifiers(student).orEmpty() + + (updatedSecondSemesterGrades + updatedFirstSemesterGrades).calcAverage( + isOptionalArithmeticAverage + ) + } else { + secondSemesterSubject.average + } + + private fun calculateBothSemestersAverage( + student: Student, + isAnyVulcanAverage: Boolean, + isGradeAverageForceCalc: Boolean, + secondSemesterSubject: GradeSubject, + firstSemesterSubject: GradeSubject? + ): Double = if (!isAnyVulcanAverage || isGradeAverageForceCalc) { + val divider = if (secondSemesterSubject.grades.any { it.weightValue > .0 }) 2 else 1 + + val secondSemesterAverage = secondSemesterSubject.grades.updateModifiers(student) + .calcAverage(isOptionalArithmeticAverage) + val firstSemesterAverage = firstSemesterSubject?.grades?.updateModifiers(student) + ?.calcAverage(isOptionalArithmeticAverage) ?: secondSemesterAverage + + (secondSemesterAverage + firstSemesterAverage) / divider + } else { + val divider = if (secondSemesterSubject.average > 0) 2 else 1 + + (secondSemesterSubject.average + (firstSemesterSubject?.average + ?: secondSemesterSubject.average)) / divider + } + + private fun getGradeSubjects( + student: Student, + semester: Semester, + forceRefresh: Boolean + ): Flow>> { + val isGradeAverageForceCalc = preferencesRepository.gradeAverageForceCalc + + return gradeRepository.getGrades(student, semester, forceRefresh = forceRefresh) + .mapResourceData { res -> + val (details, summaries) = res + val isAnyAverage = summaries.any { it.average != .0 } + val allGrades = details.groupBy { it.subject } + + val items = summaries.emulateEmptySummaries( + student = student, + semester = semester, + grades = allGrades.toList(), + calcAverage = isAnyAverage + ).map { summary -> + val grades = allGrades[summary.subject].orEmpty() + GradeSubject( + subject = summary.subject, + average = if (!isAnyAverage || isGradeAverageForceCalc) { + grades.updateModifiers(student).calcAverage(isOptionalArithmeticAverage) + } else summary.average, + points = summary.pointsSum, + summary = summary, + grades = grades, + isVulcanAverage = isAnyAverage + ) + } + + items + } + } + + private fun List.emulateEmptySummaries( + student: Student, + semester: Semester, + grades: List>>, + calcAverage: Boolean + ): List { + if (isNotEmpty() && size > grades.size) return this + + return grades.mapIndexed { i, (subject, details) -> + singleOrNull { it.subject == subject }?.let { return@mapIndexed it } + GradeSummary( + studentId = student.studentId, + semesterId = semester.semesterId, + position = i, + subject = subject, + predictedGrade = "", + finalGrade = "", + proposedPoints = "", + finalPoints = "", + pointsSum = "", + average = if (calcAverage) details.updateModifiers(student) + .calcAverage(isOptionalArithmeticAverage) else .0 + ) + } + } + + private fun List.updateModifiers(student: Student): List { + return if (student.loginMode == Sdk.Mode.SCRAPPER.name) { + map { it.changeModifier(plusModifier, minusModifier) } + } else this + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt new file mode 100644 index 000000000..0a8561eec --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt @@ -0,0 +1,186 @@ +package io.github.wulkanowy.ui.modules.grade + +import android.os.Bundle +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.View.INVISIBLE +import android.view.View.VISIBLE +import androidx.appcompat.app.AlertDialog +import com.google.android.material.tabs.TabLayoutMediator +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.databinding.FragmentGradeBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter +import io.github.wulkanowy.ui.modules.grade.details.GradeDetailsFragment +import io.github.wulkanowy.ui.modules.grade.statistics.GradeStatisticsFragment +import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.dpToPx +import io.github.wulkanowy.utils.setOnSelectPageListener +import javax.inject.Inject + +@AndroidEntryPoint +class GradeFragment : BaseFragment(R.layout.fragment_grade), GradeView, + MainView.MainChildView, MainView.TitledView { + + @Inject + lateinit var presenter: GradePresenter + + private val pagerAdapter by lazy { + BaseFragmentPagerAdapter( + fragmentManager = childFragmentManager, + pagesCount = 3, + lifecycle = lifecycle, + ) + } + + private var semesterSwitchMenu: MenuItem? = null + + companion object { + + fun newInstance() = GradeFragment() + } + + override val titleStringId get() = R.string.grade_title + + override var subtitleString = "" + + override val currentPageIndex get() = binding.gradeViewPager.currentItem + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentGradeBinding.bind(view) + presenter.onAttachView(this) + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.action_menu_grade, menu) + semesterSwitchMenu = menu.findItem(R.id.gradeMenuSemester) + presenter.onCreateMenu() + } + + override fun initView() { + with(binding.gradeViewPager) { + adapter = pagerAdapter + offscreenPageLimit = 3 + setOnSelectPageListener(presenter::onPageSelected) + } + + with(pagerAdapter) { + containerId = binding.gradeViewPager.id + titleFactory = { + when (it) { + 0 -> getString(R.string.all_details) + 1 -> getString(R.string.grade_menu_summary) + 2 -> getString(R.string.grade_menu_statistics) + else -> throw IllegalStateException() + } + } + itemFactory = { + when (it) { + 0 -> GradeDetailsFragment.newInstance() + 1 -> GradeSummaryFragment.newInstance() + 2 -> GradeStatisticsFragment.newInstance() + else -> throw IllegalStateException() + } + } + TabLayoutMediator(binding.gradeTabLayout, binding.gradeViewPager, this).attach() + } + + binding.gradeTabLayout.elevation = requireContext().dpToPx(4f) + + with(binding) { + gradeErrorRetry.setOnClickListener { presenter.onRetry() } + gradeErrorDetails.setOnClickListener { presenter.onDetailsClick() } + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return if (item.itemId == R.id.gradeMenuSemester) presenter.onSemesterSwitch() + else false + } + + override fun onFragmentReselected() { + if (::presenter.isInitialized) presenter.onViewReselected() + } + + override fun showContent(show: Boolean) { + with(binding) { + gradeViewPager.visibility = if (show) VISIBLE else INVISIBLE + gradeTabLayout.visibility = if (show) VISIBLE else INVISIBLE + } + } + + override fun showProgress(show: Boolean) { + binding.gradeProgress.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showErrorView(show: Boolean) { + binding.gradeError.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun setErrorDetails(message: String) { + binding.gradeErrorMessage.text = message + } + + override fun showSemesterSwitch(show: Boolean) { + semesterSwitchMenu?.isVisible = show + } + + override fun showSemesterDialog(selectedIndex: Int, semesters: List) { + val choices = semesters.map { getString(R.string.grade_semester, it.semesterName) } + .toTypedArray() + + AlertDialog.Builder(requireContext()) + .setSingleChoiceItems(choices, selectedIndex) { dialog, which -> + presenter.onSemesterSelected(which) + dialog.dismiss() + } + .setTitle(R.string.grade_switch_semester) + .setNegativeButton(android.R.string.cancel) { _, _ -> } + .show() + } + + override fun setCurrentSemesterName(semester: Int, schoolYear: Int) { + subtitleString = getString(R.string.grade_subtitle, semester, schoolYear, schoolYear + 1) + + if (isVisible) { + (activity as MainView?)?.setViewSubTitle(subtitleString) + } + } + + fun onChildRefresh() { + presenter.onChildViewRefresh() + } + + fun onChildFragmentLoaded(semesterId: Int) { + presenter.onChildViewLoaded(semesterId) + } + + override fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean) { + (pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView) + ?.onParentLoadData(semesterId, forceRefresh) + } + + override fun notifyChildParentReselected(index: Int) { + (pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentReselected() + } + + override fun notifyChildSemesterChange(index: Int) { + (pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentChangeSemester() + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt new file mode 100644 index 000000000..0ae6521cf --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt @@ -0,0 +1,152 @@ +package io.github.wulkanowy.ui.modules.grade + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.logResourceStatus +import io.github.wulkanowy.data.onResourceData +import io.github.wulkanowy.data.onResourceError +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.resourceFlow +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.getCurrentOrLast +import timber.log.Timber +import javax.inject.Inject + +class GradePresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val semesterRepository: SemesterRepository, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + private var selectedIndex = 0 + + private var schoolYear = 0 + + private var semesters = emptyList() + + private val loadedSemesterId = mutableMapOf() + + private lateinit var lastError: Throwable + + override fun onAttachView(view: GradeView) { + super.onAttachView(view) + view.initView() + Timber.i("Grade view was initialized with $selectedIndex index") + errorHandler.showErrorMessage = ::showErrorViewOnError + loadData() + } + + fun onCreateMenu() { + if (semesters.isEmpty()) view?.showSemesterSwitch(false) + } + + fun onViewReselected() { + Timber.i("Grade view is reselected") + view?.run { notifyChildParentReselected(currentPageIndex) } + } + + fun onSemesterSwitch(): Boolean { + if (semesters.isNotEmpty()) { + view?.showSemesterDialog(selectedIndex - 1, semesters.take(2)) + } + return true + } + + fun onSemesterSelected(index: Int) { + if (selectedIndex != index - 1) { + Timber.i("Change semester in grade view to ${index + 1}") + selectedIndex = index + 1 + loadedSemesterId.clear() + view?.let { + it.setCurrentSemesterName(index + 1, schoolYear) + notifyChildrenSemesterChange() + loadChild(it.currentPageIndex) + } + analytics.logEvent("changed_semester", "number" to index + 1) + } + } + + fun onChildViewRefresh() { + view?.let { loadChild(it.currentPageIndex, true) } + } + + fun onChildViewLoaded(semesterId: Int) { + view?.apply { + showContent(true) + showProgress(false) + showErrorView(false) + loadedSemesterId[currentPageIndex] = semesterId + } + } + + fun onPageSelected(index: Int) { + if (semesters.isNotEmpty()) loadChild(index) + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData() + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + private fun loadData() { + resourceFlow { + val student = studentRepository.getCurrentStudent() + semesterRepository.getSemesters(student, refreshOnNoCurrent = true) + } + .logResourceStatus("load grade data") + .onResourceData { + val current = it.getCurrentOrLast() + selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex + schoolYear = current.schoolYear + semesters = it.filter { semester -> semester.diaryId == current.diaryId } + view?.setCurrentSemesterName(current.semesterName, schoolYear) + view?.run { + Timber.i("Loading grade data: Attempt load index $currentPageIndex") + loadChild(currentPageIndex) + showErrorView(false) + showSemesterSwitch(true) + } + } + .onResourceError(errorHandler::dispatch) + .launch() + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + lastError = error + view?.run { + showProgress(false) + showErrorView(true) + setErrorDetails(message) + } + } + + private fun loadChild(index: Int, forceRefresh: Boolean = false) { + Timber.d("Load grade tab child. Selected semester: $selectedIndex, semesters: ${semesters.joinToString { it.semesterName.toString() }}") + + val newSelectedSemesterId = try { + semesters.first { it.semesterName == selectedIndex }.semesterId + } catch (e: NoSuchElementException) { + Timber.e(e, "Selected semester no exists") + return + } + + if (forceRefresh || loadedSemesterId[index] != newSelectedSemesterId) { + Timber.i("Load grade child view index: $index") + view?.notifyChildLoadData(index, newSelectedSemesterId, forceRefresh) + } + } + + private fun notifyChildrenSemesterChange() { + for (i in 0..2) view?.notifyChildSemesterChange(i) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeSubject.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeSubject.kt new file mode 100644 index 000000000..57be55ee3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeSubject.kt @@ -0,0 +1,13 @@ +package io.github.wulkanowy.ui.modules.grade + +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.data.db.entities.GradeSummary + +data class GradeSubject( + val subject: String, + val average: Double, + val points: String, + val summary: GradeSummary, + val grades: List, + val isVulcanAverage: Boolean +) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt new file mode 100644 index 000000000..104f8505a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt @@ -0,0 +1,40 @@ +package io.github.wulkanowy.ui.modules.grade + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.ui.base.BaseView + +interface GradeView : BaseView { + + val currentPageIndex: Int + + fun initView() + + fun showContent(show: Boolean) + + fun showProgress(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun showSemesterSwitch(show: Boolean) + + fun showSemesterDialog(selectedIndex: Int, semesters: List) + + fun setCurrentSemesterName(semester: Int, schoolYear: Int) + + fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean) + + fun notifyChildParentReselected(index: Int) + + fun notifyChildSemesterChange(index: Int) + + interface GradeChildView { + + fun onParentChangeSemester() + + fun onParentLoadData(semesterId: Int, forceRefresh: Boolean) + + fun onParentReselected() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsAdapter.kt new file mode 100644 index 000000000..e5c3bb63e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsAdapter.kt @@ -0,0 +1,255 @@ +package io.github.wulkanowy.ui.modules.grade.details + +import android.annotation.SuppressLint +import android.content.res.Resources +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.NO_POSITION +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.data.enums.GradeColorTheme +import io.github.wulkanowy.data.enums.GradeExpandMode +import io.github.wulkanowy.databinding.HeaderGradeDetailsBinding +import io.github.wulkanowy.databinding.ItemGradeDetailsBinding +import io.github.wulkanowy.ui.base.BaseExpandableAdapter +import io.github.wulkanowy.utils.getBackgroundColor +import io.github.wulkanowy.utils.toFormattedString +import timber.log.Timber +import java.util.BitSet +import javax.inject.Inject + +class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter() { + + private var headers = mutableListOf() + + private var items = mutableListOf() + + private val expandedPositions = BitSet(items.size) + + private var expandMode = GradeExpandMode.ONE + + var onClickListener: (Grade, position: Int) -> Unit = { _, _ -> } + + lateinit var gradeColorTheme: GradeColorTheme + + fun setDataItems(data: List, expandMode: GradeExpandMode = this.expandMode) { + headers = data.filter { it.viewType == ViewType.HEADER }.toMutableList() + items = + (if (expandMode != GradeExpandMode.ALWAYS_EXPANDED) headers else data).toMutableList() + this.expandMode = expandMode + expandedPositions.clear() + } + + fun updateDetailsItem(position: Int, grade: Grade) { + items[position] = GradeDetailsItem(grade, ViewType.ITEM) + notifyItemChanged(position) + } + + fun getHeaderItem(subject: String): GradeDetailsItem { + val candidates = headers.filter { (it.value as GradeDetailsHeader).subject == subject } + + if (candidates.size > 1) { + Timber.e("Header with subject $subject found ${candidates.size} times! Expanded: $expandedPositions. Items: $candidates") + } + + return candidates.first() + } + + fun updateHeaderItem(item: GradeDetailsItem) { + val headerPosition = headers.indexOf(item) + val itemPosition = items.indexOf(item) + + headers[headerPosition] = item + items[itemPosition] = item + notifyItemChanged(itemPosition) + } + + fun collapseAll() { + if (!expandedPositions.isEmpty) { + refreshList(headers.toMutableList()) + expandedPositions.clear() + } + } + + @Synchronized + private fun refreshList(newItems: MutableList) { + val diffCallback = GradeDetailsDiffUtil(items, newItems) + val diffResult = DiffUtil.calculateDiff(diffCallback) + items = newItems + diffResult.dispatchUpdatesTo(this) + } + + override fun getItemCount() = items.size + + override fun getItemViewType(position: Int) = items[position].viewType.id + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + + return when (viewType) { + ViewType.HEADER.id -> HeaderViewHolder( + HeaderGradeDetailsBinding.inflate(inflater, parent, false) + ) + ViewType.ITEM.id -> ItemViewHolder( + ItemGradeDetailsBinding.inflate(inflater, parent, false) + ) + else -> throw IllegalStateException() + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is HeaderViewHolder -> bindHeaderViewHolder( + holder = holder, + header = items[position].value as GradeDetailsHeader, + position = position + ) + is ItemViewHolder -> bindItemViewHolder( + holder = holder, + grade = items[position].value as Grade + ) + } + } + + private fun bindHeaderViewHolder( + holder: HeaderViewHolder, + header: GradeDetailsHeader, + position: Int + ) { + val context = holder.binding.root.context + val item = items[position] + val headerPosition = headers.indexOf(item) + + with(holder.binding) { + gradeHeaderDivider.isVisible = holder.bindingAdapterPosition != 0 + with(gradeHeaderSubject) { + text = header.subject + maxLines = if (expandedPositions[headerPosition]) 2 else 1 + } + gradeHeaderAverage.text = formatAverage(header.average, root.context.resources) + gradeHeaderPointsSum.text = + context.getString(R.string.grade_points_sum, header.pointsSum) + gradeHeaderPointsSum.isVisible = !header.pointsSum.isNullOrEmpty() + gradeHeaderNumber.text = context.resources.getQuantityString( + R.plurals.grade_number_item, + header.grades.size, + header.grades.size + ) + gradeHeaderNote.isVisible = header.newGrades > 0 + + if (header.newGrades > 0) { + gradeHeaderNote.text = header.newGrades.toString() + } + + gradeHeaderContainer.isEnabled = expandMode != GradeExpandMode.ALWAYS_EXPANDED + gradeHeaderContainer.setOnClickListener { + expandGradeHeader(headerPosition, header, holder) + } + } + } + + private fun expandGradeHeader( + headerPosition: Int, + header: GradeDetailsHeader, + holder: HeaderViewHolder + ) { + if (expandMode == GradeExpandMode.ONE) { + val isHeaderExpanded = expandedPositions[headerPosition] + + expandedPositions.clear() + + if (!isHeaderExpanded) { + val updatedItemList = headers.toMutableList() + .apply { addAll(headerPosition + 1, header.grades) } + + expandedPositions.set(headerPosition) + refreshList(updatedItemList) + scrollToHeaderWithSubItems(headerPosition, header.grades.size) + } else { + refreshList(headers.toMutableList()) + } + } else if (expandMode == GradeExpandMode.UNLIMITED) { + val headerAdapterPosition = holder.bindingAdapterPosition + val isHeaderExpanded = expandedPositions[headerPosition] + + expandedPositions.flip(headerPosition) + + if (!isHeaderExpanded) { + val updatedList = items.toMutableList() + .apply { addAll(headerAdapterPosition + 1, header.grades) } + + refreshList(updatedList) + scrollToHeaderWithSubItems(headerAdapterPosition, header.grades.size) + } else { + val startPosition = headerAdapterPosition + 1 + val updatedList = items.toMutableList() + .apply { + subList(startPosition, startPosition + header.grades.size).clear() + } + + refreshList(updatedList) + } + } + } + + @SuppressLint("SetTextI18n") + private fun bindItemViewHolder(holder: ItemViewHolder, grade: Grade) { + val context = holder.binding.root.context + + with(holder.binding) { + gradeItemValue.run { + text = grade.entry + setBackgroundResource(grade.getBackgroundColor(gradeColorTheme)) + } + gradeItemDescription.text = when { + grade.description.isNotBlank() -> grade.description + grade.gradeSymbol.isNotBlank() -> grade.gradeSymbol + else -> context.getString(R.string.all_no_description) + } + gradeItemDate.text = grade.date.toFormattedString() + gradeItemWeight.text = "${context.getString(R.string.grade_weight)}: ${grade.weight}" + gradeItemNote.visibility = if (!grade.isRead) View.VISIBLE else View.GONE + + root.setOnClickListener { + holder.bindingAdapterPosition.let { + if (it != NO_POSITION) onClickListener(grade, it) + } + } + } + } + + private fun formatAverage(average: Double?, resources: Resources) = + if (average == null || average == .0) { + resources.getString(R.string.grade_no_average) + } else { + resources.getString(R.string.grade_average, average) + } + + private class HeaderViewHolder(val binding: HeaderGradeDetailsBinding) : + RecyclerView.ViewHolder(binding.root) + + private class ItemViewHolder(val binding: ItemGradeDetailsBinding) : + RecyclerView.ViewHolder(binding.root) + + private class GradeDetailsDiffUtil( + private val old: List, + private val new: List + ) : DiffUtil.Callback() { + + override fun getOldListSize() = old.size + + override fun getNewListSize() = new.size + + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + return old[oldItemPosition] == new[newItemPosition] + } + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + return old[oldItemPosition] == new[newItemPosition] + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt new file mode 100644 index 000000000..34594111f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt @@ -0,0 +1,94 @@ +package io.github.wulkanowy.ui.modules.grade.details + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.View.GONE +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.data.enums.GradeColorTheme +import io.github.wulkanowy.databinding.DialogGradeBinding +import io.github.wulkanowy.utils.* + + +class GradeDetailsDialog : DialogFragment() { + + private var binding: DialogGradeBinding by lifecycleAwareVariable() + + private lateinit var grade: Grade + + private lateinit var gradeColorTheme: GradeColorTheme + + companion object { + + private const val ARGUMENT_KEY = "Item" + + private const val COLOR_THEME_KEY = "Theme" + + fun newInstance(grade: Grade, colorTheme: GradeColorTheme) = + GradeDetailsDialog().apply { + arguments = Bundle().apply { + putSerializable(ARGUMENT_KEY, grade) + putSerializable(COLOR_THEME_KEY, colorTheme) + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + grade = getSerializable(ARGUMENT_KEY) as Grade + gradeColorTheme = getSerializable(COLOR_THEME_KEY) as GradeColorTheme + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogGradeBinding.inflate(inflater).apply { binding = this }.root + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + with(binding) { + gradeDialogSubject.text = grade.subject + + gradeDialogColorAndWeightValue.run { + text = context.getString(R.string.grade_weight_value, grade.weight) + setBackgroundResource(grade.getGradeColor()) + } + + gradeDialogDateValue.text = grade.date.toFormattedString() + gradeDialogColorValue.text = getString(grade.colorStringId) + + gradeDialogCommentValue.apply { + if (grade.comment.isBlank()) { + visibility = GONE + gradeDialogComment.visibility = GONE + } else text = grade.comment + } + + gradeDialogValue.run { + text = grade.entry + setBackgroundResource(grade.getBackgroundColor(gradeColorTheme)) + } + + gradeDialogTeacherValue.text = grade.teacher.ifBlank { getString(R.string.all_no_data) } + + gradeDialogDescriptionValue.text = grade.run { + when { + description.isBlank() && gradeSymbol.isNotBlank() -> gradeSymbol + description.isBlank() && gradeSymbol.isBlank() -> getString(R.string.all_no_description) + gradeSymbol.isNotBlank() && description.isNotBlank() -> "$gradeSymbol - $description" + else -> description + } + } + + gradeDialogClose.setOnClickListener { dismiss() } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt new file mode 100644 index 000000000..81f3226ad --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt @@ -0,0 +1,179 @@ +package io.github.wulkanowy.ui.modules.grade.details + +import android.os.Bundle +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.View.GONE +import android.view.View.INVISIBLE +import android.view.View.VISIBLE +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.data.enums.GradeColorTheme +import io.github.wulkanowy.data.enums.GradeExpandMode +import io.github.wulkanowy.databinding.FragmentGradeDetailsBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.grade.GradeFragment +import io.github.wulkanowy.ui.modules.grade.GradeView +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.utils.getThemeAttrColor +import javax.inject.Inject + +@AndroidEntryPoint +class GradeDetailsFragment : + BaseFragment(R.layout.fragment_grade_details), GradeDetailsView, + GradeView.GradeChildView { + + @Inject + lateinit var presenter: GradeDetailsPresenter + + @Inject + lateinit var gradeDetailsAdapter: GradeDetailsAdapter + + private var gradeDetailsMenu: Menu? = null + + companion object { + fun newInstance() = GradeDetailsFragment() + } + + override val isViewEmpty + get() = gradeDetailsAdapter.itemCount == 0 + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentGradeDetailsBinding.bind(view) + messageContainer = binding.gradeDetailsRecycler + presenter.onAttachView(this) + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.action_menu_grade_details, menu) + gradeDetailsMenu = menu + presenter.updateMarkAsDoneButton() + } + + override fun initView() { + gradeDetailsAdapter.onClickListener = presenter::onGradeItemSelected + + with(binding) { + with(gradeDetailsRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = gradeDetailsAdapter + } + gradeDetailsSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + gradeDetailsSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) + gradeDetailsSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh)) + gradeDetailsErrorRetry.setOnClickListener { presenter.onRetry() } + gradeDetailsErrorDetails.setOnClickListener { presenter.onDetailsClick() } + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return if (item.itemId == R.id.gradeDetailsMenuRead) presenter.onMarkAsReadSelected() + else false + } + + override fun updateData(data: List, expandMode: GradeExpandMode, gradeColorTheme: GradeColorTheme) { + with(gradeDetailsAdapter) { + this.gradeColorTheme = gradeColorTheme + setDataItems(data, expandMode) + notifyDataSetChanged() + } + } + + override fun updateItem(item: Grade, position: Int) { + gradeDetailsAdapter.updateDetailsItem(position, item) + } + + override fun clearView() { + with(gradeDetailsAdapter) { + setDataItems(mutableListOf()) + notifyDataSetChanged() + } + } + + override fun collapseAllItems() { + gradeDetailsAdapter.collapseAll() + } + + override fun scrollToStart() { + binding.gradeDetailsRecycler.smoothScrollToPosition(0) + } + + override fun getHeaderOfItem(subject: String): GradeDetailsItem { + return gradeDetailsAdapter.getHeaderItem(subject) + } + + override fun updateHeaderItem(item: GradeDetailsItem) { + gradeDetailsAdapter.updateHeaderItem(item) + } + + override fun showProgress(show: Boolean) { + binding.gradeDetailsProgress.visibility = if (show) VISIBLE else GONE + } + + override fun enableSwipe(enable: Boolean) { + binding.gradeDetailsSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + binding.gradeDetailsRecycler.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showEmpty(show: Boolean) { + binding.gradeDetailsEmpty.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showErrorView(show: Boolean) { + binding.gradeDetailsError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + binding.gradeDetailsErrorMessage.text = message + } + + override fun showRefresh(show: Boolean) { + binding.gradeDetailsSwipe.isRefreshing = show + } + + override fun showGradeDialog(grade: Grade, colorTheme: GradeColorTheme) { + (activity as? MainActivity)?.showDialogFragment(GradeDetailsDialog.newInstance(grade, colorTheme)) + } + + override fun onParentLoadData(semesterId: Int, forceRefresh: Boolean) { + presenter.onParentViewLoadData(semesterId, forceRefresh) + } + + override fun onParentReselected() { + presenter.onParentViewReselected() + } + + override fun onParentChangeSemester() { + presenter.onParentViewChangeSemester() + } + + override fun notifyParentDataLoaded(semesterId: Int) { + (parentFragment as? GradeFragment)?.onChildFragmentLoaded(semesterId) + } + + override fun notifyParentRefresh() { + (parentFragment as? GradeFragment)?.onChildRefresh() + } + + override fun enableMarkAsDoneButton(enable: Boolean) { + gradeDetailsMenu?.findItem(R.id.gradeDetailsMenuRead)?.isEnabled = enable + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsItem.kt new file mode 100644 index 000000000..479aff801 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsItem.kt @@ -0,0 +1,20 @@ +package io.github.wulkanowy.ui.modules.grade.details + +enum class ViewType(val id: Int) { + HEADER(1), + ITEM(2) +} + +data class GradeDetailsItem( + val value: Any, + val viewType: ViewType +) + +data class GradeDetailsHeader( + val subject: String, + val average: Double?, + val pointsSum: String?, + val grades: List +) { + var newGrades = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt new file mode 100644 index 000000000..746601a68 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt @@ -0,0 +1,241 @@ +package io.github.wulkanowy.ui.modules.grade.details + +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.data.enums.GradeExpandMode +import io.github.wulkanowy.data.enums.GradeSortingMode.ALPHABETIC +import io.github.wulkanowy.data.enums.GradeSortingMode.DATE +import io.github.wulkanowy.data.repositories.GradeRepository +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider +import io.github.wulkanowy.ui.modules.grade.GradeSubject +import io.github.wulkanowy.utils.AnalyticsHelper +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.first +import timber.log.Timber +import javax.inject.Inject + +class GradeDetailsPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val gradeRepository: GradeRepository, + private val semesterRepository: SemesterRepository, + private val preferencesRepository: PreferencesRepository, + private val averageProvider: GradeAverageProvider, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + private var newGradesAmount: Int = 0 + + private var currentSemesterId = 0 + + private lateinit var lastError: Throwable + + override fun onAttachView(view: GradeDetailsView) { + super.onAttachView(view) + view.initView() + errorHandler.showErrorMessage = ::showErrorViewOnError + } + + fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) { + currentSemesterId = semesterId + + if (!forceRefresh) view?.showErrorView(false) + loadData(semesterId, forceRefresh) + } + + fun onGradeItemSelected(grade: Grade, position: Int) { + Timber.i("Select grade item ${grade.id}, position: $position") + view?.apply { + showGradeDialog(grade, preferencesRepository.gradeColorTheme) + if (!grade.isRead) { + grade.isRead = true + updateItem(grade, position) + getHeaderOfItem(grade.subject).let { header -> + (header.value as GradeDetailsHeader).newGrades-- + updateHeaderItem(header) + } + newGradesAmount-- + updateMarkAsDoneButton() + updateGrade(grade) + } + } + } + + fun onMarkAsReadSelected(): Boolean { + resourceFlow { + val student = studentRepository.getCurrentStudent() + val semesters = semesterRepository.getSemesters(student) + val semester = semesters.first { item -> item.semesterId == currentSemesterId } + val unreadGrades = gradeRepository.getUnreadGrades(semester).first() + + Timber.i("Mark as read ${unreadGrades.size} grades") + gradeRepository.updateGrades(unreadGrades.map { it.apply { isRead = true } }) + } + .logResourceStatus("mark grades as read") + .onResourceSuccess { loadData(currentSemesterId, false) } + .onResourceError(errorHandler::dispatch) + .launch("mark") + return true + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the grade details") + view?.notifyParentRefresh() + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + view?.notifyParentRefresh() + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + fun onParentViewReselected() { + view?.run { + if (!isViewEmpty) { + if (preferencesRepository.gradeExpandMode != GradeExpandMode.ALWAYS_EXPANDED) collapseAllItems() + scrollToStart() + } + } + } + + fun onParentViewChangeSemester() { + view?.run { + showProgress(true) + enableSwipe(false) + showRefresh(false) + showContent(false) + showEmpty(false) + clearView() + } + cancelJobs("load") + } + + fun updateMarkAsDoneButton() { + view?.enableMarkAsDoneButton(newGradesAmount > 0) + } + + private fun loadData(semesterId: Int, forceRefresh: Boolean) { + flatResourceFlow { + val student = studentRepository.getCurrentStudent() + averageProvider.getGradesDetailsWithAverage(student, semesterId, forceRefresh) + } + .logResourceStatus("load grade details") + .onResourceData { + view?.run { + enableSwipe(true) + showProgress(false) + showErrorView(false) + showContent(it.isNotEmpty()) + showEmpty(it.isEmpty()) + updateNewGradesAmount(it) + updateMarkAsDoneButton() + updateData( + data = createGradeItems(it), + expandMode = preferencesRepository.gradeExpandMode, + preferencesRepository.gradeColorTheme + ) + } + } + .onResourceIntermediate { view?.showRefresh(true) } + .onResourceSuccess { + analytics.logEvent( + "load_data", + "type" to "grade_details", + "items" to it.size + ) + } + .onResourceNotLoading { + view?.run { + enableSwipe(true) + showRefresh(false) + showProgress(false) + notifyParentDataLoaded(semesterId) + } + } + .catch { + errorHandler.dispatch(it) + view?.notifyParentDataLoaded(semesterId) + } + .onResourceError(errorHandler::dispatch) + .launch() + } + + private fun updateNewGradesAmount(grades: List) { + newGradesAmount = grades.sumOf { item -> + item.grades.sumOf { grade -> (if (!grade.isRead) 1 else 0).toInt() } + } + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + showProgress(false) + } else showError(message, error) + } + } + + private fun createGradeItems(items: List): List { + return items + .let { gradesWithAverages -> + if (!preferencesRepository.showSubjectsWithoutGrades) { + gradesWithAverages.filter { it.grades.isNotEmpty() } + } else gradesWithAverages + } + .let { gradeSubjects -> + when (preferencesRepository.gradeSortingMode) { + DATE -> gradeSubjects.sortedByDescending { gradeDetailsWithAverage -> + gradeDetailsWithAverage.grades.maxByOrNull { it.date }?.date + } + ALPHABETIC -> gradeSubjects.sortedBy { gradeDetailsWithAverage -> + gradeDetailsWithAverage.subject.lowercase() + } + } + } + .map { (subject, average, points, _, grades) -> + val subItems = grades + .sortedByDescending { it.date } + .map { GradeDetailsItem(it, ViewType.ITEM) } + + val gradeDetailsItems = listOf( + GradeDetailsItem( + GradeDetailsHeader( + subject = subject, + average = average, + pointsSum = points, + grades = subItems + ).apply { + newGrades = grades.filter { grade -> !grade.isRead }.size + }, ViewType.HEADER + ) + ) + + if (preferencesRepository.gradeExpandMode == GradeExpandMode.ALWAYS_EXPANDED) { + gradeDetailsItems + subItems + } else { + gradeDetailsItems + } + }.flatten() + } + + private fun updateGrade(grade: Grade) { + resourceFlow { gradeRepository.updateGrade(grade) } + .logResourceStatus("update grade result ${grade.id}") + .onResourceError(errorHandler::dispatch) + .launch("update") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt new file mode 100644 index 000000000..491bf3003 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt @@ -0,0 +1,49 @@ +package io.github.wulkanowy.ui.modules.grade.details + +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.data.enums.GradeColorTheme +import io.github.wulkanowy.data.enums.GradeExpandMode +import io.github.wulkanowy.ui.base.BaseView + +interface GradeDetailsView : BaseView { + + val isViewEmpty: Boolean + + fun initView() + + fun updateData(data: List, expandMode: GradeExpandMode, gradeColorTheme: GradeColorTheme) + + fun updateItem(item: Grade, position: Int) + + fun updateHeaderItem(item: GradeDetailsItem) + + fun clearView() + + fun scrollToStart() + + fun collapseAllItems() + + fun showGradeDialog(grade: Grade, colorTheme: GradeColorTheme) + + fun showContent(show: Boolean) + + fun showEmpty(show: Boolean) + + fun showProgress(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun enableSwipe(enable: Boolean) + + fun showRefresh(show: Boolean) + + fun notifyParentDataLoaded(semesterId: Int) + + fun notifyParentRefresh() + + fun enableMarkAsDoneButton(enable: Boolean) + + fun getHeaderOfItem(subject: String): GradeDetailsItem +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsAdapter.kt new file mode 100644 index 000000000..fd0ac5471 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsAdapter.kt @@ -0,0 +1,330 @@ +package io.github.wulkanowy.ui.modules.grade.statistics + +import android.graphics.Color +import android.view.LayoutInflater +import android.view.View.GONE +import android.view.View.VISIBLE +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.RecyclerView +import com.github.mikephil.charting.components.Legend +import com.github.mikephil.charting.components.LegendEntry +import com.github.mikephil.charting.data.* +import com.github.mikephil.charting.formatter.ValueFormatter +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.GradePartialStatistics +import io.github.wulkanowy.data.db.entities.GradePointsStatistics +import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics +import io.github.wulkanowy.data.enums.GradeColorTheme +import io.github.wulkanowy.data.pojos.GradeStatisticsItem +import io.github.wulkanowy.databinding.ItemGradeStatisticsBarBinding +import io.github.wulkanowy.databinding.ItemGradeStatisticsHeaderBinding +import io.github.wulkanowy.databinding.ItemGradeStatisticsPieBinding +import io.github.wulkanowy.utils.getThemeAttrColor +import javax.inject.Inject + +class GradeStatisticsAdapter @Inject constructor() : + RecyclerView.Adapter() { + + var currentDataType = GradeStatisticsItem.DataType.PARTIAL + + var items = emptyList() + + lateinit var gradeColorTheme: GradeColorTheme + + var showAllSubjectsOnList: Boolean = false + + var onDataTypeChangeListener: () -> Unit = {} + + private val vulcanGradeColors = listOf( + 6 to R.color.grade_vulcan_six, + 5 to R.color.grade_vulcan_five, + 4 to R.color.grade_vulcan_four, + 3 to R.color.grade_vulcan_three, + 2 to R.color.grade_vulcan_two, + 1 to R.color.grade_vulcan_one + ) + + private val materialGradeColors = listOf( + 6 to R.color.grade_material_six, + 5 to R.color.grade_material_five, + 4 to R.color.grade_material_four, + 3 to R.color.grade_material_three, + 2 to R.color.grade_material_two, + 1 to R.color.grade_material_one + ) + + private val gradePointsColors = listOf( + Color.parseColor("#37c69c"), + Color.parseColor("#d8b12a") + ) + + private val gradeLabels = listOf( + "6, 6-", "5, 5-, 5+", "4, 4-, 4+", "3, 3-, 3+", "2, 2-, 2+", "1, 1+" + ) + + override fun getItemCount() = + (if (showAllSubjectsOnList) items.size else (if (items.isEmpty()) 0 else 1)) + 1 + + override fun getItemViewType(position: Int) = + if (position == 0) { + ViewType.HEADER.id + } else { + when (items[position - 1].type) { + GradeStatisticsItem.DataType.PARTIAL -> ViewType.PARTIAL.id + GradeStatisticsItem.DataType.POINTS -> ViewType.POINTS.id + GradeStatisticsItem.DataType.SEMESTER -> ViewType.SEMESTER.id + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + return when (viewType) { + ViewType.PARTIAL.id -> PartialViewHolder( + ItemGradeStatisticsPieBinding.inflate(inflater, parent, false) + ) + ViewType.SEMESTER.id -> SemesterViewHolder( + ItemGradeStatisticsPieBinding.inflate(inflater, parent, false) + ) + ViewType.POINTS.id -> PointsViewHolder( + ItemGradeStatisticsBarBinding.inflate(inflater, parent, false) + ) + ViewType.HEADER.id -> HeaderViewHolder( + ItemGradeStatisticsHeaderBinding.inflate(inflater, parent, false) + ) + else -> throw IllegalStateException() + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val index = position - 1 + + when (holder) { + is PartialViewHolder -> bindPartialChart(holder.binding, items[index].partial!!) + is SemesterViewHolder -> bindSemesterChart(holder.binding, items[index].semester!!) + is PointsViewHolder -> bindBarChart(holder.binding, items[index].points!!) + is HeaderViewHolder -> bindHeader(holder.binding) + } + } + + private fun bindHeader(binding: ItemGradeStatisticsHeaderBinding) { + binding.gradeStatisticsTypeSwitch.check( + when (currentDataType) { + GradeStatisticsItem.DataType.PARTIAL -> R.id.gradeStatisticsTypePartial + GradeStatisticsItem.DataType.SEMESTER -> R.id.gradeStatisticsTypeSemester + GradeStatisticsItem.DataType.POINTS -> R.id.gradeStatisticsTypePoints + } + ) + + binding.gradeStatisticsTypeSwitch.setOnCheckedChangeListener { _, checkedId -> + currentDataType = when (checkedId) { + R.id.gradeStatisticsTypePartial -> GradeStatisticsItem.DataType.PARTIAL + R.id.gradeStatisticsTypeSemester -> GradeStatisticsItem.DataType.SEMESTER + R.id.gradeStatisticsTypePoints -> GradeStatisticsItem.DataType.POINTS + else -> GradeStatisticsItem.DataType.PARTIAL + } + onDataTypeChangeListener() + } + } + + private fun bindPartialChart( + binding: ItemGradeStatisticsPieBinding, + partials: GradePartialStatistics + ) { + val studentAverage = partials.studentAverage.takeIf { it.isNotEmpty() }?.let { + binding.root.context.getString(R.string.grade_statistics_student_average, it) + } + bindPieChart( + binding = binding, + subject = partials.subject, + average = partials.classAverage, + studentValue = studentAverage, + amounts = partials.classAmounts + ) + } + + private fun bindSemesterChart( + binding: ItemGradeStatisticsPieBinding, + semester: GradeSemesterStatistics + ) { + val studentAverage = semester.studentAverage.takeIf { it.isNotBlank() } + val studentGrade = semester.studentGrade.takeIf { it != 0 } + + val studentValue = when { + studentAverage != null -> binding.root.context.getString( + R.string.grade_statistics_student_average, + studentAverage + ) + studentGrade != null -> binding.root.context.getString( + R.string.grade_statistics_student_grade, + studentGrade.toString() + ) + else -> null + } + bindPieChart( + binding = binding, + subject = semester.subject, + average = semester.classAverage, + studentValue = studentValue, + amounts = semester.amounts + ) + } + + private fun bindPieChart( + binding: ItemGradeStatisticsPieBinding, + subject: String, + average: String, + studentValue: String?, + amounts: List + ) { + with(binding.gradeStatisticsPieTitle) { + text = subject + visibility = if (items.size == 1 || !showAllSubjectsOnList) GONE else VISIBLE + } + + val gradeColors = when (gradeColorTheme) { + GradeColorTheme.VULCAN -> vulcanGradeColors + else -> materialGradeColors + } + + val dataset = PieDataSet( + amounts.mapIndexed { grade, amount -> + PieEntry(amount.toFloat(), (grade + 1).toString()) + }.reversed().filterNot { it.value == 0f }, + binding.root.context.getString(R.string.grade_statistics_legend) + ) + + with(dataset) { + valueTextSize = 12f + sliceSpace = 1f + valueTextColor = Color.WHITE + val grades = amounts.mapIndexed { grade, amount -> (grade + 1) to amount } + .filterNot { it.second == 0 } + setColors(grades.reversed().map { (grade, _) -> + gradeColors.single { color -> color.first == grade }.second + }.toIntArray(), binding.root.context) + } + + with(binding.gradeStatisticsPie) { + setTouchEnabled(false) + if (amounts.size == 1) animateXY(1000, 1000) + data = PieData(dataset).apply { + setValueFormatter(object : ValueFormatter() { + override fun getPieLabel(value: Float, pieEntry: PieEntry): String { + return resources.getQuantityString( + R.plurals.grade_number_item, + value.toInt(), + value.toInt() + ) + } + }) + } + with(legend) { + textColor = context.getThemeAttrColor(android.R.attr.textColorPrimary) + setCustom(gradeLabels.mapIndexed { i, it -> + LegendEntry().apply { + label = it + formColor = ContextCompat.getColor(context, gradeColors[i].second) + form = Legend.LegendForm.SQUARE + } + }) + } + + val numberOfGradesString = amounts.fold(0) { acc, it -> acc + it } + .let { resources.getQuantityString(R.plurals.grade_number_item, it, it) } + val averageString = + binding.root.context.getString(R.string.grade_statistics_class_average, average) + + minAngleForSlices = 25f + description.isEnabled = false + centerText = + numberOfGradesString + ("\n\n" + averageString).takeIf { average.isNotBlank() } + .orEmpty() + studentValue?.let { "\n$it" }.orEmpty() + + setHoleColor(context.getThemeAttrColor(android.R.attr.windowBackground)) + setCenterTextColor(context.getThemeAttrColor(android.R.attr.textColorPrimary)) + invalidate() + } + } + + private fun bindBarChart( + binding: ItemGradeStatisticsBarBinding, + points: GradePointsStatistics + ) { + with(binding.gradeStatisticsBarTitle) { + text = points.subject + visibility = if (items.size == 1) GONE else VISIBLE + } + + val dataset = BarDataSet( + listOf( + BarEntry(1f, points.others.toFloat()), + BarEntry(2f, points.student.toFloat()) + ), binding.root.context.getString(R.string.grade_statistics_legend) + ) + + with(dataset) { + valueTextSize = 12f + valueTextColor = binding.root.context.getThemeAttrColor(android.R.attr.textColorPrimary) + valueFormatter = object : ValueFormatter() { + override fun getBarLabel(barEntry: BarEntry) = "${barEntry.y}%" + } + colors = gradePointsColors + } + + with(binding.gradeStatisticsBar) { + setTouchEnabled(false) + if (items.size == 1) animateXY(1000, 1000) + data = BarData(dataset).apply { + barWidth = 0.5f + setFitBars(true) + } + legend.setCustom(listOf( + LegendEntry().apply { + label = binding.root.context.getString(R.string.grade_statistics_average_class) + formColor = gradePointsColors[0] + form = Legend.LegendForm.SQUARE + }, + LegendEntry().apply { + label = + binding.root.context.getString(R.string.grade_statistics_average_student) + formColor = gradePointsColors[1] + form = Legend.LegendForm.SQUARE + } + )) + legend.textColor = context.getThemeAttrColor(android.R.attr.textColorPrimary) + + description.isEnabled = false + + binding.root.context.getThemeAttrColor(android.R.attr.textColorPrimary).let { + axisLeft.textColor = it + axisRight.textColor = it + } + xAxis.setDrawLabels(false) + xAxis.setDrawGridLines(false) + with(axisLeft) { + axisMinimum = 0f + axisMaximum = 100f + labelCount = 11 + } + with(axisRight) { + axisMinimum = 0f + axisMaximum = 100f + labelCount = 11 + } + invalidate() + } + } + + private class PartialViewHolder(val binding: ItemGradeStatisticsPieBinding) : + RecyclerView.ViewHolder(binding.root) + + private class SemesterViewHolder(val binding: ItemGradeStatisticsPieBinding) : + RecyclerView.ViewHolder(binding.root) + + private class PointsViewHolder(val binding: ItemGradeStatisticsBarBinding) : + RecyclerView.ViewHolder(binding.root) + + private class HeaderViewHolder(val binding: ItemGradeStatisticsHeaderBinding) : + RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt new file mode 100644 index 000000000..2af59c011 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt @@ -0,0 +1,175 @@ +package io.github.wulkanowy.ui.modules.grade.statistics + +import android.os.Bundle +import android.view.View +import android.widget.ArrayAdapter +import android.widget.TextView +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.enums.GradeColorTheme +import io.github.wulkanowy.data.pojos.GradeStatisticsItem +import io.github.wulkanowy.databinding.FragmentGradeStatisticsBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.grade.GradeFragment +import io.github.wulkanowy.ui.modules.grade.GradeView +import io.github.wulkanowy.utils.dpToPx +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.setOnItemSelectedListener +import javax.inject.Inject + +@AndroidEntryPoint +class GradeStatisticsFragment : + BaseFragment(R.layout.fragment_grade_statistics), + GradeStatisticsView, GradeView.GradeChildView { + + @Inject + lateinit var presenter: GradeStatisticsPresenter + + @Inject + lateinit var statisticsAdapter: GradeStatisticsAdapter + + private lateinit var subjectsAdapter: ArrayAdapter + + companion object { + private const val SAVED_CHART_TYPE = "CURRENT_TYPE" + private const val SAVED_SUBJECT_NAME = "SUBJECT_NAME" + + fun newInstance() = GradeStatisticsFragment() + } + + override val isViewEmpty get() = statisticsAdapter.items.isEmpty() + + override val currentType get() = statisticsAdapter.currentDataType + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentGradeStatisticsBinding.bind(view) + messageContainer = binding.gradeStatisticsRecycler + presenter.onAttachView( + view = this, + type = savedInstanceState?.getSerializable(SAVED_CHART_TYPE) as? GradeStatisticsItem.DataType, + subjectName = savedInstanceState?.getSerializable(SAVED_SUBJECT_NAME) as? String, + ) + } + + override fun initView() { + statisticsAdapter.onDataTypeChangeListener = presenter::onTypeChange + + with(binding.gradeStatisticsRecycler) { + layoutManager = LinearLayoutManager(requireContext()) + statisticsAdapter.currentDataType = presenter.currentType + adapter = statisticsAdapter + } + + subjectsAdapter = + ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, mutableListOf()) + subjectsAdapter.setDropDownViewResource(R.layout.item_attendance_summary_subject) + + with(binding.gradeStatisticsSubjects) { + adapter = subjectsAdapter + setOnItemSelectedListener { presenter.onSubjectSelected(it?.text?.toString()) } + } + + with(binding) { + gradeStatisticsSubjectsContainer.elevation = requireContext().dpToPx(1f) + + gradeStatisticsSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + gradeStatisticsSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) + gradeStatisticsSwipe.setProgressBackgroundColorSchemeColor( + requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh) + ) + gradeStatisticsErrorRetry.setOnClickListener { presenter.onRetry() } + gradeStatisticsErrorDetails.setOnClickListener { presenter.onDetailsClick() } + } + } + + override fun updateSubjects(data: List, selectedIndex: Int) { + binding.gradeStatisticsSubjects.setSelection(selectedIndex) + with(subjectsAdapter) { + clear() + addAll(data) + notifyDataSetChanged() + } + } + + override fun updateData( + newItems: List, + newTheme: GradeColorTheme, + showAllSubjectsOnStatisticsList: Boolean + ) { + with(statisticsAdapter) { + showAllSubjectsOnList = showAllSubjectsOnStatisticsList + gradeColorTheme = newTheme + items = newItems + notifyDataSetChanged() + } + } + + override fun showSubjects(show: Boolean) { + binding.gradeStatisticsSubjectsContainer.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun clearView() { + statisticsAdapter.items = emptyList() + } + + override fun resetView() { + binding.gradeStatisticsRecycler.scrollToPosition(0) + } + + override fun showEmpty(show: Boolean) { + binding.gradeStatisticsEmpty.visibility = if (show) View.VISIBLE else View.INVISIBLE + } + + override fun showErrorView(show: Boolean) { + binding.gradeStatisticsError.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun setErrorDetails(message: String) { + binding.gradeStatisticsErrorMessage.text = message + } + + override fun showProgress(show: Boolean) { + binding.gradeStatisticsProgress.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun enableSwipe(enable: Boolean) { + binding.gradeStatisticsSwipe.isEnabled = enable + } + + override fun showRefresh(show: Boolean) { + binding.gradeStatisticsSwipe.isRefreshing = show + } + + override fun onParentLoadData(semesterId: Int, forceRefresh: Boolean) { + presenter.onParentViewLoadData(semesterId, forceRefresh) + } + + override fun onParentReselected() { + presenter.onParentViewReselected() + } + + override fun onParentChangeSemester() { + presenter.onParentViewChangeSemester() + } + + override fun notifyParentDataLoaded(semesterId: Int) { + (parentFragment as? GradeFragment)?.onChildFragmentLoaded(semesterId) + } + + override fun notifyParentRefresh() { + (parentFragment as? GradeFragment)?.onChildRefresh() + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putSerializable(SAVED_CHART_TYPE, presenter.currentType) + outState.putSerializable(SAVED_SUBJECT_NAME, presenter.currentSubjectName) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt new file mode 100644 index 000000000..aa0e5999e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt @@ -0,0 +1,265 @@ +package io.github.wulkanowy.ui.modules.grade.statistics + +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.db.entities.Subject +import io.github.wulkanowy.data.pojos.GradeStatisticsItem +import io.github.wulkanowy.data.repositories.* +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import timber.log.Timber +import javax.inject.Inject + +class GradeStatisticsPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val gradeStatisticsRepository: GradeStatisticsRepository, + private val subjectRepository: SubjectRepository, + private val semesterRepository: SemesterRepository, + private val preferencesRepository: PreferencesRepository, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + private var subjects = emptyList() + + private var currentSemesterId = 0 + + var currentSubjectName: String = "Wszystkie" + private set + + private lateinit var lastError: Throwable + + var currentType: GradeStatisticsItem.DataType = GradeStatisticsItem.DataType.PARTIAL + private set + + fun onAttachView( + view: GradeStatisticsView, + type: GradeStatisticsItem.DataType?, + subjectName: String? + ) { + super.onAttachView(view) + currentType = type ?: GradeStatisticsItem.DataType.PARTIAL + currentSubjectName = subjectName ?: currentSubjectName + view.initView() + errorHandler.showErrorMessage = ::showErrorViewOnError + } + + fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) { + currentSemesterId = semesterId + loadSubjects() + if (!forceRefresh) view?.showErrorView(false) + loadDataByType(semesterId, currentSubjectName, currentType, forceRefresh) + } + + fun onParentViewReselected() { + view?.run { + if (!isViewEmpty) resetView() + } + } + + fun onParentViewChangeSemester() { + clearDataInView() + view?.run { + showProgress(true) + enableSwipe(false) + showRefresh(false) + showErrorView(false) + showEmpty(false) + clearView() + } + cancelJobs("load") + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the grade stats") + view?.notifyParentRefresh() + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + view?.notifyParentRefresh() + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + fun onSubjectSelected(name: String?) { + Timber.i("Select grade stats subject $name") + clearDataInView() + view?.run { + showProgress(true) + enableSwipe(false) + showEmpty(false) + showErrorView(false) + clearView() + } + (subjects.singleOrNull { it.name == name }?.name)?.let { + if (it != currentSubjectName) loadDataByType(currentSemesterId, it, currentType) + } + } + + fun onTypeChange() { + val type = view?.currentType ?: GradeStatisticsItem.DataType.POINTS + Timber.i("Select grade stats semester: $type") + cancelJobs("load") + clearDataInView() + view?.run { + showProgress(true) + enableSwipe(false) + showEmpty(false) + showErrorView(false) + clearView() + } + loadDataByType(currentSemesterId, currentSubjectName, type) + } + + private fun loadSubjects() { + flatResourceFlow { + val student = studentRepository.getCurrentStudent() + val semester = semesterRepository.getCurrentSemester(student) + subjectRepository.getSubjects(student, semester) + } + .logResourceStatus("load grade stats subjects") + .onResourceData { + subjects = it + view?.run { + showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList) + updateSubjects( + data = it.map { subject -> subject.name }, + selectedIndex = it.indexOfFirst { subject -> + subject.name == currentSubjectName + }, + ) + } + } + .onResourceError(errorHandler::dispatch) + .launch("subjects") + } + + private fun loadDataByType( + semesterId: Int, + subjectName: String, + type: GradeStatisticsItem.DataType, + forceRefresh: Boolean = false + ) { + Timber.i("Loading grade stats data started") + + currentType = type + currentSubjectName = when { + preferencesRepository.showAllSubjectsOnStatisticsList -> "Wszystkie" + else -> subjectName + } + + flatResourceFlow { + val student = studentRepository.getCurrentStudent() + val semesters = semesterRepository.getSemesters(student) + val semester = semesters.first { item -> item.semesterId == semesterId } + + with(gradeStatisticsRepository) { + when (type) { + GradeStatisticsItem.DataType.PARTIAL -> { + getGradesPartialStatistics( + student = student, + semester = semester, + subjectName = currentSubjectName, + forceRefresh = forceRefresh + ) + } + GradeStatisticsItem.DataType.SEMESTER -> { + getGradesSemesterStatistics( + student = student, + semester = semester, + subjectName = currentSubjectName, + forceRefresh = forceRefresh + ) + } + GradeStatisticsItem.DataType.POINTS -> { + getGradesPointsStatistics( + student = student, + semester = semester, + subjectName = currentSubjectName, + forceRefresh = forceRefresh + ) + } + } + } + } + .logResourceStatus("load grade stats data") + .mapResourceData { + val isNoContent = checkIsNoContent(it, type) + if (isNoContent) emptyList() else it + } + .onResourceData { + view?.run { + enableSwipe(true) + showProgress(false) + showErrorView(false) + showEmpty(it.isEmpty()) + updateData( + newItems = it, + newTheme = preferencesRepository.gradeColorTheme, + showAllSubjectsOnStatisticsList = preferencesRepository.showAllSubjectsOnStatisticsList + ) + } + } + .onResourceIntermediate { view?.showRefresh(true) } + .onResourceSuccess { + analytics.logEvent( + "load_data", + "type" to "grade_statistics", + "items" to it.size + ) + } + .onResourceNotLoading { + view?.run { + enableSwipe(true) + showRefresh(false) + showProgress(false) + notifyParentDataLoaded(semesterId) + } + } + .onResourceError(errorHandler::dispatch) + .launch("load") + } + + private fun checkIsNoContent( + items: List, + type: GradeStatisticsItem.DataType + ): Boolean { + return items.isEmpty() || when (type) { + GradeStatisticsItem.DataType.SEMESTER -> { + items.firstOrNull()?.semester?.amounts.orEmpty().sum() == 0 + } + GradeStatisticsItem.DataType.PARTIAL -> { + items.firstOrNull()?.partial?.classAmounts.orEmpty().sum() == 0 + } + GradeStatisticsItem.DataType.POINTS -> { + items.firstOrNull()?.points?.let { points -> points.student == .0 && points.others == .0 } + ?: false + } + } + } + + private fun clearDataInView() { + view?.updateData( + emptyList(), + preferencesRepository.gradeColorTheme, + preferencesRepository.showAllSubjectsOnStatisticsList + ) + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt new file mode 100644 index 000000000..4333bb0a9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt @@ -0,0 +1,44 @@ +package io.github.wulkanowy.ui.modules.grade.statistics + +import io.github.wulkanowy.data.enums.GradeColorTheme +import io.github.wulkanowy.data.pojos.GradeStatisticsItem +import io.github.wulkanowy.ui.base.BaseView + +interface GradeStatisticsView : BaseView { + + val isViewEmpty: Boolean + + val currentType: GradeStatisticsItem.DataType + + fun initView() + + fun updateSubjects(data: List, selectedIndex: Int) + + fun updateData( + newItems: List, + newTheme: GradeColorTheme, + showAllSubjectsOnStatisticsList: Boolean + ) + + fun showSubjects(show: Boolean) + + fun notifyParentDataLoaded(semesterId: Int) + + fun notifyParentRefresh() + + fun clearView() + + fun resetView() + + fun showEmpty(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showRefresh(show: Boolean) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/ViewType.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/ViewType.kt new file mode 100644 index 000000000..f695eaf9d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/ViewType.kt @@ -0,0 +1,8 @@ +package io.github.wulkanowy.ui.modules.grade.statistics + +enum class ViewType(val id: Int) { + SEMESTER(1), + PARTIAL(2), + POINTS(3), + HEADER(4) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryAdapter.kt new file mode 100644 index 000000000..082c847e5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryAdapter.kt @@ -0,0 +1,120 @@ +package io.github.wulkanowy.ui.modules.grade.summary + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.GradeSummary +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.databinding.ItemGradeSummaryBinding +import io.github.wulkanowy.databinding.ScrollableHeaderGradeSummaryBinding +import io.github.wulkanowy.utils.calcFinalAverage +import java.util.Locale +import javax.inject.Inject + +class GradeSummaryAdapter @Inject constructor( + private val preferencesRepository: PreferencesRepository +) : RecyclerView.Adapter() { + + private enum class ViewType(val id: Int) { + HEADER(1), + ITEM(2) + } + + var items = emptyList() + + var onCalculatedHelpClickListener: () -> Unit = {} + + var onFinalHelpClickListener: () -> Unit = {} + + override fun getItemCount() = items.size + if (items.isNotEmpty()) 1 else 0 + + override fun getItemViewType(position: Int) = when (position) { + 0 -> ViewType.HEADER.id + else -> ViewType.ITEM.id + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + + return when (viewType) { + ViewType.HEADER.id -> HeaderViewHolder( + ScrollableHeaderGradeSummaryBinding.inflate(inflater, parent, false) + ) + ViewType.ITEM.id -> ItemViewHolder( + ItemGradeSummaryBinding.inflate(inflater, parent, false) + ) + else -> throw IllegalStateException() + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is HeaderViewHolder -> bindHeaderViewHolder(holder.binding) + is ItemViewHolder -> bindItemViewHolder(holder.binding, items[position - 1]) + } + } + + private fun bindHeaderViewHolder(binding: ScrollableHeaderGradeSummaryBinding) { + if (items.isEmpty()) return + + val context = binding.root.context + val finalItemsCount = items.count { it.finalGrade.matches("[0-6][+-]?".toRegex()) } + val calculatedItemsCount = items.count { value -> value.average != 0.0 } + val allItemsCount = items.count { !it.subject.equals("zachowanie", true) } + val finalAverage = items.calcFinalAverage( + preferencesRepository.gradePlusModifier, + preferencesRepository.gradeMinusModifier + ) + val calculatedAverage = items.filter { value -> value.average != 0.0 } + .map { values -> values.average } + .reversed() // fix average precision + .average() + + with(binding) { + gradeSummaryScrollableHeaderFinal.text = formatAverage(finalAverage) + gradeSummaryScrollableHeaderCalculated.text = formatAverage(calculatedAverage) + gradeSummaryScrollableHeaderFinalSubjectCount.text = + context.getString( + R.string.grade_summary_from_subjects, + finalItemsCount, + allItemsCount + ) + gradeSummaryScrollableHeaderCalculatedSubjectCount.text = context.getString( + R.string.grade_summary_from_subjects, + calculatedItemsCount, + allItemsCount + ) + + gradeSummaryCalculatedAverageHelp.setOnClickListener { onCalculatedHelpClickListener() } + gradeSummaryFinalAverageHelp.setOnClickListener { onFinalHelpClickListener() } + } + } + + @SuppressLint("SetTextI18n") + private fun bindItemViewHolder(binding: ItemGradeSummaryBinding, item: GradeSummary) { + with(binding) { + gradeSummaryItemTitle.text = item.subject + gradeSummaryItemPoints.text = item.pointsSum + gradeSummaryItemAverage.text = formatAverage(item.average, "") + gradeSummaryItemPredicted.text = "${item.predictedGrade} ${item.proposedPoints}".trim() + gradeSummaryItemFinal.text = "${item.finalGrade} ${item.finalPoints}".trim() + + gradeSummaryItemPointsContainer.visibility = + if (item.pointsSum.isBlank()) View.GONE else View.VISIBLE + } + } + + private fun formatAverage(average: Double, defaultValue: String = "-- --"): String { + return if (average == 0.0) defaultValue + else String.format(Locale.FRANCE, "%.2f", average) + } + + private class HeaderViewHolder(val binding: ScrollableHeaderGradeSummaryBinding) : + RecyclerView.ViewHolder(binding.root) + + private class ItemViewHolder(val binding: ItemGradeSummaryBinding) : + RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt new file mode 100644 index 000000000..3810902ff --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt @@ -0,0 +1,160 @@ +package io.github.wulkanowy.ui.modules.grade.summary + +import android.os.Bundle +import android.view.View +import android.view.View.GONE +import android.view.View.INVISIBLE +import android.view.View.VISIBLE +import androidx.appcompat.app.AlertDialog +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.GradeSummary +import io.github.wulkanowy.databinding.FragmentGradeSummaryBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.grade.GradeFragment +import io.github.wulkanowy.ui.modules.grade.GradeView +import io.github.wulkanowy.utils.getThemeAttrColor +import javax.inject.Inject + +@AndroidEntryPoint +class GradeSummaryFragment : + BaseFragment(R.layout.fragment_grade_summary), GradeSummaryView, + GradeView.GradeChildView { + + @Inject + lateinit var presenter: GradeSummaryPresenter + + @Inject + lateinit var gradeSummaryAdapter: GradeSummaryAdapter + + companion object { + fun newInstance() = GradeSummaryFragment() + } + + override val isViewEmpty + get() = gradeSummaryAdapter.items.isEmpty() + + override val predictedString + get() = getString(R.string.grade_summary_predicted_grade) + + override val finalString + get() = getString(R.string.grade_summary_final_grade) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentGradeSummaryBinding.bind(view) + messageContainer = binding.gradeSummaryRecycler + presenter.onAttachView(this) + } + + override fun initView() { + with(gradeSummaryAdapter) { + onCalculatedHelpClickListener = presenter::onCalculatedAverageHelpClick + onFinalHelpClickListener = presenter::onFinalAverageHelpClick + } + + with(binding.gradeSummaryRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = gradeSummaryAdapter + } + with(binding) { + gradeSummarySwipe.setOnRefreshListener(presenter::onSwipeRefresh) + gradeSummarySwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) + gradeSummarySwipe.setProgressBackgroundColorSchemeColor( + requireContext().getThemeAttrColor( + R.attr.colorSwipeRefresh + ) + ) + gradeSummaryErrorRetry.setOnClickListener { presenter.onRetry() } + gradeSummaryErrorDetails.setOnClickListener { presenter.onDetailsClick() } + } + } + + override fun updateData(data: List) { + with(gradeSummaryAdapter) { + items = data + notifyDataSetChanged() + } + } + + override fun clearView() { + with(gradeSummaryAdapter) { + items = emptyList() + notifyDataSetChanged() + } + } + + override fun resetView() { + binding.gradeSummaryRecycler.scrollToPosition(0) + } + + override fun showContent(show: Boolean) { + binding.gradeSummaryRecycler.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showEmpty(show: Boolean) { + binding.gradeSummaryEmpty.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showErrorView(show: Boolean) { + binding.gradeSummaryError.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun setErrorDetails(message: String) { + binding.gradeSummaryErrorMessage.text = message + } + + override fun showProgress(show: Boolean) { + binding.gradeSummaryProgress.visibility = if (show) VISIBLE else GONE + } + + override fun enableSwipe(enable: Boolean) { + binding.gradeSummarySwipe.isEnabled = enable + } + + override fun showRefresh(show: Boolean) { + binding.gradeSummarySwipe.isRefreshing = show + } + + override fun showCalculatedAverageHelpDialog() { + AlertDialog.Builder(requireContext()) + .setTitle(R.string.grade_summary_calculated_average_help_dialog_title) + .setMessage(R.string.grade_summary_calculated_average_help_dialog_message) + .setPositiveButton(R.string.all_close) { _, _ -> } + .show() + } + + override fun showFinalAverageHelpDialog() { + AlertDialog.Builder(requireContext()) + .setTitle(R.string.grade_summary_final_average_help_dialog_title) + .setMessage(R.string.grade_summary_final_average_help_dialog_message) + .setPositiveButton(R.string.all_close) { _, _ -> } + .show() + } + + override fun onParentLoadData(semesterId: Int, forceRefresh: Boolean) { + presenter.onParentViewLoadData(semesterId, forceRefresh) + } + + override fun onParentReselected() { + presenter.onParentViewReselected() + } + + override fun onParentChangeSemester() { + presenter.onParentViewChangeSemester() + } + + override fun notifyParentDataLoaded(semesterId: Int) { + (parentFragment as? GradeFragment)?.onChildFragmentLoaded(semesterId) + } + + override fun notifyParentRefresh() { + (parentFragment as? GradeFragment)?.onChildRefresh() + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt new file mode 100644 index 000000000..b07570cb2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt @@ -0,0 +1,142 @@ +package io.github.wulkanowy.ui.modules.grade.summary + +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.db.entities.GradeSummary +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider +import io.github.wulkanowy.ui.modules.grade.GradeSubject +import io.github.wulkanowy.utils.AnalyticsHelper +import timber.log.Timber +import javax.inject.Inject + +class GradeSummaryPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val averageProvider: GradeAverageProvider, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + private lateinit var lastError: Throwable + + override fun onAttachView(view: GradeSummaryView) { + super.onAttachView(view) + view.initView() + errorHandler.showErrorMessage = ::showErrorViewOnError + } + + fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) { + Timber.i("Loading grade summary data started") + + loadData(semesterId, forceRefresh) + if (!forceRefresh) view?.showErrorView(false) + } + + private fun loadData(semesterId: Int, forceRefresh: Boolean) { + flatResourceFlow { + val student = studentRepository.getCurrentStudent() + averageProvider.getGradesDetailsWithAverage(student, semesterId, forceRefresh) + } + .logResourceStatus("load grade summary", showData = true) + .mapResourceData { createGradeSummaryItems(it) } + .onResourceData { + view?.run { + enableSwipe(true) + showProgress(false) + showErrorView(false) + showContent(it.isNotEmpty()) + showEmpty(it.isEmpty()) + updateData(it) + } + } + .onResourceIntermediate { view?.showRefresh(true) } + .onResourceSuccess { + analytics.logEvent( + "load_data", + "type" to "grade_summary", + "items" to it.size + ) + } + .onResourceNotLoading { + view?.run { + enableSwipe(true) + showRefresh(false) + showProgress(false) + notifyParentDataLoaded(semesterId) + } + } + .onResourceError(errorHandler::dispatch) + .launch() + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the grade summary") + view?.notifyParentRefresh() + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + view?.notifyParentRefresh() + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + fun onParentViewReselected() { + view?.run { + if (!isViewEmpty) resetView() + } + } + + fun onParentViewChangeSemester() { + view?.run { + showProgress(true) + enableSwipe(false) + showRefresh(false) + showContent(false) + showEmpty(false) + clearView() + } + cancelJobs("load") + } + + fun onCalculatedAverageHelpClick() { + view?.showCalculatedAverageHelpDialog() + } + + fun onFinalAverageHelpClick() { + view?.showFinalAverageHelpDialog() + } + + private fun createGradeSummaryItems(items: List): List { + return items + .filter { !checkEmpty(it) } + .sortedBy { it.subject } + .map { it.summary.copy(average = it.average) } + } + + private fun checkEmpty(gradeSummary: GradeSubject): Boolean { + return gradeSummary.run { + summary.finalGrade.isBlank() + && summary.predictedGrade.isBlank() + && average == .0 + && points.isBlank() + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt new file mode 100644 index 000000000..156731c31 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt @@ -0,0 +1,43 @@ +package io.github.wulkanowy.ui.modules.grade.summary + +import io.github.wulkanowy.data.db.entities.GradeSummary +import io.github.wulkanowy.ui.base.BaseView + +interface GradeSummaryView : BaseView { + + val isViewEmpty: Boolean + + val predictedString: String + + val finalString: String + + fun initView() + + fun updateData(data: List) + + fun resetView() + + fun clearView() + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showRefresh(show: Boolean) + + fun showContent(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun showEmpty(show: Boolean) + + fun showCalculatedAverageHelpDialog() + + fun showFinalAverageHelpDialog() + + fun notifyParentDataLoaded(semesterId: Int) + + fun notifyParentRefresh() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkAdapter.kt new file mode 100644 index 000000000..9dcd3d4a2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkAdapter.kt @@ -0,0 +1,68 @@ +package io.github.wulkanowy.ui.modules.homework + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.databinding.HeaderHomeworkBinding +import io.github.wulkanowy.databinding.ItemHomeworkBinding +import io.github.wulkanowy.utils.capitalise +import io.github.wulkanowy.utils.toFormattedString +import io.github.wulkanowy.utils.weekDayName +import java.time.LocalDate +import javax.inject.Inject + +class HomeworkAdapter @Inject constructor() : RecyclerView.Adapter() { + + var items = emptyList>() + + var onClickListener: (Homework) -> Unit = {} + + override fun getItemCount() = items.size + + override fun getItemViewType(position: Int) = items[position].viewType.id + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + + return when (viewType) { + HomeworkItem.ViewType.HEADER.id -> HeaderViewHolder(HeaderHomeworkBinding.inflate(inflater, parent, false)) + HomeworkItem.ViewType.ITEM.id -> ItemViewHolder(ItemHomeworkBinding.inflate(inflater, parent, false)) + else -> throw IllegalStateException() + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is HeaderViewHolder -> bindHeaderViewHolder(holder.binding, items[position].value as LocalDate) + is ItemViewHolder -> bindItemViewHolder(holder.binding, items[position].value as Homework) + } + } + + @SuppressLint("DefaultLocale") + private fun bindHeaderViewHolder(binding: HeaderHomeworkBinding, date: LocalDate) { + with(binding) { + homeworkHeaderDay.text = date.weekDayName.capitalise() + homeworkHeaderDate.text = date.toFormattedString() + } + } + + private fun bindItemViewHolder(binding: ItemHomeworkBinding, homework: Homework) { + with(binding) { + homeworkItemSubject.text = homework.subject + homeworkItemTeacher.text = homework.teacher + homeworkItemContent.text = homework.content + homeworkItemCheckImage.visibility = if (homework.isDone) View.VISIBLE else View.GONE + homeworkItemAttachmentImage.visibility = if (!homework.isDone && homework.attachments.isNotEmpty()) View.VISIBLE else View.GONE + + root.setOnClickListener { onClickListener(homework) } + } + } + + class HeaderViewHolder(val binding: HeaderHomeworkBinding) : + RecyclerView.ViewHolder(binding.root) + + class ItemViewHolder(val binding: ItemHomeworkBinding) : RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt new file mode 100644 index 000000000..d4eaade2c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt @@ -0,0 +1,145 @@ +package io.github.wulkanowy.ui.modules.homework + +import android.os.Bundle +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.databinding.FragmentHomeworkBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.homework.add.HomeworkAddDialog +import io.github.wulkanowy.ui.modules.homework.details.HomeworkDetailsDialog +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.widgets.DividerItemDecoration +import io.github.wulkanowy.utils.dpToPx +import io.github.wulkanowy.utils.getThemeAttrColor +import javax.inject.Inject + +@AndroidEntryPoint +class HomeworkFragment : BaseFragment(R.layout.fragment_homework), + HomeworkView, MainView.TitledView { + + @Inject + lateinit var presenter: HomeworkPresenter + + @Inject + lateinit var homeworkAdapter: HomeworkAdapter + + companion object { + private const val SAVED_DATE_KEY = "CURRENT_DATE" + + fun newInstance() = HomeworkFragment() + } + + override val titleStringId get() = R.string.homework_title + + override val isViewEmpty get() = homeworkAdapter.items.isEmpty() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentHomeworkBinding.bind(view) + messageContainer = binding.homeworkRecycler + presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY)) + } + + override fun initView() { + homeworkAdapter.onClickListener = presenter::onHomeworkItemSelected + + with(binding.homeworkRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = homeworkAdapter + addItemDecoration(DividerItemDecoration(context)) + } + + with(binding) { + homeworkSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + homeworkSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) + homeworkSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh)) + homeworkErrorRetry.setOnClickListener { presenter.onRetry() } + homeworkErrorDetails.setOnClickListener { presenter.onDetailsClick() } + + homeworkPreviousButton.setOnClickListener { presenter.onPreviousDay() } + homeworkNextButton.setOnClickListener { presenter.onNextDay() } + + openAddHomeworkButton.setOnClickListener { presenter.onHomeworkAddButtonClicked() } + + homeworkNavContainer.elevation = requireContext().dpToPx(8f) + } + } + + override fun updateData(data: List>) { + with(homeworkAdapter) { + items = data + notifyDataSetChanged() + } + } + + override fun clearData() { + with(homeworkAdapter) { + items = emptyList() + notifyDataSetChanged() + } + } + + override fun updateNavigationWeek(date: String) { + binding.homeworkNavDate.text = date + } + + override fun showRefresh(show: Boolean) { + binding.homeworkSwipe.isRefreshing = show + } + + override fun showEmpty(show: Boolean) { + binding.homeworkEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showErrorView(show: Boolean) { + binding.homeworkError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + binding.homeworkErrorMessage.text = message + } + + override fun showProgress(show: Boolean) { + binding.homeworkProgress.visibility = if (show) VISIBLE else GONE + } + + override fun enableSwipe(enable: Boolean) { + binding.homeworkSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + binding.homeworkRecycler.visibility = if (show) VISIBLE else GONE + } + + override fun showPreButton(show: Boolean) { + binding.homeworkPreviousButton.visibility = if (show) VISIBLE else View.INVISIBLE + } + + override fun showNextButton(show: Boolean) { + binding.homeworkNextButton.visibility = if (show) VISIBLE else View.INVISIBLE + } + + override fun showHomeworkDialog(homework: Homework) { + (activity as? MainActivity)?.showDialogFragment(HomeworkDetailsDialog.newInstance(homework)) + } + + override fun showAddHomeworkDialog() { + (activity as? MainActivity)?.showDialogFragment(HomeworkAddDialog()) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt new file mode 100644 index 000000000..7e0039583 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt @@ -0,0 +1,9 @@ +package io.github.wulkanowy.ui.modules.homework + +data class HomeworkItem(val value: T, val viewType: ViewType) { + + enum class ViewType(val id: Int) { + HEADER(1), + ITEM(2) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt new file mode 100644 index 000000000..2ac552b41 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt @@ -0,0 +1,184 @@ +package io.github.wulkanowy.ui.modules.homework + +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.data.repositories.HomeworkRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.* +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.onEach +import timber.log.Timber +import java.time.LocalDate +import java.time.LocalDate.ofEpochDay +import javax.inject.Inject + +class HomeworkPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val homeworkRepository: HomeworkRepository, + private val semesterRepository: SemesterRepository, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + private var baseDate: LocalDate = LocalDate.now().nextOrSameSchoolDay + + lateinit var currentDate: LocalDate + private set + + private lateinit var lastError: Throwable + + fun onAttachView(view: HomeworkView, date: Long?) { + super.onAttachView(view) + view.initView() + Timber.i("Homework view was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError + reloadView(ofEpochDay(date ?: baseDate.toEpochDay())) + loadData() + if (currentDate.isHolidays) setBaseDateOnHolidays() + } + + fun onPreviousDay() { + reloadView(currentDate.minusDays(7)) + loadData() + } + + fun onNextDay() { + reloadView(currentDate.plusDays(7)) + loadData() + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the homework") + loadData(true) + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + fun onHomeworkItemSelected(homework: Homework) { + Timber.i("Select homework item ${homework.id}") + view?.showHomeworkDialog(homework) + } + + fun onHomeworkAddButtonClicked() { + view?.showAddHomeworkDialog() + } + + private fun setBaseDateOnHolidays() { + flow { + val student = studentRepository.getCurrentStudent() + emit(semesterRepository.getCurrentSemester(student)) + } + .catch { Timber.i("Loading semester result: An exception occurred") } + .onEach { + baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear) + currentDate = baseDate + reloadNavigation() + } + .launch("holidays") + } + + private fun loadData(forceRefresh: Boolean = false) { + Timber.i("Loading homework data started") + + flatResourceFlow { + val student = studentRepository.getCurrentStudent() + val semester = semesterRepository.getCurrentSemester(student) + homeworkRepository.getHomework( + student = student, + semester = semester, + start = currentDate, + end = currentDate, + forceRefresh = forceRefresh + ) + } + .logResourceStatus("loading homework") + .mapResourceData { createHomeworkItem(it) } + .onResourceData { + view?.run { + enableSwipe(true) + showProgress(false) + showErrorView(false) + showContent(it.isNotEmpty()) + showEmpty(it.isEmpty()) + updateData(it) + } + } + .onResourceIntermediate { view?.showRefresh(true) } + .onResourceSuccess { + analytics.logEvent( + "load_data", + "type" to "homework", + "items" to it.size + ) + } + .onResourceNotLoading { + view?.run { + enableSwipe(true) + showProgress(false) + showRefresh(false) + } + } + .onResourceError(errorHandler::dispatch) + .launch() + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + + private fun createHomeworkItem(items: List): List> { + return items.groupBy { it.date }.toSortedMap().map { (date, exams) -> + listOf(HomeworkItem(date, HomeworkItem.ViewType.HEADER)) + exams.reversed() + .map { exam -> + HomeworkItem(exam, HomeworkItem.ViewType.ITEM) + } + }.flatten() + } + + private fun reloadView(date: LocalDate) { + currentDate = date + + Timber.i("Reload homework view with the date ${currentDate.toFormattedString()}") + view?.apply { + showProgress(true) + enableSwipe(false) + showContent(false) + showEmpty(false) + showErrorView(false) + clearData() + reloadNavigation() + } + } + + private fun reloadNavigation() { + view?.apply { + showPreButton(!currentDate.minusDays(7).isHolidays) + showNextButton(!currentDate.plusDays(7).isHolidays) + updateNavigationWeek( + "${currentDate.monday.toFormattedString("dd.MM")} - " + + currentDate.sunday.toFormattedString("dd.MM") + ) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt new file mode 100644 index 000000000..7c05ab865 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt @@ -0,0 +1,39 @@ +package io.github.wulkanowy.ui.modules.homework + +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.ui.base.BaseView + +interface HomeworkView : BaseView { + + val isViewEmpty: Boolean + + fun initView() + + fun updateData(data: List>) + + fun clearData() + + fun updateNavigationWeek(date: String) + + fun showRefresh(show: Boolean) + + fun showEmpty(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun showPreButton(show: Boolean) + + fun showNextButton(show: Boolean) + + fun showHomeworkDialog(homework: Homework) + + fun showAddHomeworkDialog() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddDialog.kt new file mode 100644 index 000000000..c2aff2b13 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddDialog.kt @@ -0,0 +1,115 @@ +package io.github.wulkanowy.ui.modules.homework.add + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.widget.doOnTextChanged +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.databinding.DialogHomeworkAddBinding +import io.github.wulkanowy.ui.base.BaseDialogFragment +import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear +import io.github.wulkanowy.utils.openMaterialDatePicker +import io.github.wulkanowy.utils.toFormattedString +import java.time.LocalDate +import javax.inject.Inject + +@AndroidEntryPoint +class HomeworkAddDialog : BaseDialogFragment(), HomeworkAddView { + + @Inject + lateinit var presenter: HomeworkAddPresenter + + // todo: move it to presenter + private var date: LocalDate? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogHomeworkAddBinding.inflate(inflater).apply { binding = this }.root + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + presenter.onAttachView(this) + } + + override fun initView() { + with(binding) { + homeworkDialogSubjectEdit.doOnTextChanged { _, _, _, _ -> + homeworkDialogSubject.error = null + homeworkDialogSubject.isErrorEnabled = false + } + homeworkDialogDateEdit.doOnTextChanged { _, _, _, _ -> + homeworkDialogDate.error = null + homeworkDialogDate.isErrorEnabled = false + } + homeworkDialogContentEdit.doOnTextChanged { _, _, _, _ -> + homeworkDialogContent.error = null + homeworkDialogContent.isErrorEnabled = false + } + homeworkDialogClose.setOnClickListener { dismiss() } + homeworkDialogDateEdit.setOnClickListener { presenter.showDatePicker(date) } + homeworkDialogAdd.setOnClickListener { + presenter.onAddHomeworkClicked( + subject = homeworkDialogSubjectEdit.text?.toString(), + teacher = homeworkDialogTeacherEdit.text?.toString(), + date = homeworkDialogDateEdit.text?.toString(), + content = homeworkDialogContentEdit.text?.toString() + ) + } + } + } + + override fun showSuccessMessage() { + showMessage(getString(R.string.homework_add_success)) + } + + override fun setErrorSubjectRequired() { + with(binding.homeworkDialogSubject) { + isErrorEnabled = true + error = getString(R.string.error_field_required) + } + } + + override fun setErrorDateRequired() { + with(binding.homeworkDialogDate) { + isErrorEnabled = true + error = getString(R.string.error_field_required) + } + } + + override fun setErrorContentRequired() { + with(binding.homeworkDialogContent) { + isErrorEnabled = true + error = getString(R.string.error_field_required) + } + } + + override fun closeDialog() { + dismiss() + } + + override fun showDatePickerDialog(selectedDate: LocalDate) { + openMaterialDatePicker( + selected = selectedDate, + rangeStart = LocalDate.now(), + rangeEnd = LocalDate.now().lastSchoolDayInSchoolYear, + onDateSelected = { + date = it + binding.homeworkDialogDate.editText?.setText(date!!.toFormattedString()) + } + ) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddPresenter.kt new file mode 100644 index 000000000..a21f6aef7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddPresenter.kt @@ -0,0 +1,87 @@ +package io.github.wulkanowy.ui.modules.homework.add + +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.data.logResourceStatus +import io.github.wulkanowy.data.onResourceError +import io.github.wulkanowy.data.onResourceSuccess +import io.github.wulkanowy.data.repositories.HomeworkRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.resourceFlow +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.toLocalDate +import timber.log.Timber +import java.time.LocalDate +import javax.inject.Inject + +class HomeworkAddPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val homeworkRepository: HomeworkRepository, + private val semesterRepository: SemesterRepository +) : BasePresenter(errorHandler, studentRepository) { + + override fun onAttachView(view: HomeworkAddView) { + super.onAttachView(view) + view.initView() + Timber.i("Homework details view was initialized") + } + + fun showDatePicker(date: LocalDate?) { + view?.showDatePickerDialog(date ?: LocalDate.now()) + } + + fun onAddHomeworkClicked(subject: String?, teacher: String?, date: String?, content: String?) { + var isError = false + + if (subject.isNullOrBlank()) { + view?.setErrorSubjectRequired() + isError = true + } + + if (date.isNullOrBlank()) { + view?.setErrorDateRequired() + isError = true + } + + if (content.isNullOrBlank()) { + view?.setErrorContentRequired() + isError = true + } + + if (!isError) { + saveHomework(subject!!, teacher.orEmpty(), date!!.toLocalDate(), content!!) + } + } + + private fun saveHomework(subject: String, teacher: String, date: LocalDate, content: String) { + resourceFlow { + val student = studentRepository.getCurrentStudent() + val semester = semesterRepository.getCurrentSemester(student) + val entryDate = LocalDate.now() + homeworkRepository.saveHomework( + Homework( + semesterId = semester.semesterId, + studentId = student.studentId, + date = date, + entryDate = entryDate, + subject = subject, + content = content, + teacher = teacher, + teacherSymbol = "", + attachments = emptyList(), + ).apply { isAddedByUser = true } + ) + } + .logResourceStatus("homework insert") + .onResourceSuccess { + view?.run { + showSuccessMessage() + closeDialog() + } + } + .onResourceError(errorHandler::dispatch) + .launch("add_homework") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddView.kt new file mode 100644 index 000000000..91414ae2f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddView.kt @@ -0,0 +1,21 @@ +package io.github.wulkanowy.ui.modules.homework.add + +import io.github.wulkanowy.ui.base.BaseView +import java.time.LocalDate + +interface HomeworkAddView : BaseView { + + fun initView() + + fun showSuccessMessage() + + fun setErrorSubjectRequired() + + fun setErrorDateRequired() + + fun setErrorContentRequired() + + fun closeDialog() + + fun showDatePickerDialog(selectedDate: LocalDate) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsAdapter.kt new file mode 100644 index 000000000..e03707a5c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsAdapter.kt @@ -0,0 +1,122 @@ +package io.github.wulkanowy.ui.modules.homework.details + +import android.view.LayoutInflater +import android.view.View.GONE +import android.view.View.VISIBLE +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.databinding.ItemHomeworkDialogAttachmentBinding +import io.github.wulkanowy.databinding.ItemHomeworkDialogAttachmentsHeaderBinding +import io.github.wulkanowy.databinding.ItemHomeworkDialogDetailsBinding +import io.github.wulkanowy.utils.ifNullOrBlank +import io.github.wulkanowy.utils.toFormattedString +import javax.inject.Inject + +class HomeworkDetailsAdapter @Inject constructor() : + RecyclerView.Adapter() { + + private enum class ViewType(val id: Int) { + DETAILS(1), + ATTACHMENTS_HEADER(2), + ATTACHMENT(3) + } + + private var attachments = emptyList>() + + var homework: Homework? = null + set(value) { + field = value + attachments = value?.attachments.orEmpty() + } + + var isHomeworkFullscreen = false + + var onAttachmentClickListener: (url: String) -> Unit = {} + + var onFullScreenClickListener = {} + + var onFullScreenExitClickListener = {} + + var onDeleteClickListener: (homework: Homework) -> Unit = {} + + override fun getItemCount() = 1 + if (attachments.isNotEmpty()) attachments.size + 1 else 0 + + override fun getItemViewType(position: Int) = when (position) { + 0 -> ViewType.DETAILS.id + 1 -> ViewType.ATTACHMENTS_HEADER.id + else -> ViewType.ATTACHMENT.id + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + + return when (viewType) { + ViewType.ATTACHMENTS_HEADER.id -> AttachmentsHeaderViewHolder( + ItemHomeworkDialogAttachmentsHeaderBinding.inflate(inflater, parent, false) + ) + ViewType.ATTACHMENT.id -> AttachmentViewHolder( + ItemHomeworkDialogAttachmentBinding.inflate(inflater, parent, false) + ) + else -> DetailsViewHolder( + ItemHomeworkDialogDetailsBinding.inflate(inflater, parent, false) + ) + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is DetailsViewHolder -> bindDetailsViewHolder(holder) + is AttachmentViewHolder -> bindAttachmentViewHolder(holder, position - 2) + } + } + + private fun bindDetailsViewHolder(holder: DetailsViewHolder) { + val noDataString = holder.binding.root.context.getString(R.string.all_no_data) + + with(holder.binding) { + homeworkDialogDate.text = homework?.date?.toFormattedString() + homeworkDialogEntryDate.text = homework?.entryDate?.toFormattedString() + homeworkDialogSubject.text = homework?.subject.ifNullOrBlank { noDataString } + homeworkDialogTeacher.text = homework?.teacher.ifNullOrBlank { noDataString } + homeworkDialogContent.text = homework?.content.ifNullOrBlank { noDataString } + homeworkDialogDelete.visibility = if (homework?.isAddedByUser == true) VISIBLE else GONE + homeworkDialogFullScreen.visibility = if (isHomeworkFullscreen) GONE else VISIBLE + homeworkDialogFullScreenExit.visibility = if (isHomeworkFullscreen) VISIBLE else GONE + homeworkDialogFullScreen.setOnClickListener { + homeworkDialogFullScreen.visibility = GONE + homeworkDialogFullScreenExit.visibility = VISIBLE + onFullScreenClickListener() + } + homeworkDialogFullScreenExit.setOnClickListener { + homeworkDialogFullScreen.visibility = VISIBLE + homeworkDialogFullScreenExit.visibility = GONE + onFullScreenExitClickListener() + } + homeworkDialogDelete.setOnClickListener { + onDeleteClickListener(homework!!) + } + } + } + + private fun bindAttachmentViewHolder(holder: AttachmentViewHolder, position: Int) { + val item = attachments[position] + + with(holder.binding) { + homeworkDialogAttachment.text = item.second + root.setOnClickListener { + onAttachmentClickListener(item.first) + } + } + } + + class DetailsViewHolder(val binding: ItemHomeworkDialogDetailsBinding) : + RecyclerView.ViewHolder(binding.root) + + class AttachmentsHeaderViewHolder(val binding: ItemHomeworkDialogAttachmentsHeaderBinding) : + RecyclerView.ViewHolder(binding.root) + + class AttachmentViewHolder(val binding: ItemHomeworkDialogAttachmentBinding) : + RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsDialog.kt new file mode 100644 index 000000000..f9d463510 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsDialog.kt @@ -0,0 +1,108 @@ +package io.github.wulkanowy.ui.modules.homework.details + +import android.annotation.SuppressLint +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.ViewGroup.LayoutParams.MATCH_PARENT +import android.view.ViewGroup.LayoutParams.WRAP_CONTENT +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.databinding.DialogHomeworkBinding +import io.github.wulkanowy.ui.base.BaseDialogFragment +import io.github.wulkanowy.utils.openInternetBrowser +import javax.inject.Inject + +@AndroidEntryPoint +class HomeworkDetailsDialog : BaseDialogFragment(), HomeworkDetailsView { + + @Inject + lateinit var presenter: HomeworkDetailsPresenter + + @Inject + lateinit var detailsAdapter: HomeworkDetailsAdapter + + override val homeworkDeleteSuccess: String + get() = getString(R.string.homework_delete_success) + + private lateinit var homework: Homework + + companion object { + + private const val ARGUMENT_KEY = "Item" + + fun newInstance(homework: Homework) = HomeworkDetailsDialog().apply { + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, homework) } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + homework = getSerializable(ARGUMENT_KEY) as Homework + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogHomeworkBinding.inflate(inflater).apply { binding = this }.root + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + presenter.onAttachView(this) + } + + @SuppressLint("SetTextI18n") + override fun initView() { + with(binding) { + homeworkDialogRead.text = + view?.context?.getString(if (homework.isDone) R.string.homework_mark_as_undone else R.string.homework_mark_as_done) + homeworkDialogRead.setOnClickListener { presenter.toggleDone(homework) } + homeworkDialogClose.setOnClickListener { dismiss() } + } + + if (presenter.isHomeworkFullscreen) { + dialog?.window?.setLayout(MATCH_PARENT, MATCH_PARENT) + } else { + dialog?.window?.setLayout(WRAP_CONTENT, WRAP_CONTENT) + } + + with(binding.homeworkDialogRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = detailsAdapter.apply { + onAttachmentClickListener = { context.openInternetBrowser(it, ::showMessage) } + onFullScreenClickListener = { + dialog?.window?.setLayout(MATCH_PARENT, MATCH_PARENT) + presenter.isHomeworkFullscreen = true + } + onFullScreenExitClickListener = { + dialog?.window?.setLayout(WRAP_CONTENT, WRAP_CONTENT) + presenter.isHomeworkFullscreen = false + } + onDeleteClickListener = { homework -> presenter.deleteHomework(homework) } + isHomeworkFullscreen = presenter.isHomeworkFullscreen + homework = this@HomeworkDetailsDialog.homework + } + } + } + + override fun closeDialog() { + dismiss() + } + + override fun updateMarkAsDoneLabel(isDone: Boolean) { + binding.homeworkDialogRead.text = + view?.context?.getString(if (isDone) R.string.homework_mark_as_undone else R.string.homework_mark_as_done) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsPresenter.kt new file mode 100644 index 000000000..e76df6bd0 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsPresenter.kt @@ -0,0 +1,60 @@ +package io.github.wulkanowy.ui.modules.homework.details + +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.data.logResourceStatus +import io.github.wulkanowy.data.onResourceError +import io.github.wulkanowy.data.onResourceSuccess +import io.github.wulkanowy.data.repositories.HomeworkRepository +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.resourceFlow +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import timber.log.Timber +import javax.inject.Inject + +class HomeworkDetailsPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val homeworkRepository: HomeworkRepository, + private val analytics: AnalyticsHelper, + private val preferencesRepository: PreferencesRepository +) : BasePresenter(errorHandler, studentRepository) { + + var isHomeworkFullscreen + get() = preferencesRepository.isHomeworkFullscreen + set(value) { + preferencesRepository.isHomeworkFullscreen = value + } + + override fun onAttachView(view: HomeworkDetailsView) { + super.onAttachView(view) + view.initView() + Timber.i("Homework details view was initialized") + } + + fun deleteHomework(homework: Homework) { + resourceFlow { homeworkRepository.deleteHomework(homework) } + .logResourceStatus("homework delete") + .onResourceSuccess { + view?.run { + showMessage(homeworkDeleteSuccess) + closeDialog() + } + } + .onResourceError(errorHandler::dispatch) + .launch("delete") + } + + fun toggleDone(homework: Homework) { + resourceFlow { homeworkRepository.toggleDone(homework) } + .logResourceStatus("homework details update") + .onResourceSuccess { + view?.updateMarkAsDoneLabel(homework.isDone) + analytics.logEvent("homework_mark_as_done") + } + .onResourceError(errorHandler::dispatch) + .launch("toggle") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsView.kt new file mode 100644 index 000000000..4a47de43b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsView.kt @@ -0,0 +1,14 @@ +package io.github.wulkanowy.ui.modules.homework.details + +import io.github.wulkanowy.ui.base.BaseView + +interface HomeworkDetailsView : BaseView { + + val homeworkDeleteSuccess: String + + fun initView() + + fun closeDialog() + + fun updateMarkAsDoneLabel(isDone: Boolean) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt new file mode 100644 index 000000000..d7d77f73d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt @@ -0,0 +1,101 @@ +package io.github.wulkanowy.ui.modules.login + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.MenuItem +import androidx.fragment.app.Fragment +import androidx.fragment.app.commit +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.databinding.ActivityLoginBinding +import io.github.wulkanowy.ui.base.BaseActivity +import io.github.wulkanowy.ui.modules.login.advanced.LoginAdvancedFragment +import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment +import io.github.wulkanowy.ui.modules.login.recover.LoginRecoverFragment +import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment +import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment +import io.github.wulkanowy.utils.UpdateHelper +import javax.inject.Inject + +@AndroidEntryPoint +class LoginActivity : BaseActivity(), LoginView { + + @Inject + override lateinit var presenter: LoginPresenter + + @Inject + lateinit var updateHelper: UpdateHelper + + companion object { + fun getStartIntent(context: Context) = Intent(context, LoginActivity::class.java) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(ActivityLoginBinding.inflate(layoutInflater).apply { binding = this }.root) + setSupportActionBar(binding.loginToolbar) + messageContainer = binding.loginContainer + updateHelper.messageContainer = binding.loginContainer + + presenter.onAttachView(this) + updateHelper.checkAndInstallUpdates(this) + + if (savedInstanceState == null) { + openFragment(LoginFormFragment.newInstance(), clearBackStack = true) + } + } + + override fun initView() { + with(requireNotNull(supportActionBar)) { + setDisplayHomeAsUpEnabled(true) + setDisplayShowTitleEnabled(false) + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == android.R.id.home) onBackPressed() + return true + } + + fun showActionBar(show: Boolean) { + supportActionBar?.run { if (show) show() else hide() } + } + + fun navigateToSymbolFragment(loginData: LoginData) { + openFragment(LoginSymbolFragment.newInstance(loginData)) + } + + fun navigateToStudentSelect(studentsWithSemesters: List) { + openFragment(LoginStudentSelectFragment.newInstance(studentsWithSemesters)) + } + + fun onAdvancedLoginClick() { + openFragment(LoginAdvancedFragment.newInstance()) + } + + fun onRecoverClick() { + openFragment(LoginRecoverFragment.newInstance()) + } + + private fun openFragment(fragment: Fragment, clearBackStack: Boolean = false) { + supportFragmentManager.commit { + replace(R.id.loginContainer, fragment) + setReorderingAllowed(true) + if (!clearBackStack) addToBackStack(fragment::class.java.name) + } + } + + override fun onResume() { + super.onResume() + updateHelper.onResume(this) + } + + //https://developer.android.com/guide/playcore/in-app-updates#status_callback + @Suppress("DEPRECATION") + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + updateHelper.onActivityResult(requestCode, resultCode) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginData.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginData.kt new file mode 100644 index 000000000..5d4743589 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginData.kt @@ -0,0 +1,9 @@ +package io.github.wulkanowy.ui.modules.login + +import java.io.Serializable + +data class LoginData( + val login: String, + val password: String, + val baseUrl: String, +) : Serializable diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt new file mode 100644 index 000000000..37ab71dce --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt @@ -0,0 +1,50 @@ +package io.github.wulkanowy.ui.modules.login + +import android.content.Context +import android.database.sqlite.SQLiteConstraintException +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import io.github.wulkanowy.sdk.mobile.exception.InvalidPinException +import io.github.wulkanowy.sdk.mobile.exception.InvalidSymbolException +import io.github.wulkanowy.sdk.mobile.exception.InvalidTokenException +import io.github.wulkanowy.sdk.mobile.exception.TokenDeadException +import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException +import io.github.wulkanowy.ui.base.ErrorHandler +import javax.inject.Inject + +class LoginErrorHandler @Inject constructor( + @ApplicationContext context: Context, +) : ErrorHandler(context) { + + var onBadCredentials: (String?) -> Unit = {} + + var onInvalidToken: (String) -> Unit = {} + + var onInvalidPin: (String) -> Unit = {} + + var onInvalidSymbol: (String) -> Unit = {} + + var onStudentDuplicate: (String) -> Unit = {} + + override fun proceed(error: Throwable) { + val resources = context.resources + when (error) { + is BadCredentialsException -> onBadCredentials(error.message) + is SQLiteConstraintException -> onStudentDuplicate(resources.getString(R.string.login_duplicate_student)) + is TokenDeadException -> onInvalidToken(resources.getString(R.string.login_expired_token)) + is InvalidTokenException -> onInvalidToken(resources.getString(R.string.login_invalid_token)) + is InvalidPinException -> onInvalidPin(resources.getString(R.string.login_invalid_pin)) + is InvalidSymbolException -> onInvalidSymbol(resources.getString(R.string.login_invalid_symbol)) + else -> super.proceed(error) + } + } + + override fun clear() { + super.clear() + onBadCredentials = {} + onStudentDuplicate = {} + onInvalidToken = {} + onInvalidPin = {} + onInvalidSymbol = {} + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt new file mode 100644 index 000000000..9031cb8ab --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt @@ -0,0 +1,19 @@ +package io.github.wulkanowy.ui.modules.login + +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import timber.log.Timber +import javax.inject.Inject + +class LoginPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository +) : BasePresenter(errorHandler, studentRepository) { + + override fun onAttachView(view: LoginView) { + super.onAttachView(view) + view.initView() + Timber.i("Login view was initialized") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginView.kt new file mode 100644 index 000000000..a0949e6d9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginView.kt @@ -0,0 +1,8 @@ +package io.github.wulkanowy.ui.modules.login + +import io.github.wulkanowy.ui.base.BaseView + +interface LoginView : BaseView { + + fun initView() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt new file mode 100644 index 000000000..37dcb38b3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt @@ -0,0 +1,343 @@ +package io.github.wulkanowy.ui.modules.login.advanced + +import android.os.Bundle +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import android.widget.ArrayAdapter +import androidx.core.widget.doOnTextChanged +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.databinding.FragmentLoginAdvancedBinding +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.login.LoginActivity +import io.github.wulkanowy.ui.modules.login.LoginData +import io.github.wulkanowy.ui.modules.login.form.LoginSymbolAdapter +import io.github.wulkanowy.utils.hideSoftInput +import io.github.wulkanowy.utils.setOnEditorDoneSignIn +import io.github.wulkanowy.utils.showSoftInput +import javax.inject.Inject + +@AndroidEntryPoint +class LoginAdvancedFragment : + BaseFragment(R.layout.fragment_login_advanced), + LoginAdvancedView { + + @Inject + lateinit var presenter: LoginAdvancedPresenter + + companion object { + fun newInstance() = LoginAdvancedFragment() + } + + override val formLoginType: String + get() = when (binding.loginTypeSwitch.checkedRadioButtonId) { + R.id.loginTypeApi -> "API" + R.id.loginTypeScrapper -> "SCRAPPER" + else -> "HYBRID" + } + + override val formUsernameValue: String + get() = binding.loginFormUsername.text.toString().trim() + + override val formPassValue: String + get() = binding.loginFormPass.text.toString().trim() + + private lateinit var hostKeys: Array + + private lateinit var hostValues: Array + + private lateinit var hostSymbols: Array + + override val formHostValue: String + get() = hostValues.getOrNull(hostKeys.indexOf(binding.loginFormHost.text.toString())) + .orEmpty() + + override val formHostSymbol: String + get() = hostSymbols.getOrNull(hostKeys.indexOf(binding.loginFormHost.text.toString())) + .orEmpty() + + override val formPinValue: String + get() = binding.loginFormPin.text.toString().trim() + + override val formSymbolValue: String + get() = binding.loginFormSymbol.text.toString().trim() + + override val formTokenValue: String + get() = binding.loginFormToken.text.toString().trim() + + override val nicknameLabel: String + get() = getString(R.string.login_nickname_hint) + + override val emailLabel: String + get() = getString(R.string.login_email_hint) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentLoginAdvancedBinding.bind(view) + presenter.onAttachView(this) + } + + override fun initView() { + (requireActivity() as LoginActivity).showActionBar(true) + + hostKeys = resources.getStringArray(R.array.hosts_keys) + hostValues = resources.getStringArray(R.array.hosts_values) + hostSymbols = resources.getStringArray(R.array.hosts_symbols) + + with(binding) { + loginFormUsername.doOnTextChanged { _, _, _, _ -> presenter.onUsernameTextChanged() } + loginFormPass.doOnTextChanged { _, _, _, _ -> presenter.onPassTextChanged() } + loginFormPin.doOnTextChanged { _, _, _, _ -> presenter.onPinTextChanged() } + loginFormSymbol.doOnTextChanged { _, _, _, _ -> presenter.onSymbolTextChanged() } + loginFormToken.doOnTextChanged { _, _, _, _ -> presenter.onTokenTextChanged() } + loginFormHost.setOnItemClickListener { _, _, _, _ -> presenter.onHostSelected() } + loginFormSignIn.setOnClickListener { presenter.onSignInClick() } + + loginTypeSwitch.setOnCheckedChangeListener { _, checkedId -> + presenter.onLoginModeSelected( + when (checkedId) { + R.id.loginTypeApi -> Sdk.Mode.API + R.id.loginTypeScrapper -> Sdk.Mode.SCRAPPER + else -> Sdk.Mode.HYBRID + } + ) + } + + loginFormPin.setOnEditorDoneSignIn { loginFormSignIn.callOnClick() } + loginFormPass.setOnEditorDoneSignIn { loginFormSignIn.callOnClick() } + + loginFormSymbol.setAdapter( + ArrayAdapter( + requireContext(), + android.R.layout.simple_list_item_1, + resources.getStringArray(R.array.symbols_values) + ) + ) + } + + with(binding.loginFormHost) { + setText(hostKeys.getOrNull(0).orEmpty()) + setAdapter( + LoginSymbolAdapter( + context, + R.layout.support_simple_spinner_dropdown_item, + hostKeys + ) + ) + setOnClickListener { if (binding.loginFormContainer.visibility == GONE) dismissDropDown() } + } + } + + override fun showMobileApiWarningMessage() { + binding.loginFormAdvancedWarningInfo.text = + getString(R.string.login_advanced_warning_mobile_api) + } + + override fun showScraperWarningMessage() { + binding.loginFormAdvancedWarningInfo.text = + getString(R.string.login_advanced_warning_scraper) + } + + override fun showHybridWarningMessage() { + binding.loginFormAdvancedWarningInfo.text = + getString(R.string.login_advanced_warning_hybrid) + } + + override fun setDefaultCredentials( + username: String, + pass: String, + symbol: String, + token: String, + pin: String + ) { + with(binding) { + loginFormUsername.setText(username) + loginFormPass.setText(pass) + loginFormToken.setText(token) + loginFormSymbol.setText(symbol) + loginFormPin.setText(pin) + } + } + + override fun setUsernameLabel(label: String) { + binding.loginFormUsernameLayout.hint = label + } + + override fun setSymbol(symbol: String) { + binding.loginFormSymbol.setText(symbol) + } + + override fun setErrorUsernameRequired() { + with(binding.loginFormUsernameLayout) { + requestFocus() + error = getString(R.string.error_field_required) + } + } + + override fun setErrorLoginRequired() { + with(binding.loginFormUsernameLayout) { + requestFocus() + error = getString(R.string.login_invalid_login) + } + } + + override fun setErrorEmailRequired() { + with(binding.loginFormUsernameLayout) { + requestFocus() + error = getString(R.string.login_invalid_email) + } + } + + override fun setErrorPassRequired(focus: Boolean) { + with(binding.loginFormPassLayout) { + if (focus) requestFocus() + error = getString(R.string.error_field_required) + } + } + + override fun setErrorPassInvalid(focus: Boolean) { + with(binding.loginFormPassLayout) { + if (focus) requestFocus() + error = getString(R.string.login_invalid_password) + } + } + + override fun setErrorPassIncorrect(message: String?) { + with(binding.loginFormPassLayout) { + requestFocus() + error = message ?: getString(R.string.login_incorrect_password_default) + } + } + + override fun setErrorPinRequired() { + with(binding.loginFormPinLayout) { + requestFocus() + error = getString(R.string.error_field_required) + } + } + + override fun setErrorPinInvalid(message: String) { + with(binding.loginFormPinLayout) { + requestFocus() + error = message + } + } + + override fun setErrorSymbolRequired() { + with(binding.loginFormSymbolLayout) { + requestFocus() + error = getString(R.string.error_field_required) + } + } + + override fun setErrorSymbolInvalid(message: String) { + with(binding.loginFormSymbolLayout) { + requestFocus() + error = message + } + } + + override fun setErrorTokenRequired() { + with(binding.loginFormTokenLayout) { + requestFocus() + error = getString(R.string.error_field_required) + } + } + + override fun setErrorTokenInvalid(message: String) { + with(binding.loginFormTokenLayout) { + requestFocus() + error = message + } + } + + override fun clearUsernameError() { + binding.loginFormUsernameLayout.error = null + } + + override fun clearPassError() { + binding.loginFormPassLayout.error = null + } + + override fun clearPinKeyError() { + binding.loginFormPinLayout.error = null + } + + override fun clearSymbolError() { + binding.loginFormSymbolLayout.error = null + } + + override fun clearTokenError() { + binding.loginFormTokenLayout.error = null + } + + override fun showOnlyHybridModeInputs() { + with(binding) { + loginFormUsernameLayout.visibility = VISIBLE + loginFormPassLayout.visibility = VISIBLE + loginFormHostLayout.visibility = VISIBLE + loginFormPinLayout.visibility = GONE + loginFormSymbolLayout.visibility = VISIBLE + loginFormTokenLayout.visibility = GONE + } + } + + override fun showOnlyScrapperModeInputs() { + with(binding) { + loginFormUsernameLayout.visibility = VISIBLE + loginFormPassLayout.visibility = VISIBLE + loginFormHostLayout.visibility = VISIBLE + loginFormPinLayout.visibility = GONE + loginFormSymbolLayout.visibility = VISIBLE + loginFormTokenLayout.visibility = GONE + } + } + + override fun showOnlyMobileApiModeInputs() { + with(binding) { + loginFormUsernameLayout.visibility = GONE + loginFormPassLayout.visibility = GONE + loginFormHostLayout.visibility = GONE + loginFormPinLayout.visibility = VISIBLE + loginFormSymbolLayout.visibility = VISIBLE + loginFormTokenLayout.visibility = VISIBLE + } + } + + override fun showSoftKeyboard() { + activity?.showSoftInput() + } + + override fun hideSoftKeyboard() { + activity?.hideSoftInput() + } + + override fun showProgress(show: Boolean) { + binding.loginFormProgress.visibility = if (show) VISIBLE else GONE + } + + override fun showContent(show: Boolean) { + binding.loginFormContainer.visibility = if (show) VISIBLE else GONE + } + + override fun navigateToSymbol(loginData: LoginData) { + (activity as? LoginActivity)?.navigateToSymbolFragment(loginData) + } + + override fun navigateToStudentSelect(studentsWithSemesters: List) { + (activity as? LoginActivity)?.navigateToStudentSelect(studentsWithSemesters) + } + + override fun onResume() { + super.onResume() + presenter.updateUsernameLabel() + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt new file mode 100644 index 000000000..1b42c6c52 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt @@ -0,0 +1,255 @@ +package io.github.wulkanowy.ui.modules.login.advanced + +import io.github.wulkanowy.data.Resource +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.data.logResourceStatus +import io.github.wulkanowy.data.onResourceNotLoading +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.resourceFlow +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.modules.login.LoginData +import io.github.wulkanowy.ui.modules.login.LoginErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.ifNullOrBlank +import kotlinx.coroutines.flow.onEach +import timber.log.Timber +import javax.inject.Inject + +class LoginAdvancedPresenter @Inject constructor( + studentRepository: StudentRepository, + private val loginErrorHandler: LoginErrorHandler, + private val analytics: AnalyticsHelper +) : BasePresenter(loginErrorHandler, studentRepository) { + + override fun onAttachView(view: LoginAdvancedView) { + super.onAttachView(view) + view.run { + initView() + showOnlyScrapperModeInputs() + with(loginErrorHandler) { + onBadCredentials = ::onBadCredentials + onInvalidToken = ::onInvalidToken + onInvalidSymbol = ::onInvalidSymbol + onInvalidPin = ::onInvalidPin + } + } + } + + private fun onBadCredentials(message: String?) { + view?.run { + setErrorPassIncorrect(message) + showSoftKeyboard() + Timber.i("Entered wrong username or password") + } + } + + private fun onInvalidToken(message: String) { + view?.run { + setErrorTokenInvalid(message) + showSoftKeyboard() + Timber.i("Entered invalid token") + } + } + + private fun onInvalidSymbol(message: String) { + view?.run { + setErrorSymbolInvalid(message) + showSoftKeyboard() + Timber.i("Entered invalid symbol") + } + } + + private fun onInvalidPin(message: String) { + view?.run { + setErrorPinInvalid(message) + showSoftKeyboard() + Timber.i("Entered invalid PIN") + } + } + + fun updateUsernameLabel() { + view?.apply { + setUsernameLabel(if ("vulcan" in formHostValue || "fakelog" in formHostValue) emailLabel else nicknameLabel) + } + } + + fun onHostSelected() { + view?.apply { + clearPassError() + clearUsernameError() + if (formHostValue.contains("fakelog")) { + setDefaultCredentials( + "jan@fakelog.cf", "jan123", "powiatwulkanowy", "FK100000", "999999" + ) + } + setSymbol(formHostSymbol) + updateUsernameLabel() + } + } + + fun onLoginModeSelected(type: Sdk.Mode) { + view?.run { + when (type) { + Sdk.Mode.API -> { + showOnlyMobileApiModeInputs() + showMobileApiWarningMessage() + } + Sdk.Mode.SCRAPPER -> { + showOnlyScrapperModeInputs() + showScraperWarningMessage() + } + Sdk.Mode.HYBRID -> { + showOnlyHybridModeInputs() + showHybridWarningMessage() + } + } + } + } + + fun onPassTextChanged() { + view?.clearPassError() + } + + fun onUsernameTextChanged() { + view?.clearUsernameError() + } + + fun onPinTextChanged() { + view?.clearPinKeyError() + } + + fun onSymbolTextChanged() { + view?.clearSymbolError() + } + + fun onTokenTextChanged() { + view?.clearTokenError() + } + + fun onSignInClick() { + if (!validateCredentials()) return + + resourceFlow { getStudentsAppropriatesToLoginType() } + .logResourceStatus("login") + .onEach { + when (it) { + is Resource.Loading -> view?.run { + hideSoftKeyboard() + showProgress(true) + showContent(false) + } + is Resource.Success -> { + analytics.logEvent( + "registration_form", + "success" to true, + "students" to it.data.size, + "error" to "No error" + ) + val loginData = LoginData( + login = view?.formUsernameValue.orEmpty().trim(), + password = view?.formPassValue.orEmpty().trim(), + baseUrl = view?.formHostValue.orEmpty().trim() + ) + when (it.data.size) { + 0 -> view?.navigateToSymbol(loginData) + else -> view?.navigateToStudentSelect(it.data) + } + } + is Resource.Error -> { + analytics.logEvent( + "registration_form", + "success" to false, "students" to -1, + "error" to it.error.message.ifNullOrBlank { "No message" } + ) + loginErrorHandler.dispatch(it.error) + } + } + }.onResourceNotLoading { + view?.apply { + showProgress(false) + showContent(true) + } + }.launch("login") + } + + private suspend fun getStudentsAppropriatesToLoginType(): List { + val email = view?.formUsernameValue.orEmpty() + val password = view?.formPassValue.orEmpty() + val endpoint = view?.formHostValue.orEmpty() + + val pin = view?.formPinValue.orEmpty() + val symbol = view?.formSymbolValue.orEmpty() + val token = view?.formTokenValue.orEmpty() + + return when (Sdk.Mode.valueOf(view?.formLoginType.orEmpty())) { + Sdk.Mode.API -> studentRepository.getStudentsApi(pin, symbol, token) + Sdk.Mode.SCRAPPER -> studentRepository.getStudentsScrapper( + email, password, endpoint, symbol + ) + Sdk.Mode.HYBRID -> studentRepository.getStudentsHybrid( + email, password, endpoint, symbol + ) + } + } + + private fun validateCredentials(): Boolean { + val login = view?.formUsernameValue.orEmpty() + val password = view?.formPassValue.orEmpty() + + val host = view?.formHostValue.orEmpty() + + val pin = view?.formPinValue.orEmpty() + val symbol = view?.formSymbolValue.orEmpty() + val token = view?.formTokenValue.orEmpty() + + var isCorrect = true + + when (Sdk.Mode.valueOf(view?.formLoginType ?: "")) { + Sdk.Mode.API -> { + if (pin.isEmpty()) { + view?.setErrorPinRequired() + isCorrect = false + } + + if (symbol.isEmpty()) { + view?.setErrorSymbolRequired() + isCorrect = false + } + + if (token.isEmpty()) { + view?.setErrorTokenRequired() + isCorrect = false + } + } + Sdk.Mode.HYBRID, Sdk.Mode.SCRAPPER -> { + if (login.isEmpty()) { + view?.setErrorUsernameRequired() + isCorrect = false + } else { + if ("@" in login && "standard" !in host) { + view?.setErrorLoginRequired() + isCorrect = false + } + + if ("@" !in login && "standard" in host) { + view?.setErrorEmailRequired() + isCorrect = false + } + } + + if (password.isEmpty()) { + view?.setErrorPassRequired(focus = isCorrect) + isCorrect = false + } + + if (password.length < 6 && password.isNotEmpty()) { + view?.setErrorPassInvalid(focus = isCorrect) + isCorrect = false + } + } + } + + return isCorrect + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt new file mode 100644 index 000000000..f9b84f1ab --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt @@ -0,0 +1,94 @@ +package io.github.wulkanowy.ui.modules.login.advanced + +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.ui.base.BaseView +import io.github.wulkanowy.ui.modules.login.LoginData + +interface LoginAdvancedView : BaseView { + + val formUsernameValue: String + + val formPassValue: String + + val formHostValue: String + + val formHostSymbol: String + + val formLoginType: String + + val formPinValue: String + + val formSymbolValue: String + + val formTokenValue: String + + val nicknameLabel: String + + val emailLabel: String + + fun initView() + + fun showMobileApiWarningMessage() + + fun showScraperWarningMessage() + + fun showHybridWarningMessage() + + fun setDefaultCredentials(username: String, pass: String, symbol: String, token: String, pin: String) + + fun setUsernameLabel(label: String) + + fun setSymbol(symbol: String) + + fun setErrorUsernameRequired() + + fun setErrorLoginRequired() + + fun setErrorEmailRequired() + + fun setErrorPassRequired(focus: Boolean) + + fun setErrorPassInvalid(focus: Boolean) + + fun setErrorPassIncorrect(message: String?) + + fun clearUsernameError() + + fun clearPassError() + + fun clearPinKeyError() + + fun clearSymbolError() + + fun clearTokenError() + + fun showSoftKeyboard() + + fun hideSoftKeyboard() + + fun showProgress(show: Boolean) + + fun showContent(show: Boolean) + + fun navigateToSymbol(loginData: LoginData) + + fun navigateToStudentSelect(studentsWithSemesters: List) + + fun setErrorPinRequired() + + fun setErrorPinInvalid(message: String) + + fun setErrorSymbolRequired() + + fun setErrorSymbolInvalid(message: String) + + fun setErrorTokenRequired() + + fun setErrorTokenInvalid(message: String) + + fun showOnlyHybridModeInputs() + + fun showOnlyScrapperModeInputs() + + fun showOnlyMobileApiModeInputs() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt new file mode 100644 index 000000000..d31f5cf0f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt @@ -0,0 +1,269 @@ +package io.github.wulkanowy.ui.modules.login.form + +import android.annotation.SuppressLint +import android.os.Bundle +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import androidx.core.view.isVisible +import androidx.core.widget.doOnTextChanged +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.databinding.FragmentLoginFormBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.login.LoginActivity +import io.github.wulkanowy.ui.modules.login.LoginData +import io.github.wulkanowy.utils.AppInfo +import io.github.wulkanowy.utils.hideSoftInput +import io.github.wulkanowy.utils.openEmailClient +import io.github.wulkanowy.utils.openInternetBrowser +import io.github.wulkanowy.utils.setOnEditorDoneSignIn +import io.github.wulkanowy.utils.showSoftInput +import javax.inject.Inject + +@AndroidEntryPoint +class LoginFormFragment : BaseFragment(R.layout.fragment_login_form), + LoginFormView { + + @Inject + lateinit var presenter: LoginFormPresenter + + @Inject + lateinit var appInfo: AppInfo + + companion object { + fun newInstance() = LoginFormFragment() + } + + override val formUsernameValue: String + get() = binding.loginFormUsername.text.toString() + + override val formPassValue: String + get() = binding.loginFormPass.text.toString() + + override val formHostValue: String + get() = hostValues.getOrNull(hostKeys.indexOf(binding.loginFormHost.text.toString())) + .orEmpty() + + override val formHostSymbol: String + get() = hostSymbols.getOrNull(hostKeys.indexOf(binding.loginFormHost.text.toString())) + .orEmpty() + + override val nicknameLabel: String + get() = getString(R.string.login_nickname_hint) + + override val emailLabel: String + get() = getString(R.string.login_email_hint) + + private lateinit var hostKeys: Array + + private lateinit var hostValues: Array + + private lateinit var hostSymbols: Array + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentLoginFormBinding.bind(view) + presenter.onAttachView(this) + } + + override fun initView() { + (requireActivity() as LoginActivity).showActionBar(false) + + hostKeys = resources.getStringArray(R.array.hosts_keys) + hostValues = resources.getStringArray(R.array.hosts_values) + hostSymbols = resources.getStringArray(R.array.hosts_symbols) + + with(binding) { + loginFormUsername.doOnTextChanged { _, _, _, _ -> presenter.onUsernameTextChanged() } + loginFormPass.doOnTextChanged { _, _, _, _ -> presenter.onPassTextChanged() } + loginFormHost.setOnItemClickListener { _, _, _, _ -> presenter.onHostSelected() } + loginFormSignIn.setOnClickListener { presenter.onSignInClick() } + loginFormAdvancedButton.setOnClickListener { presenter.onAdvancedLoginClick() } + loginFormPrivacyLink.setOnClickListener { presenter.onPrivacyLinkClick() } + loginFormFaq.setOnClickListener { presenter.onFaqClick() } + loginFormContactEmail.setOnClickListener { presenter.onEmailClick() } + loginFormRecoverLink.setOnClickListener { presenter.onRecoverClick() } + loginFormRecoverLinkSecond.setOnClickListener { presenter.onRecoverClick() } + loginFormPass.setOnEditorDoneSignIn { loginFormSignIn.callOnClick() } + loginFormHost.setOnFocusChangeListener { _, hasFocus -> + if (hasFocus) requireActivity().hideSoftInput() + } + } + + with(binding.loginFormHost) { + setText(hostKeys.getOrNull(0).orEmpty()) + setAdapter( + LoginSymbolAdapter( + context, + R.layout.support_simple_spinner_dropdown_item, + hostKeys + ) + ) + setOnClickListener { if (binding.loginFormContainer.visibility == GONE) dismissDropDown() } + } + } + + override fun getHostsValues(): List = hostValues.toList() + + override fun setCredentials(username: String, pass: String) { + with(binding) { + loginFormUsername.setText(username) + loginFormPass.setText(pass) + } + } + + override fun setHost(host: String) { + binding.loginFormHost.setText( + hostKeys.getOrNull(hostValues.indexOf(host)).orEmpty() + ) + } + + override fun setUsernameLabel(label: String) { + binding.loginFormUsernameLayout.hint = label + } + + override fun setErrorUsernameRequired() { + with(binding.loginFormUsernameLayout) { + error = getString(R.string.error_field_required) + } + } + + override fun setErrorLoginRequired() { + with(binding.loginFormUsernameLayout) { + error = getString(R.string.login_invalid_login) + } + } + + override fun setErrorEmailRequired() { + with(binding.loginFormUsernameLayout) { + error = getString(R.string.login_invalid_email) + } + } + + override fun setErrorPassRequired(focus: Boolean) { + with(binding.loginFormPassLayout) { + error = getString(R.string.error_field_required) + } + } + + override fun setErrorPassInvalid(focus: Boolean) { + with(binding.loginFormPassLayout) { + error = getString(R.string.login_invalid_password) + } + } + + override fun setErrorPassIncorrect(message: String?) { + with(binding) { + loginFormUsernameLayout.error = " " + loginFormPassLayout.error = " " + loginFormHostLayout.error = " " + loginFormErrorBox.text = message ?: getString(R.string.login_incorrect_password_default) + loginFormErrorBox.isVisible = true + } + } + + override fun setErrorEmailInvalid(domain: String) { + with(binding.loginFormUsernameLayout) { + error = getString(R.string.login_invalid_custom_email, domain) + } + } + + override fun clearUsernameError() { + binding.loginFormUsernameLayout.error = null + binding.loginFormErrorBox.isVisible = false + } + + override fun clearPassError() { + binding.loginFormPassLayout.error = null + binding.loginFormErrorBox.isVisible = false + } + + override fun clearHostError() { + binding.loginFormHostLayout.error = null + binding.loginFormErrorBox.isVisible = false + } + + override fun showSoftKeyboard() { + activity?.showSoftInput() + } + + override fun hideSoftKeyboard() { + activity?.hideSoftInput() + } + + override fun showProgress(show: Boolean) { + binding.loginFormProgress.visibility = if (show) VISIBLE else GONE + } + + override fun showContent(show: Boolean) { + binding.loginFormContainer.visibility = if (show) VISIBLE else GONE + } + + @SuppressLint("SetTextI18n") + override fun showVersion() { + binding.loginFormVersion.text = "v${appInfo.versionName}" + } + + override fun showContact(show: Boolean) { + binding.loginFormContact.visibility = if (show) VISIBLE else GONE + binding.loginFormRecoverLink.visibility = if (show) GONE else VISIBLE + } + + override fun openPrivacyPolicyPage() { + context?.openInternetBrowser( + "https://wulkanowy.github.io/polityka-prywatnosci.html", + ::showMessage + ) + } + + override fun navigateToSymbol(loginData: LoginData) { + (activity as? LoginActivity)?.navigateToSymbolFragment(loginData) + } + + override fun navigateToStudentSelect(studentsWithSemesters: List) { + (activity as? LoginActivity)?.navigateToStudentSelect(studentsWithSemesters) + } + + override fun openAdvancedLogin() { + (activity as? LoginActivity)?.onAdvancedLoginClick() + } + + override fun onRecoverClick() { + (activity as? LoginActivity)?.onRecoverClick() + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } + + override fun openFaqPage() { + context?.openInternetBrowser( + "https://wulkanowy.github.io/czesto-zadawane-pytania/dlaczego-nie-moge-sie-zalogowac", + ::showMessage + ) + } + + override fun onResume() { + super.onResume() + presenter.updateUsernameLabel() + } + + override fun openEmail(lastError: String) { + context?.openEmailClient( + chooserTitle = requireContext().getString(R.string.login_email_intent_title), + email = "wulkanowyinc@gmail.com", + subject = requireContext().getString(R.string.login_email_subject), + body = requireContext().getString( + R.string.login_email_text, + "${appInfo.systemManufacturer} ${appInfo.systemModel}", + appInfo.systemVersion.toString(), + appInfo.versionName, + "$formHostValue/$formHostSymbol", + lastError + ) + ) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt new file mode 100644 index 000000000..b4291ff47 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt @@ -0,0 +1,194 @@ +package io.github.wulkanowy.ui.modules.login.form + +import androidx.core.net.toUri +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.modules.login.LoginData +import io.github.wulkanowy.ui.modules.login.LoginErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.ifNullOrBlank +import timber.log.Timber +import java.net.URL +import javax.inject.Inject + +class LoginFormPresenter @Inject constructor( + studentRepository: StudentRepository, + private val loginErrorHandler: LoginErrorHandler, + private val analytics: AnalyticsHelper +) : BasePresenter(loginErrorHandler, studentRepository) { + + private var lastError: Throwable? = null + + override fun onAttachView(view: LoginFormView) { + super.onAttachView(view) + view.run { + initView() + showContact(false) + showVersion() + + loginErrorHandler.onBadCredentials = { + setErrorPassIncorrect(it.takeIf { !it.isNullOrBlank() }) + showSoftKeyboard() + Timber.i("Entered wrong username or password") + } + } + } + + fun onPrivacyLinkClick() { + view?.openPrivacyPolicyPage() + } + + fun onAdvancedLoginClick() { + view?.openAdvancedLogin() + } + + fun onHostSelected() { + view?.apply { + clearPassError() + clearUsernameError() + clearHostError() + if (formHostValue.contains("fakelog")) { + setCredentials("jan@fakelog.cf", "jan123") + } else if (formUsernameValue == "jan@fakelog.cf" && formPassValue == "jan123") { + setCredentials("", "") + } + updateUsernameLabel() + } + } + + fun updateUsernameLabel() { + view?.run { + setUsernameLabel(if ("email" !in formHostValue) nicknameLabel else emailLabel) + } + } + + fun onPassTextChanged() { + view?.clearPassError() + } + + fun onUsernameTextChanged() { + view?.clearUsernameError() + + val username = view?.formUsernameValue.orEmpty().trim() + if ("@" in username && "@vulcan" !in username) { + val hosts = view?.getHostsValues().orEmpty().associateBy { it.toUri().host } + val usernameHost = username.substringAfter("@") + + hosts[usernameHost]?.let { + view?.run { + setHost(it) + clearHostError() + } + } + } + } + + fun onSignInClick() { + val email = view?.formUsernameValue.orEmpty().trim() + val password = view?.formPassValue.orEmpty().trim() + val host = view?.formHostValue.orEmpty().trim() + val symbol = view?.formHostSymbol.orEmpty().trim() + + if (!validateCredentials(email, password, host)) return + + resourceFlow { + studentRepository.getStudentsScrapper( + email = email, + password = password, + scrapperBaseUrl = host, + symbol = symbol + ) + } + .logResourceStatus("login") + .onResourceLoading { + view?.run { + hideSoftKeyboard() + showProgress(true) + showContent(false) + } + } + .onResourceSuccess { + when (it.size) { + 0 -> view?.navigateToSymbol(LoginData(email, password, host)) + else -> view?.navigateToStudentSelect(it) + } + analytics.logEvent( + "registration_form", + "success" to true, + "students" to it.size, + "scrapperBaseUrl" to host, + "error" to "No error" + ) + } + .onResourceNotLoading { + view?.apply { + showProgress(false) + showContent(true) + } + } + .onResourceError { + loginErrorHandler.dispatch(it) + lastError = it + view?.showContact(true) + analytics.logEvent( + "registration_form", + "success" to false, + "students" to -1, + "scrapperBaseUrl" to host, + "error" to it.message.ifNullOrBlank { "No message" } + ) + } + .launch("login") + } + + fun onFaqClick() { + view?.openFaqPage() + } + + fun onEmailClick() { + view?.openEmail(lastError?.message.ifNullOrBlank { "none" }) + } + + fun onRecoverClick() { + view?.onRecoverClick() + } + + private fun validateCredentials(login: String, password: String, host: String): Boolean { + var isCorrect = true + + if (login.isEmpty()) { + view?.setErrorUsernameRequired() + isCorrect = false + } else { + if ("@" in login && "login" in host) { + view?.setErrorLoginRequired() + isCorrect = false + } + if ("@" !in login && "email" in host) { + view?.setErrorEmailRequired() + isCorrect = false + } + if ("@" in login && "||" !in login && "login" !in host && "email" !in host) { + val emailHost = login.substringAfter("@") + val emailDomain = URL(host).host + if (emailHost != emailDomain) { + view?.setErrorEmailInvalid(domain = emailDomain) + isCorrect = false + } + } + } + + if (password.isEmpty()) { + view?.setErrorPassRequired(focus = isCorrect) + isCorrect = false + } + + if (password.length < 6 && password.isNotEmpty()) { + view?.setErrorPassInvalid(focus = isCorrect) + isCorrect = false + } + + return isCorrect + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt new file mode 100644 index 000000000..8003975db --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt @@ -0,0 +1,76 @@ +package io.github.wulkanowy.ui.modules.login.form + +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.ui.base.BaseView +import io.github.wulkanowy.ui.modules.login.LoginData + +interface LoginFormView : BaseView { + + fun initView() + + val formUsernameValue: String + + val formPassValue: String + + val formHostValue: String + + val formHostSymbol: String + + val nicknameLabel: String + + val emailLabel: String + + fun getHostsValues(): List + + fun setCredentials(username: String, pass: String) + + fun setHost(host: String) + + fun setUsernameLabel(label: String) + + fun setErrorUsernameRequired() + + fun setErrorLoginRequired() + + fun setErrorEmailRequired() + + fun setErrorPassRequired(focus: Boolean) + + fun setErrorPassInvalid(focus: Boolean) + + fun setErrorPassIncorrect(message: String?) + + fun setErrorEmailInvalid(domain: String) + + fun clearUsernameError() + + fun clearPassError() + + fun clearHostError() + + fun showSoftKeyboard() + + fun hideSoftKeyboard() + + fun showProgress(show: Boolean) + + fun showContent(show: Boolean) + + fun showVersion() + + fun navigateToSymbol(loginData: LoginData) + + fun navigateToStudentSelect(studentsWithSemesters: List) + + fun openPrivacyPolicyPage() + + fun showContact(show: Boolean) + + fun openFaqPage() + + fun openEmail(lastError: String) + + fun openAdvancedLogin() + + fun onRecoverClick() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginSymbolAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginSymbolAdapter.kt new file mode 100644 index 000000000..87fa038ec --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginSymbolAdapter.kt @@ -0,0 +1,16 @@ +package io.github.wulkanowy.ui.modules.login.form + +import android.content.Context +import android.widget.ArrayAdapter +import android.widget.Filter + +class LoginSymbolAdapter(context: Context, resource: Int, objects: Array) : + ArrayAdapter(context, resource, objects) { + + override fun getFilter() = object : Filter() { + + override fun performFiltering(constraint: CharSequence?) = null + + override fun publishResults(constraint: CharSequence?, results: FilterResults?) {} + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt new file mode 100644 index 000000000..c1c111d46 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt @@ -0,0 +1,244 @@ +package io.github.wulkanowy.ui.modules.login.recover + +import android.annotation.SuppressLint +import android.graphics.Color +import android.os.Bundle +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import android.webkit.JavascriptInterface +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.core.view.isVisible +import androidx.core.widget.doOnTextChanged +import com.yariksoffice.lingver.Lingver +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.databinding.FragmentLoginRecoverBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.login.LoginActivity +import io.github.wulkanowy.ui.modules.login.form.LoginSymbolAdapter +import io.github.wulkanowy.utils.hideSoftInput +import io.github.wulkanowy.utils.showSoftInput +import javax.inject.Inject + +@AndroidEntryPoint +class LoginRecoverFragment : + BaseFragment(R.layout.fragment_login_recover), LoginRecoverView { + + private var _binding: FragmentLoginRecoverBinding? = null + + private val bindingLocal: FragmentLoginRecoverBinding get() = _binding!! + + @Inject + lateinit var presenter: LoginRecoverPresenter + + @Inject + lateinit var lingver: Lingver + + @Inject + lateinit var preferencesRepository: PreferencesRepository + + companion object { + fun newInstance() = LoginRecoverFragment() + } + + private lateinit var hostKeys: Array + + private lateinit var hostValues: Array + + private lateinit var hostSymbols: Array + + override val recoverHostValue: String + get() = hostValues.getOrNull(hostKeys.indexOf(bindingLocal.loginRecoverHost.text.toString())) + .orEmpty() + + override val formHostSymbol: String + get() = hostSymbols.getOrNull(hostKeys.indexOf(bindingLocal.loginRecoverHost.text.toString())) + .orEmpty() + + override val recoverNameValue: String + get() = bindingLocal.loginRecoverName.text.toString().trim() + + override val emailHintString: String + get() = getString(R.string.login_email_hint) + + override val loginPeselEmailHintString: String + get() = getString(R.string.login_login_pesel_email_hint) + + override val invalidEmailString: String + get() = getString(R.string.login_invalid_email) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + restoreCorrectLocale() + _binding = FragmentLoginRecoverBinding.bind(view) + presenter.onAttachView(this) + } + + // https://issuetracker.google.com/issues/37113860 + private fun restoreCorrectLocale() { + if (preferencesRepository.appLanguage == "system") { + lingver.setFollowSystemLocale(requireContext()) + } else { + lingver.setLocale(requireContext(), lingver.getLocale()) + } + } + + override fun initView() { + (requireActivity() as LoginActivity).showActionBar(true) + + hostKeys = resources.getStringArray(R.array.hosts_keys) + hostValues = resources.getStringArray(R.array.hosts_values) + hostSymbols = resources.getStringArray(R.array.hosts_symbols) + + with(bindingLocal) { + loginRecoverWebView.setBackgroundColor(Color.TRANSPARENT) + loginRecoverName.doOnTextChanged { _, _, _, _ -> presenter.onNameTextChanged() } + loginRecoverHost.setOnItemClickListener { _, _, _, _ -> presenter.onHostSelected() } + loginRecoverButton.setOnClickListener { presenter.onRecoverClick() } + loginRecoverErrorRetry.setOnClickListener { presenter.onRecoverClick() } + loginRecoverErrorDetails.setOnClickListener { presenter.onDetailsClick() } + loginRecoverLogin.setOnClickListener { (activity as LoginActivity).onBackPressed() } + } + + with(bindingLocal.loginRecoverHost) { + setText(hostKeys.getOrNull(0).orEmpty()) + setAdapter( + LoginSymbolAdapter(context, R.layout.support_simple_spinner_dropdown_item, hostKeys) + ) + setOnClickListener { if (bindingLocal.loginRecoverFormContainer.visibility == GONE) dismissDropDown() } + } + } + + override fun setDefaultCredentials(username: String) { + bindingLocal.loginRecoverName.setText(username) + } + + override fun setErrorNameRequired() { + with(bindingLocal.loginRecoverNameLayout) { + requestFocus() + error = getString(R.string.error_field_required) + } + } + + override fun setUsernameHint(hint: String) { + bindingLocal.loginRecoverNameLayout.hint = hint + } + + override fun setUsernameError(message: String) { + with(bindingLocal.loginRecoverNameLayout) { + requestFocus() + error = message + } + } + + override fun clearUsernameError() { + bindingLocal.loginRecoverNameLayout.error = null + } + + override fun showProgress(show: Boolean) { + bindingLocal.loginRecoverProgress.visibility = if (show) VISIBLE else GONE + } + + override fun showRecoverForm(show: Boolean) { + bindingLocal.loginRecoverFormContainer.visibility = if (show) VISIBLE else GONE + } + + override fun showCaptcha(show: Boolean) { + bindingLocal.loginRecoverCaptchaContainer.visibility = if (show) VISIBLE else GONE + } + + override fun showErrorView(show: Boolean) { + bindingLocal.loginRecoverError.visibility = if (show) VISIBLE else GONE + bindingLocal.loginRecoverErrorDetails.isVisible = true + } + + override fun setErrorDetails(message: String) { + bindingLocal.loginRecoverErrorMessage.text = message + } + + override fun showSuccessView(show: Boolean) { + bindingLocal.loginRecoverSuccess.visibility = if (show) VISIBLE else GONE + } + + override fun setSuccessTitle(title: String) { + bindingLocal.loginRecoverSuccessTitle.text = title + } + + override fun setSuccessMessage(message: String) { + bindingLocal.loginRecoverSuccessMessage.text = message + } + + override fun showSoftKeyboard() { + activity?.showSoftInput() + } + + override fun hideSoftKeyboard() { + activity?.hideSoftInput() + } + + @SuppressLint("SetJavaScriptEnabled", "AddJavascriptInterface") + override fun loadReCaptcha(siteKey: String, url: String) { + val html = """ +
+ + + """.trimIndent() + + with(bindingLocal.loginRecoverWebView) { + settings.javaScriptEnabled = true + webViewClient = object : WebViewClient() { + private var recoverWebViewSuccess = true + + override fun onPageFinished(view: WebView?, url: String?) { + if (recoverWebViewSuccess) { + showCaptcha(true) + showProgress(false) + } else { + showProgress(false) + showErrorView(true) + bindingLocal.loginRecoverErrorDetails.isVisible = false + } + } + + override fun onReceivedError( + view: WebView, + errorCode: Int, + description: String, + failingUrl: String + ) { + recoverWebViewSuccess = false + } + } + + loadDataWithBaseURL(url, html, "text/html", "UTF-8", null) + addJavascriptInterface(object { + + @Suppress("UNUSED") + @JavascriptInterface + fun captchaCallback(reCaptchaResponse: String) { + activity?.runOnUiThread { + presenter.onReCaptchaVerified(reCaptchaResponse) + } + } + }, "Android") + } + } + + override fun onResume() { + super.onResume() + presenter.updateFields() + } + + override fun onDestroyView() { + bindingLocal.loginRecoverWebView.destroy() + _binding = null + presenter.onDetachView() + + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt new file mode 100644 index 000000000..3d0493012 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt @@ -0,0 +1,178 @@ +package io.github.wulkanowy.ui.modules.login.recover + +import io.github.wulkanowy.data.Resource +import io.github.wulkanowy.data.onResourceNotLoading +import io.github.wulkanowy.data.repositories.RecoverRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.resourceFlow +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.ifNullOrBlank +import kotlinx.coroutines.flow.onEach +import timber.log.Timber +import javax.inject.Inject + +class LoginRecoverPresenter @Inject constructor( + studentRepository: StudentRepository, + private val loginErrorHandler: RecoverErrorHandler, + private val analytics: AnalyticsHelper, + private val recoverRepository: RecoverRepository +) : BasePresenter(loginErrorHandler, studentRepository) { + + private lateinit var lastError: Throwable + + override fun onAttachView(view: LoginRecoverView) { + super.onAttachView(view) + view.initView() + + with(loginErrorHandler) { + showErrorMessage = ::showErrorMessage + onInvalidUsername = ::onInvalidUsername + onInvalidCaptcha = ::onInvalidCaptcha + } + } + + fun onNameTextChanged() { + view?.clearUsernameError() + } + + fun onHostSelected() { + view?.run { + if ("fakelog" in recoverHostValue) setDefaultCredentials("jan@fakelog.cf") + clearUsernameError() + updateFields() + } + } + + fun updateFields() { + view?.run { + setUsernameHint(if ("standard" in recoverHostValue) emailHintString else loginPeselEmailHintString) + } + } + + fun onRecoverClick() { + val username = view?.recoverNameValue.orEmpty() + val host = view?.recoverHostValue.orEmpty() + val symbol = view?.formHostSymbol.orEmpty() + + if (!validateInput(username, host)) return + + resourceFlow { + recoverRepository.getReCaptchaSiteKey( + host, + symbol.ifBlank { "Default" }) + }.onEach { + when (it) { + is Resource.Loading -> view?.run { + hideSoftKeyboard() + showRecoverForm(false) + showProgress(true) + showErrorView(false) + showCaptcha(false) + } + is Resource.Success -> view?.run { + loadReCaptcha(url = it.data.first, siteKey = it.data.second) + showProgress(false) + showErrorView(false) + showCaptcha(true) + } + is Resource.Error -> { + Timber.i("Obtain captcha site key result: An exception occurred") + errorHandler.dispatch(it.error) + } + } + }.launch("captcha") + } + + private fun validateInput(username: String, host: String): Boolean { + var isCorrect = true + + if (username.isEmpty()) { + view?.setErrorNameRequired() + isCorrect = false + } + + if ("standard" in host && "@" !in username) { + view?.setUsernameError(view?.invalidEmailString.orEmpty()) + isCorrect = false + } + + return isCorrect + } + + fun onReCaptchaVerified(reCaptchaResponse: String) { + val username = view?.recoverNameValue.orEmpty() + val host = view?.recoverHostValue.orEmpty() + val symbol = view?.formHostSymbol.ifNullOrBlank { "Default" } + + resourceFlow { + recoverRepository.sendRecoverRequest( + host, + symbol, + username, + reCaptchaResponse + ) + }.onEach { + when (it) { + is Resource.Loading -> view?.run { + showProgress(true) + showRecoverForm(false) + showCaptcha(false) + } + is Resource.Success -> view?.run { + showSuccessView(true) + setSuccessTitle(it.data.substringBefore(". ")) + setSuccessMessage(it.data.substringAfter(". ")) + analytics.logEvent( + "account_recover", + "register" to host, + "symbol" to symbol, + "success" to true + ) + } + is Resource.Error -> { + Timber.i("Send recover request result: An exception occurred") + errorHandler.dispatch(it.error) + analytics.logEvent( + "account_recover", + "register" to host, + "symbol" to symbol, + "success" to false + ) + } + } + }.onResourceNotLoading { + view?.showProgress(false) + }.launch("verified") + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + private fun showErrorMessage(message: String, error: Throwable) { + view?.run { + lastError = error + showProgress(false) + setErrorDetails(message) + showErrorView(true) + } + } + + private fun onInvalidUsername(message: String) { + view?.run { + setUsernameError(message) + showRecoverForm(true) + } + } + + private fun onInvalidCaptcha(message: String, error: Throwable) { + view?.run { + lastError = error + setErrorDetails(message) + showCaptcha(false) + showRecoverForm(false) + showErrorView(true) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverView.kt new file mode 100644 index 000000000..2e21d3351 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverView.kt @@ -0,0 +1,52 @@ +package io.github.wulkanowy.ui.modules.login.recover + +import io.github.wulkanowy.ui.base.BaseView + +interface LoginRecoverView : BaseView { + + val recoverHostValue: String + + val formHostSymbol: String + + val recoverNameValue: String + + val emailHintString: String + + val loginPeselEmailHintString: String + + val invalidEmailString: String + + fun initView() + + fun setDefaultCredentials(username: String) + + fun clearUsernameError() + + fun setErrorNameRequired() + + fun setUsernameHint(hint: String) + + fun setUsernameError(message: String) + + fun showSoftKeyboard() + + fun hideSoftKeyboard() + + fun showProgress(show: Boolean) + + fun showRecoverForm(show: Boolean) + + fun showCaptcha(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun showSuccessView(show: Boolean) + + fun setSuccessMessage(message: String) + + fun setSuccessTitle(title: String) + + fun loadReCaptcha(siteKey: String, url: String) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/RecoverErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/RecoverErrorHandler.kt new file mode 100644 index 000000000..28686d626 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/RecoverErrorHandler.kt @@ -0,0 +1,32 @@ +package io.github.wulkanowy.ui.modules.login.recover + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.sdk.scrapper.exception.InvalidCaptchaException +import io.github.wulkanowy.sdk.scrapper.exception.InvalidEmailException +import io.github.wulkanowy.sdk.scrapper.exception.NoAccountFoundException +import io.github.wulkanowy.ui.base.ErrorHandler +import javax.inject.Inject + +class RecoverErrorHandler @Inject constructor( + @ApplicationContext context: Context, +) : ErrorHandler(context) { + + var onInvalidUsername: (String) -> Unit = {} + + var onInvalidCaptcha: (String, Throwable) -> Unit = { _, _ -> } + + override fun proceed(error: Throwable) { + when (error) { + is InvalidEmailException, + is NoAccountFoundException -> onInvalidUsername(error.localizedMessage.orEmpty()) + is InvalidCaptchaException -> onInvalidCaptcha(error.localizedMessage.orEmpty(), error) + else -> super.proceed(error) + } + } + + override fun clear() { + super.clear() + onInvalidUsername = {} + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectAdapter.kt new file mode 100644 index 000000000..c046c2ff5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectAdapter.kt @@ -0,0 +1,66 @@ +package io.github.wulkanowy.ui.modules.login.studentselect + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.databinding.ItemLoginStudentSelectBinding +import javax.inject.Inject + +class LoginStudentSelectAdapter @Inject constructor() : + RecyclerView.Adapter() { + + private val checkedList = mutableMapOf() + + var items = emptyList>() + set(value) { + field = value + checkedList.clear() + } + + var onClickListener: (StudentWithSemesters, alreadySaved: Boolean) -> Unit = { _, _ -> } + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + ItemLoginStudentSelectBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + @SuppressLint("SetTextI18n") + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + val (studentAndSemesters, alreadySaved) = items[position] + val student = studentAndSemesters.student + val semesters = studentAndSemesters.semesters + val diary = semesters.maxByOrNull { it.semesterId } + + with(holder.binding) { + loginItemName.text = "${student.studentName} ${diary?.diaryName.orEmpty()}" + loginItemSchool.text = student.schoolName + loginItemName.isEnabled = !alreadySaved + loginItemSchool.isEnabled = !alreadySaved + loginItemSignedIn.visibility = if (alreadySaved) View.VISIBLE else View.GONE + + with(loginItemCheck) { + isEnabled = !alreadySaved + keyListener = null + isChecked = checkedList[position] ?: false + } + + root.setOnClickListener { + onClickListener(studentAndSemesters, alreadySaved) + + with(loginItemCheck) { + if (isEnabled) { + isChecked = !isChecked + checkedList[position] = isChecked + } + } + } + } + } + + class ItemViewHolder(val binding: ItemLoginStudentSelectBinding) : + RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt new file mode 100644 index 000000000..6c910fe03 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt @@ -0,0 +1,122 @@ +package io.github.wulkanowy.ui.modules.login.studentselect + +import android.os.Bundle +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import androidx.core.os.bundleOf +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.databinding.FragmentLoginStudentSelectBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.login.LoginActivity +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.utils.AppInfo +import io.github.wulkanowy.utils.openEmailClient +import io.github.wulkanowy.utils.openInternetBrowser +import javax.inject.Inject + +@AndroidEntryPoint +class LoginStudentSelectFragment : + BaseFragment(R.layout.fragment_login_student_select), + LoginStudentSelectView { + + @Inject + lateinit var presenter: LoginStudentSelectPresenter + + @Inject + lateinit var loginAdapter: LoginStudentSelectAdapter + + @Inject + lateinit var appInfo: AppInfo + + companion object { + const val ARG_STUDENTS = "STUDENTS" + + fun newInstance(studentsWithSemesters: List) = + LoginStudentSelectFragment().apply { + arguments = bundleOf(ARG_STUDENTS to studentsWithSemesters) + } + } + + @Suppress("UNCHECKED_CAST") + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentLoginStudentSelectBinding.bind(view) + presenter.onAttachView( + view = this, + students = requireArguments().getSerializable(ARG_STUDENTS) as List, + ) + } + + override fun initView() { + (requireActivity() as LoginActivity).showActionBar(true) + + loginAdapter.onClickListener = presenter::onItemSelected + + with(binding) { + loginStudentSelectSignIn.setOnClickListener { presenter.onSignIn() } + loginStudentSelectContactDiscord.setOnClickListener { presenter.onDiscordClick() } + loginStudentSelectContactEmail.setOnClickListener { presenter.onEmailClick() } + + with(loginStudentSelectRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = loginAdapter + } + } + } + + override fun updateData(data: List>) { + with(loginAdapter) { + items = data + notifyDataSetChanged() + } + } + + override fun openMainView() { + startActivity(MainActivity.getStartIntent(requireContext())) + requireActivity().finish() + } + + override fun showProgress(show: Boolean) { + binding.loginStudentSelectProgress.visibility = if (show) VISIBLE else GONE + } + + override fun showContent(show: Boolean) { + binding.loginStudentSelectContent.visibility = if (show) VISIBLE else GONE + } + + override fun enableSignIn(enable: Boolean) { + binding.loginStudentSelectSignIn.isEnabled = enable + } + + override fun showContact(show: Boolean) { + binding.loginStudentSelectContact.visibility = if (show) VISIBLE else GONE + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } + + override fun openDiscordInvite() { + context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage) + } + + override fun openEmail(lastError: String) { + context?.openEmailClient( + chooserTitle = requireContext().getString(R.string.login_email_intent_title), + email = "wulkanowyinc@gmail.com", + subject = requireContext().getString(R.string.login_email_subject), + body = requireContext().getString( + R.string.login_email_text, appInfo.systemModel, + appInfo.systemVersion.toString(), + appInfo.versionName, + "Select users to log in", + lastError + ) + ) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt new file mode 100644 index 000000000..3455b3cf1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt @@ -0,0 +1,142 @@ +package io.github.wulkanowy.ui.modules.login.studentselect + +import io.github.wulkanowy.data.Resource +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.data.logResourceStatus +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.resourceFlow +import io.github.wulkanowy.services.sync.SyncManager +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.modules.login.LoginErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.ifNullOrBlank +import kotlinx.coroutines.flow.onEach +import timber.log.Timber +import javax.inject.Inject + +class LoginStudentSelectPresenter @Inject constructor( + studentRepository: StudentRepository, + private val loginErrorHandler: LoginErrorHandler, + private val syncManager: SyncManager, + private val analytics: AnalyticsHelper +) : BasePresenter(loginErrorHandler, studentRepository) { + + private var lastError: Throwable? = null + + private val selectedStudents = mutableListOf() + + fun onAttachView(view: LoginStudentSelectView, students: List) { + super.onAttachView(view) + with(view) { + initView() + showContact(false) + enableSignIn(false) + loginErrorHandler.onStudentDuplicate = { + showMessage(it) + Timber.i("The student already registered in the app was selected") + } + } + + if (students.size == 1) registerStudents(students) + loadData(students) + } + + fun onSignIn() { + registerStudents(selectedStudents) + } + + fun onItemSelected(studentWithSemester: StudentWithSemesters, alreadySaved: Boolean) { + if (alreadySaved) return + + selectedStudents + .removeAll { it == studentWithSemester } + .let { if (!it) selectedStudents.add(studentWithSemester) } + + view?.enableSignIn(selectedStudents.isNotEmpty()) + } + + private fun compareStudents(a: Student, b: Student): Boolean { + return a.email == b.email + && a.symbol == b.symbol + && a.studentId == b.studentId + && a.schoolSymbol == b.schoolSymbol + && a.classId == b.classId + } + + private fun loadData(studentsWithSemesters: List) { + resetSelectedState() + + resourceFlow { studentRepository.getSavedStudents(false) }.onEach { + when (it) { + is Resource.Loading -> Timber.d("Login student select students load started") + is Resource.Success -> view?.updateData(studentsWithSemesters.map { studentWithSemesters -> + studentWithSemesters to it.data.any { item -> + compareStudents(studentWithSemesters.student, item.student) + } + }) + is Resource.Error -> { + errorHandler.dispatch(it.error) + lastError = it.error + view?.updateData(studentsWithSemesters.map { student -> student to false }) + } + } + }.launch() + } + + private fun resetSelectedState() { + selectedStudents.clear() + view?.enableSignIn(false) + } + + private fun registerStudents(studentsWithSemesters: List) { + resourceFlow { studentRepository.saveStudents(studentsWithSemesters) } + .logResourceStatus("registration") + .onEach { + when (it) { + is Resource.Loading -> view?.run { + showProgress(true) + showContent(false) + } + is Resource.Success -> { + syncManager.startOneTimeSyncWorker(quiet = true) + view?.openMainView() + logRegisterEvent(studentsWithSemesters) + } + is Resource.Error -> { + view?.apply { + showProgress(false) + showContent(true) + showContact(true) + } + lastError = it.error + loginErrorHandler.dispatch(it.error) + logRegisterEvent(studentsWithSemesters, it.error) + } + } + }.launch("register") + } + + fun onDiscordClick() { + view?.openDiscordInvite() + } + + fun onEmailClick() { + view?.openEmail(lastError?.message.ifNullOrBlank { "empty" }) + } + + private fun logRegisterEvent( + studentsWithSemesters: List, + error: Throwable? = null + ) { + studentsWithSemesters.forEach { student -> + analytics.logEvent( + "registration_student_select", + "success" to (error != null), + "scrapperBaseUrl" to student.student.scrapperBaseUrl, + "symbol" to student.student.symbol, + "error" to (error?.message?.ifBlank { "No message" } ?: "No error") + ) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt new file mode 100644 index 000000000..f2acd76c5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt @@ -0,0 +1,25 @@ +package io.github.wulkanowy.ui.modules.login.studentselect + +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.ui.base.BaseView + +interface LoginStudentSelectView : BaseView { + + fun initView() + + fun updateData(data: List>) + + fun openMainView() + + fun showProgress(show: Boolean) + + fun showContent(show: Boolean) + + fun enableSignIn(enable: Boolean) + + fun showContact(show: Boolean) + + fun openDiscordInvite() + + fun openEmail(lastError: String) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt new file mode 100644 index 000000000..58bdf6cef --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt @@ -0,0 +1,168 @@ +package io.github.wulkanowy.ui.modules.login.symbol + +import android.os.Bundle +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import android.view.inputmethod.EditorInfo.IME_ACTION_DONE +import android.view.inputmethod.EditorInfo.IME_NULL +import android.widget.ArrayAdapter +import androidx.core.os.bundleOf +import androidx.core.text.parseAsHtml +import androidx.core.widget.doOnTextChanged +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.databinding.FragmentLoginSymbolBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.login.LoginActivity +import io.github.wulkanowy.ui.modules.login.LoginData +import io.github.wulkanowy.utils.AppInfo +import io.github.wulkanowy.utils.hideSoftInput +import io.github.wulkanowy.utils.openEmailClient +import io.github.wulkanowy.utils.openInternetBrowser +import io.github.wulkanowy.utils.showSoftInput +import javax.inject.Inject + +@AndroidEntryPoint +class LoginSymbolFragment : + BaseFragment(R.layout.fragment_login_symbol), LoginSymbolView { + + @Inject + lateinit var presenter: LoginSymbolPresenter + + @Inject + lateinit var appInfo: AppInfo + + companion object { + private const val SAVED_LOGIN_DATA = "LOGIN_DATA" + + fun newInstance(loginData: LoginData) = LoginSymbolFragment().apply { + arguments = bundleOf(SAVED_LOGIN_DATA to loginData) + } + } + + override val symbolNameError: CharSequence? + get() = binding.loginSymbolNameLayout.error + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentLoginSymbolBinding.bind(view) + presenter.onAttachView( + view = this, + loginData = requireArguments().getSerializable(SAVED_LOGIN_DATA) as LoginData, + ) + } + + override fun initView() { + (requireActivity() as LoginActivity).showActionBar(true) + + with(binding) { + loginSymbolSignIn.setOnClickListener { presenter.attemptLogin(loginSymbolName.text.toString()) } + loginSymbolFaq.setOnClickListener { presenter.onFaqClick() } + loginSymbolContactEmail.setOnClickListener { presenter.onEmailClick() } + + loginSymbolName.doOnTextChanged { _, _, _, _ -> presenter.onSymbolTextChanged() } + + loginSymbolName.apply { + setOnEditorActionListener { _, id, _ -> + if (id == IME_ACTION_DONE || id == IME_NULL) loginSymbolSignIn.callOnClick() else false + } + setAdapter( + ArrayAdapter( + context, + android.R.layout.simple_list_item_1, + resources.getStringArray(R.array.symbols_values) + ) + ) + } + } + } + + override fun setLoginToHeading(login: String) { + binding.loginSymbolHeader.text = + getString(R.string.login_header_symbol, login).parseAsHtml() + } + + override fun setErrorSymbolIncorrect() { + binding.loginSymbolNameLayout.apply { + requestFocus() + error = getString(R.string.login_incorrect_symbol) + } + } + + override fun setErrorSymbolRequire() { + binding.loginSymbolNameLayout.apply { + requestFocus() + error = getString(R.string.error_field_required) + } + } + + override fun clearSymbolError() { + binding.loginSymbolNameLayout.error = null + } + + override fun clearAndFocusSymbol() { + binding.loginSymbolNameLayout.apply { + editText?.text = null + requestFocus() + } + } + + override fun showSoftKeyboard() { + activity?.showSoftInput() + } + + override fun hideSoftKeyboard() { + activity?.hideSoftInput() + } + + override fun showProgress(show: Boolean) { + binding.loginSymbolProgress.visibility = if (show) VISIBLE else GONE + } + + override fun showContent(show: Boolean) { + binding.loginSymbolContainer.visibility = if (show) VISIBLE else GONE + } + + override fun navigateToStudentSelect(studentsWithSemesters: List) { + (activity as? LoginActivity)?.navigateToStudentSelect(studentsWithSemesters) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putSerializable(SAVED_LOGIN_DATA, presenter.loginData) + } + + override fun showContact(show: Boolean) { + binding.loginSymbolContact.visibility = if (show) VISIBLE else GONE + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } + + override fun openFaqPage() { + context?.openInternetBrowser( + "https://wulkanowy.github.io/czesto-zadawane-pytania/co-to-jest-symbol", + ::showMessage + ) + } + + override fun openEmail(host: String, lastError: String) { + context?.openEmailClient( + chooserTitle = requireContext().getString(R.string.login_email_intent_title), + email = "wulkanowyinc@gmail.com", + subject = requireContext().getString(R.string.login_email_subject), + body = requireContext().getString( + R.string.login_email_text, + "${appInfo.systemManufacturer} ${appInfo.systemModel}", + appInfo.systemVersion.toString(), + appInfo.versionName, + "$host/${binding.loginSymbolName.text}", + lastError + ) + ) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt new file mode 100644 index 000000000..691cd4481 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt @@ -0,0 +1,116 @@ +package io.github.wulkanowy.ui.modules.login.symbol + +import io.github.wulkanowy.data.Resource +import io.github.wulkanowy.data.onResourceNotLoading +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.resourceFlow +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.modules.login.LoginData +import io.github.wulkanowy.ui.modules.login.LoginErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.ifNullOrBlank +import kotlinx.coroutines.flow.onEach +import timber.log.Timber +import javax.inject.Inject + +class LoginSymbolPresenter @Inject constructor( + studentRepository: StudentRepository, + private val loginErrorHandler: LoginErrorHandler, + private val analytics: AnalyticsHelper +) : BasePresenter(loginErrorHandler, studentRepository) { + + private var lastError: Throwable? = null + + lateinit var loginData: LoginData + + fun onAttachView(view: LoginSymbolView, loginData: LoginData) { + super.onAttachView(view) + this.loginData = loginData + with(view) { + initView() + showContact(false) + setLoginToHeading(loginData.login) + clearAndFocusSymbol() + showSoftKeyboard() + } + } + + fun onSymbolTextChanged() { + view?.apply { if (symbolNameError != null) clearSymbolError() } + } + + fun attemptLogin(symbol: String) { + if (symbol.isBlank()) { + view?.setErrorSymbolRequire() + return + } + + resourceFlow { + studentRepository.getStudentsScrapper( + email = loginData.login, + password = loginData.password, + scrapperBaseUrl = loginData.baseUrl, + symbol = symbol, + ) + }.onEach { + when (it) { + is Resource.Loading -> view?.run { + Timber.i("Login with symbol started") + hideSoftKeyboard() + showProgress(true) + showContent(false) + } + is Resource.Success -> { + when (it.data.size) { + 0 -> { + Timber.i("Login with symbol result: Empty student list") + view?.run { + setErrorSymbolIncorrect() + showContact(true) + } + } + else -> { + Timber.i("Login with symbol result: Success") + view?.navigateToStudentSelect(requireNotNull(it.data)) + } + } + analytics.logEvent( + "registration_symbol", + "success" to true, + "students" to it.data.size, + "scrapperBaseUrl" to loginData.baseUrl, + "symbol" to symbol, + "error" to "No error" + ) + } + is Resource.Error -> { + Timber.i("Login with symbol result: An exception occurred") + analytics.logEvent( + "registration_symbol", + "success" to false, + "students" to -1, + "scrapperBaseUrl" to loginData.baseUrl, + "symbol" to symbol, + "error" to it.error.message.ifNullOrBlank { "No message" } + ) + loginErrorHandler.dispatch(it.error) + lastError = it.error + view?.showContact(true) + } + } + }.onResourceNotLoading { + view?.apply { + showProgress(false) + showContent(true) + } + }.launch("login") + } + + fun onFaqClick() { + view?.openFaqPage() + } + + fun onEmailClick() { + view?.openEmail(loginData.baseUrl, lastError?.message.ifNullOrBlank { "empty" }) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt new file mode 100644 index 000000000..527895b77 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt @@ -0,0 +1,37 @@ +package io.github.wulkanowy.ui.modules.login.symbol + +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.ui.base.BaseView + +interface LoginSymbolView : BaseView { + + val symbolNameError: CharSequence? + + fun initView() + + fun setLoginToHeading(login: String) + + fun setErrorSymbolIncorrect() + + fun setErrorSymbolRequire() + + fun clearSymbolError() + + fun clearAndFocusSymbol() + + fun showSoftKeyboard() + + fun hideSoftKeyboard() + + fun showProgress(show: Boolean) + + fun showContent(show: Boolean) + + fun navigateToStudentSelect(studentsWithSemesters: List) + + fun showContact(show: Boolean) + + fun openFaqPage() + + fun openEmail(host: String, lastError: String) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt new file mode 100644 index 000000000..0a73fe15d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt @@ -0,0 +1,93 @@ +package io.github.wulkanowy.ui.modules.luckynumber + +import android.os.Bundle +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.databinding.FragmentLuckyNumberBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.luckynumber.history.LuckyNumberHistoryFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.getThemeAttrColor +import javax.inject.Inject + +@AndroidEntryPoint +class LuckyNumberFragment : + BaseFragment(R.layout.fragment_lucky_number), LuckyNumberView, + MainView.TitledView { + + @Inject + lateinit var presenter: LuckyNumberPresenter + + companion object { + fun newInstance() = LuckyNumberFragment() + } + + override val titleStringId: Int + get() = R.string.lucky_number_title + + override val isViewEmpty get() = binding.luckyNumberText.text.isBlank() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentLuckyNumberBinding.bind(view) + messageContainer = binding.luckyNumberSwipe + presenter.onAttachView(this) + } + + override fun initView() { + with(binding) { + luckyNumberSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + luckyNumberSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) + luckyNumberSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh)) + luckyNumberHistoryButton.setOnClickListener { openLuckyNumberHistory() } + luckyNumberErrorRetry.setOnClickListener { presenter.onRetry() } + luckyNumberErrorDetails.setOnClickListener { presenter.onDetailsClick() } + } + } + + override fun updateData(data: LuckyNumber) { + binding.luckyNumberText.text = data.luckyNumber.toString() + } + + override fun hideRefresh() { + binding.luckyNumberSwipe.isRefreshing = false + } + + override fun showEmpty(show: Boolean) { + binding.luckyNumberEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showErrorView(show: Boolean) { + binding.luckyNumberError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + binding.luckyNumberErrorMessage.text = message + } + + override fun showProgress(show: Boolean) { + binding.luckyNumberProgress.visibility = if (show) VISIBLE else GONE + } + + override fun enableSwipe(enable: Boolean) { + binding.luckyNumberSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + binding.luckyNumberContent.visibility = if (show) VISIBLE else GONE + } + + override fun openLuckyNumberHistory() { + (activity as? MainActivity)?.pushView(LuckyNumberHistoryFragment.newInstance()) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt new file mode 100644 index 000000000..6f5c8e740 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt @@ -0,0 +1,102 @@ +package io.github.wulkanowy.ui.modules.luckynumber + +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.repositories.LuckyNumberRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import timber.log.Timber +import javax.inject.Inject + +class LuckyNumberPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val luckyNumberRepository: LuckyNumberRepository, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + private lateinit var lastError: Throwable + + override fun onAttachView(view: LuckyNumberView) { + super.onAttachView(view) + view.run { + initView() + showContent(false) + enableSwipe(false) + } + Timber.i("Lucky number view was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError + loadData() + } + + private fun loadData(forceRefresh: Boolean = false) { + flatResourceFlow { + val student = studentRepository.getCurrentStudent() + luckyNumberRepository.getLuckyNumber(student, forceRefresh) + } + .logResourceStatus("load lucky number") + .onResourceData { + if (it != null) { + view?.apply { + updateData(it) + showContent(true) + showEmpty(false) + showErrorView(false) + } + } else { + view?.run { + showContent(false) + showEmpty(true) + showErrorView(false) + } + } + } + .onResourceSuccess { + if (it != null) { + analytics.logEvent( + "load_item", + "type" to "lucky_number", + "number" to it.luckyNumber + ) + } + } + .onResourceNotLoading { + view?.run { + hideRefresh() + showProgress(false) + enableSwipe(true) + } + } + .onResourceError(errorHandler::dispatch) + .launch() + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the lucky number") + loadData(true) + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt new file mode 100644 index 000000000..0c05a1566 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt @@ -0,0 +1,29 @@ +package io.github.wulkanowy.ui.modules.luckynumber + +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.ui.base.BaseView + +interface LuckyNumberView : BaseView { + + val isViewEmpty: Boolean + + fun initView() + + fun updateData(data: LuckyNumber) + + fun hideRefresh() + + fun showEmpty(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun openLuckyNumberHistory() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryAdapter.kt new file mode 100644 index 000000000..0c1b89c8e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryAdapter.kt @@ -0,0 +1,36 @@ +package io.github.wulkanowy.ui.modules.luckynumber.history + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.databinding.ItemLuckyNumberHistoryBinding +import io.github.wulkanowy.utils.capitalise +import io.github.wulkanowy.utils.toFormattedString +import io.github.wulkanowy.utils.weekDayName +import javax.inject.Inject + +class LuckyNumberHistoryAdapter @Inject constructor() : + RecyclerView.Adapter() { + + var items = emptyList() + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + ItemLuckyNumberHistoryBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + @SuppressLint("DefaultLocale") + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + val item = items[position] + with(holder.binding) { + luckyNumberHistoryWeekName.text = item.date.weekDayName.capitalise() + luckyNumberHistoryDate.text = item.date.toFormattedString() + luckyNumberHistory.text = item.luckyNumber.toString() + } + } + + class ItemViewHolder(val binding: ItemLuckyNumberHistoryBinding) : RecyclerView.ViewHolder(binding.root) +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryFragment.kt new file mode 100644 index 000000000..53f06cacd --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryFragment.kt @@ -0,0 +1,129 @@ +package io.github.wulkanowy.ui.modules.luckynumber.history + +import android.os.Bundle +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.databinding.FragmentLuckyNumberHistoryBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.widgets.DividerItemDecoration +import io.github.wulkanowy.utils.dpToPx +import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear +import io.github.wulkanowy.utils.openMaterialDatePicker +import java.time.LocalDate +import javax.inject.Inject + +@AndroidEntryPoint +class LuckyNumberHistoryFragment : + BaseFragment(R.layout.fragment_lucky_number_history), + LuckyNumberHistoryView, + MainView.TitledView { + + @Inject + lateinit var presenter: LuckyNumberHistoryPresenter + + @Inject + lateinit var luckyNumberHistoryAdapter: LuckyNumberHistoryAdapter + + companion object { + fun newInstance() = LuckyNumberHistoryFragment() + } + + override val titleStringId: Int + get() = R.string.lucky_number_history_title + + override val isViewEmpty get() = luckyNumberHistoryAdapter.items.isEmpty() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentLuckyNumberHistoryBinding.bind(view) + messageContainer = binding.luckyNumberHistoryRecycler + presenter.onAttachView(this) + } + + override fun initView() { + with(binding.luckyNumberHistoryRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = luckyNumberHistoryAdapter + addItemDecoration(DividerItemDecoration(context)) + } + + with(binding) { + luckyNumberHistoryNavDate.setOnClickListener { presenter.onPickDate() } + luckyNumberHistoryErrorRetry.setOnClickListener { presenter.onRetry() } + luckyNumberHistoryErrorDetails.setOnClickListener { presenter.onDetailsClick() } + + luckyNumberHistoryPreviousButton.setOnClickListener { presenter.onPreviousWeek() } + luckyNumberHistoryNextButton.setOnClickListener { presenter.onNextWeek() } + + luckyNumberHistoryNavContainer.elevation = requireContext().dpToPx(8f) + } + } + + override fun updateData(data: List) { + with(luckyNumberHistoryAdapter) { + items = data + notifyDataSetChanged() + } + } + + override fun clearData() { + with(luckyNumberHistoryAdapter) { + items = emptyList() + notifyDataSetChanged() + } + } + + override fun showEmpty(show: Boolean) { + binding.luckyNumberHistoryEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showErrorView(show: Boolean) { + binding.luckyNumberHistoryError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + binding.luckyNumberHistoryErrorMessage.text = message + } + + override fun updateNavigationWeek(date: String) { + binding.luckyNumberHistoryNavDate.text = date + } + + override fun showProgress(show: Boolean) { + binding.luckyNumberHistoryProgress.visibility = if (show) VISIBLE else GONE + } + + override fun showPreButton(show: Boolean) { + binding.luckyNumberHistoryPreviousButton.visibility = if (show) VISIBLE else View.INVISIBLE + } + + override fun showNextButton(show: Boolean) { + binding.luckyNumberHistoryNextButton.visibility = if (show) VISIBLE else View.INVISIBLE + } + + override fun showDatePickerDialog(selectedDate: LocalDate) { + openMaterialDatePicker( + selected = selectedDate, + rangeStart = selectedDate.firstSchoolDayInSchoolYear, + rangeEnd = LocalDate.now().plusWeeks(1), + onDateSelected = { + presenter.onDateSet(it.year, it.monthValue, it.dayOfMonth) + } + ) + } + + override fun showContent(show: Boolean) { + binding.luckyNumberHistoryRecycler.visibility = if (show) VISIBLE else GONE + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryPresenter.kt new file mode 100644 index 000000000..fc753950b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryPresenter.kt @@ -0,0 +1,155 @@ +package io.github.wulkanowy.ui.modules.luckynumber.history + +import io.github.wulkanowy.data.repositories.LuckyNumberRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.* +import kotlinx.coroutines.flow.* +import timber.log.Timber +import java.time.LocalDate +import javax.inject.Inject + +class LuckyNumberHistoryPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val semesterRepository: SemesterRepository, + private val luckyNumberRepository: LuckyNumberRepository, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + private lateinit var lastError: Throwable + + var currentDate: LocalDate = LocalDate.now().previousOrSameSchoolDay + + override fun onAttachView(view: LuckyNumberHistoryView) { + super.onAttachView(view) + view.run { + initView() + reloadNavigation() + showContent(false) + } + Timber.i("Lucky number history view was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError + loadData() + if (currentDate.isHolidays) setBaseDateOnHolidays() + } + + private fun setBaseDateOnHolidays() { + flow { + val student = studentRepository.getCurrentStudent() + emit(semesterRepository.getCurrentSemester(student)) + } + .catch { Timber.i("Loading semester result: An exception occurred") } + .onEach { + currentDate = currentDate.getLastSchoolDayIfHoliday(it.schoolYear) + reloadNavigation() + } + .launch("holidays") + } + + private fun loadData() { + flow { + val student = studentRepository.getCurrentStudent() + emitAll( + luckyNumberRepository.getLuckyNumberHistory( + student = student, + start = currentDate.monday, + end = currentDate.sunday + ) + ) + } + .onEach { + if (!it.isNullOrEmpty()) { + view?.apply { + updateData(it) + showContent(true) + showEmpty(false) + showErrorView(false) + showProgress(false) + } + } else { + view?.run { + showContent(false) + showEmpty(true) + showErrorView(false) + showProgress(false) + } + } + + analytics.logEvent( + "load_items", + "type" to "lucky_number_history", + ) + } + .catch { errorHandler.dispatch(it) } + .launchIn(presenterScope) + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + + private fun reloadView(date: LocalDate) { + currentDate = date + Timber.i("Reload lucky number history view with the date ${currentDate.toFormattedString()}") + view?.apply { + showProgress(true) + showContent(false) + showEmpty(false) + showErrorView(false) + clearData() + reloadNavigation() + } + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData() + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + private fun reloadNavigation() { + view?.apply { + showPreButton(!currentDate.minusDays(7).isHolidays) + showNextButton(!currentDate.plusDays(7).isHolidays) + updateNavigationWeek( + "${currentDate.monday.toFormattedString("dd.MM")} - " + + currentDate.sunday.toFormattedString("dd.MM") + ) + } + } + + fun onDateSet(year: Int, month: Int, day: Int) { + reloadView(LocalDate.of(year, month, day)) + loadData() + } + + fun onPickDate() { + view?.showDatePickerDialog(currentDate) + } + + fun onPreviousWeek() { + reloadView(currentDate.minusDays(7)) + loadData() + } + + fun onNextWeek() { + reloadView(currentDate.plusDays(7)) + loadData() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryView.kt new file mode 100644 index 000000000..7b9b0294f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryView.kt @@ -0,0 +1,36 @@ +package io.github.wulkanowy.ui.modules.luckynumber.history + +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.ui.base.BaseView +import java.time.LocalDate + +interface LuckyNumberHistoryView : BaseView { + + val isViewEmpty: Boolean + + fun initView() + + fun updateData(data: List) + + fun clearData() + + fun showEmpty(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun updateNavigationWeek(date: String) + + fun showProgress(show: Boolean) + + fun showPreButton(show: Boolean) + + fun showNextButton(show: Boolean) + + fun showDatePickerDialog(selectedDate: LocalDate) + + fun showContent(show: Boolean) + + fun onDestroyView() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt new file mode 100644 index 000000000..024beff85 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt @@ -0,0 +1,111 @@ +package io.github.wulkanowy.ui.modules.luckynumberwidget + +import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS +import android.content.Intent +import android.os.Build +import android.os.Bundle +import android.widget.Toast +import androidx.appcompat.app.AlertDialog +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.databinding.ActivityWidgetConfigureBinding +import io.github.wulkanowy.ui.base.BaseActivity +import io.github.wulkanowy.ui.base.WidgetConfigureAdapter +import io.github.wulkanowy.ui.modules.login.LoginActivity +import io.github.wulkanowy.utils.AppInfo +import javax.inject.Inject + +@AndroidEntryPoint +class LuckyNumberWidgetConfigureActivity : + BaseActivity(), + LuckyNumberWidgetConfigureView { + + @Inject + lateinit var configureAdapter: WidgetConfigureAdapter + + @Inject + override lateinit var presenter: LuckyNumberWidgetConfigurePresenter + + @Inject + lateinit var appInfo: AppInfo + + private var dialog: AlertDialog? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setResult(RESULT_CANCELED) + setContentView( + ActivityWidgetConfigureBinding.inflate(layoutInflater).apply { binding = this }.root + ) + + intent.extras.let { + presenter.onAttachView(this, it?.getInt(EXTRA_APPWIDGET_ID)) + } + } + + override fun initView() { + with(binding.widgetConfigureRecycler) { + adapter = configureAdapter + layoutManager = LinearLayoutManager(context) + } + + configureAdapter.onClickListener = presenter::onItemSelect + } + + override fun showThemeDialog() { + var items = arrayOf( + getString(R.string.widget_timetable_theme_light), + getString(R.string.widget_timetable_theme_dark) + ) + if (appInfo.systemVersion >= Build.VERSION_CODES.Q) items += (getString(R.string.widget_timetable_theme_system)) + + dialog = AlertDialog.Builder(this, R.style.WulkanowyTheme_WidgetAccountSwitcher) + .setTitle(R.string.widget_timetable_theme_title) + .setOnDismissListener { presenter.onDismissThemeView() } + .setSingleChoiceItems(items, -1) { _, which -> + presenter.onThemeSelect(which) + } + .show() + } + + override fun updateData(data: List, selectedStudentId: Long) { + with(configureAdapter) { + selectedId = selectedStudentId + items = data + notifyDataSetChanged() + } + } + + override fun updateLuckyNumberWidget(widgetId: Int) { + sendBroadcast(Intent(this, LuckyNumberWidgetProvider::class.java) + .apply { + action = ACTION_APPWIDGET_UPDATE + putExtra(EXTRA_APPWIDGET_IDS, intArrayOf(widgetId)) + }) + } + + override fun setSuccessResult(widgetId: Int) { + setResult(RESULT_OK, Intent().apply { putExtra(EXTRA_APPWIDGET_ID, widgetId) }) + } + + override fun showError(text: String, error: Throwable) { + Toast.makeText(this, text, Toast.LENGTH_LONG).show() + } + + override fun finishView() { + finish() + } + + override fun openLoginView() { + startActivity(LoginActivity.getStartIntent(this)) + } + + override fun onDestroy() { + super.onDestroy() + dialog?.dismiss() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt new file mode 100644 index 000000000..cac648da8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt @@ -0,0 +1,83 @@ +package io.github.wulkanowy.ui.modules.luckynumberwidget + +import io.github.wulkanowy.data.Resource +import io.github.wulkanowy.data.db.SharedPrefProvider +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.resourceFlow +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider.Companion.getStudentWidgetKey +import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider.Companion.getThemeWidgetKey +import kotlinx.coroutines.flow.onEach +import timber.log.Timber +import javax.inject.Inject + +class LuckyNumberWidgetConfigurePresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val sharedPref: SharedPrefProvider +) : BasePresenter(errorHandler, studentRepository) { + + private var appWidgetId: Int? = null + + private var selectedStudent: Student? = null + + fun onAttachView(view: LuckyNumberWidgetConfigureView, appWidgetId: Int?) { + super.onAttachView(view) + this.appWidgetId = appWidgetId + view.initView() + loadData() + } + + fun onItemSelect(student: Student) { + selectedStudent = student + view?.showThemeDialog() + } + + fun onThemeSelect(index: Int) { + appWidgetId?.let { + sharedPref.putLong(getThemeWidgetKey(it), index.toLong()) + } + registerStudent(selectedStudent) + } + + fun onDismissThemeView() { + view?.finishView() + } + + private fun loadData() { + resourceFlow { studentRepository.getSavedStudents(false) }.onEach { + when (it) { + is Resource.Loading -> Timber.d("Lucky number widget configure students data load") + is Resource.Success -> { + val selectedStudentId = appWidgetId?.let { id -> + sharedPref.getLong(getStudentWidgetKey(id), 0) + } ?: -1 + when { + it.data.isEmpty() -> view?.openLoginView() + it.data.size == 1 -> { + selectedStudent = it.data.single().student + view?.showThemeDialog() + } + else -> view?.updateData(it.data, selectedStudentId) + } + } + is Resource.Error -> errorHandler.dispatch(it.error) + } + }.launch() + } + + private fun registerStudent(student: Student?) { + requireNotNull(student) + + appWidgetId?.let { id -> + sharedPref.putLong(getStudentWidgetKey(id), student.id) + view?.run { + updateLuckyNumberWidget(id) + setSuccessResult(id) + } + } + view?.finishView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt new file mode 100644 index 000000000..b4556f7ef --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt @@ -0,0 +1,21 @@ +package io.github.wulkanowy.ui.modules.luckynumberwidget + +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.ui.base.BaseView + +interface LuckyNumberWidgetConfigureView : BaseView { + + fun initView() + + fun showThemeDialog() + + fun updateData(data: List, selectedStudentId: Long) + + fun updateLuckyNumberWidget(widgetId: Int) + + fun setSuccessResult(widgetId: Int) + + fun finishView() + + fun openLoginView() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt new file mode 100644 index 000000000..e03e3e90e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt @@ -0,0 +1,202 @@ +package io.github.wulkanowy.ui.modules.luckynumberwidget + +import android.app.PendingIntent +import android.appwidget.AppWidgetManager +import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT +import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH +import android.appwidget.AppWidgetProvider +import android.content.Context +import android.content.res.Configuration +import android.os.Bundle +import android.view.View.GONE +import android.view.View.VISIBLE +import android.widget.RemoteViews +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.Resource +import io.github.wulkanowy.data.dataOrNull +import io.github.wulkanowy.data.db.SharedPrefProvider +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.data.exceptions.NoCurrentStudentException +import io.github.wulkanowy.data.repositories.LuckyNumberRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.toFirstResult +import io.github.wulkanowy.ui.modules.Destination +import io.github.wulkanowy.ui.modules.splash.SplashActivity +import io.github.wulkanowy.utils.PendingIntentCompat +import kotlinx.coroutines.runBlocking +import timber.log.Timber +import javax.inject.Inject + +@AndroidEntryPoint +class LuckyNumberWidgetProvider : AppWidgetProvider() { + + @Inject + lateinit var studentRepository: StudentRepository + + @Inject + lateinit var luckyNumberRepository: LuckyNumberRepository + + @Inject + lateinit var sharedPref: SharedPrefProvider + + companion object { + + const val LUCKY_NUMBER_PENDING_INTENT_ID = 200 + + fun getStudentWidgetKey(appWidgetId: Int) = "lucky_number_widget_student_$appWidgetId" + + fun getThemeWidgetKey(appWidgetId: Int) = "lucky_number_widget_theme_$appWidgetId" + + fun getHeightWidgetKey(appWidgetId: Int) = "lucky_number_widget_height_$appWidgetId" + + fun getWidthWidgetKey(appWidgetId: Int) = "lucky_number_widget_width_$appWidgetId" + } + + override fun onUpdate( + context: Context, + appWidgetManager: AppWidgetManager, + appWidgetIds: IntArray? + ) { + super.onUpdate(context, appWidgetManager, appWidgetIds) + appWidgetIds?.forEach { appWidgetId -> + val luckyNumber = + getLuckyNumber(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), appWidgetId) + val appIntent = PendingIntent.getActivity( + context, + LUCKY_NUMBER_PENDING_INTENT_ID, + SplashActivity.getStartIntent(context, Destination.LuckyNumber), + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE + ) + + if (luckyNumber is Resource.Error) { + Timber.e("Error loading lucky number for widget", luckyNumber.error) + } + + val remoteView = + RemoteViews(context.packageName, getCorrectLayoutId(appWidgetId, context)) + .apply { + setTextViewText( + R.id.luckyNumberWidgetNumber, + luckyNumber.dataOrNull?.luckyNumber?.toString() ?: "#" + ) + setOnClickPendingIntent(R.id.luckyNumberWidgetContainer, appIntent) + } + + setStyles(remoteView, appWidgetId) + appWidgetManager.updateAppWidget(appWidgetId, remoteView) + } + } + + override fun onDeleted(context: Context?, appWidgetIds: IntArray?) { + super.onDeleted(context, appWidgetIds) + appWidgetIds?.forEach { appWidgetId -> + with(sharedPref) { + delete(getHeightWidgetKey(appWidgetId)) + delete(getStudentWidgetKey(appWidgetId)) + delete(getThemeWidgetKey(appWidgetId)) + delete(getWidthWidgetKey(appWidgetId)) + } + } + } + + override fun onAppWidgetOptionsChanged( + context: Context, + appWidgetManager: AppWidgetManager, + appWidgetId: Int, + newOptions: Bundle? + ) { + super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions) + + val remoteView = RemoteViews(context.packageName, getCorrectLayoutId(appWidgetId, context)) + + setStyles(remoteView, appWidgetId, newOptions) + appWidgetManager.updateAppWidget(appWidgetId, remoteView) + } + + private fun setStyles(views: RemoteViews, appWidgetId: Int, options: Bundle? = null) { + val width = options?.getInt(OPTION_APPWIDGET_MIN_WIDTH) ?: sharedPref.getLong( + getWidthWidgetKey(appWidgetId), 74 + ).toInt() + val height = options?.getInt(OPTION_APPWIDGET_MAX_HEIGHT) ?: sharedPref.getLong( + getHeightWidgetKey(appWidgetId), 74 + ).toInt() + + with(sharedPref) { + putLong(getWidthWidgetKey(appWidgetId), width.toLong()) + putLong(getHeightWidgetKey(appWidgetId), height.toLong()) + } + + val rows = getCellsForSize(height) + val cols = getCellsForSize(width) + + Timber.d("New lucky number widget measurement: %dx%d", width, height) + Timber.d("Widget size: $cols x $rows") + + when { + 1 == cols && 1 == rows -> views.setVisibility(imageTop = false, imageLeft = false) + 1 == cols && 1 < rows -> views.setVisibility(imageTop = true, imageLeft = false) + 1 < cols && 1 == rows -> views.setVisibility(imageTop = false, imageLeft = true) + 1 == cols && 1 == rows -> views.setVisibility(imageTop = true, imageLeft = false) + 2 == cols && 1 == rows -> views.setVisibility(imageTop = false, imageLeft = true) + else -> views.setVisibility(imageTop = false, imageLeft = false, title = true) + } + } + + private fun RemoteViews.setVisibility( + imageTop: Boolean, + imageLeft: Boolean, + title: Boolean = false + ) { + setViewVisibility(R.id.luckyNumberWidgetImageTop, if (imageTop) VISIBLE else GONE) + setViewVisibility(R.id.luckyNumberWidgetImageLeft, if (imageLeft) VISIBLE else GONE) + setViewVisibility(R.id.luckyNumberWidgetTitle, if (title) VISIBLE else GONE) + setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE) + } + + private fun getCellsForSize(size: Int): Int { + var n = 2 + while (74 * n - 30 < size) ++n + return n - 1 + } + + private fun getLuckyNumber(studentId: Long, appWidgetId: Int) = runBlocking { + try { + val students = studentRepository.getSavedStudents() + val student = students.singleOrNull { it.student.id == studentId }?.student + val currentStudent = when { + student != null -> student + studentId != 0L && studentRepository.isCurrentStudentSet() -> { + studentRepository.getCurrentStudent(false).also { + sharedPref.putLong(getStudentWidgetKey(appWidgetId), it.id) + } + } + else -> null + } + + if (currentStudent != null) { + luckyNumberRepository.getLuckyNumber(currentStudent, forceRefresh = false) + .toFirstResult() + } else { + Resource.Success(null) + } + } catch (e: Exception) { + if (e.cause !is NoCurrentStudentException) { + Timber.e(e, "An error has occurred in lucky number provider") + } + Resource.Error(e) + } + } + + private fun getCorrectLayoutId(appWidgetId: Int, context: Context): Int { + val savedTheme = sharedPref.getLong(getThemeWidgetKey(appWidgetId), 0) + val isSystemDarkMode = + context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES + + return if (savedTheme == 1L || (savedTheme == 2L && isSystemDarkMode)) { + R.layout.widget_luckynumber_dark + } else { + R.layout.widget_luckynumber + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt new file mode 100644 index 000000000..1bfc8ba58 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt @@ -0,0 +1,294 @@ +package io.github.wulkanowy.ui.modules.main + +import android.content.Context +import android.content.Intent +import android.os.Build.VERSION_CODES.P +import android.os.Bundle +import android.view.Menu +import android.view.MenuItem +import androidx.core.view.ViewCompat +import androidx.core.view.isVisible +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.Fragment +import androidx.preference.Preference +import androidx.preference.PreferenceFragmentCompat +import com.google.android.material.elevation.ElevationOverlayProvider +import com.ncapdevi.fragnav.FragNavController +import com.ncapdevi.fragnav.FragNavController.Companion.HIDE +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.databinding.ActivityMainBinding +import io.github.wulkanowy.ui.base.BaseActivity +import io.github.wulkanowy.ui.modules.Destination +import io.github.wulkanowy.ui.modules.account.accountquick.AccountQuickDialog +import io.github.wulkanowy.utils.* +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import timber.log.Timber +import javax.inject.Inject + +@AndroidEntryPoint +class MainActivity : BaseActivity(), MainView, + PreferenceFragmentCompat.OnPreferenceStartFragmentCallback { + + @Inject + override lateinit var presenter: MainPresenter + + @Inject + lateinit var analytics: AnalyticsHelper + + @Inject + lateinit var updateHelper: UpdateHelper + + @Inject + lateinit var inAppReviewHelper: InAppReviewHelper + + @Inject + lateinit var appInfo: AppInfo + + private var accountMenu: MenuItem? = null + + private val overlayProvider by lazy { ElevationOverlayProvider(this) } + + private val navController = + FragNavController(supportFragmentManager, R.id.main_fragment_container) + + companion object { + + private const val EXTRA_START_DESTINATION = "start_destination_json" + + fun getStartIntent( + context: Context, + destination: Destination? = null, + ) = Intent(context, MainActivity::class.java).apply { + destination?.let { putExtra(EXTRA_START_DESTINATION, Json.encodeToString(it)) } + } + } + + override val isRootView get() = navController.isRootFragment + + override val currentStackSize get() = navController.currentStack?.size + + override val currentViewTitle + get() = (navController.currentFrag as? MainView.TitledView)?.titleStringId + ?.let { getString(it) } + + override val currentViewSubtitle get() = (navController.currentFrag as? MainView.TitledView)?.subtitleString + + private var savedInstanceState: Bundle? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(ActivityMainBinding.inflate(layoutInflater).apply { binding = this }.root) + setSupportActionBar(binding.mainToolbar) + this.savedInstanceState = savedInstanceState + messageContainer = binding.mainMessageContainer + updateHelper.messageContainer = binding.mainFragmentContainer + + val destination = intent.getStringExtra(EXTRA_START_DESTINATION) + ?.takeIf { savedInstanceState == null } + + presenter.onAttachView(this, destination) + updateHelper.checkAndInstallUpdates(this) + } + + override fun onResume() { + super.onResume() + updateHelper.onResume(this) + } + + //https://developer.android.com/guide/playcore/in-app-updates#status_callback + @Suppress("DEPRECATION") + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + updateHelper.onActivityResult(requestCode, resultCode) + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.action_menu_main, menu) + accountMenu = menu.findItem(R.id.mainMenuAccount) + + presenter.onActionMenuCreated() + return true + } + + override fun initView(startMenuIndex: Int, rootDestinations: List) { + initializeToolbar() + initializeBottomNavigation(startMenuIndex) + initializeNavController(startMenuIndex, rootDestinations) + } + + private fun initializeNavController(startMenuIndex: Int, rootDestinations: List) { + with(navController) { + setOnViewChangeListener { destinationView -> + presenter.onViewChange(destinationView) + analytics.setCurrentScreen( + this@MainActivity, + destinationView::class.java.simpleName + ) + } + fragmentHideStrategy = HIDE + rootFragments = rootDestinations.map { it.destinationFragment } + + initialize(startMenuIndex, savedInstanceState) + } + savedInstanceState = null + } + + private fun initializeToolbar() { + with(binding.mainToolbar) { + stateListAnimator = null + setBackgroundColor( + overlayProvider.compositeOverlayWithThemeSurfaceColorIfNeeded(dpToPx(4f)) + ) + } + } + + private fun initializeBottomNavigation(startMenuIndex: Int) { + with(binding.mainBottomNav) { + with(menu) { + add(Menu.NONE, 0, Menu.NONE, R.string.dashboard_title) + .setIcon(R.drawable.ic_main_dashboard) + add(Menu.NONE, 1, Menu.NONE, R.string.grade_title) + .setIcon(R.drawable.ic_main_grade) + add(Menu.NONE, 2, Menu.NONE, R.string.attendance_title) + .setIcon(R.drawable.ic_main_attendance) + add(Menu.NONE, 3, Menu.NONE, R.string.timetable_title) + .setIcon(R.drawable.ic_main_timetable) + add(Menu.NONE, 4, Menu.NONE, R.string.more_title) + .setIcon(R.drawable.ic_main_more) + } + selectedItemId = startMenuIndex + setOnItemSelectedListener { + this@MainActivity.presenter.onTabSelected(it.itemId, false) + } + setOnItemReselectedListener { + this@MainActivity.presenter.onTabSelected(it.itemId, true) + } + } + } + + override fun onPreferenceStartFragment( + caller: PreferenceFragmentCompat, + pref: Preference + ): Boolean { + val fragment = supportFragmentManager.fragmentFactory.instantiate( + classLoader, + pref.fragment.toString() + ) + pushView(fragment) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return if (item.itemId == R.id.mainMenuAccount) presenter.onAccountManagerSelected() + else false + } + + override fun onSupportNavigateUp(): Boolean { + return presenter.onUpNavigate() + } + + override fun switchMenuView(position: Int) { + if (supportFragmentManager.isStateSaved) return + + analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName) + navController.switchTab(position) + } + + override fun setViewTitle(title: String) { + supportActionBar?.title = title + } + + override fun setViewSubTitle(subtitle: String?) { + supportActionBar?.subtitle = subtitle + } + + override fun showHomeArrow(show: Boolean) { + supportActionBar?.setDisplayHomeAsUpEnabled(show) + } + + override fun showAccountPicker(studentWithSemesters: List) { + showDialogFragment(AccountQuickDialog.newInstance(studentWithSemesters)) + } + + override fun showActionBarElevation(show: Boolean) { + ViewCompat.setElevation(binding.mainToolbar, if (show) dpToPx(4f) else 0f) + } + + override fun showBottomNavigation(show: Boolean) { + binding.mainBottomNav.isVisible = show + + if (appInfo.systemVersion >= P) { + window.navigationBarColor = if (show) { + getThemeAttrColor(android.R.attr.navigationBarColor) + } else { + getThemeAttrColor(R.attr.colorSurface) + } + } + } + + override fun openMoreDestination(destination: Destination) { + pushView(destination.destinationFragment) + } + + override fun notifyMenuViewReselected() { + (navController.currentStack?.getOrNull(0) as? MainView.MainChildView)?.onFragmentReselected() + } + + override fun notifyMenuViewChanged() { + Timber.d("Menu view changed") + (navController.currentStack?.getOrNull(0) as? MainView.MainChildView)?.onFragmentChanged() + } + + @Suppress("DEPRECATION") + fun showDialogFragment(dialog: DialogFragment) { + if (supportFragmentManager.isStateSaved) return + + //Deprecated method is used here to avoid fragnav bug + if (navController.currentDialogFrag?.fragmentManager == null) { + FragNavController::class.java.getDeclaredField("mCurrentDialogFrag").apply { + isAccessible = true + set(navController, null) + } + } + + navController.showDialogFragment(dialog) + } + + fun pushView(fragment: Fragment) { + if (supportFragmentManager.isStateSaved) return + + analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName) + navController.pushFragment(fragment) + } + + override fun popView(depth: Int) { + if (supportFragmentManager.isStateSaved) return + + analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName) + navController.safelyPopFragments(depth) + } + + override fun onBackPressed() { + presenter.onBackPressed { super.onBackPressed() } + } + + override fun showStudentAvatar(student: Student) { + accountMenu?.run { + icon = createNameInitialsDrawable(student.nickOrName, student.avatarColor, 0.44f) + title = getString(R.string.main_account_picker) + } + } + + override fun showInAppReview() { + inAppReviewHelper.showInAppReview(this) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + navController.onSaveInstanceState(outState) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt new file mode 100644 index 000000000..a07bdb371 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt @@ -0,0 +1,179 @@ +package io.github.wulkanowy.ui.modules.main + +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.data.logResourceStatus +import io.github.wulkanowy.data.onResourceError +import io.github.wulkanowy.data.onResourceSuccess +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.resourceFlow +import io.github.wulkanowy.services.sync.SyncManager +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.BaseView +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.ui.modules.Destination +import io.github.wulkanowy.ui.modules.account.AccountView +import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsView +import io.github.wulkanowy.ui.modules.grade.GradeView +import io.github.wulkanowy.ui.modules.message.MessageView +import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersView +import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView +import io.github.wulkanowy.utils.AnalyticsHelper +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json +import timber.log.Timber +import java.time.Duration +import java.time.Instant +import javax.inject.Inject + +class MainPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val prefRepository: PreferencesRepository, + private val syncManager: SyncManager, + private val analytics: AnalyticsHelper, + private val json: Json +) : BasePresenter(errorHandler, studentRepository) { + + private var studentsWitSemesters: List? = null + + private val rootDestinationTypeList = listOf( + Destination.Type.DASHBOARD, + Destination.Type.GRADE, + Destination.Type.ATTENDANCE, + Destination.Type.TIMETABLE, + Destination.Type.MORE + ) + + private val Destination?.startMenuIndex + get() = when { + this == null -> prefRepository.startMenuIndex + destinationType in rootDestinationTypeList -> { + rootDestinationTypeList.indexOf(destinationType) + } + else -> 4 + } + + fun onAttachView(view: MainView, initDestinationJson: String?) { + super.onAttachView(view) + + val initDestination: Destination? = initDestinationJson?.let { json.decodeFromString(it) } + + val startMenuIndex = initDestination.startMenuIndex + val destinations = rootDestinationTypeList.map { + if (it == initDestination?.destinationType) initDestination else it.defaultDestination + } + + view.initView(startMenuIndex, destinations) + if (initDestination != null && startMenuIndex == 4) { + view.openMoreDestination(initDestination) + } + + syncManager.startPeriodicSyncWorker() + + analytics.logEvent("app_open", "destination" to initDestination.toString()) + Timber.i("Main view was initialized with $initDestination") + } + + fun onActionMenuCreated() { + if (!studentsWitSemesters.isNullOrEmpty()) { + showCurrentStudentAvatar() + return + } + + resourceFlow { studentRepository.getSavedStudents(false) } + .logResourceStatus("load student avatar") + .onResourceSuccess { + studentsWitSemesters = it + showCurrentStudentAvatar() + } + .onResourceError(errorHandler::dispatch) + .launch("avatar") + } + + fun onViewChange(destinationView: BaseView) { + view?.apply { + showBottomNavigation(shouldShowBottomNavigation(destinationView)) + showActionBarElevation(shouldShowActionBarElevation(destinationView)) + currentViewTitle?.let { setViewTitle(it) } + currentViewSubtitle?.let { setViewSubTitle(it.ifBlank { null }) } + currentStackSize?.let { + if (it > 1) showHomeArrow(true) + else showHomeArrow(false) + } + } + } + + private fun shouldShowActionBarElevation(destination: BaseView) = when (destination) { + is GradeView, + is MessageView, + is SchoolAndTeachersView -> false + else -> true + } + + private fun shouldShowBottomNavigation(destination: BaseView) = when (destination) { + is AccountView, + is StudentInfoView, + is AccountDetailsView -> false + else -> true + } + + fun onAccountManagerSelected(): Boolean { + if (studentsWitSemesters.isNullOrEmpty()) return true + + Timber.i("Select account manager") + view?.showAccountPicker(studentsWitSemesters!!) + return true + } + + fun onUpNavigate(): Boolean { + Timber.i("Up navigate pressed") + view?.popView() + return true + } + + fun onBackPressed(default: () -> Unit) { + Timber.i("Back pressed in main view") + view?.run { + if (isRootView) default() + else popView() + } + } + + fun onTabSelected(index: Int, wasSelected: Boolean): Boolean { + return view?.run { + Timber.i("Switch main tab index: $index, reselected: $wasSelected") + if (wasSelected) { + notifyMenuViewReselected() + false + } else { + notifyMenuViewChanged() + switchMenuView(index) + checkInAppReview() + true + } + } == true + } + + private fun checkInAppReview() { + prefRepository.inAppReviewCount++ + + if (prefRepository.inAppReviewDate == null) { + prefRepository.inAppReviewDate = Instant.now() + } + + if (!prefRepository.isAppReviewDone && prefRepository.inAppReviewCount >= 50 && + Instant.now().minus(Duration.ofDays(14)).isAfter(prefRepository.inAppReviewDate) + ) { + view?.showInAppReview() + prefRepository.isAppReviewDone = true + } + } + + private fun showCurrentStudentAvatar() { + val currentStudent = + studentsWitSemesters?.singleOrNull { it.student.isCurrent }?.student ?: return + + view?.showStudentAvatar(currentStudent) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt new file mode 100644 index 000000000..3a57fcc6b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt @@ -0,0 +1,61 @@ +package io.github.wulkanowy.ui.modules.main + +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.ui.base.BaseView +import io.github.wulkanowy.ui.modules.Destination + +interface MainView : BaseView { + + val isRootView: Boolean + + val currentViewTitle: String? + + val currentViewSubtitle: String? + + val currentStackSize: Int? + + fun initView(startMenuIndex: Int, rootDestinations: List) + + fun switchMenuView(position: Int) + + fun showHomeArrow(show: Boolean) + + fun showAccountPicker(studentWithSemesters: List) + + fun showActionBarElevation(show: Boolean) + + fun showBottomNavigation(show: Boolean) + + fun notifyMenuViewReselected() + + fun notifyMenuViewChanged() + + fun setViewTitle(title: String) + + fun setViewSubTitle(subtitle: String?) + + fun popView(depth: Int = 1) + + fun showStudentAvatar(student: Student) + + fun showInAppReview() + + fun openMoreDestination(destination: Destination) + + interface MainChildView { + + fun onFragmentReselected() + + fun onFragmentChanged() {} + } + + interface TitledView { + + val titleStringId: Int + + var subtitleString: String + get() = "" + set(_) {} + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt new file mode 100644 index 000000000..4607793c9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt @@ -0,0 +1,150 @@ +package io.github.wulkanowy.ui.modules.message + +import android.os.Bundle +import android.view.View +import android.view.View.INVISIBLE +import android.view.View.VISIBLE +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.core.view.updateLayoutParams +import androidx.core.view.updateMargins +import com.google.android.material.tabs.TabLayoutMediator +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.enums.MessageFolder.* +import io.github.wulkanowy.databinding.FragmentMessageBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity +import io.github.wulkanowy.ui.modules.message.tab.MessageTabFragment +import io.github.wulkanowy.utils.dpToPx +import io.github.wulkanowy.utils.setOnSelectPageListener +import javax.inject.Inject + +@AndroidEntryPoint +class MessageFragment : BaseFragment(R.layout.fragment_message), + MessageView, MainView.TitledView { + + @Inject + lateinit var presenter: MessagePresenter + + private val pagerAdapter by lazy { + BaseFragmentPagerAdapter( + fragmentManager = childFragmentManager, + pagesCount = 3, + lifecycle = lifecycle, + ) + } + + companion object { + fun newInstance() = MessageFragment() + } + + override val titleStringId get() = R.string.message_title + + override val currentPageIndex get() = binding.messageViewPager.currentItem + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentMessageBinding.bind(view) + presenter.onAttachView(this) + } + + override fun initView() { + with(binding.messageViewPager) { + adapter = pagerAdapter + offscreenPageLimit = 2 + setOnSelectPageListener(presenter::onPageSelected) + } + + with(pagerAdapter) { + containerId = binding.messageViewPager.id + titleFactory = { + when (it) { + 0 -> getString(R.string.message_inbox) + 1 -> getString(R.string.message_sent) + 2 -> getString(R.string.message_trash) + else -> throw IllegalStateException() + } + } + itemFactory = { + when (it) { + 0 -> MessageTabFragment.newInstance(RECEIVED) + 1 -> MessageTabFragment.newInstance(SENT) + 2 -> MessageTabFragment.newInstance(TRASHED) + else -> throw IllegalStateException() + } + } + TabLayoutMediator(binding.messageTabLayout, binding.messageViewPager, this).attach() + } + + binding.messageTabLayout.elevation = requireContext().dpToPx(4f) + binding.openSendMessageButton.setOnClickListener { presenter.onSendMessageButtonClicked() } + } + + override fun showContent(show: Boolean) { + with(binding) { + messageViewPager.visibility = if (show) VISIBLE else INVISIBLE + messageTabLayout.visibility = if (show) VISIBLE else INVISIBLE + } + } + + override fun showProgress(show: Boolean) { + binding.messageProgress.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showNewMessage(show: Boolean) { + binding.openSendMessageButton.run { + if (show) show() else hide() + } + } + + override fun showTabLayout(show: Boolean) { + binding.messageTabLayout.isVisible = show + + with(binding.messageViewPager) { + isUserInputEnabled = show + updateLayoutParams { + updateMargins(top = if (show) requireContext().dpToPx(48f).toInt() else 0) + } + } + } + + fun onChildFragmentShowActionMode(show: Boolean) { + presenter.onChildViewShowActionMode(show) + } + + fun onChildFragmentLoaded() { + presenter.onChildViewLoaded() + } + + fun onChildFragmentShowNewMessage(show: Boolean) { + presenter.onChildViewShowNewMessage(show) + } + + fun onFragmentChanged() { + presenter.onFragmentChanged() + } + + override fun notifyChildLoadData(index: Int, forceRefresh: Boolean) { + (pagerAdapter.getFragmentInstance(index) as? MessageTabFragment) + ?.onParentLoadData(forceRefresh) + } + + override fun notifyChildrenFinishActionMode() { + repeat(3) { + (pagerAdapter.getFragmentInstance(it) as? MessageTabFragment) + ?.onParentFinishActionMode() + } + } + + override fun openSendMessage() { + context?.let { it.startActivity(SendMessageActivity.getStartIntent(it)) } + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt new file mode 100644 index 000000000..68bdc4b7c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt @@ -0,0 +1,60 @@ +package io.github.wulkanowy.ui.modules.message + +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import kotlinx.coroutines.launch +import timber.log.Timber +import javax.inject.Inject + +class MessagePresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository +) : BasePresenter(errorHandler, studentRepository) { + + override fun onAttachView(view: MessageView) { + super.onAttachView(view) + presenterScope.launch { + view.initView() + Timber.i("Message view was initialized") + loadData() + } + } + + fun onPageSelected(index: Int) { + loadChild(index) + view?.notifyChildrenFinishActionMode() + } + + private fun loadData() { + view?.run { loadChild(currentPageIndex) } + } + + private fun loadChild(index: Int, forceRefresh: Boolean = false) { + Timber.i("Load message child view index: $index") + view?.notifyChildLoadData(index, forceRefresh) + } + + fun onFragmentChanged() { + view?.notifyChildrenFinishActionMode() + } + + fun onChildViewLoaded() { + view?.apply { + showContent(true) + showProgress(false) + } + } + + fun onChildViewShowNewMessage(show: Boolean) { + view?.showNewMessage(show) + } + + fun onChildViewShowActionMode(show: Boolean) { + view?.showTabLayout(!show) + } + + fun onSendMessageButtonClicked() { + view?.openSendMessage() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt new file mode 100644 index 000000000..e0cc5098c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt @@ -0,0 +1,24 @@ +package io.github.wulkanowy.ui.modules.message + +import io.github.wulkanowy.ui.base.BaseView + +interface MessageView : BaseView { + + val currentPageIndex: Int + + fun initView() + + fun showContent(show: Boolean) + + fun showProgress(show: Boolean) + + fun showNewMessage(show: Boolean) + + fun showTabLayout(show: Boolean) + + fun notifyChildLoadData(index: Int, forceRefresh: Boolean) + + fun notifyChildrenFinishActionMode() + + fun openSendMessage() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt new file mode 100644 index 000000000..d75128be1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt @@ -0,0 +1,122 @@ +package io.github.wulkanowy.ui.modules.message.preview + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.MessageAttachment +import io.github.wulkanowy.data.db.entities.MessageWithAttachment +import io.github.wulkanowy.databinding.ItemMessageAttachmentBinding +import io.github.wulkanowy.databinding.ItemMessageDividerBinding +import io.github.wulkanowy.databinding.ItemMessagePreviewBinding +import io.github.wulkanowy.utils.openInternetBrowser +import io.github.wulkanowy.utils.toFormattedString +import javax.inject.Inject + +class MessagePreviewAdapter @Inject constructor() : + RecyclerView.Adapter() { + + enum class ViewType(val id: Int) { + MESSAGE(1), + DIVIDER(2), + ATTACHMENT(3) + } + + var messageWithAttachment: MessageWithAttachment? = null + set(value) { + field = value + attachments = value?.attachments.orEmpty() + } + + private var attachments: List = emptyList() + + override fun getItemCount() = + if (messageWithAttachment == null) 0 else attachments.size + 1 + if (attachments.isNotEmpty()) 1 else 0 + + override fun getItemViewType(position: Int) = when (position) { + 0 -> ViewType.MESSAGE.id + 1 -> ViewType.DIVIDER.id + else -> ViewType.ATTACHMENT.id + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + + return when (viewType) { + ViewType.MESSAGE.id -> MessageViewHolder( + ItemMessagePreviewBinding.inflate(inflater, parent, false) + ) + ViewType.DIVIDER.id -> DividerViewHolder( + ItemMessageDividerBinding.inflate(inflater, parent, false) + ) + ViewType.ATTACHMENT.id -> AttachmentViewHolder( + ItemMessageAttachmentBinding.inflate(inflater, parent, false) + ) + else -> throw IllegalStateException() + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is MessageViewHolder -> bindMessage( + holder, + requireNotNull(messageWithAttachment).message + ) + is AttachmentViewHolder -> bindAttachment( + holder, + requireNotNull(messageWithAttachment).attachments[position - 2] + ) + } + } + + @SuppressLint("SetTextI18n") + private fun bindMessage(holder: MessageViewHolder, message: Message) { + val context = holder.binding.root.context + val recipientCount = message.unreadBy + message.readBy + + val readText = when { + recipientCount > 1 -> { + context.getString(R.string.message_read_by, message.readBy, recipientCount) + } + message.readBy == 1 -> { + context.getString(R.string.message_read, context.getString(R.string.all_yes)) + } + else -> context.getString(R.string.message_read, context.getString(R.string.all_no)) + } + + with(holder.binding) { + messagePreviewSubject.text = + message.subject.ifBlank { root.context.getString(R.string.message_no_subject) } + messagePreviewDate.text = root.context.getString( + R.string.message_date, + message.date.toFormattedString("yyyy-MM-dd HH:mm:ss") + ) + messagePreviewRead.text = readText + messagePreviewContent.text = message.content + messagePreviewFromSender.text = message.sender + messagePreviewToRecipient.text = message.recipient + } + } + + private fun bindAttachment(holder: AttachmentViewHolder, attachment: MessageAttachment) { + with(holder.binding) { + messagePreviewAttachment.visibility = View.VISIBLE + messagePreviewAttachment.text = attachment.filename + root.setOnClickListener { + root.context.openInternetBrowser(attachment.url) { } + } + } + } + + class MessageViewHolder(val binding: ItemMessagePreviewBinding) : + RecyclerView.ViewHolder(binding.root) + + class DividerViewHolder(val binding: ItemMessageDividerBinding) : + RecyclerView.ViewHolder(binding.root) + + class AttachmentViewHolder(val binding: ItemMessageAttachmentBinding) : + RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt new file mode 100644 index 000000000..860ecc571 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt @@ -0,0 +1,211 @@ +package io.github.wulkanowy.ui.modules.message.preview + +import android.os.Bundle +import android.print.PrintAttributes +import android.print.PrintManager +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import android.webkit.WebResourceRequest +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.core.content.getSystemService +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.MessageWithAttachment +import io.github.wulkanowy.databinding.FragmentMessagePreviewBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity +import io.github.wulkanowy.utils.shareText +import javax.inject.Inject + +@AndroidEntryPoint +class MessagePreviewFragment : + BaseFragment(R.layout.fragment_message_preview), + MessagePreviewView, MainView.TitledView { + + @Inject + lateinit var presenter: MessagePreviewPresenter + + @Inject + lateinit var previewAdapter: MessagePreviewAdapter + + private var menuReplyButton: MenuItem? = null + + private var menuForwardButton: MenuItem? = null + + private var menuDeleteButton: MenuItem? = null + + private var menuShareButton: MenuItem? = null + + private var menuPrintButton: MenuItem? = null + + override val titleStringId: Int + get() = R.string.message_title + + override val deleteMessageSuccessString: String + get() = getString(R.string.message_delete_success) + + override val messageNoSubjectString: String + get() = getString(R.string.message_no_subject) + + override val printHTML: String + get() = requireContext().assets.open("message-print-page.html").bufferedReader().use { it.readText() } + + override val messageNotExists: String + get() = getString(R.string.message_not_exists) + + companion object { + const val MESSAGE_ID_KEY = "message_id" + + fun newInstance(message: Message): MessagePreviewFragment { + return MessagePreviewFragment().apply { + arguments = Bundle().apply { putSerializable(MESSAGE_ID_KEY, message) } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentMessagePreviewBinding.bind(view) + messageContainer = binding.messagePreviewContainer + presenter.onAttachView(this, (savedInstanceState ?: arguments)?.getSerializable(MESSAGE_ID_KEY) as? Message) + } + + override fun initView() { + binding.messagePreviewErrorDetails.setOnClickListener { presenter.onDetailsClick() } + + with(binding.messagePreviewRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = previewAdapter + } + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.action_menu_message_preview, menu) + menuReplyButton = menu.findItem(R.id.messagePreviewMenuReply) + menuForwardButton = menu.findItem(R.id.messagePreviewMenuForward) + menuDeleteButton = menu.findItem(R.id.messagePreviewMenuDelete) + menuShareButton = menu.findItem(R.id.messagePreviewMenuShare) + menuPrintButton = menu.findItem(R.id.messagePreviewMenuPrint) + presenter.onCreateOptionsMenu() + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.messagePreviewMenuReply -> presenter.onReply() + R.id.messagePreviewMenuForward -> presenter.onForward() + R.id.messagePreviewMenuDelete -> presenter.onMessageDelete() + R.id.messagePreviewMenuShare -> presenter.onShare() + R.id.messagePreviewMenuPrint -> presenter.onPrint() + else -> false + } + } + + override fun setMessageWithAttachment(item: MessageWithAttachment) { + with(previewAdapter) { + messageWithAttachment = item + notifyDataSetChanged() + } + } + + override fun showProgress(show: Boolean) { + binding.messagePreviewProgress.visibility = if (show) VISIBLE else GONE + } + + override fun showContent(show: Boolean) { + binding.messagePreviewRecycler.visibility = if (show) VISIBLE else GONE + } + + override fun showOptions(show: Boolean) { + menuReplyButton?.isVisible = show + menuForwardButton?.isVisible = show + menuDeleteButton?.isVisible = show + menuShareButton?.isVisible = show + menuPrintButton?.isVisible = show + } + + override fun setDeletedOptionsLabels() { + menuDeleteButton?.setTitle(R.string.message_delete_forever) + } + + override fun setNotDeletedOptionsLabels() { + menuDeleteButton?.setTitle(R.string.message_move_to_trash) + } + + override fun showErrorView(show: Boolean) { + binding.messagePreviewError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + binding.messagePreviewErrorMessage.text = message + } + + override fun setErrorRetryCallback(callback: () -> Unit) { + binding.messagePreviewErrorRetry.setOnClickListener { callback() } + } + + override fun openMessageReply(message: Message?) { + context?.let { it.startActivity(SendMessageActivity.getStartIntent(it, message, true)) } + } + + override fun openMessageForward(message: Message?) { + context?.let { it.startActivity(SendMessageActivity.getStartIntent(it, message)) } + } + + override fun shareText(text: String, subject: String) { + context?.shareText(text, subject) + } + + override fun printDocument(html: String, jobName: String) { + val webView = WebView(requireContext()) + webView.webViewClient = object : WebViewClient() { + + override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest) = false + + override fun onPageFinished(view: WebView, url: String) { + createWebPrintJob(view, jobName) + } + } + + webView.loadDataWithBaseURL("file:///android_asset/", html, "text/HTML", "UTF-8", null) + } + + private fun createWebPrintJob(webView: WebView, jobName: String) { + activity?.getSystemService()?.let { printManager -> + val printAdapter = webView.createPrintDocumentAdapter(jobName) + + printManager.print( + jobName, + printAdapter, + PrintAttributes.Builder().build() + ) + } + } + + override fun popView() { + (activity as MainActivity).popView() + } + + override fun onSaveInstanceState(outState: Bundle) { + outState.putSerializable(MESSAGE_ID_KEY, presenter.message) + super.onSaveInstanceState(outState) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt new file mode 100644 index 000000000..39c337bf2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt @@ -0,0 +1,228 @@ +package io.github.wulkanowy.ui.modules.message.preview + +import android.annotation.SuppressLint +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.MessageAttachment +import io.github.wulkanowy.data.enums.MessageFolder +import io.github.wulkanowy.data.repositories.MessageRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.toFormattedString +import kotlinx.coroutines.launch +import timber.log.Timber +import javax.inject.Inject + +class MessagePreviewPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val messageRepository: MessageRepository, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + var message: Message? = null + + var attachments: List? = null + + private lateinit var lastError: Throwable + + private var retryCallback: () -> Unit = {} + + fun onAttachView(view: MessagePreviewView, message: Message?) { + super.onAttachView(view) + view.initView() + errorHandler.showErrorMessage = ::showErrorViewOnError + this.message = message + loadData(requireNotNull(message)) + } + + private fun onMessageLoadRetry(message: Message) { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(message) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + private fun loadData(messageToLoad: Message) { + flatResourceFlow { + val student = studentRepository.getStudentById(messageToLoad.studentId) + messageRepository.getMessage(student, messageToLoad, true) + } + .logResourceStatus("message ${messageToLoad.messageId} preview") + .onResourceData { + if (it != null) { + message = it.message + attachments = it.attachments + view?.apply { + setMessageWithAttachment(it) + showContent(true) + initOptions() + } + } else { + view?.run { + showMessage(messageNotExists) + popView() + } + } + } + .onResourceSuccess { + if (it != null) { + analytics.logEvent( + "load_item", + "type" to "message_preview", + "length" to it.message.content.length + ) + } + } + .onResourceNotLoading { view?.showProgress(false) } + .onResourceError { + retryCallback = { onMessageLoadRetry(messageToLoad) } + errorHandler.dispatch(it) + } + .launch() + } + + fun onReply(): Boolean { + return if (message != null) { + view?.openMessageReply(message) + true + } else false + } + + fun onForward(): Boolean { + return if (message != null) { + view?.openMessageForward(message) + true + } else false + } + + fun onShare(): Boolean { + message?.let { + var text = + "Temat: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }}\n" + when (it.sender.isNotEmpty()) { + true -> "Od: ${it.sender}\n" + false -> "Do: ${it.recipient}\n" + } + "Data: ${it.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${it.content}" + + attachments?.let { attachments -> + if (attachments.isNotEmpty()) { + text += "\n\nZałączniki:" + + attachments.forEach { attachment -> + text += "\n${attachment.filename}: ${attachment.url}" + } + } + } + + view?.shareText( + text, + "FW: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }}" + ) + return true + } + return false + } + + @SuppressLint("NewApi") + fun onPrint(): Boolean { + message?.let { + val dateString = it.date.toFormattedString("yyyy-MM-dd HH:mm:ss") + val infoContent = "

Data wysłania

$dateString
" + when { + it.sender.isNotEmpty() -> "

Od

${it.sender}
" + else -> "

Do

${it.recipient}
" + } + + val messageContent = "

${it.content}

" + .replace(Regex("[\\n\\r]{2,}"), "

") + .replace(Regex("[\\n\\r]"), "
") + + val jobName = "Wiadomość " + when { + it.sender.isNotEmpty() -> "od ${it.sender}" + else -> "do ${it.recipient}" + } + " $dateString: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }} | Wulkanowy" + + view?.apply { + val html = printHTML + .replace( + "%SUBJECT%", + it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }) + .replace("%CONTENT%", messageContent) + .replace("%INFO%", infoContent) + printDocument(html, jobName) + } + return true + } + return false + } + + private fun deleteMessage() { + message ?: return + + view?.run { + showContent(false) + showProgress(true) + showOptions(false) + showErrorView(false) + } + + Timber.i("Delete message ${message?.id}") + + presenterScope.launch { + runCatching { + val student = studentRepository.getCurrentStudent() + messageRepository.deleteMessage(student, message!!) + } + .onFailure { + retryCallback = { onMessageDelete() } + errorHandler.dispatch(it) + } + .onSuccess { + view?.run { + showMessage(deleteMessageSuccessString) + popView() + } + } + + view?.showProgress(false) + } + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + lastError = error + setErrorDetails(message) + showContent(false) + showErrorView(true) + setErrorRetryCallback { retryCallback() } + } + } + + fun onMessageDelete(): Boolean { + deleteMessage() + return true + } + + private fun initOptions() { + view?.apply { + showOptions(message != null) + message?.let { + when (it.folderId == MessageFolder.TRASHED.id) { + true -> setDeletedOptionsLabels() + false -> setNotDeletedOptionsLabels() + } + } + + } + } + + fun onCreateOptionsMenu() { + initOptions() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt new file mode 100644 index 000000000..88fe77d94 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt @@ -0,0 +1,46 @@ +package io.github.wulkanowy.ui.modules.message.preview + +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.MessageWithAttachment +import io.github.wulkanowy.ui.base.BaseView + +interface MessagePreviewView : BaseView { + + val deleteMessageSuccessString: String + + val messageNoSubjectString: String + + val printHTML: String + + val messageNotExists: String + + fun initView() + + fun setMessageWithAttachment(item: MessageWithAttachment) + + fun showProgress(show: Boolean) + + fun showContent(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun setErrorRetryCallback(callback: () -> Unit) + + fun showOptions(show: Boolean) + + fun setDeletedOptionsLabels() + + fun setNotDeletedOptionsLabels() + + fun openMessageReply(message: Message?) + + fun openMessageForward(message: Message?) + + fun shareText(text: String, subject: String) + + fun popView() + + fun printDocument(html: String, jobName: String) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/RecipientChipItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/RecipientChipItem.kt new file mode 100644 index 000000000..bd14bc893 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/RecipientChipItem.kt @@ -0,0 +1,16 @@ +package io.github.wulkanowy.ui.modules.message.send + +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.materialchipsinput.ChipItem +import kotlinx.serialization.Serializable + +@Serializable +data class RecipientChipItem( + + override val title: String, + + override val summary: String, + + val recipient: Recipient + +) : ChipItem diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt new file mode 100644 index 000000000..70f9a9b54 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt @@ -0,0 +1,240 @@ +package io.github.wulkanowy.ui.modules.message.send + +import android.annotation.SuppressLint +import android.app.AlertDialog +import android.content.Context +import android.content.Intent +import android.graphics.Rect +import android.os.Bundle +import android.view.Menu +import android.view.MenuItem +import android.view.TouchDelegate +import android.view.View.GONE +import android.view.View.VISIBLE +import android.widget.Toast +import android.widget.Toast.LENGTH_LONG +import androidx.core.widget.doOnTextChanged +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.databinding.ActivitySendMessageBinding +import io.github.wulkanowy.ui.base.BaseActivity +import io.github.wulkanowy.utils.dpToPx +import io.github.wulkanowy.utils.hideSoftInput +import io.github.wulkanowy.utils.showSoftInput +import javax.inject.Inject + +@AndroidEntryPoint +class SendMessageActivity : BaseActivity(), + SendMessageView { + + @Inject + override lateinit var presenter: SendMessagePresenter + + companion object { + private const val EXTRA_MESSAGE = "EXTRA_MESSAGE" + + private const val EXTRA_REASON = "EXTRA_REASON" + + private const val EXTRA_REPLY = "EXTRA_REPLY" + + fun getStartIntent(context: Context) = Intent(context, SendMessageActivity::class.java) + + fun getStartIntent(context: Context, message: Message?, reply: Boolean = false): Intent { + return getStartIntent(context) + .putExtra(EXTRA_MESSAGE, message) + .putExtra(EXTRA_REPLY, reply) + } + + fun getStartIntent(context: Context, reason: String): Intent { + return getStartIntent(context) + .putExtra(EXTRA_REASON, reason) + } + } + + override val isDropdownListVisible: Boolean + get() = binding.sendMessageTo.isDropdownListVisible + + @Suppress("UNCHECKED_CAST") + override lateinit var formRecipientsData: List + + override lateinit var formSubjectValue: String + + override lateinit var formContentValue: String + + override val messageRequiredRecipients: String + get() = getString(R.string.message_required_recipients) + + override val messageContentMinLength: String + get() = getString(R.string.message_content_min_length) + + override val messageSuccess: String + get() = getString(R.string.message_send_successful) + + @Suppress("UNCHECKED_CAST") + public override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(ActivitySendMessageBinding.inflate(layoutInflater).apply { binding = this }.root) + setSupportActionBar(binding.sendMessageToolbar) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + messageContainer = binding.sendMessageContainer + + formRecipientsData = binding.sendMessageTo.addedChipItems as List + formSubjectValue = binding.sendMessageSubject.text.toString() + formContentValue = binding.sendMessageMessageContent.text.toString() + + presenter.onAttachView( + view = this, + reason = intent.getSerializableExtra(EXTRA_REASON) as? String, + message = intent.getSerializableExtra(EXTRA_MESSAGE) as? Message, + reply = intent.getSerializableExtra(EXTRA_REPLY) as? Boolean + ) + } + + @SuppressLint("ClickableViewAccessibility") + override fun initView() { + setUpExtendedHitArea() + with(binding) { + sendMessageScroll.setOnTouchListener { _, _ -> presenter.onTouchScroll() } + sendMessageTo.onChipAddListener = { onRecipientChange() } + sendMessageTo.onTextChangeListener = presenter::onRecipientsTextChange + sendMessageSubject.doOnTextChanged { text, _, _, _ -> onMessageSubjectChange(text) } + sendMessageMessageContent.doOnTextChanged { text, _, _, _ -> onMessageContentChange(text) } + } + } + + private fun onMessageSubjectChange(text: CharSequence?) { + formSubjectValue = text.toString() + presenter.onMessageContentChange() + } + + private fun onMessageContentChange(text: CharSequence?) { + formContentValue = text.toString() + presenter.onMessageContentChange() + } + + private fun onRecipientChange() { + presenter.onMessageContentChange() + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.action_menu_send_message, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return if (item.itemId == R.id.sendMessageMenuSend) presenter.onSend() + else false + } + + override fun onSupportNavigateUp(): Boolean { + return presenter.onUpNavigate() + } + + override fun setReportingUnit(unit: ReportingUnit) { + binding.sendMessageFrom.text = unit.senderName + } + + override fun setRecipients(recipients: List) { + binding.sendMessageTo.filterableChipItems = recipients + } + + override fun setSelectedRecipients(recipients: List) { + binding.sendMessageTo.addChips(recipients) + } + + override fun showProgress(show: Boolean) { + binding.sendMessageProgress.visibility = if (show) VISIBLE else GONE + } + + override fun showContent(show: Boolean) { + binding.sendMessageContent.visibility = if (show) VISIBLE else GONE + } + + override fun showEmpty(show: Boolean) { + binding.sendMessageEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showActionBar(show: Boolean) { + supportActionBar?.apply { if (show) show() else hide() } + } + + override fun setSubject(subject: String) { + binding.sendMessageSubject.setText(subject) + } + + override fun setContent(content: String) { + binding.sendMessageMessageContent.setText(content) + } + + override fun showMessage(text: String) { + Toast.makeText(this, text, LENGTH_LONG).show() + } + + override fun showSoftInput(show: Boolean) { + if (show) showSoftInput() else hideSoftInput() + } + + override fun hideDropdownList() { + binding.sendMessageTo.hideDropdownList() + } + + override fun scrollToRecipients() { + with(binding.sendMessageScroll) { + post { + scrollTo(0, binding.sendMessageTo.bottom - dpToPx(53f).toInt()) + } + } + } + + override fun popView() { + finish() + } + + private fun setUpExtendedHitArea() { + fun extendHitArea() { + val containerHitRect = Rect().apply { + binding.sendMessageContent.getHitRect(this) + } + + val contentHitRect = Rect().apply { + binding.sendMessageMessageContent.getHitRect(this) + } + + contentHitRect.top = contentHitRect.bottom + contentHitRect.bottom = containerHitRect.bottom + + binding.sendMessageContent.touchDelegate = + TouchDelegate(contentHitRect, binding.sendMessageMessageContent) + } + + with(binding.sendMessageMessageContent) { + post { + addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ -> extendHitArea() } + extendHitArea() + } + } + } + + override fun showMessageBackupDialog() { + AlertDialog.Builder(this) + .setTitle(R.string.message_title) + .setMessage(presenter.getMessageBackupContent(presenter.getRecipientsNames())) + .setPositiveButton(R.string.all_yes) { _, _ -> presenter.restoreMessageParts() } + .setNegativeButton(R.string.all_no) { _, _ -> presenter.clearDraft() } + .show() + } + + @Suppress("UNCHECKED_CAST") + override fun clearDraft() { + formRecipientsData = binding.sendMessageTo.addedChipItems as List + presenter.clearDraft() + } + + override fun getMessageBackupDialogString() = + resources.getString(R.string.message_restore_dialog) + + override fun getMessageBackupDialogStringWithRecipients(recipients: String) = + resources.getString(R.string.message_restore_dialog_with_recipients, recipients) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt new file mode 100644 index 000000000..e5770955a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt @@ -0,0 +1,277 @@ +package io.github.wulkanowy.ui.modules.message.send + +import io.github.wulkanowy.data.Resource +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.logResourceStatus +import io.github.wulkanowy.data.onResourceNotLoading +import io.github.wulkanowy.data.pojos.MessageDraft +import io.github.wulkanowy.data.repositories.* +import io.github.wulkanowy.data.resourceFlow +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.toFormattedString +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.consumeAsFlow +import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import timber.log.Timber +import javax.inject.Inject + +class SendMessagePresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val semesterRepository: SemesterRepository, + private val messageRepository: MessageRepository, + private val reportingUnitRepository: ReportingUnitRepository, + private val recipientRepository: RecipientRepository, + private val preferencesRepository: PreferencesRepository, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + private val messageUpdateChannel = Channel() + + fun onAttachView(view: SendMessageView, reason: String?, message: Message?, reply: Boolean?) { + super.onAttachView(view) + view.initView() + initializeSubjectStream() + Timber.i("Send message view was initialized") + loadData(message, reply) + with(view) { + if (messageRepository.draftMessage != null && reply == null) { + view.showMessageBackupDialog() + } + reason?.let { + setSubject("Usprawiedliwenie") + setContent(it) + } + message?.let { + setSubject( + when (reply) { + true -> "Re: " + else -> "FW: " + } + message.subject + ) + if (preferencesRepository.fillMessageContent || reply != true) { + setContent( + when (reply) { + true -> "\n\n" + else -> "" + } + when (message.sender.isNotEmpty()) { + true -> "Od: ${message.sender}\n" + false -> "Do: ${message.recipient}\n" + } + "Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${message.content}" + ) + } + } + } + } + + fun onTouchScroll(): Boolean { + return view?.run { + if (isDropdownListVisible) { + hideDropdownList() + true + } else false + } == true + } + + fun onRecipientsTextChange(text: String) { + if (text.isBlank()) return + view?.scrollToRecipients() + } + + fun onUpNavigate(): Boolean { + view?.popView() + return true + } + + fun onSend(): Boolean { + view?.run { + when { + formRecipientsData.isEmpty() -> showMessage(messageRequiredRecipients) + formContentValue.length < 3 -> showMessage(messageContentMinLength) + else -> { + sendMessage( + subject = formSubjectValue, + content = formContentValue, + recipients = formRecipientsData.map { it.recipient } + ) + return true + } + } + } + return false + } + + private fun loadData(message: Message?, reply: Boolean?) { + resourceFlow { + val student = studentRepository.getCurrentStudent() + val semester = semesterRepository.getCurrentSemester(student) + val unit = reportingUnitRepository.getReportingUnit(student, semester.unitId) + + Timber.i("Loading recipients started") + val recipients = when { + unit != null -> recipientRepository.getRecipients(student, unit, 2) + else -> listOf() + }.let { createChips(it) } + Timber.i("Loading recipients result: Success, fetched %d recipients", recipients.size) + + Timber.i("Loading message recipients started") + val messageRecipients = when { + message != null && reply == true -> recipientRepository.getMessageRecipients( + student, + message + ) + else -> emptyList() + }.let { createChips(it) } + Timber.i( + "Loaded message recipients to reply result: Success, fetched %d recipients", + messageRecipients.size + ) + + Triple(unit, recipients, messageRecipients) + } + .logResourceStatus("load recipients") + .onEach { + when (it) { + is Resource.Loading -> view?.run { + showProgress(true) + showContent(false) + } + is Resource.Success -> it.data.let { (reportingUnit, recipientChips, selectedRecipientChips) -> + view?.run { + if (reportingUnit != null) { + setReportingUnit(reportingUnit) + setRecipients(recipientChips) + if (selectedRecipientChips.isNotEmpty()) setSelectedRecipients( + selectedRecipientChips + ) + showContent(true) + } else { + Timber.i("Loading recipients result: Can't find the reporting unit") + view?.showEmpty(true) + } + } + } + is Resource.Error -> { + view?.showContent(true) + errorHandler.dispatch(it.error) + } + } + }.onResourceNotLoading { + view?.run { showProgress(false) } + }.launch() + } + + private fun sendMessage(subject: String, content: String, recipients: List) { + resourceFlow { + val student = studentRepository.getCurrentStudent() + messageRepository.sendMessage(student, subject, content, recipients) + }.logResourceStatus("sending message").onEach { + when (it) { + is Resource.Loading -> view?.run { + showSoftInput(false) + showContent(false) + showProgress(true) + showActionBar(false) + } + is Resource.Success -> { + view?.clearDraft() + view?.run { + showMessage(messageSuccess) + popView() + } + analytics.logEvent("send_message", "recipients" to recipients.size) + } + is Resource.Error -> { + view?.run { + showContent(true) + showProgress(false) + showActionBar(true) + } + errorHandler.dispatch(it.error) + } + } + }.launch("send") + } + + private fun createChips(recipients: List): List { + fun generateCorrectSummary(recipientRealName: String): String { + val substring = recipientRealName.substringBeforeLast("-") + return when { + substring == recipientRealName -> recipientRealName + substring.indexOf("(") != -1 -> { + recipientRealName.indexOf("(") + .let { recipientRealName.substring(if (it != -1) it else 0) } + } + substring.indexOf("[") != -1 -> { + recipientRealName.indexOf("[") + .let { recipientRealName.substring(if (it != -1) it else 0) } + } + else -> recipientRealName.substringAfter("-") + }.trim() + } + + return recipients.map { + RecipientChipItem( + title = it.name, + summary = generateCorrectSummary(it.realName), + recipient = it + ) + } + } + + fun onMessageContentChange() { + presenterScope.launch { + messageUpdateChannel.send(Unit) + } + } + + @OptIn(FlowPreview::class) + private fun initializeSubjectStream() { + presenterScope.launch { + messageUpdateChannel.consumeAsFlow() + .debounce(250) + .catch { Timber.e(it) } + .collect { + saveDraftMessage() + Timber.i("Draft message was saved!") + } + } + } + + private fun saveDraftMessage() { + messageRepository.draftMessage = MessageDraft( + view?.formRecipientsData!!, + view?.formSubjectValue!!, + view?.formContentValue!! + ) + } + + fun restoreMessageParts() { + val draftMessage = messageRepository.draftMessage ?: return + view?.setSelectedRecipients(draftMessage.recipients) + view?.setSubject(draftMessage.subject) + view?.setContent(draftMessage.content) + Timber.i("Continue work on draft") + } + + fun getRecipientsNames(): String { + return messageRepository.draftMessage?.recipients.orEmpty() + .joinToString { it.recipient.name } + } + + fun clearDraft() { + messageRepository.draftMessage = null + Timber.i("Draft cleared!") + } + + fun getMessageBackupContent(recipients: String) = + if (recipients.isEmpty()) view?.getMessageBackupDialogString() + else view?.getMessageBackupDialogStringWithRecipients(recipients) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt new file mode 100644 index 000000000..21b42e3e4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt @@ -0,0 +1,56 @@ +package io.github.wulkanowy.ui.modules.message.send + +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.ui.base.BaseView + +interface SendMessageView : BaseView { + val isDropdownListVisible: Boolean + + var formRecipientsData: List + + var formSubjectValue: String + + var formContentValue: String + + val messageRequiredRecipients: String + + val messageContentMinLength: String + + val messageSuccess: String + + fun initView() + + fun setReportingUnit(unit: ReportingUnit) + + fun setRecipients(recipients: List) + + fun setSelectedRecipients(recipients: List) + + fun showProgress(show: Boolean) + + fun showContent(show: Boolean) + + fun showEmpty(show: Boolean) + + fun showActionBar(show: Boolean) + + fun setSubject(subject: String) + + fun setContent(content: String) + + fun showSoftInput(show: Boolean) + + fun hideDropdownList() + + fun scrollToRecipients() + + fun popView() + + fun showMessageBackupDialog() + + fun getMessageBackupDialogString(): String + + fun getMessageBackupDialogStringWithRecipients(recipients: String): String + + fun clearDraft() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt new file mode 100644 index 000000000..af0923b94 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt @@ -0,0 +1,157 @@ +package io.github.wulkanowy.ui.modules.message.tab + +import android.graphics.Typeface +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.CompoundButton +import androidx.core.view.isVisible +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.R +import io.github.wulkanowy.data.enums.MessageFolder +import io.github.wulkanowy.databinding.ItemMessageBinding +import io.github.wulkanowy.databinding.ItemMessageChipsBinding +import io.github.wulkanowy.utils.toFormattedString +import javax.inject.Inject + +class MessageTabAdapter @Inject constructor() : + RecyclerView.Adapter() { + + var onItemClickListener: (MessageTabDataItem.MessageItem, position: Int) -> Unit = { _, _ -> } + + var onLongItemClickListener: (MessageTabDataItem.MessageItem) -> Unit = {} + + var onHeaderClickListener: (CompoundButton, Boolean) -> Unit = { _, _ -> } + + var onChangesDetectedListener = {} + + private var items = mutableListOf() + + fun submitData(data: List) { + val originalMessagesSize = items.count { it.viewType == MessageItemViewType.MESSAGE } + val newMessagesSize = data.count { it.viewType == MessageItemViewType.MESSAGE } + + if (originalMessagesSize != newMessagesSize) onChangesDetectedListener() + + val diffResult = DiffUtil.calculateDiff(MessageTabDiffUtil(items, data)) + items = data.toMutableList() + + diffResult.dispatchUpdatesTo(this) + } + + override fun getItemViewType(position: Int) = items[position].viewType.ordinal + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + + return when (MessageItemViewType.values()[viewType]) { + MessageItemViewType.MESSAGE -> ItemViewHolder( + ItemMessageBinding.inflate(inflater, parent, false) + ) + MessageItemViewType.FILTERS -> HeaderViewHolder( + ItemMessageChipsBinding.inflate(inflater, parent, false) + ) + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is ItemViewHolder -> bindItemViewHolder(holder, position) + is HeaderViewHolder -> bindHeaderViewHolder(holder, position) + } + } + + private fun bindHeaderViewHolder(holder: HeaderViewHolder, position: Int) { + val item = items[position] as MessageTabDataItem.FilterHeader + + with(holder.binding) { + if (item.onlyUnread == null) { + chipUnread.isVisible = false + } else { + chipUnread.isVisible = true + chipUnread.isChecked = item.onlyUnread + chipUnread.setOnCheckedChangeListener(onHeaderClickListener) + } + chipUnread.isEnabled = item.isEnabled + chipAttachments.isEnabled = item.isEnabled + chipAttachments.isChecked = item.onlyWithAttachments + chipAttachments.setOnCheckedChangeListener(onHeaderClickListener) + } + } + + private fun bindItemViewHolder(holder: ItemViewHolder, position: Int) { + val item = (items[position] as MessageTabDataItem.MessageItem) + val message = item.message + + with(holder.binding) { + val style = if (message.unread) Typeface.BOLD else Typeface.NORMAL + + messageItemAuthor.run { + text = if (message.folderId == MessageFolder.SENT.id) { + message.recipient + } else { + message.sender + } + setTypeface(null, style) + } + messageItemSubject.run { + text = message.subject.ifBlank { context.getString(R.string.message_no_subject) } + setTypeface(null, style) + } + messageItemDate.run { + text = message.date.toFormattedString() + setTypeface(null, style) + } + messageItemAttachmentIcon.isVisible = message.hasAttachments + + root.setOnClickListener { + holder.bindingAdapterPosition.let { + if (it != RecyclerView.NO_POSITION) { + onItemClickListener(item, it) + } + } + } + + root.setOnLongClickListener { + onLongItemClickListener(item) + return@setOnLongClickListener true + } + + with(messageItemCheckbox) { + isChecked = item.isSelected + isVisible = item.isActionMode + } + } + } + + class ItemViewHolder(val binding: ItemMessageBinding) : RecyclerView.ViewHolder(binding.root) + + class HeaderViewHolder(val binding: ItemMessageChipsBinding) : + RecyclerView.ViewHolder(binding.root) + + private class MessageTabDiffUtil( + private val old: List, + private val new: List + ) : DiffUtil.Callback() { + + override fun getOldListSize(): Int = old.size + + override fun getNewListSize(): Int = new.size + + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + val oldItem = old[oldItemPosition] + val newItem = new[newItemPosition] + + return if (oldItem is MessageTabDataItem.MessageItem && newItem is MessageTabDataItem.MessageItem) { + oldItem.message.id == newItem.message.id + } else { + oldItem.viewType == newItem.viewType + } + } + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) = + old[oldItemPosition] == new[newItemPosition] + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt new file mode 100644 index 000000000..634dfc0e7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt @@ -0,0 +1,20 @@ +package io.github.wulkanowy.ui.modules.message.tab + +import io.github.wulkanowy.data.db.entities.Message + +sealed class MessageTabDataItem(val viewType: MessageItemViewType) { + + data class MessageItem( + val message: Message, + val isSelected: Boolean, + val isActionMode: Boolean + ) : MessageTabDataItem(MessageItemViewType.MESSAGE) + + data class FilterHeader( + val onlyUnread: Boolean?, + val onlyWithAttachments: Boolean, + val isEnabled: Boolean + ) : MessageTabDataItem(MessageItemViewType.FILTERS) +} + +enum class MessageItemViewType { FILTERS, MESSAGE } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt new file mode 100644 index 000000000..654b0e226 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt @@ -0,0 +1,262 @@ +package io.github.wulkanowy.ui.modules.message.tab + +import android.os.Bundle +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.View.* +import android.widget.CompoundButton +import androidx.appcompat.view.ActionMode +import androidx.appcompat.widget.SearchView +import androidx.core.view.updatePadding +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.enums.MessageFolder +import io.github.wulkanowy.databinding.FragmentMessageTabBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.message.MessageFragment +import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment +import io.github.wulkanowy.ui.widgets.DividerItemDecoration +import io.github.wulkanowy.utils.dpToPx +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.hideSoftInput +import javax.inject.Inject + +@AndroidEntryPoint +class MessageTabFragment : BaseFragment(R.layout.fragment_message_tab), + MessageTabView { + + @Inject + lateinit var presenter: MessageTabPresenter + + @Inject + lateinit var messageTabAdapter: MessageTabAdapter + + companion object { + + const val MESSAGE_TAB_FOLDER_ID = "message_tab_folder_id" + + fun newInstance(folder: MessageFolder): MessageTabFragment { + return MessageTabFragment().apply { + arguments = Bundle().apply { + putString(MESSAGE_TAB_FOLDER_ID, folder.name) + } + } + } + } + + override val isViewEmpty + get() = messageTabAdapter.itemCount == 0 + + private var actionMode: ActionMode? = null + + private val actionModeCallback = object : ActionMode.Callback { + override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { + val inflater = mode.menuInflater + inflater.inflate(R.menu.context_menu_message_tab, menu) + return true + } + + override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { + if (presenter.folder == MessageFolder.TRASHED) { + val menuItem = menu.findItem(R.id.messageTabContextMenuDelete) + menuItem.setTitle(R.string.message_delete_forever) + } + return presenter.onPrepareActionMode() + } + + override fun onDestroyActionMode(mode: ActionMode) { + presenter.onDestroyActionMode() + actionMode = null + } + + override fun onActionItemClicked(mode: ActionMode, menu: MenuItem): Boolean { + when (menu.itemId) { + R.id.messageTabContextMenuDelete -> presenter.onActionModeSelectDelete() + R.id.messageTabContextMenuSelectAll -> presenter.onActionModeSelectCheckAll() + } + return true + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentMessageTabBinding.bind(view) + messageContainer = binding.messageTabRecycler + + val folder = MessageFolder.valueOf( + (savedInstanceState ?: requireArguments()).getString(MESSAGE_TAB_FOLDER_ID).orEmpty() + ) + presenter.onAttachView(this, folder) + } + + override fun initView() { + with(messageTabAdapter) { + onItemClickListener = presenter::onMessageItemSelected + onLongItemClickListener = presenter::onMessageItemLongSelected + onHeaderClickListener = ::onChipChecked + onChangesDetectedListener = ::resetListPosition + } + + with(binding.messageTabRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = messageTabAdapter + addItemDecoration(DividerItemDecoration(context, false)) + itemAnimator = null + } + + with(binding) { + messageTabSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + messageTabSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) + messageTabSwipe.setProgressBackgroundColorSchemeColor( + requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh) + ) + messageTabErrorRetry.setOnClickListener { presenter.onRetry() } + messageTabErrorDetails.setOnClickListener { presenter.onDetailsClick() } + } + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + super.onCreateOptionsMenu(menu, inflater) + inflater.inflate(R.menu.action_menu_message_tab, menu) + + val searchView = menu.findItem(R.id.action_search).actionView as SearchView + searchView.queryHint = getString(R.string.all_search_hint) + searchView.maxWidth = Int.MAX_VALUE + searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String) = false + override fun onQueryTextChange(query: String): Boolean { + presenter.onSearchQueryTextChange(query) + return true + } + }) + } + + override fun updateData(data: List) { + messageTabAdapter.submitData(data) + } + + override fun updateActionModeTitle(selectedMessagesSize: Int) { + actionMode?.title = resources.getQuantityString( + R.plurals.message_selected_messages_count, + selectedMessagesSize, + selectedMessagesSize + ) + } + + override fun updateSelectAllMenu(isAllSelected: Boolean) { + val menuItem = actionMode?.menu?.findItem(R.id.messageTabContextMenuSelectAll) ?: return + + if (isAllSelected) { + menuItem.setTitle(R.string.message_unselect_all) + menuItem.setIcon(R.drawable.ic_message_unselect_all) + } else { + menuItem.setTitle(R.string.message_select_all) + menuItem.setIcon(R.drawable.ic_message_select_all) + } + } + + override fun showProgress(show: Boolean) { + binding.messageTabProgress.visibility = if (show) VISIBLE else GONE + } + + override fun enableSwipe(enable: Boolean) { + binding.messageTabSwipe.isEnabled = enable + } + + override fun resetListPosition() { + binding.messageTabRecycler.scrollToPosition(0) + } + + override fun showContent(show: Boolean) { + binding.messageTabRecycler.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showEmpty(show: Boolean) { + binding.messageTabEmpty.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showErrorView(show: Boolean) { + binding.messageTabError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + binding.messageTabErrorMessage.text = message + } + + override fun showRefresh(show: Boolean) { + binding.messageTabSwipe.isRefreshing = show + } + + override fun showMessagesDeleted() { + showMessage(getString(R.string.message_messages_deleted)) + } + + override fun notifyParentShowNewMessage(show: Boolean) { + (parentFragment as? MessageFragment)?.onChildFragmentShowNewMessage(show) + } + + override fun openMessage(message: Message) { + (activity as? MainActivity)?.pushView(MessagePreviewFragment.newInstance(message)) + } + + override fun notifyParentDataLoaded() { + (parentFragment as? MessageFragment)?.onChildFragmentLoaded() + } + + override fun notifyParentShowActionMode(show: Boolean) { + (parentFragment as? MessageFragment)?.onChildFragmentShowActionMode(show) + } + + fun onParentLoadData(forceRefresh: Boolean) { + presenter.onParentViewLoadData(forceRefresh) + } + + fun onParentFinishActionMode() { + presenter.onParentFinishActionMode() + } + + private fun onChipChecked(chip: CompoundButton, isChecked: Boolean) { + when (chip.id) { + R.id.chip_unread -> presenter.onUnreadFilterSelected(isChecked) + R.id.chip_attachments -> presenter.onAttachmentsFilterSelected(isChecked) + } + } + + override fun showActionMode(show: Boolean) { + if (show) { + actionMode = (activity as MainActivity?)?.startSupportActionMode(actionModeCallback) + } else { + actionMode?.finish() + } + } + + override fun showRecyclerBottomPadding(show: Boolean) { + binding.messageTabRecycler.updatePadding( + bottom = if (show) requireContext().dpToPx(64f).toInt() else 0 + ) + } + + override fun hideKeyboard() { + activity?.hideSoftInput() + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putString(MESSAGE_TAB_FOLDER_ID, presenter.folder.name) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt new file mode 100644 index 000000000..870b6433e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt @@ -0,0 +1,371 @@ +package io.github.wulkanowy.ui.modules.message.tab + +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.enums.MessageFolder +import io.github.wulkanowy.data.repositories.MessageRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.toFormattedString +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.consumeAsFlow +import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import me.xdrop.fuzzywuzzy.FuzzySearch +import timber.log.Timber +import javax.inject.Inject +import kotlin.math.pow + +class MessageTabPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val messageRepository: MessageRepository, + private val semesterRepository: SemesterRepository, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + lateinit var folder: MessageFolder + + private lateinit var lastError: Throwable + + private var lastSearchQuery = "" + + private var messages = emptyList() + + private val searchChannel = Channel() + + private val messagesToDelete = mutableSetOf() + + private var onlyUnread: Boolean? = false + + private var onlyWithAttachments = false + + private var isActionMode = false + + fun onAttachView(view: MessageTabView, folder: MessageFolder) { + super.onAttachView(view) + view.initView() + initializeSearchStream() + errorHandler.showErrorMessage = ::showErrorViewOnError + this.folder = folder + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the $folder message") + view?.run { loadData(true) } + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + loadData(true) + } + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + fun onParentViewLoadData(forceRefresh: Boolean) { + loadData(forceRefresh) + } + + fun onParentFinishActionMode() { + view?.showActionMode(false) + } + + fun onDestroyActionMode() { + isActionMode = false + messagesToDelete.clear() + updateDataInView() + + view?.run { + enableSwipe(true) + notifyParentShowNewMessage(true) + notifyParentShowActionMode(false) + showRecyclerBottomPadding(true) + } + } + + fun onPrepareActionMode(): Boolean { + isActionMode = true + messagesToDelete.clear() + updateDataInView() + + view?.apply { + enableSwipe(false) + notifyParentShowNewMessage(false) + notifyParentShowActionMode(true) + showRecyclerBottomPadding(false) + hideKeyboard() + } + return true + } + + fun onActionModeSelectDelete() { + Timber.i("Delete ${messagesToDelete.size} messages)") + val messageList = messagesToDelete.toList() + + presenterScope.launch { + view?.run { + showProgress(true) + showContent(false) + showActionMode(false) + } + + runCatching { + val student = studentRepository.getCurrentStudent(true) + messageRepository.deleteMessages(student, messageList) + } + .onFailure(errorHandler::dispatch) + .onSuccess { view?.showMessagesDeleted() } + } + } + + fun onActionModeSelectCheckAll() { + val messagesToSelect = getFilteredData() + val isAllSelected = messagesToDelete.containsAll(messagesToSelect) + + if (isAllSelected) { + messagesToDelete.clear() + view?.showActionMode(false) + } else { + messagesToDelete.addAll(messagesToSelect) + updateDataInView() + } + + view?.run { + updateSelectAllMenu(!isAllSelected) + updateActionModeTitle(messagesToDelete.size) + } + } + + fun onMessageItemLongSelected(messageItem: MessageTabDataItem.MessageItem) { + if (!isActionMode) { + view?.showActionMode(true) + + messagesToDelete.add(messageItem.message) + + view?.updateActionModeTitle(messagesToDelete.size) + updateDataInView() + } + } + + fun onMessageItemSelected(messageItem: MessageTabDataItem.MessageItem, position: Int) { + Timber.i("Select message ${messageItem.message.id} item (position: $position)") + + if (!isActionMode) { + view?.run { + showActionMode(false) + openMessage(messageItem.message) + } + } else { + if (!messageItem.isSelected) { + messagesToDelete.add(messageItem.message) + } else { + messagesToDelete.remove(messageItem.message) + } + + if (messagesToDelete.isEmpty()) { + view?.showActionMode(false) + } + + val filteredData = getFilteredData() + + view?.run { + updateActionModeTitle(messagesToDelete.size) + updateSelectAllMenu(messagesToDelete.containsAll(filteredData)) + } + updateDataInView() + } + } + + fun onUnreadFilterSelected(isChecked: Boolean) { + view?.run { + onlyUnread = isChecked + loadData(false) + } + } + + fun onAttachmentsFilterSelected(isChecked: Boolean) { + view?.run { + onlyWithAttachments = isChecked + loadData(false) + } + } + + private fun loadData(forceRefresh: Boolean) { + Timber.i("Loading $folder message data started") + + flatResourceFlow { + val student = studentRepository.getCurrentStudent() + val semester = semesterRepository.getCurrentSemester(student) + messageRepository.getMessages(student, semester, folder, forceRefresh) + } + .logResourceStatus("load $folder message") + .onResourceData { + messages = it + + val filteredData = getFilteredData() + + view?.run { + enableSwipe(true) + showErrorView(false) + showProgress(false) + showContent(true) + showEmpty(filteredData.isEmpty()) + } + + updateDataInView() + } + .onResourceIntermediate { view?.showRefresh(true) } + .onResourceSuccess { + analytics.logEvent( + "load_data", + "type" to "messages", + "items" to it.size, + "folder" to folder.name + ) + } + .onResourceNotLoading { + view?.run { + showRefresh(false) + showProgress(false) + enableSwipe(true) + notifyParentDataLoaded() + } + } + .onResourceError(errorHandler::dispatch) + .catch { + errorHandler.dispatch(it) + view?.notifyParentDataLoaded() + } + .launch() + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + showProgress(false) + } else showError(message, error) + } + } + + fun onSearchQueryTextChange(query: String) { + presenterScope.launch { + searchChannel.send(query) + } + } + + @OptIn(FlowPreview::class) + private fun initializeSearchStream() { + presenterScope.launch { + searchChannel.consumeAsFlow() + .debounce(250) + .map { query -> + lastSearchQuery = query + + getFilteredData() + } + .catch { Timber.e(it) } + .collect { + Timber.d("Applying filter. Full list: ${messages.size}, filtered: ${it.size}") + + view?.run { + showEmpty(it.isEmpty()) + showContent(true) + showErrorView(false) + } + + updateDataInView() + view?.resetListPosition() + } + } + } + + private fun getFilteredData(): List { + if (lastSearchQuery.trim().isEmpty()) { + val sortedMessages = messages.sortedByDescending { it.date } + return when { + (onlyUnread == true) && onlyWithAttachments -> sortedMessages.filter { it.unread == onlyUnread && it.hasAttachments == onlyWithAttachments } + (onlyUnread == true) -> sortedMessages.filter { it.unread == onlyUnread } + onlyWithAttachments -> sortedMessages.filter { it.hasAttachments == onlyWithAttachments } + else -> sortedMessages + } + } else { + val sortedMessages = messages + .map { it to calculateMatchRatio(it, lastSearchQuery) } + .sortedWith(compareBy> { -it.second }.thenByDescending { it.first.date }) + .filter { it.second > 6000 } + .map { it.first } + return when { + (onlyUnread == true) && onlyWithAttachments -> sortedMessages.filter { it.unread == onlyUnread && it.hasAttachments == onlyWithAttachments } + (onlyUnread == true) -> sortedMessages.filter { it.unread == onlyUnread } + onlyWithAttachments -> sortedMessages.filter { it.hasAttachments == onlyWithAttachments } + else -> sortedMessages + } + } + } + + private fun updateDataInView() { + val data = getFilteredData() + + val list = buildList { + add( + MessageTabDataItem.FilterHeader( + onlyUnread = onlyUnread.takeIf { folder != MessageFolder.SENT }, + onlyWithAttachments = onlyWithAttachments, + isEnabled = !isActionMode + ) + ) + + addAll(data.map { message -> + MessageTabDataItem.MessageItem( + message = message, + isSelected = messagesToDelete.any { it.id == message.id }, + isActionMode = isActionMode + ) + }) + } + + view?.updateData(list) + } + + private fun calculateMatchRatio(message: Message, query: String): Int { + val subjectRatio = FuzzySearch.tokenSortPartialRatio(query.lowercase(), message.subject) + + val senderOrRecipientRatio = FuzzySearch.tokenSortPartialRatio( + query.lowercase(), + if (message.sender.isNotEmpty()) message.sender.lowercase() + else message.recipient.lowercase() + ) + + val dateRatio = listOf( + FuzzySearch.ratio( + query.lowercase(), + message.date.toFormattedString("dd.MM").lowercase() + ), + FuzzySearch.ratio( + query.lowercase(), + message.date.toFormattedString("dd.MM.yyyy").lowercase() + ) + ).maxOrNull() ?: 0 + + + return (subjectRatio.toDouble().pow(2) + + senderOrRecipientRatio.toDouble().pow(2) + + dateRatio.toDouble().pow(2) * 2 + ).toInt() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt new file mode 100644 index 000000000..bfa43b209 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt @@ -0,0 +1,49 @@ +package io.github.wulkanowy.ui.modules.message.tab + +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.ui.base.BaseView + +interface MessageTabView : BaseView { + + val isViewEmpty: Boolean + + fun initView() + + fun resetListPosition() + + fun updateData(data: List) + + fun updateActionModeTitle(selectedMessagesSize: Int) + + fun updateSelectAllMenu(isAllSelected: Boolean) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun showEmpty(show: Boolean) + + fun showMessagesDeleted() + + fun showErrorView(show: Boolean) + + fun notifyParentShowNewMessage(show: Boolean) + + fun setErrorDetails(message: String) + + fun showRefresh(show: Boolean) + + fun openMessage(message: Message) + + fun notifyParentDataLoaded() + + fun notifyParentShowActionMode(show: Boolean) + + fun hideKeyboard() + + fun showActionMode(show: Boolean) + + fun showRecyclerBottomPadding(show: Boolean) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceAdapter.kt new file mode 100644 index 000000000..4bc3097d6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceAdapter.kt @@ -0,0 +1,38 @@ +package io.github.wulkanowy.ui.modules.mobiledevice + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.data.db.entities.MobileDevice +import io.github.wulkanowy.databinding.ItemMobileDeviceBinding +import io.github.wulkanowy.utils.toFormattedString +import javax.inject.Inject + +class MobileDeviceAdapter @Inject constructor() : + RecyclerView.Adapter() { + + var items = mutableListOf() + + var onDeviceUnregisterListener: (device: MobileDevice, position: Int) -> Unit = { _, _ -> } + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + ItemMobileDeviceBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + val device = items[position] + + with(holder.binding) { + mobileDeviceItemDate.text = device.date.toFormattedString("dd.MM.yyyy HH:mm:ss") + mobileDeviceItemName.text = device.name + mobileDeviceItemUnregister.setOnClickListener { + onDeviceUnregisterListener(device, position) + } + } + } + + class ItemViewHolder(val binding: ItemMobileDeviceBinding) : + RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt new file mode 100644 index 000000000..f8e367c57 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt @@ -0,0 +1,142 @@ +package io.github.wulkanowy.ui.modules.mobiledevice + +import android.os.Bundle +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import androidx.core.view.postDelayed +import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.snackbar.Snackbar +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.MobileDevice +import io.github.wulkanowy.databinding.FragmentMobileDeviceBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.modules.mobiledevice.token.MobileDeviceTokenDialog +import io.github.wulkanowy.ui.widgets.DividerItemDecoration +import io.github.wulkanowy.utils.getThemeAttrColor +import javax.inject.Inject + +@AndroidEntryPoint +class MobileDeviceFragment : + BaseFragment(R.layout.fragment_mobile_device), MobileDeviceView, + MainView.TitledView { + + @Inject + lateinit var presenter: MobileDevicePresenter + + @Inject + lateinit var devicesAdapter: MobileDeviceAdapter + + companion object { + fun newInstance() = MobileDeviceFragment() + } + + override val titleStringId: Int + get() = R.string.mobile_devices_title + + override val isViewEmpty: Boolean + get() = devicesAdapter.items.isEmpty() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentMobileDeviceBinding.bind(view) + messageContainer = binding.mobileDevicesRecycler + presenter.onAttachView(this) + } + + override fun initView() { + devicesAdapter.onDeviceUnregisterListener = presenter::onUnregisterDevice + + with(binding.mobileDevicesRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = devicesAdapter + addItemDecoration(DividerItemDecoration(context)) + } + + with(binding) { + mobileDevicesSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + mobileDevicesSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) + mobileDevicesSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh)) + mobileDevicesErrorRetry.setOnClickListener { presenter.onRetry() } + mobileDevicesErrorDetails.setOnClickListener { presenter.onDetailsClick() } + mobileDeviceAddButton.setOnClickListener { presenter.onRegisterDevice() } + } + } + + override fun updateData(data: List) { + with(devicesAdapter) { + items = data.toMutableList() + notifyDataSetChanged() + } + } + + override fun deleteItem(device: MobileDevice, position: Int) { + with(devicesAdapter) { + items.removeAt(position) + notifyItemRemoved(position) + notifyItemRangeChanged(position, itemCount) + } + } + + override fun restoreDeleteItem(device: MobileDevice, position: Int) { + with(devicesAdapter) { + items.add(position, device) + notifyItemInserted(position) + notifyItemRangeChanged(position, itemCount) + } + } + + override fun showUndo(device: MobileDevice, position: Int) { + var confirmed = true + + Snackbar.make(binding.mobileDevicesRecycler, getString(R.string.mobile_device_removed), 3000) + .setAction(R.string.all_undo) { + confirmed = false + presenter.onUnregisterCancelled(device, position) + }.show() + + view?.postDelayed(3000) { + if (confirmed) presenter.onUnregisterConfirmed(device) + } + } + + override fun showRefresh(show: Boolean) { + binding.mobileDevicesSwipe.isRefreshing = show + } + + override fun showProgress(show: Boolean) { + binding.mobileDevicesProgress.visibility = if (show) VISIBLE else GONE + } + + override fun showEmpty(show: Boolean) { + binding.mobileDevicesEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showErrorView(show: Boolean) { + binding.mobileDevicesError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + binding.mobileDevicesErrorMessage.text = message + } + + override fun enableSwipe(enable: Boolean) { + binding.mobileDevicesSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + binding.mobileDevicesRecycler.visibility = if (show) VISIBLE else GONE + } + + override fun showTokenDialog() { + (activity as? MainActivity)?.showDialogFragment(MobileDeviceTokenDialog.newInstance()) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt new file mode 100644 index 000000000..36a720e53 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt @@ -0,0 +1,132 @@ +package io.github.wulkanowy.ui.modules.mobiledevice + +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.db.entities.MobileDevice +import io.github.wulkanowy.data.repositories.MobileDeviceRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import timber.log.Timber +import javax.inject.Inject + +class MobileDevicePresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val semesterRepository: SemesterRepository, + private val mobileDeviceRepository: MobileDeviceRepository, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + private lateinit var lastError: Throwable + + override fun onAttachView(view: MobileDeviceView) { + super.onAttachView(view) + view.initView() + Timber.i("Mobile device view was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError + loadData() + } + + fun onSwipeRefresh() { + loadData(true) + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + private fun loadData(forceRefresh: Boolean = false) { + Timber.i("Loading mobile devices data started") + + flatResourceFlow { + val student = studentRepository.getCurrentStudent() + val semester = semesterRepository.getCurrentSemester(student) + mobileDeviceRepository.getDevices(student, semester, forceRefresh) + } + .logResourceStatus("load mobile devices data") + .onResourceData { + view?.run { + enableSwipe(true) + showProgress(false) + showErrorView(false) + showContent(it.isNotEmpty()) + showEmpty(it.isEmpty()) + updateData(it) + } + } + .onResourceIntermediate { view?.showRefresh(true) } + .onResourceSuccess { + analytics.logEvent( + "load_data", + "type" to "devices", + "items" to it.size + ) + } + .onResourceNotLoading { + view?.run { + enableSwipe(true) + showProgress(false) + showRefresh(false) + } + } + .onResourceError(errorHandler::dispatch) + .launch() + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + + fun onRegisterDevice() { + view?.showTokenDialog() + } + + fun onUnregisterDevice(device: MobileDevice, position: Int) { + view?.run { + deleteItem(device, position) + showUndo(device, position) + showEmpty(isViewEmpty) + } + } + + fun onUnregisterCancelled(device: MobileDevice, position: Int) { + view?.run { + restoreDeleteItem(device, position) + showEmpty(isViewEmpty) + } + } + + fun onUnregisterConfirmed(device: MobileDevice) { + resourceFlow { + val student = studentRepository.getCurrentStudent() + val semester = semesterRepository.getCurrentSemester(student) + mobileDeviceRepository.unregisterDevice(student, semester, device) + } + .logResourceStatus("unregister device") + .onResourceSuccess { + view?.run { + showProgress(false) + enableSwipe(true) + } + } + .onResourceError(errorHandler::dispatch) + .launch("unregister") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt new file mode 100644 index 000000000..b94646a7b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt @@ -0,0 +1,35 @@ +package io.github.wulkanowy.ui.modules.mobiledevice + +import io.github.wulkanowy.data.db.entities.MobileDevice +import io.github.wulkanowy.ui.base.BaseView + +interface MobileDeviceView : BaseView { + + val isViewEmpty: Boolean + + fun initView() + + fun updateData(data: List) + + fun deleteItem(device: MobileDevice, position: Int) + + fun restoreDeleteItem(device: MobileDevice, position: Int) + + fun showUndo(device: MobileDevice, position: Int) + + fun showRefresh(show: Boolean) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun showEmpty(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun showTokenDialog() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt new file mode 100644 index 000000000..eb420a6ae --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt @@ -0,0 +1,95 @@ +package io.github.wulkanowy.ui.modules.mobiledevice.token + +import android.content.ClipData +import android.content.ClipboardManager +import android.graphics.BitmapFactory +import android.os.Bundle +import android.util.Base64 +import android.view.LayoutInflater +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import android.view.ViewGroup +import android.widget.Toast +import androidx.core.content.getSystemService +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.pojos.MobileDeviceToken +import io.github.wulkanowy.databinding.DialogMobileDeviceBinding +import io.github.wulkanowy.ui.base.BaseDialogFragment +import javax.inject.Inject + +@AndroidEntryPoint +class MobileDeviceTokenDialog : BaseDialogFragment(), + MobileDeviceTokenVIew { + + @Inject + lateinit var presenter: MobileDeviceTokenPresenter + + companion object { + + fun newInstance() = MobileDeviceTokenDialog() + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogMobileDeviceBinding.inflate(inflater).apply { binding = this }.root + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + presenter.onAttachView(this) + } + + override fun initView() { + binding.mobileDeviceDialogClose.setOnClickListener { dismiss() } + } + + override fun updateData(token: MobileDeviceToken) { + with(binding.mobileDeviceDialogTokenValue) { + text = token.token + setOnClickListener { clickCopy(token.token) } + } + with(binding.mobileDeviceDialogSymbolValue) { + text = token.symbol + setOnClickListener { clickCopy(token.symbol) } + } + with(binding.mobileDeviceDialogPinValue) { + text = token.pin + setOnClickListener { clickCopy(token.pin) } + } + + binding.mobileDeviceQr.setImageBitmap(Base64.decode(token.qr, Base64.DEFAULT).let { + BitmapFactory.decodeByteArray(it, 0, it.size) + }) + } + + private fun clickCopy(text: String) { + val clip = ClipData.newPlainText("wulkanowy", text) + activity?.getSystemService()?.setPrimaryClip(clip) + Toast.makeText(context, R.string.all_copied, Toast.LENGTH_LONG).show() + } + + override fun hideLoading() { + binding.mobileDeviceDialogProgress.visibility = GONE + } + + override fun showContent() { + binding.mobileDeviceDialogContent.visibility = VISIBLE + } + + override fun closeDialog() { + dismiss() + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenPresenter.kt new file mode 100644 index 000000000..875b73ad7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenPresenter.kt @@ -0,0 +1,54 @@ +package io.github.wulkanowy.ui.modules.mobiledevice.token + +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.repositories.MobileDeviceRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import timber.log.Timber +import javax.inject.Inject + +class MobileDeviceTokenPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val semesterRepository: SemesterRepository, + private val mobileDeviceRepository: MobileDeviceRepository, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + override fun onAttachView(view: MobileDeviceTokenVIew) { + super.onAttachView(view) + view.initView() + Timber.i("Mobile device view was initialized") + loadData() + } + + private fun loadData() { + resourceFlow { + val student = studentRepository.getCurrentStudent() + val semester = semesterRepository.getCurrentSemester(student) + mobileDeviceRepository.getToken(student, semester) + } + .logResourceStatus("load mobile device registration") + .onResourceData { + view?.run { + updateData(it) + showContent() + } + } + .onResourceSuccess { + analytics.logEvent( + "device_register", + "symbol" to it.token.substring(0, 3) + ) + } + .onResourceNotLoading { view?.hideLoading() } + .onResourceError { + view?.closeDialog() + errorHandler.dispatch(it) + } + .launch() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenVIew.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenVIew.kt new file mode 100644 index 000000000..950f8bcf0 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenVIew.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.ui.modules.mobiledevice.token + +import io.github.wulkanowy.data.pojos.MobileDeviceToken +import io.github.wulkanowy.ui.base.BaseView + +interface MobileDeviceTokenVIew : BaseView { + + fun initView() + + fun hideLoading() + + fun showContent() + + fun closeDialog() + + fun updateData(token: MobileDeviceToken) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreAdapter.kt new file mode 100644 index 000000000..70587b0cf --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreAdapter.kt @@ -0,0 +1,34 @@ +package io.github.wulkanowy.ui.modules.more + +import android.graphics.drawable.Drawable +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.databinding.ItemMoreBinding +import javax.inject.Inject + +class MoreAdapter @Inject constructor() : RecyclerView.Adapter() { + + var items = emptyList>() + + var onClickListener: (name: String) -> Unit = {} + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + ItemMoreBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + val (title, drawable) = items[position] + + with(holder.binding) { + moreItemTitle.text = title + moreItemImage.setImageDrawable(drawable) + + root.setOnClickListener { onClickListener(title) } + } + } + + class ItemViewHolder(val binding: ItemMoreBinding) : RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt new file mode 100644 index 000000000..df55abc9c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt @@ -0,0 +1,152 @@ +package io.github.wulkanowy.ui.modules.more + +import android.graphics.drawable.Drawable +import android.os.Bundle +import android.view.View +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.databinding.FragmentMoreBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.conference.ConferenceFragment +import io.github.wulkanowy.ui.modules.exam.ExamFragment +import io.github.wulkanowy.ui.modules.homework.HomeworkFragment +import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.modules.message.MessageFragment +import io.github.wulkanowy.ui.modules.mobiledevice.MobileDeviceFragment +import io.github.wulkanowy.ui.modules.note.NoteFragment +import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment +import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment +import io.github.wulkanowy.ui.modules.settings.SettingsFragment +import io.github.wulkanowy.utils.getCompatDrawable +import javax.inject.Inject + +@AndroidEntryPoint +class MoreFragment : BaseFragment(R.layout.fragment_more), MoreView, + MainView.TitledView, MainView.MainChildView { + + @Inject + lateinit var presenter: MorePresenter + + @Inject + lateinit var moreAdapter: MoreAdapter + + companion object { + fun newInstance() = MoreFragment() + } + + override val titleStringId: Int + get() = R.string.more_title + + override val messagesRes: Pair? + get() = context?.run { getString(R.string.message_title) to getCompatDrawable(R.drawable.ic_more_messages) } + + override val homeworkRes: Pair? + get() = context?.run { getString(R.string.homework_title) to getCompatDrawable(R.drawable.ic_more_homework) } + + override val noteRes: Pair? + get() = context?.run { getString(R.string.note_title) to getCompatDrawable(R.drawable.ic_more_note) } + + override val conferencesRes: Pair? + get() = context?.run { getString(R.string.conferences_title) to getCompatDrawable(R.drawable.ic_more_conferences) } + + override val schoolAnnouncementRes: Pair? + get() = context?.run { getString(R.string.school_announcement_title) to getCompatDrawable(R.drawable.ic_all_about) } + + override val schoolAndTeachersRes: Pair? + get() = context?.run { getString(R.string.schoolandteachers_title) to getCompatDrawable((R.drawable.ic_more_schoolandteachers)) } + + override val mobileDevicesRes: Pair? + get() = context?.run { getString(R.string.mobile_devices_title) to getCompatDrawable(R.drawable.ic_more_mobile_devices) } + + override val settingsRes: Pair? + get() = context?.run { getString(R.string.settings_title) to getCompatDrawable(R.drawable.ic_more_settings) } + + override val examRes: Pair? + get() = context?.run { getString(R.string.exam_title) to getCompatDrawable(R.drawable.ic_main_exam) } + + override val luckyNumberRes: Pair? + get() = context?.run { getString(R.string.lucky_number_title) to getCompatDrawable(R.drawable.ic_more_lucky_number) } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentMoreBinding.bind(view) + presenter.onAttachView(this) + } + + override fun initView() { + moreAdapter.onClickListener = presenter::onItemSelected + + with(binding.moreRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = moreAdapter + } + } + + override fun onFragmentReselected() { + if (::presenter.isInitialized) presenter.onViewReselected() + } + + override fun onFragmentChanged() { + (parentFragmentManager.fragments.find { it is MessageFragment } as MessageFragment?) + ?.onFragmentChanged() + } + + override fun updateData(data: List>) { + with(moreAdapter) { + items = data + notifyDataSetChanged() + } + } + + override fun openMessagesView() { + (activity as? MainActivity)?.pushView(MessageFragment.newInstance()) + } + + override fun openHomeworkView() { + (activity as? MainActivity)?.pushView(HomeworkFragment.newInstance()) + } + + override fun openNoteView() { + (activity as? MainActivity)?.pushView(NoteFragment.newInstance()) + } + + override fun openSchoolAnnouncementView() { + (activity as? MainActivity)?.pushView(SchoolAnnouncementFragment.newInstance()) + } + + override fun openConferencesView() { + (activity as? MainActivity)?.pushView(ConferenceFragment.newInstance()) + } + + override fun openSchoolAndTeachersView() { + (activity as? MainActivity)?.pushView(SchoolAndTeachersFragment.newInstance()) + } + + override fun openMobileDevicesView() { + (activity as? MainActivity)?.pushView(MobileDeviceFragment.newInstance()) + } + + override fun openSettingsView() { + (activity as? MainActivity)?.pushView(SettingsFragment.newInstance()) + } + + override fun openExamView() { + (activity as? MainActivity)?.pushView(ExamFragment.newInstance()) + } + + override fun openLuckyNumberView() { + (activity as? MainActivity)?.pushView(LuckyNumberFragment.newInstance()) + } + + override fun popView(depth: Int) { + (activity as? MainActivity)?.popView(depth) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt new file mode 100644 index 000000000..92551d6e9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt @@ -0,0 +1,61 @@ +package io.github.wulkanowy.ui.modules.more + +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import timber.log.Timber +import javax.inject.Inject + +class MorePresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository +) : BasePresenter(errorHandler, studentRepository) { + + override fun onAttachView(view: MoreView) { + super.onAttachView(view) + view.initView() + Timber.i("More view was initialized") + loadData() + } + + fun onItemSelected(title: String) { + Timber.i("Select more item \"${title}\"") + view?.run { + when (title) { + messagesRes?.first -> openMessagesView() + examRes?.first -> openExamView() + homeworkRes?.first -> openHomeworkView() + noteRes?.first -> openNoteView() + conferencesRes?.first -> openConferencesView() + schoolAnnouncementRes?.first -> openSchoolAnnouncementView() + schoolAndTeachersRes?.first -> openSchoolAndTeachersView() + mobileDevicesRes?.first -> openMobileDevicesView() + settingsRes?.first -> openSettingsView() + luckyNumberRes?.first -> openLuckyNumberView() + } + } + } + + fun onViewReselected() { + Timber.i("More view is reselected") + view?.popView(2) + } + + private fun loadData() { + Timber.i("Load items for more view") + view?.run { + updateData(listOfNotNull( + messagesRes, + examRes, + homeworkRes, + noteRes, + luckyNumberRes, + conferencesRes, + schoolAnnouncementRes, + schoolAndTeachersRes, + mobileDevicesRes, + settingsRes + )) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt new file mode 100644 index 000000000..cb895de28 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt @@ -0,0 +1,53 @@ +package io.github.wulkanowy.ui.modules.more + +import android.graphics.drawable.Drawable +import io.github.wulkanowy.ui.base.BaseView + +interface MoreView : BaseView { + + val messagesRes: Pair? + + val homeworkRes: Pair? + + val noteRes: Pair? + + val conferencesRes: Pair? + + val schoolAnnouncementRes: Pair? + + val schoolAndTeachersRes: Pair? + + val mobileDevicesRes: Pair? + + val settingsRes: Pair? + + val examRes: Pair? + + val luckyNumberRes: Pair? + + fun initView() + + fun updateData(data: List>) + + fun openSettingsView() + + fun popView(depth: Int) + + fun openMessagesView() + + fun openHomeworkView() + + fun openNoteView() + + fun openSchoolAnnouncementView() + + fun openConferencesView() + + fun openSchoolAndTeachersView() + + fun openMobileDevicesView() + + fun openExamView() + + fun openLuckyNumberView() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteAdapter.kt new file mode 100644 index 000000000..48482cb75 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteAdapter.kt @@ -0,0 +1,60 @@ +package io.github.wulkanowy.ui.modules.note + +import android.annotation.SuppressLint +import android.graphics.Typeface +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Note +import io.github.wulkanowy.databinding.ItemNoteBinding +import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.toFormattedString +import javax.inject.Inject + +class NoteAdapter @Inject constructor() : RecyclerView.Adapter() { + + var items = mutableListOf() + + var onClickListener: (Note, position: Int) -> Unit = { _, _ -> } + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + ItemNoteBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + @SuppressLint("SetTextI18n") + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + val item = items[position] + + with(holder.binding) { + with(noteItemDate) { + text = item.date.toFormattedString() + setTypeface(null, if (item.isRead) Typeface.NORMAL else Typeface.BOLD) + } + with(noteItemType) { + text = item.category + setTypeface(null, if (item.isRead) Typeface.NORMAL else Typeface.BOLD) + } + with(noteItemPoints) { + text = "${if (item.points > 0) "+" else ""}${item.points}" + visibility = if (item.isPointsShow) View.VISIBLE else View.GONE + setTextColor(when (NoteCategory.getByValue(item.categoryType)) { + NoteCategory.POSITIVE -> ContextCompat.getColor(context, R.color.note_positive) + NoteCategory.NEGATIVE -> ContextCompat.getColor(context, R.color.note_negative) + else -> context.getThemeAttrColor(android.R.attr.textColorPrimary) + }) + } + noteItemTeacher.text = item.teacher + noteItemContent.text = item.content + + root.setOnClickListener { onClickListener(item, position) } + } + } + + class ItemViewHolder(val binding: ItemNoteBinding) : RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt new file mode 100644 index 000000000..5811456b6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt @@ -0,0 +1,79 @@ +package io.github.wulkanowy.ui.modules.note + +import android.annotation.SuppressLint +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import androidx.fragment.app.DialogFragment +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Note +import io.github.wulkanowy.databinding.DialogNoteBinding +import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.lifecycleAwareVariable +import io.github.wulkanowy.utils.toFormattedString + +class NoteDialog : DialogFragment() { + + private var binding: DialogNoteBinding by lifecycleAwareVariable() + + private lateinit var note: Note + + companion object { + + private const val ARGUMENT_KEY = "Item" + + fun newInstance(exam: Note) = NoteDialog().apply { + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + note = getSerializable(ARGUMENT_KEY) as Note + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogNoteBinding.inflate(inflater).apply { binding = this }.root + + @SuppressLint("SetTextI18n") + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + with(binding) { + noteDialogDateValue.text = note.date.toFormattedString() + noteDialogCategoryValue.text = note.category + noteDialogTeacherValue.text = note.teacher + noteDialogContentValue.text = note.content + } + + if (note.isPointsShow) { + with(binding.noteDialogPointsValue) { + text = "${if (note.points > 0) "+" else ""}${note.points}" + setTextColor( + when (NoteCategory.getByValue(note.categoryType)) { + NoteCategory.POSITIVE -> ContextCompat.getColor( + requireContext(), + R.color.note_positive + ) + NoteCategory.NEGATIVE -> ContextCompat.getColor( + requireContext(), + R.color.note_negative + ) + else -> requireContext().getThemeAttrColor(android.R.attr.textColorPrimary) + } + ) + } + } + + binding.noteDialogClose.setOnClickListener { dismiss() } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt new file mode 100644 index 000000000..dd6223448 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt @@ -0,0 +1,119 @@ +package io.github.wulkanowy.ui.modules.note + +import android.os.Bundle +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Note +import io.github.wulkanowy.databinding.FragmentNoteBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.widgets.DividerItemDecoration +import io.github.wulkanowy.utils.getThemeAttrColor +import javax.inject.Inject + +@AndroidEntryPoint +class NoteFragment : BaseFragment(R.layout.fragment_note), NoteView, + MainView.TitledView { + + @Inject + lateinit var presenter: NotePresenter + + @Inject + lateinit var noteAdapter: NoteAdapter + + companion object { + fun newInstance() = NoteFragment() + } + + override val titleStringId: Int + get() = R.string.note_title + + override val isViewEmpty: Boolean + get() = noteAdapter.items.isEmpty() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentNoteBinding.bind(view) + presenter.onAttachView(this) + } + + override fun initView() { + noteAdapter.onClickListener = presenter::onNoteItemSelected + + with(binding.noteRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = noteAdapter + addItemDecoration(DividerItemDecoration(context)) + } + with(binding) { + noteSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + noteSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) + noteSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh)) + noteErrorRetry.setOnClickListener { presenter.onRetry() } + noteErrorDetails.setOnClickListener { presenter.onDetailsClick() } + } + } + + override fun showNoteDialog(note: Note) { + (activity as? MainActivity)?.showDialogFragment(NoteDialog.newInstance(note)) + } + + override fun updateData(data: List) { + with(noteAdapter) { + items = data.toMutableList() + notifyDataSetChanged() + } + } + + override fun updateItem(item: Note, position: Int) { + with(noteAdapter) { + items[position] = item + notifyItemChanged(position) + } + } + + override fun clearData() { + with(noteAdapter) { + items = mutableListOf() + notifyDataSetChanged() + } + } + + override fun showEmpty(show: Boolean) { + binding.noteEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showErrorView(show: Boolean) { + binding.noteError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + binding.noteErrorMessage.text = message + } + + override fun showProgress(show: Boolean) { + binding.noteProgress.visibility = if (show) VISIBLE else GONE + } + + override fun enableSwipe(enable: Boolean) { + binding.noteSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + binding.noteRecycler.visibility = if (show) VISIBLE else GONE + } + + override fun showRefresh(show: Boolean) { + binding.noteSwipe.isRefreshing = show + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt new file mode 100644 index 000000000..440565e11 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt @@ -0,0 +1,124 @@ +package io.github.wulkanowy.ui.modules.note + +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.db.entities.Note +import io.github.wulkanowy.data.repositories.NoteRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import kotlinx.coroutines.flow.onEach +import timber.log.Timber +import javax.inject.Inject + +class NotePresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val noteRepository: NoteRepository, + private val semesterRepository: SemesterRepository, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + private lateinit var lastError: Throwable + + override fun onAttachView(view: NoteView) { + super.onAttachView(view) + view.initView() + Timber.i("Note view was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError + loadData() + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the note") + loadData(true) + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + private fun loadData(forceRefresh: Boolean = false) { + flatResourceFlow { + val student = studentRepository.getCurrentStudent() + val semester = semesterRepository.getCurrentSemester(student) + noteRepository.getNotes(student, semester, forceRefresh) + } + .logResourceStatus("load note data") + .mapResourceData { it.sortedByDescending { note -> note.date } } + .onResourceData { + view?.run { + enableSwipe(true) + showProgress(false) + showErrorView(false) + showContent(it.isNotEmpty()) + showEmpty(it.isEmpty()) + updateData(it) + } + } + .onResourceIntermediate { view?.showRefresh(true) } + .onResourceSuccess { + analytics.logEvent( + "load_data", + "type" to "note", + "items" to it.size + ) + } + .onResourceNotLoading { + view?.run { + enableSwipe(true) + showProgress(false) + showRefresh(false) + } + } + .onResourceError(errorHandler::dispatch) + .launch() + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + + fun onNoteItemSelected(note: Note, position: Int) { + Timber.i("Select note item ${note.id}") + view?.run { + showNoteDialog(note) + if (!note.isRead) { + note.isRead = true + updateItem(note, position) + updateNote(note) + } + } + } + + private fun updateNote(note: Note) { + resourceFlow { noteRepository.updateNote(note) } + .onEach { + when (it) { + is Resource.Loading -> Timber.i("Attempt to update note ${note.id}") + is Resource.Success -> Timber.i("Update note result: Success") + is Resource.Error -> { + Timber.i("Update note result: An exception occurred") + errorHandler.dispatch(it.error) + } + } + } + .launch("update_note") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt new file mode 100644 index 000000000..9fc0be94c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt @@ -0,0 +1,33 @@ +package io.github.wulkanowy.ui.modules.note + +import io.github.wulkanowy.data.db.entities.Note +import io.github.wulkanowy.ui.base.BaseView + +interface NoteView : BaseView { + + val isViewEmpty: Boolean + + fun initView() + + fun updateData(data: List) + + fun updateItem(item: Note, position: Int) + + fun clearData() + + fun showEmpty(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun showRefresh(show: Boolean) + + fun showNoteDialog(note: Note) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterAdapter.kt new file mode 100644 index 000000000..92c54f45c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterAdapter.kt @@ -0,0 +1,46 @@ +package io.github.wulkanowy.ui.modules.notificationscenter + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.data.db.entities.Notification +import io.github.wulkanowy.databinding.ItemNotificationsCenterBinding +import io.github.wulkanowy.utils.toFormattedString +import javax.inject.Inject + +class NotificationsCenterAdapter @Inject constructor() : + ListAdapter(DiffUtilCallback()) { + + var onItemClickListener: (Notification) -> Unit = {} + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( + ItemNotificationsCenterBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val item = getItem(position) + + with(holder.binding) { + notificationsCenterItemTitle.text = item.title + notificationsCenterItemContent.text = item.content + notificationsCenterItemDate.text = item.date.toFormattedString("HH:mm, d MMM") + notificationsCenterItemIcon.setImageResource(item.type.icon) + + root.setOnClickListener { onItemClickListener(item) } + } + } + + class ViewHolder(val binding: ItemNotificationsCenterBinding) : + RecyclerView.ViewHolder(binding.root) + + private class DiffUtilCallback : DiffUtil.ItemCallback() { + + override fun areContentsTheSame(oldItem: Notification, newItem: Notification) = + oldItem == newItem + + override fun areItemsTheSame(oldItem: Notification, newItem: Notification) = + oldItem.id == newItem.id + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterFragment.kt new file mode 100644 index 000000000..ca71910a4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterFragment.kt @@ -0,0 +1,83 @@ +package io.github.wulkanowy.ui.modules.notificationscenter + +import android.os.Bundle +import android.view.View +import androidx.core.view.isVisible +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Notification +import io.github.wulkanowy.databinding.FragmentNotificationsCenterBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import javax.inject.Inject + +@AndroidEntryPoint +class NotificationsCenterFragment : + BaseFragment(R.layout.fragment_notifications_center), + NotificationsCenterView, MainView.TitledView { + + @Inject + lateinit var presenter: NotificationsCenterPresenter + + @Inject + lateinit var notificationsCenterAdapter: NotificationsCenterAdapter + + companion object { + + fun newInstance() = NotificationsCenterFragment() + } + + override val titleStringId: Int + get() = R.string.notifications_center_title + + override val isViewEmpty: Boolean + get() = notificationsCenterAdapter.itemCount == 0 + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentNotificationsCenterBinding.bind(view) + presenter.onAttachView(this) + } + + override fun initView() { + notificationsCenterAdapter.onItemClickListener = { notification -> + (requireActivity() as MainActivity).pushView(notification.destination.destinationFragment) + } + + with(binding.notificationsCenterRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = notificationsCenterAdapter + } + } + + override fun updateData(data: List) { + notificationsCenterAdapter.submitList(data) + } + + override fun showEmpty(show: Boolean) { + binding.notificationsCenterEmpty.isVisible = show + } + + override fun showProgress(show: Boolean) { + binding.notificationsCenterProgress.isVisible = show + } + + override fun showContent(show: Boolean) { + binding.notificationsCenterRecycler.isVisible = show + } + + override fun showErrorView(show: Boolean) { + binding.notificationCenterError.isVisible = show + } + + override fun setErrorDetails(message: String) { + binding.notificationCenterErrorMessage.text = message + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterPresenter.kt new file mode 100644 index 000000000..de42e5678 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterPresenter.kt @@ -0,0 +1,83 @@ +package io.github.wulkanowy.ui.modules.notificationscenter + +import io.github.wulkanowy.data.repositories.NotificationRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.emitAll +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import timber.log.Timber +import javax.inject.Inject + +class NotificationsCenterPresenter @Inject constructor( + private val notificationRepository: NotificationRepository, + errorHandler: ErrorHandler, + studentRepository: StudentRepository +) : BasePresenter(errorHandler, studentRepository) { + + private lateinit var lastError: Throwable + + override fun onAttachView(view: NotificationsCenterView) { + super.onAttachView(view) + view.initView() + Timber.i("Notifications centre view was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError + loadData() + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData() + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + private fun loadData() { + Timber.i("Loading notifications data started") + + flow { + val studentId = studentRepository.getCurrentStudent(false).id + emitAll(notificationRepository.getNotifications(studentId)) + } + .map { notificationList -> notificationList.sortedByDescending { it.date } } + .catch { Timber.i("Loading notifications result: An exception occurred: `$it`") } + .onEach { + Timber.i("Loading notifications result: Success") + + if (it.isEmpty()) { + view?.run { + showContent(false) + showProgress(false) + showEmpty(true) + } + } else { + view?.run { + showContent(true) + showProgress(false) + showEmpty(false) + updateData(it) + } + } + } + .launch() + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterView.kt new file mode 100644 index 000000000..1bfbe75e1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterView.kt @@ -0,0 +1,23 @@ +package io.github.wulkanowy.ui.modules.notificationscenter + +import io.github.wulkanowy.data.db.entities.Notification +import io.github.wulkanowy.ui.base.BaseView + +interface NotificationsCenterView : BaseView { + + val isViewEmpty: Boolean + + fun initView() + + fun updateData(data: List) + + fun showProgress(show: Boolean) + + fun showEmpty(show: Boolean) + + fun showContent(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersChildView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersChildView.kt new file mode 100644 index 000000000..b5eedb6fd --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersChildView.kt @@ -0,0 +1,8 @@ +package io.github.wulkanowy.ui.modules.schoolandteachers + +interface SchoolAndTeachersChildView { + + fun notifyParentDataLoaded() + + fun onParentLoadData(forceRefresh: Boolean) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersFragment.kt new file mode 100644 index 000000000..f4fa8e01d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersFragment.kt @@ -0,0 +1,107 @@ +package io.github.wulkanowy.ui.modules.schoolandteachers + +import android.os.Bundle +import android.view.View +import android.view.View.INVISIBLE +import android.view.View.VISIBLE +import com.google.android.material.tabs.TabLayoutMediator +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.databinding.FragmentSchoolandteachersBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.modules.schoolandteachers.school.SchoolFragment +import io.github.wulkanowy.ui.modules.schoolandteachers.teacher.TeacherFragment +import io.github.wulkanowy.utils.dpToPx +import io.github.wulkanowy.utils.setOnSelectPageListener +import javax.inject.Inject + +@AndroidEntryPoint +class SchoolAndTeachersFragment : + BaseFragment(R.layout.fragment_schoolandteachers), + SchoolAndTeachersView, MainView.TitledView { + + @Inject + lateinit var presenter: SchoolAndTeachersPresenter + + private val pagerAdapter by lazy { + BaseFragmentPagerAdapter( + fragmentManager = childFragmentManager, + pagesCount = 2, + lifecycle = lifecycle, + ) + } + + companion object { + fun newInstance() = SchoolAndTeachersFragment() + } + + override val titleStringId: Int get() = R.string.schoolandteachers_title + + override val currentPageIndex get() = binding.schoolandteachersViewPager.currentItem + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentSchoolandteachersBinding.bind(view) + presenter.onAttachView(this) + } + + override fun initView() { + with(binding.schoolandteachersViewPager) { + adapter = pagerAdapter + offscreenPageLimit = 2 + setOnSelectPageListener(presenter::onPageSelected) + } + + with(pagerAdapter) { + containerId = binding.schoolandteachersViewPager.id + titleFactory = { + when (it) { + 0 -> getString(R.string.school_title) + 1 -> getString(R.string.teachers_title) + else -> throw IllegalStateException() + } + } + itemFactory = { + when (it) { + 0 -> SchoolFragment.newInstance() + 1 -> TeacherFragment.newInstance() + else -> throw IllegalStateException() + } + } + TabLayoutMediator( + binding.schoolandteachersTabLayout, + binding.schoolandteachersViewPager, + this + ).attach() + } + + binding.schoolandteachersTabLayout.elevation = requireContext().dpToPx(4f) + } + + override fun showContent(show: Boolean) { + with(binding) { + schoolandteachersViewPager.visibility = if (show) VISIBLE else INVISIBLE + schoolandteachersTabLayout.visibility = if (show) VISIBLE else INVISIBLE + } + } + + override fun showProgress(show: Boolean) { + binding.schoolandteachersProgress.visibility = if (show) VISIBLE else INVISIBLE + } + + fun onChildFragmentLoaded() { + presenter.onChildViewLoaded() + } + + override fun notifyChildLoadData(index: Int, forceRefresh: Boolean) { + (pagerAdapter.getFragmentInstance(index) as? SchoolAndTeachersChildView) + ?.onParentLoadData(forceRefresh) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersPresenter.kt new file mode 100644 index 000000000..43823d6b4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersPresenter.kt @@ -0,0 +1,44 @@ +package io.github.wulkanowy.ui.modules.schoolandteachers + +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import timber.log.Timber +import javax.inject.Inject + +class SchoolAndTeachersPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository +) : BasePresenter(errorHandler, studentRepository) { + + override fun onAttachView(view: SchoolAndTeachersView) { + super.onAttachView(view) + presenterScope.launch { + view.initView() + Timber.i("Message view was initialized") + loadData() + } + } + + fun onPageSelected(index: Int) { + loadChild(index) + } + + private fun loadData() { + view?.run { loadChild(currentPageIndex) } + } + + private fun loadChild(index: Int, forceRefresh: Boolean = false) { + Timber.i("Load schoolandteachers child view index: $index") + view?.notifyChildLoadData(index, forceRefresh) + } + + fun onChildViewLoaded() { + view?.apply { + showContent(true) + showProgress(false) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersView.kt new file mode 100644 index 000000000..594441ec7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersView.kt @@ -0,0 +1,16 @@ +package io.github.wulkanowy.ui.modules.schoolandteachers + +import io.github.wulkanowy.ui.base.BaseView + +interface SchoolAndTeachersView : BaseView { + + val currentPageIndex: Int + + fun initView() + + fun showContent(show: Boolean) + + fun showProgress(show: Boolean) + + fun notifyChildLoadData(index: Int, forceRefresh: Boolean) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolFragment.kt new file mode 100644 index 000000000..fba2f0407 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolFragment.kt @@ -0,0 +1,115 @@ +package io.github.wulkanowy.ui.modules.schoolandteachers.school + +import android.os.Bundle +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.School +import io.github.wulkanowy.databinding.FragmentSchoolBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersChildView +import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.openDialer +import io.github.wulkanowy.utils.openNavigation +import javax.inject.Inject + +@AndroidEntryPoint +class SchoolFragment : BaseFragment(R.layout.fragment_school), SchoolView, + MainView.TitledView, SchoolAndTeachersChildView { + + @Inject + lateinit var presenter: SchoolPresenter + + override val titleStringId get() = R.string.school_title + + override val isViewEmpty get() = binding.schoolName.text.isBlank() + + companion object { + fun newInstance() = SchoolFragment() + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentSchoolBinding.bind(view) + presenter.onAttachView(this) + } + + override fun initView() { + with(binding) { + schoolSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + schoolSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) + schoolSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh)) + schoolErrorRetry.setOnClickListener { presenter.onRetry() } + schoolErrorDetails.setOnClickListener { presenter.onDetailsClick() } + + schoolAddressButton.setOnClickListener { presenter.onAddressSelected() } + schoolTelephoneButton.setOnClickListener { presenter.onTelephoneSelected() } + } + } + + override fun updateData(data: School) { + with(binding) { + val noDataString = getString(R.string.all_no_data) + schoolName.text = data.name.ifBlank { noDataString } + schoolAddress.text = data.address.ifBlank { noDataString } + schoolAddressButton.visibility = if (data.address.isNotBlank()) VISIBLE else GONE + schoolTelephone.text = data.contact.ifBlank { noDataString } + schoolTelephoneButton.visibility = if (data.contact.isNotBlank()) VISIBLE else GONE + schoolHeadmaster.text = data.headmaster.ifBlank { noDataString } + schoolPedagogue.text = data.pedagogue.ifBlank { noDataString } + } + } + + override fun showEmpty(show: Boolean) { + binding.schoolEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showErrorView(show: Boolean) { + binding.schoolError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + binding.schoolErrorMessage.text = message + } + + override fun showProgress(show: Boolean) { + binding.schoolProgress.visibility = if (show) VISIBLE else GONE + } + + override fun enableSwipe(enable: Boolean) { + binding.schoolSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + binding.schoolContent.visibility = if (show) VISIBLE else GONE + } + + override fun hideRefresh() { + binding.schoolSwipe.isRefreshing = false + } + + override fun notifyParentDataLoaded() { + (parentFragment as? SchoolAndTeachersFragment)?.onChildFragmentLoaded() + } + + override fun onParentLoadData(forceRefresh: Boolean) { + presenter.onParentViewLoadData(forceRefresh) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } + + override fun openMapsLocation(location: String) { + context?.openNavigation(location) + } + + override fun dialPhone(phone: String) { + context?.openDialer(phone) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt new file mode 100644 index 000000000..262398b8a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt @@ -0,0 +1,121 @@ +package io.github.wulkanowy.ui.modules.schoolandteachers.school + +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.repositories.SchoolRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import kotlinx.coroutines.flow.catch +import timber.log.Timber +import javax.inject.Inject + +class SchoolPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val semesterRepository: SemesterRepository, + private val schoolRepository: SchoolRepository, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + private var address: String? = null + + private var contact: String? = null + + private lateinit var lastError: Throwable + + override fun onAttachView(view: SchoolView) { + super.onAttachView(view) + view.initView() + Timber.i("School view was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError + loadData() + } + + fun onSwipeRefresh() { + loadData(true) + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + fun onParentViewLoadData(forceRefresh: Boolean) { + loadData(forceRefresh) + } + + fun onAddressSelected() { + address?.let { view?.openMapsLocation(it) } + } + + fun onTelephoneSelected() { + contact?.let { view?.dialPhone(it) } + } + + private fun loadData(forceRefresh: Boolean = false) { + flatResourceFlow { + val student = studentRepository.getCurrentStudent() + val semester = semesterRepository.getCurrentSemester(student) + schoolRepository.getSchoolInfo(student, semester, forceRefresh) + } + .logResourceStatus("load school info") + .onResourceData { + if (it != null) { + view?.run { + address = it.address.ifBlank { null } + contact = it.contact.ifBlank { null } + updateData(it) + showContent(true) + showEmpty(false) + showErrorView(false) + } + } else view?.run { + Timber.i("Loading school result: No school info found") + showContent(!isViewEmpty) + showEmpty(isViewEmpty) + showErrorView(false) + } + } + .onResourceSuccess { + if (it != null) { + analytics.logEvent("load_item", "type" to "school") + } + } + .onResourceNotLoading { + view?.run { + hideRefresh() + showProgress(false) + enableSwipe(true) + notifyParentDataLoaded() + } + } + .onResourceError(errorHandler::dispatch) + .catch { + errorHandler.dispatch(it) + view?.notifyParentDataLoaded() + } + .launch() + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + showContent(false) + showProgress(false) + } else showError(message, error) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolView.kt new file mode 100644 index 000000000..c42c2f91b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolView.kt @@ -0,0 +1,32 @@ +package io.github.wulkanowy.ui.modules.schoolandteachers.school + +import io.github.wulkanowy.data.db.entities.School +import io.github.wulkanowy.ui.base.BaseView +import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersChildView + +interface SchoolView : BaseView, SchoolAndTeachersChildView { + + val isViewEmpty: Boolean + + fun initView() + + fun updateData(data: School) + + fun showEmpty(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun hideRefresh() + + fun openMapsLocation(location: String) + + fun dialPhone(phone: String) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherAdapter.kt new file mode 100644 index 000000000..8deeae05b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherAdapter.kt @@ -0,0 +1,40 @@ +package io.github.wulkanowy.ui.modules.schoolandteachers.teacher + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Teacher +import io.github.wulkanowy.databinding.ItemTeacherBinding +import javax.inject.Inject + +class TeacherAdapter @Inject constructor() : RecyclerView.Adapter() { + + var items = emptyList() + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + ItemTeacherBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + @SuppressLint("SetTextI18n") + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + val teacher = items[position] + + with(holder.binding) { + teacherItemName.text = teacher.name + teacherItemSubject.text = if (teacher.subject.isNotBlank()) teacher.subject else root.context.getString(R.string.teacher_no_subject) + if (teacher.shortName.isNotBlank()) { + teacherItemShortName.visibility = View.VISIBLE + teacherItemShortName.text = "[${teacher.shortName}]" + } else { + teacherItemShortName.visibility = View.GONE + } + } + } + + class ItemViewHolder(val binding: ItemTeacherBinding) : RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherFragment.kt new file mode 100644 index 000000000..b052a3833 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherFragment.kt @@ -0,0 +1,110 @@ +package io.github.wulkanowy.ui.modules.schoolandteachers.teacher + +import android.os.Bundle +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Teacher +import io.github.wulkanowy.databinding.FragmentTeacherBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersChildView +import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment +import io.github.wulkanowy.ui.widgets.DividerItemDecoration +import io.github.wulkanowy.utils.getThemeAttrColor +import javax.inject.Inject + +@AndroidEntryPoint +class TeacherFragment : BaseFragment(R.layout.fragment_teacher), + TeacherView, MainView.TitledView, SchoolAndTeachersChildView { + + @Inject + lateinit var presenter: TeacherPresenter + + @Inject + lateinit var teacherAdapter: TeacherAdapter + + companion object { + fun newInstance() = TeacherFragment() + } + + override val titleStringId: Int + get() = R.string.teachers_title + + override val noSubjectString get() = getString(R.string.teacher_no_subject) + + override val isViewEmpty: Boolean + get() = teacherAdapter.items.isEmpty() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentTeacherBinding.bind(view) + presenter.onAttachView(this) + } + + override fun initView() { + with(binding.teacherRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = teacherAdapter + addItemDecoration(DividerItemDecoration(context)) + } + with(binding) { + teacherSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + teacherSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) + teacherSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh)) + teacherErrorRetry.setOnClickListener { presenter.onRetry() } + teacherErrorDetails.setOnClickListener { presenter.onDetailsClick() } + } + } + + override fun updateData(data: List) { + with(teacherAdapter) { + items = data + notifyDataSetChanged() + } + } + + override fun showEmpty(show: Boolean) { + binding.teacherEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showErrorView(show: Boolean) { + binding.teacherError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + binding.teacherErrorMessage.text = message + } + + override fun showProgress(show: Boolean) { + binding.teacherProgress.visibility = if (show) VISIBLE else GONE + } + + override fun enableSwipe(enable: Boolean) { + binding.teacherSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + binding.teacherRecycler.visibility = if (show) VISIBLE else GONE + } + + override fun hideRefresh() { + binding.teacherSwipe.isRefreshing = false + } + + override fun notifyParentDataLoaded() { + (parentFragment as? SchoolAndTeachersFragment)?.onChildFragmentLoaded() + } + + override fun onParentLoadData(forceRefresh: Boolean) { + presenter.onParentViewLoadData(forceRefresh) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt new file mode 100644 index 000000000..e2af05c92 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt @@ -0,0 +1,101 @@ +package io.github.wulkanowy.ui.modules.schoolandteachers.teacher + +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.repositories.TeacherRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import kotlinx.coroutines.flow.catch +import timber.log.Timber +import javax.inject.Inject + +class TeacherPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val semesterRepository: SemesterRepository, + private val teacherRepository: TeacherRepository, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + private lateinit var lastError: Throwable + + override fun onAttachView(view: TeacherView) { + super.onAttachView(view) + view.initView() + Timber.i("Teacher view was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError + loadData() + } + + fun onSwipeRefresh() { + loadData(true) + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData() + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + fun onParentViewLoadData(forceRefresh: Boolean) { + loadData(forceRefresh) + } + + private fun loadData(forceRefresh: Boolean = false) { + flatResourceFlow { + val student = studentRepository.getCurrentStudent() + val semester = semesterRepository.getCurrentSemester(student) + teacherRepository.getTeachers(student, semester, forceRefresh) + } + .logResourceStatus("load teachers data") + .onResourceData { + view?.run { + updateData(it.filter { item -> item.name.isNotBlank() }) + showContent(it.isNotEmpty()) + showEmpty(it.isEmpty()) + showErrorView(false) + } + } + .onResourceSuccess { + analytics.logEvent( + "load_data", + "type" to "teachers", + "items" to it.size + ) + } + .onResourceNotLoading { + view?.run { + hideRefresh() + showProgress(false) + enableSwipe(true) + notifyParentDataLoaded() + } + } + .onResourceError(errorHandler::dispatch) + .catch { + errorHandler.dispatch(it) + view?.notifyParentDataLoaded() + } + .launch() + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + showProgress(false) + } else showError(message, error) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherView.kt new file mode 100644 index 000000000..c655bfad8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherView.kt @@ -0,0 +1,30 @@ +package io.github.wulkanowy.ui.modules.schoolandteachers.teacher + +import io.github.wulkanowy.data.db.entities.Teacher +import io.github.wulkanowy.ui.base.BaseView +import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersChildView + +interface TeacherView : BaseView, SchoolAndTeachersChildView { + + val isViewEmpty: Boolean + + val noSubjectString: String + + fun initView() + + fun updateData(data: List) + + fun hideRefresh() + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun showEmpty(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementAdapter.kt new file mode 100644 index 000000000..62f6251ec --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementAdapter.kt @@ -0,0 +1,39 @@ +package io.github.wulkanowy.ui.modules.schoolannouncement + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.text.parseAsHtml +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.data.db.entities.SchoolAnnouncement +import io.github.wulkanowy.databinding.ItemSchoolAnnouncementBinding +import io.github.wulkanowy.utils.toFormattedString +import javax.inject.Inject + +class SchoolAnnouncementAdapter @Inject constructor() : + RecyclerView.Adapter() { + + var items = emptyList() + + var onItemClickListener: (SchoolAnnouncement) -> Unit = {} + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( + ItemSchoolAnnouncementBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val item = items[position] + + with(holder.binding) { + schoolAnnouncementItemDate.text = item.date.toFormattedString() + schoolAnnouncementItemType.text = item.subject + schoolAnnouncementItemContent.text = item.content.parseAsHtml() + + root.setOnClickListener { onItemClickListener(item) } + } + } + + class ViewHolder(val binding: ItemSchoolAnnouncementBinding) : + RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementDialog.kt new file mode 100644 index 000000000..7dcd51cea --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementDialog.kt @@ -0,0 +1,54 @@ +package io.github.wulkanowy.ui.modules.schoolannouncement + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.text.parseAsHtml +import androidx.fragment.app.DialogFragment +import io.github.wulkanowy.data.db.entities.SchoolAnnouncement +import io.github.wulkanowy.databinding.DialogSchoolAnnouncementBinding +import io.github.wulkanowy.utils.lifecycleAwareVariable +import io.github.wulkanowy.utils.toFormattedString + +class SchoolAnnouncementDialog : DialogFragment() { + + private var binding: DialogSchoolAnnouncementBinding by lifecycleAwareVariable() + + private lateinit var announcement: SchoolAnnouncement + + companion object { + + private const val ARGUMENT_KEY = "item" + + fun newInstance(exam: SchoolAnnouncement) = SchoolAnnouncementDialog().apply { + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + announcement = getSerializable(ARGUMENT_KEY) as SchoolAnnouncement + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogSchoolAnnouncementBinding.inflate(inflater).also { binding = it }.root + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + with(binding) { + announcementDialogSubjectValue.text = announcement.subject + announcementDialogDateValue.text = announcement.date.toFormattedString() + announcementDialogDescriptionValue.text = announcement.content.parseAsHtml() + + announcementDialogClose.setOnClickListener { dismiss() } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementFragment.kt new file mode 100644 index 000000000..baf2824ba --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementFragment.kt @@ -0,0 +1,113 @@ +package io.github.wulkanowy.ui.modules.schoolannouncement + +import android.os.Bundle +import android.view.View +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.SchoolAnnouncement +import io.github.wulkanowy.databinding.FragmentSchoolAnnouncementBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.widgets.DividerItemDecoration +import io.github.wulkanowy.utils.getThemeAttrColor +import javax.inject.Inject + +@AndroidEntryPoint +class SchoolAnnouncementFragment : + BaseFragment(R.layout.fragment_school_announcement), + SchoolAnnouncementView, MainView.TitledView { + + @Inject + lateinit var presenter: SchoolAnnouncementPresenter + + @Inject + lateinit var schoolAnnouncementAdapter: SchoolAnnouncementAdapter + + companion object { + fun newInstance() = SchoolAnnouncementFragment() + } + + override val titleStringId: Int + get() = R.string.school_announcement_title + + override val isViewEmpty: Boolean + get() = schoolAnnouncementAdapter.items.isEmpty() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentSchoolAnnouncementBinding.bind(view) + presenter.onAttachView(this) + } + + override fun initView() { + with(binding.directorInformationRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = schoolAnnouncementAdapter.apply { + onItemClickListener = presenter::onItemClickListener + } + addItemDecoration(DividerItemDecoration(context)) + } + with(binding) { + directorInformationSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + directorInformationSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) + directorInformationSwipe.setProgressBackgroundColorSchemeColor( + requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh) + ) + directorInformationErrorRetry.setOnClickListener { presenter.onRetry() } + directorInformationErrorDetails.setOnClickListener { presenter.onDetailsClick() } + } + } + + override fun updateData(data: List) { + with(schoolAnnouncementAdapter) { + items = data + notifyDataSetChanged() + } + } + + override fun clearData() { + with(schoolAnnouncementAdapter) { + items = listOf() + notifyDataSetChanged() + } + } + + override fun showEmpty(show: Boolean) { + binding.directorInformationEmpty.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showErrorView(show: Boolean) { + binding.directorInformationError.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun setErrorDetails(message: String) { + binding.directorInformationErrorMessage.text = message + } + + override fun showProgress(show: Boolean) { + binding.directorInformationProgress.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun enableSwipe(enable: Boolean) { + binding.directorInformationSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + binding.directorInformationRecycler.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showRefresh(show: Boolean) { + binding.directorInformationSwipe.isRefreshing = show + } + + override fun openSchoolAnnouncementDialog(item: SchoolAnnouncement) { + (activity as? MainActivity)?.showDialogFragment(SchoolAnnouncementDialog.newInstance(item)) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementPresenter.kt new file mode 100644 index 000000000..f77a88335 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementPresenter.kt @@ -0,0 +1,95 @@ +package io.github.wulkanowy.ui.modules.schoolannouncement + +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.db.entities.SchoolAnnouncement +import io.github.wulkanowy.data.repositories.SchoolAnnouncementRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import timber.log.Timber +import javax.inject.Inject + +class SchoolAnnouncementPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val analytics: AnalyticsHelper, + private val schoolAnnouncementRepository: SchoolAnnouncementRepository, +) : BasePresenter(errorHandler, studentRepository) { + + private lateinit var lastError: Throwable + + override fun onAttachView(view: SchoolAnnouncementView) { + super.onAttachView(view) + view.initView() + Timber.i("School announcement view was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError + loadData() + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the School announcement") + loadData(true) + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + fun onItemClickListener(item: SchoolAnnouncement) { + view?.openSchoolAnnouncementDialog(item) + } + + private fun loadData(forceRefresh: Boolean = false) { + flatResourceFlow { + val student = studentRepository.getCurrentStudent() + schoolAnnouncementRepository.getSchoolAnnouncements(student, forceRefresh) + } + .logResourceStatus("load school announcement") + .onResourceData { + view?.run { + enableSwipe(true) + showProgress(false) + showErrorView(false) + showContent(it.isNotEmpty()) + showEmpty(it.isEmpty()) + updateData(it) + } + } + .onResourceSuccess { + analytics.logEvent( + "load_school_announcement", + "items" to it.size + ) + } + .onResourceIntermediate { view?.showRefresh(true) } + .onResourceNotLoading { + view?.run { + enableSwipe(true) + showProgress(false) + showRefresh(false) + } + } + .onResourceError(errorHandler::dispatch) + .launch("load_data") + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementView.kt new file mode 100644 index 000000000..383d0f294 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementView.kt @@ -0,0 +1,31 @@ +package io.github.wulkanowy.ui.modules.schoolannouncement + +import io.github.wulkanowy.data.db.entities.SchoolAnnouncement +import io.github.wulkanowy.ui.base.BaseView + +interface SchoolAnnouncementView : BaseView { + + val isViewEmpty: Boolean + + fun initView() + + fun updateData(data: List) + + fun clearData() + + fun showEmpty(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun openSchoolAnnouncementDialog(item: SchoolAnnouncement) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun showRefresh(show: Boolean) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt new file mode 100644 index 000000000..d56cdfa7c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt @@ -0,0 +1,34 @@ +package io.github.wulkanowy.ui.modules.settings + +import android.os.Bundle +import androidx.preference.PreferenceFragmentCompat +import io.github.wulkanowy.R +import io.github.wulkanowy.ui.modules.main.MainView +import timber.log.Timber + +class SettingsFragment : PreferenceFragmentCompat(), MainView.TitledView, SettingsView { + + companion object { + + fun newInstance() = SettingsFragment() + } + + override val titleStringId get() = R.string.settings_title + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.scheme_preferences, rootKey) + Timber.i("Settings view was initialized") + } + + override fun showError(text: String, error: Throwable) {} + + override fun showMessage(text: String) {} + + override fun showExpiredDialog() {} + + override fun openClearLoginView() {} + + override fun showErrorDetailsDialog(error: Throwable) {} + + override fun showChangePasswordSnackbar(redirectUrl: String) {} +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt new file mode 100644 index 000000000..79f91bc5d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt @@ -0,0 +1,5 @@ +package io.github.wulkanowy.ui.modules.settings + +import io.github.wulkanowy.ui.base.BaseView + +interface SettingsView : BaseView \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedFragment.kt new file mode 100644 index 000000000..b4ba5bc4b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedFragment.kt @@ -0,0 +1,74 @@ +package io.github.wulkanowy.ui.modules.settings.advanced + +import android.content.SharedPreferences +import android.os.Bundle +import android.view.View +import androidx.preference.PreferenceFragmentCompat +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.ui.base.BaseActivity +import io.github.wulkanowy.ui.base.ErrorDialog +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.AppInfo +import javax.inject.Inject + +@AndroidEntryPoint +class AdvancedFragment : PreferenceFragmentCompat(), + SharedPreferences.OnSharedPreferenceChangeListener, + MainView.TitledView, AdvancedView { + + @Inject + lateinit var presenter: AdvancedPresenter + + @Inject + lateinit var appInfo: AppInfo + + override val titleStringId get() = R.string.pref_settings_advanced_title + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + presenter.onAttachView(this) + } + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.scheme_preferences_advanced, rootKey) + } + + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { + presenter.onSharedPreferenceChanged(key) + } + + override fun showError(text: String, error: Throwable) { + (activity as? BaseActivity<*, *>)?.showError(text, error) + } + + override fun showMessage(text: String) { + (activity as? BaseActivity<*, *>)?.showMessage(text) + } + + override fun showExpiredDialog() { + (activity as? BaseActivity<*, *>)?.showExpiredDialog() + } + + override fun showChangePasswordSnackbar(redirectUrl: String) { + (activity as? BaseActivity<*, *>)?.showChangePasswordSnackbar(redirectUrl) + } + + override fun openClearLoginView() { + (activity as? BaseActivity<*, *>)?.openClearLoginView() + } + + override fun showErrorDetailsDialog(error: Throwable) { + ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) + } + + override fun onResume() { + super.onResume() + preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this) + } + + override fun onPause() { + super.onPause() + preferenceScreen.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedPresenter.kt new file mode 100644 index 000000000..d38f841fe --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedPresenter.kt @@ -0,0 +1,25 @@ +package io.github.wulkanowy.ui.modules.settings.advanced + +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import timber.log.Timber +import javax.inject.Inject + +class AdvancedPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val analytics: AnalyticsHelper, +) : BasePresenter(errorHandler, studentRepository) { + + override fun onAttachView(view: AdvancedView) { + super.onAttachView(view) + Timber.i("Settings advanced view was initialized") + } + + fun onSharedPreferenceChanged(key: String) { + Timber.i("Change settings $key") + analytics.logEvent("setting_changed", "name" to key) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedView.kt new file mode 100644 index 000000000..90c0474e7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedView.kt @@ -0,0 +1,5 @@ +package io.github.wulkanowy.ui.modules.settings.advanced + +import io.github.wulkanowy.ui.base.BaseView + +interface AdvancedView : BaseView diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceFragment.kt new file mode 100644 index 000000000..1f6d5143b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceFragment.kt @@ -0,0 +1,90 @@ +package io.github.wulkanowy.ui.modules.settings.appearance + +import android.content.SharedPreferences +import android.os.Bundle +import android.view.View +import androidx.preference.PreferenceFragmentCompat +import com.yariksoffice.lingver.Lingver +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.ui.base.BaseActivity +import io.github.wulkanowy.ui.base.ErrorDialog +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.AppInfo +import javax.inject.Inject + +@AndroidEntryPoint +class AppearanceFragment : PreferenceFragmentCompat(), + SharedPreferences.OnSharedPreferenceChangeListener, + MainView.TitledView, AppearanceView { + + @Inject + lateinit var presenter: AppearancePresenter + + @Inject + lateinit var appInfo: AppInfo + + @Inject + lateinit var lingver: Lingver + + override val titleStringId get() = R.string.pref_settings_appearance_title + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + presenter.onAttachView(this) + } + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.scheme_preferences_appearance, rootKey) + } + + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { + presenter.onSharedPreferenceChanged(key) + } + + override fun recreateView() { + activity?.recreate() + } + + override fun updateLanguage(langCode: String) { + lingver.setLocale(requireContext(), langCode) + } + + override fun updateLanguageToFollowSystem() { + lingver.setFollowSystemLocale(requireContext()) + } + + override fun showError(text: String, error: Throwable) { + (activity as? BaseActivity<*, *>)?.showError(text, error) + } + + override fun showMessage(text: String) { + (activity as? BaseActivity<*, *>)?.showMessage(text) + } + + override fun showExpiredDialog() { + (activity as? BaseActivity<*, *>)?.showExpiredDialog() + } + + override fun showChangePasswordSnackbar(redirectUrl: String) { + (activity as? BaseActivity<*, *>)?.showChangePasswordSnackbar(redirectUrl) + } + + override fun openClearLoginView() { + (activity as? BaseActivity<*, *>)?.openClearLoginView() + } + + override fun showErrorDetailsDialog(error: Throwable) { + ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) + } + + override fun onResume() { + super.onResume() + preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this) + } + + override fun onPause() { + super.onPause() + preferenceScreen.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearancePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearancePresenter.kt new file mode 100644 index 000000000..14592a6cc --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearancePresenter.kt @@ -0,0 +1,45 @@ +package io.github.wulkanowy.ui.modules.settings.appearance + +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.AppInfo +import timber.log.Timber +import javax.inject.Inject + +class AppearancePresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val preferencesRepository: PreferencesRepository, + private val analytics: AnalyticsHelper, + private val appInfo: AppInfo +) : BasePresenter(errorHandler, studentRepository) { + + override fun onAttachView(view: AppearanceView) { + super.onAttachView(view) + Timber.i("Settings appearance view was initialized") + } + + fun onSharedPreferenceChanged(key: String) { + Timber.i("Change settings $key") + + preferencesRepository.apply { + when (key) { + appThemeKey -> view?.recreateView() + appLanguageKey -> view?.run { + if (appLanguage == "system") { + updateLanguageToFollowSystem() + analytics.logEvent("language", "setting_changed" to appInfo.systemLanguage) + } else { + updateLanguage(appLanguage) + analytics.logEvent("language", "setting_changed" to appLanguage) + } + recreateView() + } + } + } + analytics.logEvent("setting_changed", "name" to key) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceView.kt new file mode 100644 index 000000000..ecee4f423 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceView.kt @@ -0,0 +1,12 @@ +package io.github.wulkanowy.ui.modules.settings.appearance + +import io.github.wulkanowy.ui.base.BaseView + +interface AppearanceView : BaseView { + + fun recreateView() + + fun updateLanguage(langCode: String) + + fun updateLanguageToFollowSystem() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsFragment.kt new file mode 100644 index 000000000..364ad2137 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsFragment.kt @@ -0,0 +1,224 @@ +package io.github.wulkanowy.ui.modules.settings.notifications + +import android.annotation.SuppressLint +import android.content.Intent +import android.content.SharedPreferences +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.provider.Settings +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.activity.result.contract.ActivityResultContracts +import androidx.appcompat.app.AlertDialog +import androidx.core.app.NotificationManagerCompat +import androidx.preference.Preference +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.SwitchPreferenceCompat +import androidx.recyclerview.widget.RecyclerView +import com.thelittlefireman.appkillermanager.AppKillerManager +import com.thelittlefireman.appkillermanager.exceptions.NoActionFoundException +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.ui.base.BaseActivity +import io.github.wulkanowy.ui.base.ErrorDialog +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.AppInfo +import io.github.wulkanowy.utils.openInternetBrowser +import timber.log.Timber +import javax.inject.Inject + +@AndroidEntryPoint +class NotificationsFragment : PreferenceFragmentCompat(), + SharedPreferences.OnSharedPreferenceChangeListener, + MainView.TitledView, NotificationsView { + + @Inject + lateinit var presenter: NotificationsPresenter + + @Inject + lateinit var appInfo: AppInfo + + override val titleStringId get() = R.string.pref_settings_notifications_title + + override val isNotificationPermissionGranted: Boolean + get() { + val packageNameList = + NotificationManagerCompat.getEnabledListenerPackages(requireContext()) + val appPackageName = requireContext().packageName + + return appPackageName in packageNameList + } + + private val notificationSettingsPiggybackContract = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + presenter.onNotificationPiggybackPermissionResult() + } + + private val notificationSettingsExactAlarmsContract = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + presenter.onNotificationExactAlarmPermissionResult() + } + + override fun initView(showDebugNotificationSwitch: Boolean) { + findPreference(getString(R.string.pref_key_notification_debug))?.isVisible = + showDebugNotificationSwitch + + findPreference(getString(R.string.pref_key_notifications_fix_issues))?.run { + isVisible = AppKillerManager.isDeviceSupported() + && AppKillerManager.isAnyActionAvailable(requireContext()) + + setOnPreferenceClickListener { + presenter.onFixSyncIssuesClicked() + true + } + } + + findPreference(getString(R.string.pref_key_notifications_system_settings)) + ?.setOnPreferenceClickListener { + presenter.onOpenSystemSettingsClicked() + true + } + } + + override fun onCreateRecyclerView( + inflater: LayoutInflater, + parent: ViewGroup, + state: Bundle? + ): RecyclerView = super.onCreateRecyclerView(inflater, parent, state) + .also { + it.itemAnimator = null + it.layoutAnimation = null + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + presenter.onAttachView(this) + } + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.scheme_preferences_notifications, rootKey) + } + + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { + presenter.onSharedPreferenceChanged(key) + } + + override fun enableNotification(notificationKey: String, enable: Boolean) { + findPreference(notificationKey)?.run { + isEnabled = enable + summary = if (enable) null else getString(R.string.pref_notify_disabled_summary) + } + } + + override fun showError(text: String, error: Throwable) { + (activity as? BaseActivity<*, *>)?.showError(text, error) + } + + override fun showMessage(text: String) { + (activity as? BaseActivity<*, *>)?.showMessage(text) + } + + override fun showExpiredDialog() { + (activity as? BaseActivity<*, *>)?.showExpiredDialog() + } + + override fun showChangePasswordSnackbar(redirectUrl: String) { + (activity as? BaseActivity<*, *>)?.showChangePasswordSnackbar(redirectUrl) + } + + override fun openClearLoginView() { + (activity as? BaseActivity<*, *>)?.openClearLoginView() + } + + override fun showErrorDetailsDialog(error: Throwable) { + ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) + } + + override fun showFixSyncDialog() { + AlertDialog.Builder(requireContext()) + .setTitle(R.string.pref_notify_fix_sync_issues) + .setMessage(R.string.pref_notify_fix_sync_issues_message) + .setNegativeButton(android.R.string.cancel) { _, _ -> } + .setPositiveButton(R.string.pref_notify_open_system_settings) { _, _ -> + try { + AppKillerManager.doActionPowerSaving(requireContext()) + AppKillerManager.doActionAutoStart(requireContext()) + AppKillerManager.doActionNotification(requireContext()) + } catch (e: NoActionFoundException) { + requireContext().openInternetBrowser( + "https://dontkillmyapp.com/${AppKillerManager.getDevice()?.manufacturer}", + ::showMessage + ) + } + } + .show() + } + + @SuppressLint("InlinedApi") + override fun openSystemSettings() { + val intent = if (appInfo.systemVersion >= Build.VERSION_CODES.O) { + Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply { + putExtra("android.provider.extra.APP_PACKAGE", requireActivity().packageName) + } + } else { + Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { + data = Uri.fromParts("package", requireActivity().packageName, null) + } + } + try { + requireActivity().startActivity(intent) + } catch (e: Exception) { + Timber.e(e) + } + } + + override fun openNotificationPermissionDialog() { + AlertDialog.Builder(requireContext()) + .setTitle(getString(R.string.pref_notification_piggyback_popup_title)) + .setMessage(getString(R.string.pref_notification_piggyback_popup_description)) + .setPositiveButton(getString(R.string.pref_notification_go_to_settings)) { _, _ -> + notificationSettingsPiggybackContract.launch(Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS")) + } + .setNegativeButton(android.R.string.cancel) { _, _ -> + setNotificationPiggybackPreferenceChecked(false) + } + .setOnDismissListener { setNotificationPiggybackPreferenceChecked(false) } + .show() + } + + override fun openNotificationExactAlarmSettings() { + AlertDialog.Builder(requireContext()) + .setTitle(getString(R.string.pref_notification_exact_alarm_popup_title)) + .setMessage(getString(R.string.pref_notification_exact_alarm_popup_descriptions)) + .setPositiveButton(getString(R.string.pref_notification_go_to_settings)) { _, _ -> + notificationSettingsExactAlarmsContract.launch(Intent("android.settings.REQUEST_SCHEDULE_EXACT_ALARM")) + } + .setNegativeButton(android.R.string.cancel) { _, _ -> + setUpcomingLessonsNotificationPreferenceChecked(false) + } + .setOnDismissListener { setUpcomingLessonsNotificationPreferenceChecked(false) } + .show() + } + + override fun setNotificationPiggybackPreferenceChecked(isChecked: Boolean) { + findPreference(getString(R.string.pref_key_notifications_piggyback))?.isChecked = + isChecked + } + + override fun setUpcomingLessonsNotificationPreferenceChecked(isChecked: Boolean) { + findPreference(getString(R.string.pref_key_notifications_upcoming_lessons_enable))?.isChecked = + isChecked + } + + override fun onResume() { + super.onResume() + preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this) + } + + override fun onPause() { + super.onPause() + preferenceScreen.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsPresenter.kt new file mode 100644 index 000000000..4cbdac945 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsPresenter.kt @@ -0,0 +1,90 @@ +package io.github.wulkanowy.ui.modules.settings.notifications + +import com.chuckerteam.chucker.api.ChuckerCollector +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.AppInfo +import timber.log.Timber +import javax.inject.Inject + +class NotificationsPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val preferencesRepository: PreferencesRepository, + private val timetableNotificationHelper: TimetableNotificationSchedulerHelper, + private val appInfo: AppInfo, + private val analytics: AnalyticsHelper, + private val chuckerCollector: ChuckerCollector +) : BasePresenter(errorHandler, studentRepository) { + + override fun onAttachView(view: NotificationsView) { + super.onAttachView(view) + + with(view) { + enableNotification( + preferencesRepository.notificationsEnableKey, + preferencesRepository.isServiceEnabled + ) + initView(appInfo.isDebug) + } + + checkNotificationPiggybackState() + + Timber.i("Settings notifications view was initialized") + } + + fun onSharedPreferenceChanged(key: String) { + Timber.i("Change settings $key") + + preferencesRepository.apply { + when (key) { + isUpcomingLessonsNotificationsEnableKey, isUpcomingLessonsNotificationsPersistentKey -> { + if (!isUpcomingLessonsNotificationsEnable) { + timetableNotificationHelper.cancelNotification() + } else if (!timetableNotificationHelper.canScheduleExactAlarms()) { + view?.openNotificationExactAlarmSettings() + } + } + isDebugNotificationEnableKey -> { + chuckerCollector.showNotification = isDebugNotificationEnable + } + isNotificationPiggybackEnabledKey -> { + if (isNotificationPiggybackEnabled && view?.isNotificationPermissionGranted == false) { + view?.openNotificationPermissionDialog() + } + } + } + } + analytics.logEvent("setting_changed", "name" to key) + } + + fun onFixSyncIssuesClicked() { + view?.showFixSyncDialog() + } + + fun onOpenSystemSettingsClicked() { + view?.openSystemSettings() + } + + fun onNotificationPiggybackPermissionResult() { + view?.run { + setNotificationPiggybackPreferenceChecked(isNotificationPermissionGranted) + } + } + + fun onNotificationExactAlarmPermissionResult() { + view?.setUpcomingLessonsNotificationPreferenceChecked(timetableNotificationHelper.canScheduleExactAlarms()) + } + + private fun checkNotificationPiggybackState() { + if (preferencesRepository.isNotificationPiggybackEnabled) { + view?.run { + setNotificationPiggybackPreferenceChecked(isNotificationPermissionGranted) + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsView.kt new file mode 100644 index 000000000..2bf8e31f4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsView.kt @@ -0,0 +1,24 @@ +package io.github.wulkanowy.ui.modules.settings.notifications + +import io.github.wulkanowy.ui.base.BaseView + +interface NotificationsView : BaseView { + + val isNotificationPermissionGranted: Boolean + + fun initView(showDebugNotificationSwitch: Boolean) + + fun showFixSyncDialog() + + fun openSystemSettings() + + fun enableNotification(notificationKey: String, enable: Boolean) + + fun openNotificationPermissionDialog() + + fun openNotificationExactAlarmSettings() + + fun setNotificationPiggybackPreferenceChecked(isChecked: Boolean) + + fun setUpcomingLessonsNotificationPreferenceChecked(isChecked: Boolean) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt new file mode 100644 index 000000000..8477e3222 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt @@ -0,0 +1,106 @@ +package io.github.wulkanowy.ui.modules.settings.sync + +import android.content.SharedPreferences +import android.os.Bundle +import android.view.View +import androidx.preference.Preference +import androidx.preference.PreferenceFragmentCompat +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.ui.base.BaseActivity +import io.github.wulkanowy.ui.base.ErrorDialog +import io.github.wulkanowy.ui.modules.main.MainView +import javax.inject.Inject + +@AndroidEntryPoint +class SyncFragment : PreferenceFragmentCompat(), + SharedPreferences.OnSharedPreferenceChangeListener, + MainView.TitledView, SyncView { + + @Inject + lateinit var presenter: SyncPresenter + + override val titleStringId get() = R.string.pref_settings_sync_title + + override val syncSuccessString get() = getString(R.string.pref_services_message_sync_success) + + override val syncFailedString get() = getString(R.string.pref_services_message_sync_failed) + + override fun initView() { + findPreference(getString(R.string.pref_key_services_force_sync))?.run { + onPreferenceClickListener = Preference.OnPreferenceClickListener { + presenter.onSyncNowClicked() + true + } + } + } + + override fun setLastSyncDate(lastSyncDate: String) { + findPreference(getString(R.string.pref_key_services_force_sync))?.run { + summary = getString(R.string.pref_services_last_full_sync_date, lastSyncDate) + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + presenter.onAttachView(this) + } + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.scheme_preferences_sync, rootKey) + } + + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { + presenter.onSharedPreferenceChanged(key) + } + + override fun setServicesSuspended(serviceEnablesKey: String, isHolidays: Boolean) { + findPreference(serviceEnablesKey)?.run { + summary = if (isHolidays) getString(R.string.pref_services_suspended) else "" + isEnabled = !isHolidays + } + } + + override fun setSyncInProgress(inProgress: Boolean) { + if (activity == null || !isAdded) return + + findPreference(getString(R.string.pref_key_services_force_sync))?.run { + isEnabled = !inProgress + summary = if (inProgress) getString(R.string.pref_services_sync_in_progress) else "" + } + } + + override fun showError(text: String, error: Throwable) { + (activity as? BaseActivity<*, *>)?.showError(text, error) + } + + override fun showMessage(text: String) { + (activity as? BaseActivity<*, *>)?.showMessage(text) + } + + override fun showExpiredDialog() { + (activity as? BaseActivity<*, *>)?.showExpiredDialog() + } + + override fun showChangePasswordSnackbar(redirectUrl: String) { + (activity as? BaseActivity<*, *>)?.showChangePasswordSnackbar(redirectUrl) + } + + override fun openClearLoginView() { + (activity as? BaseActivity<*, *>)?.openClearLoginView() + } + + override fun showErrorDetailsDialog(error: Throwable) { + ErrorDialog.newInstance(error).show(childFragmentManager, "error_details") + } + + override fun onResume() { + super.onResume() + preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this) + } + + override fun onPause() { + super.onPause() + preferenceScreen.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncPresenter.kt new file mode 100644 index 000000000..1ecb4a6ef --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncPresenter.kt @@ -0,0 +1,86 @@ +package io.github.wulkanowy.ui.modules.settings.sync + +import androidx.work.WorkInfo +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.services.sync.SyncManager +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.isHolidays +import io.github.wulkanowy.utils.toFormattedString +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.onEach +import timber.log.Timber +import java.time.LocalDate.now +import javax.inject.Inject + +class SyncPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val preferencesRepository: PreferencesRepository, + private val analytics: AnalyticsHelper, + private val syncManager: SyncManager, +) : BasePresenter(errorHandler, studentRepository) { + + override fun onAttachView(view: SyncView) { + super.onAttachView(view) + Timber.i("Settings sync view was initialized") + view.setServicesSuspended(preferencesRepository.serviceEnableKey, now().isHolidays) + view.initView() + setSyncDateInView() + } + + fun onSharedPreferenceChanged(key: String) { + Timber.i("Change settings $key") + + preferencesRepository.apply { + when (key) { + serviceEnableKey -> with(syncManager) { if (isServiceEnabled) startPeriodicSyncWorker() else stopSyncWorker() } + servicesIntervalKey, servicesOnlyWifiKey -> syncManager.startPeriodicSyncWorker(true) + } + } + analytics.logEvent("setting_changed", "name" to key) + } + + fun onSyncNowClicked() { + view?.run { + syncManager.startOneTimeSyncWorker().onEach { workInfo -> + when (workInfo?.state) { + WorkInfo.State.ENQUEUED -> { + setSyncInProgress(true) + Timber.i("Setting sync now started") + analytics.logEvent("sync_now", "status" to "started") + } + WorkInfo.State.SUCCEEDED -> { + showMessage(syncSuccessString) + analytics.logEvent("sync_now", "status" to "success") + } + WorkInfo.State.FAILED -> { + showError( + syncFailedString, + Throwable( + message = workInfo.outputData.getString("error_message"), + cause = Throwable(workInfo.outputData.getString("error_stack")) + ) + ) + analytics.logEvent("sync_now", "status" to "failed") + } + else -> Timber.d("Sync now state: ${workInfo?.state}") + } + if (workInfo?.state?.isFinished == true) { + setSyncInProgress(false) + setSyncDateInView() + } + }.catch { + Timber.e(it, "Sync now failed") + }.launch("sync") + } + } + + private fun setSyncDateInView() { + val lastSyncDate = preferencesRepository.lasSyncDate ?: return + + view?.setLastSyncDate(lastSyncDate.toFormattedString("dd.MM.yyyy HH:mm:ss")) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncView.kt new file mode 100644 index 000000000..6ffe156f2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncView.kt @@ -0,0 +1,18 @@ +package io.github.wulkanowy.ui.modules.settings.sync + +import io.github.wulkanowy.ui.base.BaseView + +interface SyncView : BaseView { + + val syncSuccessString: String + + val syncFailedString: String + + fun initView() + + fun setLastSyncDate(lastSyncDate: String) + + fun setServicesSuspended(serviceEnablesKey: String, isHolidays: Boolean) + + fun setSyncInProgress(inProgress: Boolean) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt new file mode 100644 index 000000000..cfb628496 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt @@ -0,0 +1,74 @@ +package io.github.wulkanowy.ui.modules.splash + +import android.annotation.SuppressLint +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.widget.Toast +import android.widget.Toast.LENGTH_LONG +import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen +import androidx.viewbinding.ViewBinding +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.services.shortcuts.ShortcutsHelper +import io.github.wulkanowy.ui.base.BaseActivity +import io.github.wulkanowy.ui.modules.Destination +import io.github.wulkanowy.ui.modules.login.LoginActivity +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.utils.openInternetBrowser +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import javax.inject.Inject + +@SuppressLint("CustomSplashScreen") +@AndroidEntryPoint +class SplashActivity : BaseActivity(), SplashView { + + @Inject + override lateinit var presenter: SplashPresenter + + @Inject + lateinit var shortcutsHelper: ShortcutsHelper + + companion object { + + private const val EXTRA_START_DESTINATION = "start_destination_json" + + private const val EXTRA_EXTERNAL_URL = "external_url" + + fun getStartIntent(context: Context, destination: Destination? = null) = + Intent(context, SplashActivity::class.java).apply { + destination?.let { putExtra(EXTRA_START_DESTINATION, Json.encodeToString(it)) } + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + installSplashScreen().setKeepOnScreenCondition { true } + shortcutsHelper.initializeShortcuts() + + val externalLink = intent?.getStringExtra(EXTRA_EXTERNAL_URL) + val startDestinationJson = intent?.getStringExtra(EXTRA_START_DESTINATION) + + presenter.onAttachView(this, externalLink, startDestinationJson) + } + + override fun openLoginView() { + startActivity(LoginActivity.getStartIntent(this)) + finish() + } + + override fun openMainView(destination: Destination?) { + startActivity(MainActivity.getStartIntent(this, destination)) + finish() + } + + override fun openExternalUrlAndFinish(url: String) { + openInternetBrowser(url, ::showMessage) + finish() + } + + override fun showError(text: String, error: Throwable) { + Toast.makeText(this, text, LENGTH_LONG).show() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt new file mode 100644 index 000000000..767c885c1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt @@ -0,0 +1,40 @@ +package io.github.wulkanowy.ui.modules.splash + +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.ui.modules.Destination +import kotlinx.coroutines.launch +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json +import javax.inject.Inject + +class SplashPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val json: Json +) : BasePresenter(errorHandler, studentRepository) { + + fun onAttachView(view: SplashView, externalUrl: String?, startDestinationJson: String?) { + super.onAttachView(view) + + val startDestination: Destination? = startDestinationJson?.let { json.decodeFromString(it) } + + if (!externalUrl.isNullOrBlank()) { + view.openExternalUrlAndFinish(externalUrl) + return + } + + presenterScope.launch { + runCatching { studentRepository.isCurrentStudentSet() } + .onFailure(errorHandler::dispatch) + .onSuccess { + if (it) { + view.openMainView(startDestination) + } else { + view.openLoginView() + } + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashView.kt new file mode 100644 index 000000000..1c5d8bfd4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashView.kt @@ -0,0 +1,13 @@ +package io.github.wulkanowy.ui.modules.splash + +import io.github.wulkanowy.ui.base.BaseView +import io.github.wulkanowy.ui.modules.Destination + +interface SplashView : BaseView { + + fun openLoginView() + + fun openMainView(destination: Destination?) + + fun openExternalUrlAndFinish(url: String) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoAdapter.kt new file mode 100644 index 000000000..60912200c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoAdapter.kt @@ -0,0 +1,45 @@ +package io.github.wulkanowy.ui.modules.studentinfo + +import android.view.LayoutInflater +import android.view.View.GONE +import android.view.View.VISIBLE +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.databinding.ItemStudentInfoBinding +import javax.inject.Inject + +class StudentInfoAdapter @Inject constructor() : + RecyclerView.Adapter() { + + var items = listOf() + + var onItemClickListener: (StudentInfoView.Type?) -> Unit = {} + + var onItemLongClickListener: (text: String) -> Unit = {} + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( + ItemStudentInfoBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val item = items[position] + + with(holder.binding) { + studentInfoItemTitle.text = item.title + studentInfoItemSubtitle.text = item.subtitle + studentInfoItemArrow.visibility = if (item.showArrow) VISIBLE else GONE + + with(root) { + setOnClickListener { onItemClickListener(item.viewType) } + setOnLongClickListener { + onItemLongClickListener(studentInfoItemSubtitle.text.toString()) + true + } + } + } + } + + class ViewHolder(val binding: ItemStudentInfoBinding) : RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoFragment.kt new file mode 100644 index 000000000..361a59440 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoFragment.kt @@ -0,0 +1,259 @@ +package io.github.wulkanowy.ui.modules.studentinfo + +import android.content.ClipData +import android.content.ClipboardManager +import android.os.Bundle +import android.view.Menu +import android.view.MenuInflater +import android.view.View +import android.widget.Toast +import androidx.core.content.getSystemService +import androidx.core.view.get +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.StudentGuardian +import io.github.wulkanowy.data.db.entities.StudentInfo +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.data.enums.Gender +import io.github.wulkanowy.databinding.FragmentStudentInfoBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.capitalise +import io.github.wulkanowy.utils.getThemeAttrColor +import javax.inject.Inject + +@AndroidEntryPoint +class StudentInfoFragment : + BaseFragment(R.layout.fragment_student_info), StudentInfoView, + MainView.TitledView { + + @Inject + lateinit var presenter: StudentInfoPresenter + + @Inject + lateinit var studentInfoAdapter: StudentInfoAdapter + + override val titleStringId: Int + get() = when (requireArguments().getSerializable(INFO_TYPE_ARGUMENT_KEY) as? StudentInfoView.Type) { + StudentInfoView.Type.PERSONAL -> R.string.account_personal_data + StudentInfoView.Type.CONTACT -> R.string.account_contact + StudentInfoView.Type.ADDRESS -> R.string.account_address + StudentInfoView.Type.FAMILY -> R.string.account_family + StudentInfoView.Type.SECOND_GUARDIAN -> R.string.student_info_guardian + StudentInfoView.Type.FIRST_GUARDIAN -> R.string.student_info_guardian + else -> R.string.student_info_title + } + + override val isViewEmpty get() = studentInfoAdapter.items.isEmpty() + + companion object { + + private const val INFO_TYPE_ARGUMENT_KEY = "info_type" + + private const val STUDENT_ARGUMENT_KEY = "student_with_semesters" + + fun newInstance(type: StudentInfoView.Type, studentWithSemesters: StudentWithSemesters) = + StudentInfoFragment().apply { + arguments = Bundle().apply { + putSerializable(INFO_TYPE_ARGUMENT_KEY, type) + putSerializable(STUDENT_ARGUMENT_KEY, studentWithSemesters) + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentStudentInfoBinding.bind(view) + presenter.onAttachView( + this, + requireArguments().getSerializable(INFO_TYPE_ARGUMENT_KEY) as StudentInfoView.Type, + requireArguments().getSerializable(STUDENT_ARGUMENT_KEY) as StudentWithSemesters + ) + } + + override fun initView() { + with(binding) { + studentInfoSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + studentInfoSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) + studentInfoSwipe.setProgressBackgroundColorSchemeColor( + requireContext().getThemeAttrColor( + R.attr.colorSwipeRefresh + ) + ) + studentInfoErrorRetry.setOnClickListener { presenter.onRetry() } + studentInfoErrorDetails.setOnClickListener { presenter.onDetailsClick() } + } + + with(studentInfoAdapter) { + onItemClickListener = presenter::onItemSelected + onItemLongClickListener = presenter::onItemLongClick + } + + with(binding.studentInfoRecycler) { + layoutManager = LinearLayoutManager(context) + addItemDecoration(DividerItemDecoration(context, RecyclerView.VERTICAL)) + setHasFixedSize(true) + adapter = studentInfoAdapter + } + } + + override fun updateData(data: List) { + with(studentInfoAdapter) { + items = data + notifyDataSetChanged() + } + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + menu[0].isVisible = false + } + + override fun showPersonalTypeData(studentInfo: StudentInfo) { + updateData( + listOf( + getString(R.string.student_info_first_name) to studentInfo.firstName, + getString(R.string.student_info_second_name) to studentInfo.secondName, + getString(R.string.student_info_last_name) to studentInfo.surname, + getString(R.string.student_info_gender) to getString(if (studentInfo.gender == Gender.MALE) R.string.student_info_male else R.string.student_info_female), + getString(R.string.student_info_polish_citizenship) to getString(if (studentInfo.hasPolishCitizenship) R.string.all_yes else R.string.all_no), + getString(R.string.student_info_family_name) to studentInfo.familyName, + getString(R.string.student_info_parents_name) to studentInfo.parentsNames + ).map { + StudentInfoItem( + title = it.first, + subtitle = it.second.ifBlank { getString(R.string.all_no_data) }, + showArrow = false, + ) + } + ) + } + + override fun showContactTypeData(studentInfo: StudentInfo) { + updateData( + listOf( + getString(R.string.student_info_phone) to studentInfo.phoneNumber, + getString(R.string.student_info_cellphone) to studentInfo.cellPhoneNumber, + getString(R.string.student_info_email) to studentInfo.email + ).map { + StudentInfoItem( + title = it.first, + subtitle = it.second.ifBlank { getString(R.string.all_no_data) }, + showArrow = false, + ) + } + ) + } + + @OptIn(ExperimentalStdlibApi::class) + override fun showFamilyTypeData(studentInfo: StudentInfo) { + val items = buildList { + add(studentInfo.firstGuardian?.let { + Triple(it.kinship.capitalise(), it.fullName, StudentInfoView.Type.FIRST_GUARDIAN) + }) + + add(studentInfo.secondGuardian?.let { + Triple(it.kinship.capitalise(), it.fullName, StudentInfoView.Type.SECOND_GUARDIAN) + }) + }.filterNotNull() + + updateData( + items.map { (title, value, type) -> + StudentInfoItem( + title = title.ifBlank { getString(R.string.all_no_data) }, + subtitle = value.ifBlank { getString(R.string.all_no_data) }, + showArrow = true, + viewType = type, + ) + } + ) + } + + override fun showAddressTypeData(studentInfo: StudentInfo) { + updateData( + listOf( + getString(R.string.student_info_address) to studentInfo.address, + getString(R.string.student_info_registered_address) to studentInfo.registeredAddress, + getString(R.string.student_info_correspondence_address) to studentInfo.correspondenceAddress + ).map { + StudentInfoItem( + title = it.first, + subtitle = it.second.ifBlank { getString(R.string.all_no_data) }, + showArrow = false, + ) + } + ) + } + + override fun showGuardianTypeData(studentGuardian: StudentGuardian) { + updateData( + listOf( + getString(R.string.student_info_full_name) to studentGuardian.fullName, + getString(R.string.student_info_kinship) to studentGuardian.kinship, + getString(R.string.student_info_guardian_address) to studentGuardian.address, + getString(R.string.student_info_phones) to studentGuardian.phones, + getString(R.string.student_info_email) to studentGuardian.email + ).map { + StudentInfoItem( + title = it.first, + subtitle = it.second.ifBlank { getString(R.string.all_no_data) }, + showArrow = false, + ) + } + ) + } + + override fun openStudentInfoView( + infoType: StudentInfoView.Type, + studentWithSemesters: StudentWithSemesters + ) { + (requireActivity() as MainActivity).pushView(newInstance(infoType, studentWithSemesters)) + } + + override fun showEmpty(show: Boolean) { + binding.studentInfoEmpty.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showErrorView(show: Boolean) { + binding.studentInfoError.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun setErrorDetails(message: String) { + binding.studentInfoErrorMessage.text = message + } + + override fun showProgress(show: Boolean) { + binding.studentInfoProgress.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun enableSwipe(enable: Boolean) { + binding.studentInfoSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + binding.studentInfoRecycler.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun hideRefresh() { + binding.studentInfoSwipe.isRefreshing = false + } + + override fun copyToClipboard(text: String) { + val clipData = ClipData.newPlainText("student_info_wulkanowy", text) + requireActivity().getSystemService()?.setPrimaryClip(clipData) + Toast.makeText(context, R.string.all_copied, Toast.LENGTH_SHORT).show() + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoItem.kt new file mode 100644 index 000000000..21226539b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoItem.kt @@ -0,0 +1,8 @@ +package io.github.wulkanowy.ui.modules.studentinfo + +data class StudentInfoItem( + val title: String, + val subtitle: String, + val showArrow: Boolean, + val viewType: StudentInfoView.Type? = null, +) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoPresenter.kt new file mode 100644 index 000000000..083b590b2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoPresenter.kt @@ -0,0 +1,141 @@ +package io.github.wulkanowy.ui.modules.studentinfo + +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.db.entities.StudentInfo +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.data.repositories.StudentInfoRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.getCurrentOrLast +import timber.log.Timber +import javax.inject.Inject + +class StudentInfoPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val studentInfoRepository: StudentInfoRepository, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + private lateinit var infoType: StudentInfoView.Type + + private lateinit var studentWithSemesters: StudentWithSemesters + + private lateinit var lastError: Throwable + + fun onAttachView( + view: StudentInfoView, + type: StudentInfoView.Type, + studentWithSemesters: StudentWithSemesters + ) { + super.onAttachView(view) + infoType = type + this.studentWithSemesters = studentWithSemesters + view.initView() + Timber.i("Student info $infoType view was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError + loadData() + } + + fun onSwipeRefresh() { + loadData(true) + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + fun onItemSelected(viewType: StudentInfoView.Type?) { + viewType ?: return + + view?.openStudentInfoView( + studentWithSemesters = studentWithSemesters, + infoType = viewType, + ) + } + + fun onItemLongClick(text: String) { + view?.copyToClipboard(text) + } + + private fun loadData(forceRefresh: Boolean = false) { + flatResourceFlow { + val semester = studentWithSemesters.semesters.getCurrentOrLast() + studentInfoRepository.getStudentInfo( + student = studentWithSemesters.student, + semester = semester, + forceRefresh = forceRefresh + ) + } + .logResourceStatus("load student info $infoType") + .onResourceData { + val isFamily = infoType == StudentInfoView.Type.FAMILY + val isFirstGuardianEmpty = it?.firstGuardian == null + val isSecondGuardianEmpty = it?.secondGuardian == null + if (it != null && !(isFamily && isFirstGuardianEmpty && isSecondGuardianEmpty)) { + Timber.i("Loading student info $infoType result: Success") + showCorrectData(it) + view?.run { + showContent(true) + showEmpty(false) + showErrorView(false) + } + } else { + Timber.i("Loading student info $infoType result: No student or family info found") + view?.run { + showContent(!isViewEmpty) + showEmpty(isViewEmpty) + showErrorView(false) + } + } + } + .onResourceSuccess { + if (it != null) { + analytics.logEvent("load_item", "type" to "student_info") + } + } + .onResourceNotLoading { + view?.run { + hideRefresh() + showProgress(false) + enableSwipe(true) + } + } + .onResourceError(errorHandler::dispatch) + .launch() + } + + private fun showCorrectData(studentInfo: StudentInfo) { + when (infoType) { + StudentInfoView.Type.PERSONAL -> view?.showPersonalTypeData(studentInfo) + StudentInfoView.Type.CONTACT -> view?.showContactTypeData(studentInfo) + StudentInfoView.Type.ADDRESS -> view?.showAddressTypeData(studentInfo) + StudentInfoView.Type.FAMILY -> view?.showFamilyTypeData(studentInfo) + StudentInfoView.Type.SECOND_GUARDIAN -> view?.showGuardianTypeData(studentInfo.secondGuardian!!) + StudentInfoView.Type.FIRST_GUARDIAN -> view?.showGuardianTypeData(studentInfo.firstGuardian!!) + } + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + showContent(false) + showProgress(false) + } else showError(message, error) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoView.kt new file mode 100644 index 000000000..c20359df1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoView.kt @@ -0,0 +1,47 @@ +package io.github.wulkanowy.ui.modules.studentinfo + +import io.github.wulkanowy.data.db.entities.StudentGuardian +import io.github.wulkanowy.data.db.entities.StudentInfo +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.ui.base.BaseView + +interface StudentInfoView : BaseView { + + enum class Type { + PERSONAL, ADDRESS, CONTACT, FAMILY, FIRST_GUARDIAN, SECOND_GUARDIAN + } + + val isViewEmpty: Boolean + + fun initView() + + fun updateData(data: List) + + fun showPersonalTypeData(studentInfo: StudentInfo) + + fun showContactTypeData(studentInfo: StudentInfo) + + fun showAddressTypeData(studentInfo: StudentInfo) + + fun showFamilyTypeData(studentInfo: StudentInfo) + + fun showGuardianTypeData(studentGuardian: StudentGuardian) + + fun openStudentInfoView(infoType: Type, studentWithSemesters: StudentWithSemesters) + + fun showEmpty(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun hideRefresh() + + fun copyToClipboard(text: String) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt new file mode 100644 index 000000000..d6917672a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt @@ -0,0 +1,302 @@ +package io.github.wulkanowy.ui.modules.timetable + +import android.view.LayoutInflater +import android.view.View.GONE +import android.view.View.VISIBLE +import android.view.ViewGroup +import android.widget.TextView +import androidx.core.view.isVisible +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.databinding.ItemTimetableBinding +import io.github.wulkanowy.databinding.ItemTimetableSmallBinding +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.toFormattedString +import javax.inject.Inject + +class TimetableAdapter @Inject constructor() : + ListAdapter(differ) { + + override fun getItemViewType(position: Int): Int = getItem(position).type.ordinal + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + + return when (TimetableItemType.values()[viewType]) { + TimetableItemType.SMALL -> SmallViewHolder( + ItemTimetableSmallBinding.inflate(inflater, parent, false) + ) + TimetableItemType.NORMAL -> NormalViewHolder( + ItemTimetableBinding.inflate(inflater, parent, false) + ) + } + } + + override fun onBindViewHolder( + holder: RecyclerView.ViewHolder, + position: Int, + payloads: MutableList + ) { + if (payloads.isEmpty()) return super.onBindViewHolder(holder, position, payloads) + + if (holder is NormalViewHolder) updateTimeLeft( + binding = holder.binding, + timeLeft = (getItem(position) as TimetableItem.Normal).timeLeft, + ) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is SmallViewHolder -> bindSmallView( + binding = holder.binding, + item = getItem(position) as TimetableItem.Small, + ) + is NormalViewHolder -> bindNormalView( + binding = holder.binding, + item = getItem(position) as TimetableItem.Normal, + ) + } + } + + private fun bindSmallView(binding: ItemTimetableSmallBinding, item: TimetableItem.Small) { + val lesson = item.lesson + + with(binding) { + timetableSmallItemNumber.text = lesson.number.toString() + timetableSmallItemSubject.text = lesson.subject + timetableSmallItemTimeStart.text = lesson.start.toFormattedString("HH:mm") + timetableSmallItemRoom.text = lesson.room + timetableSmallItemTeacher.text = lesson.teacher + + bindSubjectStyle(timetableSmallItemSubject, lesson) + bindSmallDescription(binding, lesson) + bindSmallColors(binding, lesson) + + root.setOnClickListener { item.onClick(lesson) } + } + } + + private fun bindNormalView(binding: ItemTimetableBinding, item: TimetableItem.Normal) { + val lesson = item.lesson + + with(binding) { + timetableItemNumber.text = lesson.number.toString() + timetableItemSubject.text = lesson.subject + timetableItemGroup.text = lesson.group + timetableItemRoom.text = lesson.room + timetableItemTeacher.text = lesson.teacher + timetableItemTimeStart.text = lesson.start.toFormattedString("HH:mm") + timetableItemTimeFinish.text = lesson.end.toFormattedString("HH:mm") + + bindSubjectStyle(timetableItemSubject, lesson) + bindNormalDescription(binding, item) + bindNormalColors(binding, lesson) + updateTimeLeft(binding, item.timeLeft) + + root.setOnClickListener { item.onClick(lesson) } + } + } + + private fun updateTimeLeft(binding: ItemTimetableBinding, timeLeft: TimeLeft?) { + with(binding) { + when { + // before lesson + timeLeft?.until != null -> { + timetableItemTimeLeft.visibility = GONE + with(timetableItemTimeUntil) { + visibility = VISIBLE + text = context.getString( + R.string.timetable_time_until, + context.getString( + R.string.timetable_minutes, + timeLeft.until.toMinutes().toString(10) + ) + ) + } + } + // after lesson start + timeLeft?.left != null -> { + timetableItemTimeUntil.visibility = GONE + with(timetableItemTimeLeft) { + visibility = VISIBLE + text = context.getString( + R.string.timetable_time_left, + context.getString( + R.string.timetable_minutes, + timeLeft.left.toMinutes().toString() + ) + ) + } + } + // right after lesson finish + timeLeft?.isJustFinished == true -> { + timetableItemTimeUntil.visibility = GONE + timetableItemTimeLeft.visibility = VISIBLE + timetableItemTimeLeft.text = root.context.getString(R.string.timetable_finished) + } + else -> { + timetableItemTimeUntil.visibility = GONE + timetableItemTimeLeft.visibility = GONE + } + } + } + } + + private fun bindSubjectStyle(subjectView: TextView, lesson: Timetable) { + subjectView.paint.isStrikeThruText = lesson.canceled + } + + private fun bindSmallDescription(binding: ItemTimetableSmallBinding, lesson: Timetable) { + with(binding) { + if (lesson.info.isNotBlank() && !lesson.changes) { + timetableSmallItemDescription.visibility = VISIBLE + timetableSmallItemDescription.text = lesson.info + + timetableSmallItemRoom.visibility = GONE + timetableSmallItemTeacher.visibility = GONE + + timetableSmallItemDescription.setTextColor( + root.context.getThemeAttrColor( + if (lesson.canceled) R.attr.colorPrimary + else R.attr.colorTimetableChange + ) + ) + } else { + timetableSmallItemDescription.visibility = GONE + timetableSmallItemRoom.visibility = VISIBLE + timetableSmallItemTeacher.visibility = VISIBLE + } + } + } + + private fun bindNormalDescription(binding: ItemTimetableBinding, item: TimetableItem.Normal) { + val lesson = item.lesson + with(binding) { + if (lesson.info.isNotBlank() && !lesson.changes) { + timetableItemDescription.visibility = VISIBLE + timetableItemDescription.text = lesson.info + + timetableItemRoom.visibility = GONE + timetableItemGroup.visibility = GONE + timetableItemTeacher.visibility = GONE + + timetableItemDescription.setTextColor( + root.context.getThemeAttrColor( + if (lesson.canceled) R.attr.colorPrimary + else R.attr.colorTimetableChange + ) + ) + } else { + timetableItemDescription.visibility = GONE + timetableItemRoom.visibility = VISIBLE + timetableItemGroup.isVisible = item.showGroupsInPlan && lesson.group.isNotBlank() + timetableItemTeacher.visibility = VISIBLE + } + } + } + + private fun bindSmallColors(binding: ItemTimetableSmallBinding, lesson: Timetable) { + with(binding) { + if (lesson.canceled) { + updateNumberAndSubjectCanceledColor( + timetableSmallItemNumber, + timetableSmallItemSubject + ) + } else { + updateNumberColor(timetableSmallItemNumber, lesson) + updateSubjectColor(timetableSmallItemSubject, lesson) + updateRoomColor(timetableSmallItemRoom, lesson) + updateTeacherColor(timetableSmallItemTeacher, lesson) + } + } + } + + private fun bindNormalColors(binding: ItemTimetableBinding, lesson: Timetable) { + with(binding) { + if (lesson.canceled) { + updateNumberAndSubjectCanceledColor(timetableItemNumber, timetableItemSubject) + } else { + updateNumberColor(timetableItemNumber, lesson) + updateSubjectColor(timetableItemSubject, lesson) + updateRoomColor(timetableItemRoom, lesson) + updateTeacherColor(timetableItemTeacher, lesson) + } + } + } + + private fun updateNumberAndSubjectCanceledColor(numberView: TextView, subjectView: TextView) { + numberView.setTextColor(numberView.context.getThemeAttrColor(R.attr.colorPrimary)) + subjectView.setTextColor(subjectView.context.getThemeAttrColor(R.attr.colorPrimary)) + } + + private fun updateNumberColor(numberView: TextView, lesson: Timetable) { + numberView.setTextColor( + numberView.context.getThemeAttrColor( + if (lesson.changes || lesson.info.isNotBlank()) R.attr.colorTimetableChange + else android.R.attr.textColorPrimary + ) + ) + } + + private fun updateSubjectColor(subjectView: TextView, lesson: Timetable) { + subjectView.setTextColor( + subjectView.context.getThemeAttrColor( + if (lesson.subjectOld.isNotBlank() && lesson.subjectOld != lesson.subject) R.attr.colorTimetableChange + else android.R.attr.textColorPrimary + ) + ) + } + + private fun updateRoomColor(roomView: TextView, lesson: Timetable) { + roomView.setTextColor( + roomView.context.getThemeAttrColor( + if (lesson.roomOld.isNotBlank() && lesson.roomOld != lesson.room) R.attr.colorTimetableChange + else android.R.attr.textColorSecondary + ) + ) + } + + private fun updateTeacherColor(teacherTextView: TextView, lesson: Timetable) { + teacherTextView.setTextColor( + teacherTextView.context.getThemeAttrColor( + if (lesson.teacherOld.isNotBlank()) R.attr.colorTimetableChange + else android.R.attr.textColorSecondary + ) + ) + } + + private class NormalViewHolder(val binding: ItemTimetableBinding) : + RecyclerView.ViewHolder(binding.root) + + private class SmallViewHolder(val binding: ItemTimetableSmallBinding) : + RecyclerView.ViewHolder(binding.root) + + companion object { + private val differ = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: TimetableItem, newItem: TimetableItem): Boolean = + when { + oldItem is TimetableItem.Small && newItem is TimetableItem.Small -> { + oldItem.lesson.start == newItem.lesson.start + } + oldItem is TimetableItem.Normal && newItem is TimetableItem.Normal -> { + oldItem.lesson.start == newItem.lesson.start + } + else -> oldItem == newItem + } + + override fun areContentsTheSame(oldItem: TimetableItem, newItem: TimetableItem) = + oldItem == newItem + + override fun getChangePayload(oldItem: TimetableItem, newItem: TimetableItem): Any? { + return if (oldItem is TimetableItem.Normal && newItem is TimetableItem.Normal) { + if (oldItem.lesson == newItem.lesson && oldItem.timeLeft != newItem.timeLeft) { + "time_left" + } else super.getChangePayload(oldItem, newItem) + } else super.getChangePayload(oldItem, newItem) + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt new file mode 100644 index 000000000..c9243b12e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt @@ -0,0 +1,199 @@ +package io.github.wulkanowy.ui.modules.timetable + +import android.annotation.SuppressLint +import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.databinding.DialogTimetableBinding +import io.github.wulkanowy.utils.capitalise +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.lifecycleAwareVariable +import io.github.wulkanowy.utils.toFormattedString +import java.time.Instant + +class TimetableDialog : DialogFragment() { + + private var binding: DialogTimetableBinding by lifecycleAwareVariable() + + private lateinit var lesson: Timetable + + companion object { + + private const val ARGUMENT_KEY = "Item" + + fun newInstance(exam: Timetable) = TimetableDialog().apply { + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + lesson = getSerializable(ARGUMENT_KEY) as Timetable + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogTimetableBinding.inflate(inflater).apply { binding = this }.root + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + with(lesson) { + setInfo(info, canceled, changes) + setSubject(subject, subjectOld) + setTeacher(teacher, teacherOld) + setGroup(group) + setRoom(room, roomOld) + setTime(start, end) + } + + binding.timetableDialogClose.setOnClickListener { dismiss() } + } + + private fun setSubject(subject: String, subjectOld: String) { + with(binding) { + timetableDialogLessonValue.text = subject + if (subjectOld.isNotBlank() && subjectOld != subject) { + timetableDialogLessonValue.run { + paintFlags = paintFlags or STRIKE_THRU_TEXT_FLAG + text = subjectOld + } + timetableDialogLessonNewValue.run { + visibility = VISIBLE + text = subject + } + } + } + } + + @SuppressLint("DefaultLocale") + private fun setInfo(info: String, canceled: Boolean, changes: Boolean) { + with(binding) { + when { + info.isNotBlank() -> { + if (canceled) { + timetableDialogChangesTitle.setTextColor( + requireContext().getThemeAttrColor( + R.attr.colorPrimary + ) + ) + timetableDialogChangesValue.setTextColor( + requireContext().getThemeAttrColor( + R.attr.colorPrimary + ) + ) + } else { + timetableDialogChangesTitle.setTextColor( + requireContext().getThemeAttrColor( + R.attr.colorTimetableChange + ) + ) + timetableDialogChangesValue.setTextColor( + requireContext().getThemeAttrColor( + R.attr.colorTimetableChange + ) + ) + } + + timetableDialogChangesValue.text = when { + canceled && !changes -> "Lekcja odwołana: $info" + else -> info.capitalise() + } + } + else -> { + timetableDialogChangesTitle.visibility = GONE + timetableDialogChangesValue.visibility = GONE + } + } + } + } + + private fun setTeacher(teacher: String, teacherOld: String) { + with(binding) { + when { + teacherOld.isNotBlank() && teacherOld != teacher -> { + timetableDialogTeacherValue.run { + visibility = VISIBLE + paintFlags = paintFlags or STRIKE_THRU_TEXT_FLAG + text = teacherOld + } + if (teacher.isNotBlank()) { + timetableDialogTeacherNewValue.run { + visibility = VISIBLE + text = teacher + } + } + } + teacherOld.isNotBlank() && teacherOld == teacher -> { + timetableDialogTeacherValue.run { + visibility = GONE + } + timetableDialogTeacherNewValue.run { + visibility = VISIBLE + text = teacher + } + } + teacher.isNotBlank() -> timetableDialogTeacherValue.text = teacher + else -> { + timetableDialogTeacherTitle.visibility = GONE + timetableDialogTeacherValue.visibility = GONE + } + } + } + } + + private fun setGroup(group: String) { + with(binding) { + when { + group.isNotBlank() -> timetableDialogGroupValue.text = group + else -> { + timetableDialogGroupTitle.visibility = GONE + timetableDialogGroupValue.visibility = GONE + } + } + } + } + + private fun setRoom(room: String, roomOld: String) { + with(binding) { + when { + roomOld.isNotBlank() && roomOld != room -> { + timetableDialogRoomValue.run { + visibility = VISIBLE + paintFlags = paintFlags or STRIKE_THRU_TEXT_FLAG + text = roomOld + } + if (room.isNotBlank()) { + timetableDialogRoomNewValue.run { + visibility = VISIBLE + text = room + } + } + } + room.isNotBlank() -> timetableDialogRoomValue.text = room + else -> { + timetableDialogRoomTitle.visibility = GONE + timetableDialogRoomValue.visibility = GONE + } + } + } + } + + @SuppressLint("SetTextI18n") + private fun setTime(start: Instant, end: Instant) { + binding.timetableDialogTimeValue.text = + "${start.toFormattedString("HH:mm")} - ${end.toFormattedString("HH:mm")}" + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt new file mode 100644 index 000000000..fdd4aface --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt @@ -0,0 +1,203 @@ +package io.github.wulkanowy.ui.modules.timetable + +import android.os.Bundle +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import androidx.core.text.parseAsHtml +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.databinding.FragmentTimetableBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.modules.timetable.additional.AdditionalLessonsFragment +import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment +import io.github.wulkanowy.ui.widgets.DividerItemDecoration +import io.github.wulkanowy.utils.* +import java.time.LocalDate +import javax.inject.Inject + +@AndroidEntryPoint +class TimetableFragment : BaseFragment(R.layout.fragment_timetable), + TimetableView, MainView.MainChildView, MainView.TitledView { + + @Inject + lateinit var presenter: TimetablePresenter + + @Inject + lateinit var timetableAdapter: TimetableAdapter + + companion object { + private const val SAVED_DATE_KEY = "CURRENT_DATE" + + private const val ARGUMENT_DATE_KEY = "ARGUMENT_DATE" + + fun newInstance(date: LocalDate? = null) = TimetableFragment().apply { + arguments = Bundle().apply { + date?.let { putLong(ARGUMENT_DATE_KEY, it.toEpochDay()) } + } + } + } + + override val titleStringId get() = R.string.timetable_title + + override val isViewEmpty get() = timetableAdapter.itemCount == 0 + + override val currentStackSize get() = (activity as? MainActivity)?.currentStackSize + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentTimetableBinding.bind(view) + messageContainer = binding.timetableRecycler + + val initDate = savedInstanceState?.getLong(SAVED_DATE_KEY) + ?: arguments?.getLong(ARGUMENT_DATE_KEY)?.takeUnless { it == 0L } + + presenter.onAttachView(this, initDate) + } + + override fun initView() { + with(binding.timetableRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = timetableAdapter + addItemDecoration(DividerItemDecoration(context)) + } + + with(binding) { + timetableSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + timetableSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) + timetableSwipe.setProgressBackgroundColorSchemeColor( + requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh) + ) + timetableErrorRetry.setOnClickListener { presenter.onRetry() } + timetableErrorDetails.setOnClickListener { presenter.onDetailsClick() } + + timetablePreviousButton.setOnClickListener { presenter.onPreviousDay() } + timetableNavDate.setOnClickListener { presenter.onPickDate() } + timetableNextButton.setOnClickListener { presenter.onNextDay() } + + timetableNavContainer.elevation = requireContext().dpToPx(8f) + } + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.action_menu_timetable, menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.timetableMenuAdditionalLessons -> presenter.onAdditionalLessonsSwitchSelected() + R.id.timetableMenuCompletedLessons -> presenter.onCompletedLessonsSwitchSelected() + else -> false + } + } + + override fun updateData(data: List) { + timetableAdapter.submitList(data) + } + + override fun clearData() { + timetableAdapter.submitList(listOf()) + } + + override fun updateNavigationDay(date: String) { + binding.timetableNavDate.text = date + } + + override fun showRefresh(show: Boolean) { + binding.timetableSwipe.isRefreshing = show + } + + override fun resetView() { + binding.timetableRecycler.smoothScrollToPosition(0) + } + + override fun onFragmentReselected() { + if (::presenter.isInitialized) presenter.onViewReselected() + } + + override fun popView() { + (activity as? MainActivity)?.popView() + } + + override fun showEmpty(show: Boolean) { + binding.timetableEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun setDayHeaderMessage(message: String?) { + binding.timetableEmptyMessage.visibility = if (message.isNullOrEmpty()) GONE else VISIBLE + binding.timetableEmptyMessage.text = message.orEmpty().parseAsHtml() + } + + override fun showErrorView(show: Boolean) { + binding.timetableError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + binding.timetableErrorMessage.text = message + } + + override fun showProgress(show: Boolean) { + binding.timetableProgress.visibility = if (show) VISIBLE else GONE + } + + override fun enableSwipe(enable: Boolean) { + binding.timetableSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + binding.timetableRecycler.visibility = if (show) VISIBLE else GONE + } + + override fun showPreButton(show: Boolean) { + binding.timetablePreviousButton.visibility = if (show) VISIBLE else View.INVISIBLE + } + + override fun showNextButton(show: Boolean) { + binding.timetableNextButton.visibility = if (show) VISIBLE else View.INVISIBLE + } + + override fun showTimetableDialog(lesson: Timetable) { + (activity as? MainActivity)?.showDialogFragment(TimetableDialog.newInstance(lesson)) + } + + override fun showDatePickerDialog(selectedDate: LocalDate) { + openMaterialDatePicker( + selected = selectedDate, + rangeStart = selectedDate.firstSchoolDayInSchoolYear, + rangeEnd = LocalDate.now().lastSchoolDayInSchoolYear, + onDateSelected = { + presenter.onDateSet(it.year, it.monthValue, it.dayOfMonth) + } + ) + } + + override fun openAdditionalLessonsView() { + (activity as? MainActivity)?.pushView(AdditionalLessonsFragment.newInstance()) + } + + override fun openCompletedLessonsView() { + (activity as? MainActivity)?.pushView(CompletedLessonsFragment.newInstance()) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt new file mode 100644 index 000000000..92716ace8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt @@ -0,0 +1,30 @@ +package io.github.wulkanowy.ui.modules.timetable + +import io.github.wulkanowy.data.db.entities.Timetable +import java.time.Duration + +sealed class TimetableItem(val type: TimetableItemType) { + + data class Small( + val lesson: Timetable, + val onClick: (Timetable) -> Unit, + ) : TimetableItem(TimetableItemType.SMALL) + + data class Normal( + val lesson: Timetable, + val showGroupsInPlan: Boolean, + val timeLeft: TimeLeft?, + val onClick: (Timetable) -> Unit, + ) : TimetableItem(TimetableItemType.NORMAL) +} + +data class TimeLeft( + val until: Duration?, + val left: Duration?, + val isJustFinished: Boolean, +) + +enum class TimetableItemType { + SMALL, + NORMAL, +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt new file mode 100644 index 000000000..dc6c89213 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt @@ -0,0 +1,266 @@ +package io.github.wulkanowy.ui.modules.timetable + +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.data.enums.TimetableMode +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.repositories.TimetableRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.* +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.onEach +import timber.log.Timber +import java.time.Instant +import java.time.LocalDate +import java.time.LocalDate.* +import java.util.* +import javax.inject.Inject +import kotlin.concurrent.timer + +class TimetablePresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val timetableRepository: TimetableRepository, + private val semesterRepository: SemesterRepository, + private val prefRepository: PreferencesRepository, + private val analytics: AnalyticsHelper, +) : BasePresenter(errorHandler, studentRepository) { + + private var baseDate: LocalDate = now().nextOrSameSchoolDay + + lateinit var currentDate: LocalDate + private set + + private lateinit var lastError: Throwable + + private var tickTimer: Timer? = null + + fun onAttachView(view: TimetableView, date: Long?) { + super.onAttachView(view) + view.initView() + Timber.i("Timetable was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError + reloadView(ofEpochDay(date ?: baseDate.toEpochDay())) + loadData() + if (currentDate.isHolidays) setBaseDateOnHolidays() + } + + fun onPreviousDay() { + reloadView(currentDate.previousSchoolDay) + loadData() + } + + fun onNextDay() { + reloadView(currentDate.nextSchoolDay) + loadData() + } + + fun onPickDate() { + view?.showDatePickerDialog(currentDate) + } + + fun onDateSet(year: Int, month: Int, day: Int) { + reloadView(of(year, month, day)) + loadData() + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the timetable") + loadData(true) + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + fun onViewReselected() { + Timber.i("Timetable view is reselected") + view?.also { view -> + if (view.currentStackSize == 1) { + baseDate.also { + if (currentDate != it) { + reloadView(it) + loadData() + } else if (!view.isViewEmpty) view.resetView() + } + } else view.popView() + } + } + + fun onAdditionalLessonsSwitchSelected(): Boolean { + view?.openAdditionalLessonsView() + return true + } + + fun onCompletedLessonsSwitchSelected(): Boolean { + view?.openCompletedLessonsView() + return true + } + + private fun setBaseDateOnHolidays() { + flow { + val student = studentRepository.getCurrentStudent() + emit(semesterRepository.getCurrentSemester(student)) + }.catch { + Timber.i("Loading semester result: An exception occurred") + }.onEach { + baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear) + currentDate = baseDate + reloadNavigation() + }.launch("holidays") + } + + private fun loadData(forceRefresh: Boolean = false) { + flatResourceFlow { + val student = studentRepository.getCurrentStudent() + val semester = semesterRepository.getCurrentSemester(student) + timetableRepository.getTimetable( + student = student, + semester = semester, + start = currentDate, + end = currentDate, + forceRefresh = forceRefresh, + timetableType = TimetableRepository.TimetableType.NORMAL + ) + } + .logResourceStatus("load timetable data") + .onResourceData { + view?.run { + enableSwipe(true) + showProgress(false) + showErrorView(false) + showContent(it.lessons.isNotEmpty()) + showEmpty(it.lessons.isEmpty()) + updateData(it.lessons) + setDayHeaderMessage(it.headers.singleOrNull { header -> header.date == currentDate }?.content) + } + } + .onResourceIntermediate { view?.showRefresh(true) } + .onResourceSuccess { + analytics.logEvent( + "load_data", + "type" to "timetable", + "items" to it.lessons.size + ) + } + .onResourceNotLoading { + view?.run { + enableSwipe(true) + showProgress(false) + showRefresh(false) + } + } + .onResourceError(errorHandler::dispatch) + .launch() + } + + private fun updateData(lessons: List) { + tickTimer?.cancel() + + if (!prefRepository.showTimetableTimers) { + view?.updateData(createItems(lessons)) + } else { + tickTimer = timer(period = 2_000) { + view?.updateData(createItems(lessons)) + } + } + } + + private fun createItems(items: List): List { + val filteredItems = items + .filter { + if (prefRepository.showWholeClassPlan == TimetableMode.ONLY_CURRENT_GROUP) { + it.isStudentPlan + } else true + }.sortedWith( + compareBy({ item -> item.number }, { item -> !item.isStudentPlan }) + ) + + return filteredItems.mapIndexed { i, it -> + if (it.isStudentPlan) TimetableItem.Normal( + lesson = it, + showGroupsInPlan = prefRepository.showGroupsInPlan, + timeLeft = filteredItems.getTimeLeftForLesson(it, i), + onClick = ::onTimetableItemSelected + ) else TimetableItem.Small( + lesson = it, + onClick = ::onTimetableItemSelected + ) + } + } + + private fun List.getTimeLeftForLesson(lesson: Timetable, index: Int): TimeLeft { + val isShowTimeUntil = lesson.isShowTimeUntil(getPreviousLesson(index)) + return TimeLeft( + until = lesson.until.plusMinutes(1).takeIf { isShowTimeUntil }, + left = lesson.left?.plusMinutes(1), + isJustFinished = lesson.isJustFinished, + ) + } + + private fun List.getPreviousLesson(position: Int): Instant? { + return filter { it.isStudentPlan } + .getOrNull(position - 1 - filterIndexed { i, item -> i < position && !item.isStudentPlan }.size) + ?.let { + if (!it.canceled && it.isStudentPlan) it.end + else null + } + } + + private fun onTimetableItemSelected(lesson: Timetable) { + Timber.i("Select timetable item ${lesson.id}") + view?.showTimetableDialog(lesson) + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + + private fun reloadView(date: LocalDate) { + currentDate = date + + Timber.i("Reload timetable view with the date ${currentDate.toFormattedString()}") + view?.apply { + showProgress(true) + enableSwipe(false) + showContent(false) + showEmpty(false) + showErrorView(false) + clearData() + reloadNavigation() + } + } + + private fun reloadNavigation() { + view?.apply { + showPreButton(!currentDate.minusDays(1).isHolidays) + showNextButton(!currentDate.plusDays(1).isHolidays) + updateNavigationDay(currentDate.toFormattedString("EEEE, dd.MM").capitalise()) + } + } + + override fun onDetachView() { + tickTimer?.cancel() + tickTimer = null + super.onDetachView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt new file mode 100644 index 000000000..8cfb26204 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt @@ -0,0 +1,52 @@ +package io.github.wulkanowy.ui.modules.timetable + +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.ui.base.BaseView +import java.time.LocalDate + +interface TimetableView : BaseView { + + val isViewEmpty: Boolean + + val currentStackSize: Int? + + fun initView() + + fun updateData(data: List) + + fun updateNavigationDay(date: String) + + fun clearData() + + fun showRefresh(show: Boolean) + + fun resetView() + + fun showEmpty(show: Boolean) + + fun setDayHeaderMessage(message: String?) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun showPreButton(show: Boolean) + + fun showNextButton(show: Boolean) + + fun showTimetableDialog(lesson: Timetable) + + fun showDatePickerDialog(selectedDate: LocalDate) + + fun popView() + + fun openAdditionalLessonsView() + + fun openCompletedLessonsView() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsAdapter.kt new file mode 100644 index 000000000..c2ce80289 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsAdapter.kt @@ -0,0 +1,42 @@ +package io.github.wulkanowy.ui.modules.timetable.additional + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.data.db.entities.TimetableAdditional +import io.github.wulkanowy.databinding.ItemTimetableAdditionalBinding +import io.github.wulkanowy.utils.toFormattedString +import javax.inject.Inject + +class AdditionalLessonsAdapter @Inject constructor() : + RecyclerView.Adapter() { + + var items = emptyList() + + var onDeleteClickListener: (timetableAdditional: TimetableAdditional) -> Unit = {} + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + ItemTimetableAdditionalBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + @SuppressLint("SetTextI18n") + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + val item = items[position] + + with(holder.binding) { + additionalLessonItemTime.text = + "${item.start.toFormattedString("HH:mm")} - ${item.end.toFormattedString("HH:mm")}" + additionalLessonItemSubject.text = item.subject + + additionalLessonItemDelete.isVisible = item.isAddedByUser + additionalLessonItemDelete.setOnClickListener { onDeleteClickListener(item) } + } + } + + class ItemViewHolder(val binding: ItemTimetableAdditionalBinding) : + RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsFragment.kt new file mode 100644 index 000000000..043fa1f7d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsFragment.kt @@ -0,0 +1,177 @@ +package io.github.wulkanowy.ui.modules.timetable.additional + +import android.os.Bundle +import android.view.View +import androidx.appcompat.app.AlertDialog +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.TimetableAdditional +import io.github.wulkanowy.databinding.FragmentTimetableAdditionalBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.modules.timetable.additional.add.AdditionalLessonAddDialog +import io.github.wulkanowy.ui.widgets.DividerItemDecoration +import io.github.wulkanowy.utils.dpToPx +import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear +import io.github.wulkanowy.utils.openMaterialDatePicker +import java.time.LocalDate +import javax.inject.Inject + +@AndroidEntryPoint +class AdditionalLessonsFragment : + BaseFragment(R.layout.fragment_timetable_additional), + AdditionalLessonsView, MainView.TitledView { + + @Inject + lateinit var presenter: AdditionalLessonsPresenter + + @Inject + lateinit var additionalLessonsAdapter: AdditionalLessonsAdapter + + companion object { + private const val SAVED_DATE_KEY = "CURRENT_DATE" + + fun newInstance() = AdditionalLessonsFragment() + } + + override val titleStringId get() = R.string.additional_lessons_title + + override val isViewEmpty get() = additionalLessonsAdapter.items.isEmpty() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentTimetableAdditionalBinding.bind(view) + messageContainer = binding.additionalLessonsRecycler + presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY)) + } + + override fun initView() { + with(binding.additionalLessonsRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = additionalLessonsAdapter.apply { + onDeleteClickListener = { presenter.onDeleteLessonsSelected(it) } + } + addItemDecoration(DividerItemDecoration(context)) + } + + with(binding) { + additionalLessonsSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + additionalLessonsSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) + additionalLessonsSwipe.setProgressBackgroundColorSchemeColor( + requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh) + ) + additionalLessonsErrorRetry.setOnClickListener { presenter.onRetry() } + additionalLessonsErrorDetails.setOnClickListener { presenter.onDetailsClick() } + + additionalLessonsPreviousButton.setOnClickListener { presenter.onPreviousDay() } + additionalLessonsNavDate.setOnClickListener { presenter.onPickDate() } + additionalLessonsNextButton.setOnClickListener { presenter.onNextDay() } + + openAddAdditionalLessonButton.setOnClickListener { presenter.onAdditionalLessonAddButtonClicked() } + + additionalLessonsNavContainer.elevation = requireContext().dpToPx(8f) + } + } + + override fun updateData(data: List) { + with(additionalLessonsAdapter) { + items = data + notifyDataSetChanged() + } + } + + override fun clearData() { + with(additionalLessonsAdapter) { + items = emptyList() + notifyDataSetChanged() + } + } + + override fun showSuccessMessage() { + getString(R.string.additional_lessons_delete_success) + } + + override fun updateNavigationDay(date: String) { + binding.additionalLessonsNavDate.text = date + } + + override fun hideRefresh() { + binding.additionalLessonsSwipe.isRefreshing = false + } + + override fun showEmpty(show: Boolean) { + binding.additionalLessonsEmpty.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showErrorView(show: Boolean) { + binding.additionalLessonsError.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun setErrorDetails(message: String) { + binding.additionalLessonsErrorMessage.text = message + } + + override fun showProgress(show: Boolean) { + binding.additionalLessonsProgress.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun enableSwipe(enable: Boolean) { + binding.additionalLessonsSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + binding.additionalLessonsRecycler.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showPreButton(show: Boolean) { + binding.additionalLessonsPreviousButton.visibility = + if (show) View.VISIBLE else View.INVISIBLE + } + + override fun showNextButton(show: Boolean) { + binding.additionalLessonsNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE + } + + override fun showAddAdditionalLessonDialog() { + (activity as? MainActivity)?.showDialogFragment(AdditionalLessonAddDialog.newInstance()) + } + + override fun showDatePickerDialog(selectedDate: LocalDate) { + val now = LocalDate.now() + + openMaterialDatePicker( + selected = selectedDate, + rangeStart = now.firstSchoolDayInSchoolYear, + rangeEnd = now.lastSchoolDayInSchoolYear, + onDateSelected = { + presenter.onDateSet(it.year, it.monthValue, it.dayOfMonth) + } + ) + } + + override fun showDeleteLessonDialog(timetableAdditional: TimetableAdditional) { + AlertDialog.Builder(requireContext()) + .setTitle(getString(R.string.additional_lessons_delete_title)) + .setItems( + arrayOf( + getString(R.string.additional_lessons_delete_one), + getString(R.string.additional_lessons_delete_series) + ) + ) { _, position -> presenter.onDeleteDialogSelectItem(position, timetableAdditional) } + .show() + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsPresenter.kt new file mode 100644 index 000000000..d0a01b38c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsPresenter.kt @@ -0,0 +1,203 @@ +package io.github.wulkanowy.ui.modules.timetable.additional + +import android.annotation.SuppressLint +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.db.entities.TimetableAdditional +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.repositories.TimetableRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.* +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import timber.log.Timber +import java.time.LocalDate +import javax.inject.Inject + +class AdditionalLessonsPresenter @Inject constructor( + studentRepository: StudentRepository, + errorHandler: ErrorHandler, + private val semesterRepository: SemesterRepository, + private val timetableRepository: TimetableRepository, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + private var baseDate: LocalDate = LocalDate.now().nextOrSameSchoolDay + + lateinit var currentDate: LocalDate + private set + + private lateinit var lastError: Throwable + + fun onAttachView(view: AdditionalLessonsView, date: Long?) { + super.onAttachView(view) + view.initView() + Timber.i("Additional lessons was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError + loadData(LocalDate.ofEpochDay(date ?: baseDate.toEpochDay())) + if (currentDate.isHolidays) setBaseDateOnHolidays() + reloadView() + } + + fun onPreviousDay() { + loadData(currentDate.previousSchoolDay) + reloadView() + } + + fun onNextDay() { + loadData(currentDate.nextSchoolDay) + reloadView() + } + + fun onPickDate() { + view?.showDatePickerDialog(currentDate) + } + + fun onAdditionalLessonAddButtonClicked() { + view?.showAddAdditionalLessonDialog() + } + + fun onDateSet(year: Int, month: Int, day: Int) { + loadData(LocalDate.of(year, month, day)) + reloadView() + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the additional lessons") + loadData(currentDate, true) + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(currentDate, true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + private fun setBaseDateOnHolidays() { + flow { + val student = studentRepository.getCurrentStudent() + emit(semesterRepository.getCurrentSemester(student)) + }.catch { + Timber.i("Loading semester result: An exception occurred") + }.onEach { + baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear) + currentDate = baseDate + reloadNavigation() + }.launch("holidays") + } + + fun onDeleteLessonsSelected(timetableAdditional: TimetableAdditional) { + if (timetableAdditional.repeatId == null) { + deleteAdditionalLessons(timetableAdditional, false) + } else { + view?.showDeleteLessonDialog(timetableAdditional) + } + } + + fun onDeleteDialogSelectItem(position: Int, timetableAdditional: TimetableAdditional) { + deleteAdditionalLessons(timetableAdditional, position == 1) + } + + private fun deleteAdditionalLessons( + timetableAdditional: TimetableAdditional, + deleteSeries: Boolean + ) { + presenterScope.launch { + Timber.i("Additional Lesson delete start") + runCatching { timetableRepository.deleteAdditional(timetableAdditional, deleteSeries) } + .onSuccess { + Timber.i("Additional Lesson delete: Success") + view?.showSuccessMessage() + } + .onFailure { + Timber.i("Additional Lesson delete result: An exception occurred") + errorHandler.dispatch(it) + } + } + } + + private fun loadData(date: LocalDate, forceRefresh: Boolean = false) { + currentDate = date + + flatResourceFlow { + val student = studentRepository.getCurrentStudent() + val semester = semesterRepository.getCurrentSemester(student) + timetableRepository.getTimetable( + student = student, + semester = semester, + start = date, + end = date, + forceRefresh = forceRefresh, + refreshAdditional = true, + timetableType = TimetableRepository.TimetableType.ADDITIONAL + ) + } + .logResourceStatus("load additional lessons") + .onResourceData { + view?.apply { + updateData(it.additional.sortedBy { item -> item.start }) + showEmpty(it.additional.isEmpty()) + showErrorView(false) + showContent(it.additional.isNotEmpty()) + } + } + .onResourceSuccess { + analytics.logEvent( + "load_data", + "type" to "additional_lessons", + "items" to it.additional.size + ) + } + .onResourceNotLoading { + view?.run { + hideRefresh() + showProgress(false) + enableSwipe(true) + } + } + .onResourceError(errorHandler::dispatch) + .launch() + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + + private fun reloadView() { + Timber.i("Reload additional lessons view with the date ${currentDate.toFormattedString()}") + view?.apply { + showProgress(true) + enableSwipe(false) + showContent(false) + showEmpty(false) + showErrorView(false) + clearData() + reloadNavigation() + } + } + + @SuppressLint("DefaultLocale") + private fun reloadNavigation() { + view?.apply { + showPreButton(!currentDate.minusDays(1).isHolidays) + showNextButton(!currentDate.plusDays(1).isHolidays) + updateNavigationDay(currentDate.toFormattedString("EEEE, dd.MM").capitalise()) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsView.kt new file mode 100644 index 000000000..76d37b754 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsView.kt @@ -0,0 +1,44 @@ +package io.github.wulkanowy.ui.modules.timetable.additional + +import io.github.wulkanowy.data.db.entities.TimetableAdditional +import io.github.wulkanowy.ui.base.BaseView +import java.time.LocalDate + +interface AdditionalLessonsView : BaseView { + + val isViewEmpty: Boolean + + fun initView() + + fun updateData(data: List) + + fun clearData() + + fun updateNavigationDay(date: String) + + fun hideRefresh() + + fun showEmpty(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun showPreButton(show: Boolean) + + fun showNextButton(show: Boolean) + + fun showDatePickerDialog(selectedDate: LocalDate) + + fun showAddAdditionalLessonDialog() + + fun showSuccessMessage() + + fun showDeleteLessonDialog(timetableAdditional: TimetableAdditional) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddDialog.kt new file mode 100644 index 000000000..f82d64830 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddDialog.kt @@ -0,0 +1,172 @@ +package io.github.wulkanowy.ui.modules.timetable.additional.add + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.widget.doOnTextChanged +import com.google.android.material.timepicker.MaterialTimePicker +import com.google.android.material.timepicker.TimeFormat +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.databinding.DialogAdditionalAddBinding +import io.github.wulkanowy.ui.base.BaseDialogFragment +import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear +import io.github.wulkanowy.utils.openMaterialDatePicker +import io.github.wulkanowy.utils.toFormattedString +import java.time.LocalDate +import java.time.LocalTime +import javax.inject.Inject + +@AndroidEntryPoint +class AdditionalLessonAddDialog : BaseDialogFragment(), + AdditionalLessonAddView { + + @Inject + lateinit var presenter: AdditionalLessonAddPresenter + + companion object { + fun newInstance() = AdditionalLessonAddDialog() + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogAdditionalAddBinding.inflate(inflater).apply { binding = this }.root + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + presenter.onAttachView(this) + } + + override fun initView() { + with(binding) { + additionalLessonDialogStartEdit.doOnTextChanged { _, _, _, _ -> + additionalLessonDialogStart.isErrorEnabled = false + additionalLessonDialogStart.error = null + } + additionalLessonDialogEndEdit.doOnTextChanged { _, _, _, _ -> + additionalLessonDialogEnd.isErrorEnabled = false + additionalLessonDialogEnd.error = null + } + additionalLessonDialogDateEdit.doOnTextChanged { _, _, _, _ -> + additionalLessonDialogDate.isErrorEnabled = false + additionalLessonDialogDate.error = null + } + additionalLessonDialogContentEdit.doOnTextChanged { _, _, _, _ -> + additionalLessonDialogContent.isErrorEnabled = false + additionalLessonDialogContent.error = null + } + + additionalLessonDialogAdd.setOnClickListener { + presenter.onAddAdditionalClicked( + start = additionalLessonDialogStartEdit.text?.toString(), + end = additionalLessonDialogEndEdit.text?.toString(), + date = additionalLessonDialogDateEdit.text?.toString(), + content = additionalLessonDialogContentEdit.text?.toString(), + isRepeat = additionalLessonDialogRepeat.isChecked + ) + } + additionalLessonDialogClose.setOnClickListener { dismiss() } + additionalLessonDialogDateEdit.setOnClickListener { presenter.showDatePicker() } + additionalLessonDialogStartEdit.setOnClickListener { presenter.showStartTimePicker() } + additionalLessonDialogEndEdit.setOnClickListener { presenter.showEndTimePicker() } + } + } + + override fun showSuccessMessage() { + showMessage(getString(R.string.additional_lessons_add_success)) + } + + override fun setErrorDateRequired() { + with(binding.additionalLessonDialogDate) { + isErrorEnabled = true + error = getString(R.string.error_field_required) + } + } + + override fun setErrorStartRequired() { + with(binding.additionalLessonDialogStart) { + isErrorEnabled = true + error = getString(R.string.error_field_required) + } + } + + override fun setErrorEndRequired() { + with(binding.additionalLessonDialogEnd) { + isErrorEnabled = true + error = getString(R.string.error_field_required) + } + } + + override fun setErrorContentRequired() { + with(binding.additionalLessonDialogContent) { + isErrorEnabled = true + error = getString(R.string.error_field_required) + } + } + + override fun setErrorIncorrectEndTime() { + with(binding.additionalLessonDialogEnd) { + isErrorEnabled = true + error = getString(R.string.additional_lessons_end_time_error) + } + } + + override fun closeDialog() { + dismiss() + } + + override fun showDatePickerDialog(selectedDate: LocalDate) { + openMaterialDatePicker( + selected = selectedDate, + rangeStart = LocalDate.now(), + rangeEnd = LocalDate.now().lastSchoolDayInSchoolYear, + onDateSelected = { + presenter.onDateSelected(it) + binding.additionalLessonDialogDateEdit.setText(it.toFormattedString()) + } + ) + } + + override fun showStartTimePickerDialog(selectedTime: LocalTime) { + showTimePickerDialog(selectedTime) { + presenter.onStartTimeSelected(it) + binding.additionalLessonDialogStartEdit.setText(it.toString()) + } + } + + override fun showEndTimePickerDialog(selectedTime: LocalTime) { + showTimePickerDialog(selectedTime) { + presenter.onEndTimeSelected(it) + binding.additionalLessonDialogEndEdit.setText(it.toString()) + } + } + + private fun showTimePickerDialog(defaultTime: LocalTime, onTimeSelected: (LocalTime) -> Unit) { + val timePicker = MaterialTimePicker.Builder() + .setTimeFormat(TimeFormat.CLOCK_24H) + .setHour(defaultTime.hour) + .setMinute(defaultTime.minute) + .build() + + timePicker.addOnPositiveButtonClickListener { + onTimeSelected(LocalTime.of(timePicker.hour, timePicker.minute)) + } + + if (!parentFragmentManager.isStateSaved) { + timePicker.show(parentFragmentManager, null) + } + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddPresenter.kt new file mode 100644 index 000000000..c207165d3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddPresenter.kt @@ -0,0 +1,164 @@ +package io.github.wulkanowy.ui.modules.timetable.additional.add + +import io.github.wulkanowy.data.db.entities.TimetableAdditional +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.repositories.TimetableRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear +import io.github.wulkanowy.utils.toLocalDate +import kotlinx.coroutines.launch +import timber.log.Timber +import java.time.* +import java.time.temporal.ChronoUnit +import java.util.* +import javax.inject.Inject + +class AdditionalLessonAddPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val timetableRepository: TimetableRepository, + private val semesterRepository: SemesterRepository +) : BasePresenter(errorHandler, studentRepository) { + + private var selectedStartTime = LocalTime.of(15, 0) + + private var selectedEndTime = LocalTime.of(15, 45) + + private var selectedDate = LocalDate.now() + + override fun onAttachView(view: AdditionalLessonAddView) { + super.onAttachView(view) + view.initView() + Timber.i("AdditionalLesson details view was initialized") + } + + fun showDatePicker() { + view?.showDatePickerDialog(selectedDate) + } + + fun showStartTimePicker() { + view?.showStartTimePickerDialog(selectedStartTime) + } + + fun showEndTimePicker() { + view?.showEndTimePickerDialog(selectedEndTime) + } + + fun onStartTimeSelected(time: LocalTime) { + selectedStartTime = time + } + + fun onEndTimeSelected(time: LocalTime) { + selectedEndTime = time + } + + fun onDateSelected(date: LocalDate) { + selectedDate = date + } + + fun onAddAdditionalClicked( + start: String?, + end: String?, + date: String?, + content: String?, + isRepeat: Boolean + ) { + if (isUserInputValid(start, end, date, content)) { + addAdditionalLesson( + start = LocalTime.parse(start!!), + end = LocalTime.parse(end), + date = date!!.toLocalDate(), + subject = content!!, + isRepeat = isRepeat + ) + } + } + + private fun isUserInputValid( + start: String?, + end: String?, + date: String?, + content: String? + ): Boolean { + var isValid = true + + if (start.isNullOrBlank()) { + view?.setErrorStartRequired() + isValid = false + } + + if (end.isNullOrBlank()) { + view?.setErrorEndRequired() + isValid = false + } + + if (date.isNullOrBlank()) { + view?.setErrorDateRequired() + isValid = false + } + + if (content.isNullOrBlank()) { + view?.setErrorContentRequired() + isValid = false + } + + if (selectedStartTime >= selectedEndTime) { + view?.setErrorIncorrectEndTime() + isValid = false + } + + return isValid + } + + private fun addAdditionalLesson( + start: LocalTime, + end: LocalTime, + date: LocalDate, + subject: String, + isRepeat: Boolean + ) { + presenterScope.launch { + val semester = runCatching { + val student = studentRepository.getCurrentStudent() + semesterRepository.getCurrentSemester(student) + } + .onFailure(errorHandler::dispatch) + .getOrNull() ?: return@launch + + val weeks = if (isRepeat) { + ChronoUnit.WEEKS.between(date, date.lastSchoolDayInSchoolYear) + } else 0 + val uniqueRepeatId = UUID.randomUUID().takeIf { isRepeat } + + val lessonsToAdd = (0..weeks).map { + TimetableAdditional( + studentId = semester.studentId, + diaryId = semester.diaryId, + start = ZonedDateTime.of(date, start, ZoneId.systemDefault()).toInstant(), + end = ZonedDateTime.of(date, end, ZoneId.systemDefault()).toInstant(), + date = date.plusWeeks(it), + subject = subject + ).apply { + isAddedByUser = true + repeatId = uniqueRepeatId + } + } + + Timber.i("AdditionalLesson insert start") + runCatching { timetableRepository.saveAdditionalList(lessonsToAdd) } + .onSuccess { + Timber.i("AdditionalLesson insert: Success") + view?.run { + showSuccessMessage() + closeDialog() + } + } + .onFailure { + Timber.i("AdditionalLesson insert result: An exception occurred") + errorHandler.dispatch(it) + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddView.kt new file mode 100644 index 000000000..0df53815b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddView.kt @@ -0,0 +1,30 @@ +package io.github.wulkanowy.ui.modules.timetable.additional.add + +import io.github.wulkanowy.ui.base.BaseView +import java.time.LocalDate +import java.time.LocalTime + +interface AdditionalLessonAddView : BaseView { + + fun initView() + + fun closeDialog() + + fun showDatePickerDialog(selectedDate: LocalDate) + + fun showStartTimePickerDialog(selectedTime: LocalTime) + + fun showEndTimePickerDialog(selectedTime: LocalTime) + + fun showSuccessMessage() + + fun setErrorDateRequired() + + fun setErrorStartRequired() + + fun setErrorEndRequired() + + fun setErrorContentRequired() + + fun setErrorIncorrectEndTime() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt new file mode 100644 index 000000000..7d32278f0 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt @@ -0,0 +1,82 @@ +package io.github.wulkanowy.ui.modules.timetable.completed + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import io.github.wulkanowy.data.db.entities.CompletedLesson +import io.github.wulkanowy.databinding.DialogLessonCompletedBinding +import io.github.wulkanowy.utils.lifecycleAwareVariable + +class CompletedLessonDialog : DialogFragment() { + + private var binding: DialogLessonCompletedBinding by lifecycleAwareVariable() + + private lateinit var completedLesson: CompletedLesson + + companion object { + + private const val ARGUMENT_KEY = "Item" + + fun newInstance(exam: CompletedLesson) = CompletedLessonDialog().apply { + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + completedLesson = getSerializable(ARGUMENT_KEY) as CompletedLesson + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogLessonCompletedBinding.inflate(inflater).apply { binding = this }.root + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + with(binding) { + completedLessonDialogSubjectValue.text = completedLesson.subject + completedLessonDialogTopicValue.text = completedLesson.topic + completedLessonDialogTeacherValue.text = completedLesson.teacher + completedLessonDialogAbsenceValue.text = completedLesson.absence + completedLessonDialogChangesValue.text = completedLesson.substitution + completedLessonDialogResourcesValue.text = completedLesson.resources + } + + completedLesson.substitution.let { + if (it.isBlank()) { + with(binding) { + completedLessonDialogChangesTitle.visibility = View.GONE + completedLessonDialogChangesValue.visibility = View.GONE + } + } else binding.completedLessonDialogChangesValue.text = it + } + + completedLesson.absence.let { + if (it.isBlank()) { + with(binding) { + completedLessonDialogAbsenceTitle.visibility = View.GONE + completedLessonDialogAbsenceValue.visibility = View.GONE + } + } else binding.completedLessonDialogAbsenceValue.text = it + } + + completedLesson.resources.let { + if (it.isBlank()) { + with(binding) { + completedLessonDialogResourcesTitle.visibility = View.GONE + completedLessonDialogResourcesValue.visibility = View.GONE + } + } else binding.completedLessonDialogResourcesValue.text = it + } + + binding.completedLessonDialogClose.setOnClickListener { dismiss() } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsAdapter.kt new file mode 100644 index 000000000..3399a8a23 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsAdapter.kt @@ -0,0 +1,45 @@ +package io.github.wulkanowy.ui.modules.timetable.completed + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.CompletedLesson +import io.github.wulkanowy.databinding.ItemCompletedLessonBinding +import io.github.wulkanowy.utils.getThemeAttrColor +import javax.inject.Inject + +class CompletedLessonsAdapter @Inject constructor() : + RecyclerView.Adapter() { + + var items = emptyList() + + var onClickListener: (CompletedLesson) -> Unit = {} + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + ItemCompletedLessonBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + val item = items[position] + + with(holder.binding) { + completedLessonItemNumber.text = item.number.toString() + completedLessonItemNumber.setTextColor(root.context.getThemeAttrColor( + if (item.substitution.isNotEmpty()) R.attr.colorTimetableChange + else android.R.attr.textColorPrimary + )) + completedLessonItemSubject.text = item.subject + completedLessonItemTopic.text = item.topic + completedLessonItemAlert.visibility = if (item.substitution.isNotEmpty()) View.VISIBLE else View.GONE + + root.setOnClickListener { onClickListener(item) } + } + } + + class ItemViewHolder(val binding: ItemCompletedLessonBinding) : + RecyclerView.ViewHolder(binding.root) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt new file mode 100644 index 000000000..36e38fb96 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt @@ -0,0 +1,25 @@ +package io.github.wulkanowy.ui.modules.timetable.completed + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException +import io.github.wulkanowy.ui.base.ErrorHandler +import javax.inject.Inject + +class CompletedLessonsErrorHandler @Inject constructor(@ApplicationContext context: Context) : + ErrorHandler(context) { + + var onFeatureDisabled: () -> Unit = {} + + override fun proceed(error: Throwable) { + when (error) { + is FeatureDisabledException -> onFeatureDisabled() + else -> super.proceed(error) + } + } + + override fun clear() { + super.clear() + onFeatureDisabled = {} + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt new file mode 100644 index 000000000..34a69e6ab --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt @@ -0,0 +1,171 @@ +package io.github.wulkanowy.ui.modules.timetable.completed + +import android.os.Bundle +import android.view.View +import android.view.View.GONE +import android.view.View.INVISIBLE +import android.view.View.VISIBLE +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.CompletedLesson +import io.github.wulkanowy.databinding.FragmentTimetableCompletedBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.widgets.DividerItemDecoration +import io.github.wulkanowy.utils.dpToPx +import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear +import io.github.wulkanowy.utils.getCompatDrawable +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear +import io.github.wulkanowy.utils.openMaterialDatePicker +import java.time.LocalDate +import javax.inject.Inject + +@AndroidEntryPoint +class CompletedLessonsFragment : + BaseFragment(R.layout.fragment_timetable_completed), + CompletedLessonsView, MainView.TitledView { + + @Inject + lateinit var presenter: CompletedLessonsPresenter + + @Inject + lateinit var completedLessonsAdapter: CompletedLessonsAdapter + + companion object { + private const val SAVED_DATE_KEY = "CURRENT_DATE" + + fun newInstance() = CompletedLessonsFragment() + } + + override val titleStringId get() = R.string.completed_lessons_title + + override val isViewEmpty get() = completedLessonsAdapter.items.isEmpty() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentTimetableCompletedBinding.bind(view) + messageContainer = binding.completedLessonsRecycler + presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY)) + } + + override fun initView() { + completedLessonsAdapter.onClickListener = presenter::onCompletedLessonsItemSelected + + with(binding.completedLessonsRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = completedLessonsAdapter + addItemDecoration(DividerItemDecoration(context)) + } + + with(binding) { + completedLessonsSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + completedLessonsSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) + completedLessonsSwipe.setProgressBackgroundColorSchemeColor( + requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh) + ) + completedLessonErrorRetry.setOnClickListener { presenter.onRetry() } + completedLessonErrorDetails.setOnClickListener { presenter.onDetailsClick() } + + completedLessonsPreviousButton.setOnClickListener { presenter.onPreviousDay() } + completedLessonsNavDate.setOnClickListener { presenter.onPickDate() } + completedLessonsNextButton.setOnClickListener { presenter.onNextDay() } + + completedLessonsNavContainer.elevation = requireContext().dpToPx(8f) + } + } + + override fun updateData(data: List) { + with(completedLessonsAdapter) { + items = data + notifyDataSetChanged() + } + } + + override fun clearData() { + with(completedLessonsAdapter) { + items = emptyList() + notifyDataSetChanged() + } + } + + override fun updateNavigationDay(date: String) { + binding.completedLessonsNavDate.text = date + } + + override fun showRefresh(show: Boolean) { + binding.completedLessonsSwipe.isRefreshing = show + } + + override fun showEmpty(show: Boolean) { + binding.completedLessonsEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showErrorView(show: Boolean) { + binding.completedLessonError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + binding.completedLessonErrorMessage.text = message + } + + override fun showFeatureDisabled() { + with(binding) { + completedLessonsInfo.text = getString(R.string.error_feature_disabled) + completedLessonsInfoImage.setImageDrawable(requireContext().getCompatDrawable(R.drawable.ic_all_close_circle)) + } + } + + override fun showProgress(show: Boolean) { + binding.completedLessonsProgress.visibility = if (show) VISIBLE else GONE + } + + override fun enableSwipe(enable: Boolean) { + binding.completedLessonsSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + binding.completedLessonsRecycler.visibility = if (show) VISIBLE else GONE + } + + override fun showPreButton(show: Boolean) { + binding.completedLessonsPreviousButton.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showNextButton(show: Boolean) { + binding.completedLessonsNextButton.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showCompletedLessonDialog(completedLesson: CompletedLesson) { + (activity as? MainActivity)?.showDialogFragment( + CompletedLessonDialog.newInstance( + completedLesson + ) + ) + } + + override fun showDatePickerDialog(selectedDate: LocalDate) { + val now = LocalDate.now() + + openMaterialDatePicker( + selected = selectedDate, + rangeStart = now.firstSchoolDayInSchoolYear, + rangeEnd = now.lastSchoolDayInSchoolYear, + onDateSelected = { + presenter.onDateSet(it.year, it.monthValue, it.dayOfMonth) + } + ) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt new file mode 100644 index 000000000..16c51fd2e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt @@ -0,0 +1,181 @@ +package io.github.wulkanowy.ui.modules.timetable.completed + +import android.annotation.SuppressLint +import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.db.entities.CompletedLesson +import io.github.wulkanowy.data.repositories.CompletedLessonsRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.utils.* +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.onEach +import timber.log.Timber +import java.time.LocalDate +import java.time.LocalDate.now +import java.time.LocalDate.ofEpochDay +import javax.inject.Inject + +class CompletedLessonsPresenter @Inject constructor( + studentRepository: StudentRepository, + private val completedLessonsErrorHandler: CompletedLessonsErrorHandler, + private val semesterRepository: SemesterRepository, + private val completedLessonsRepository: CompletedLessonsRepository, + private val analytics: AnalyticsHelper +) : BasePresenter(completedLessonsErrorHandler, studentRepository) { + + private var baseDate: LocalDate = now().nextOrSameSchoolDay + + lateinit var currentDate: LocalDate + private set + + private lateinit var lastError: Throwable + + fun onAttachView(view: CompletedLessonsView, date: Long?) { + super.onAttachView(view) + Timber.i("Completed lessons is attached") + view.initView() + completedLessonsErrorHandler.showErrorMessage = ::showErrorViewOnError + completedLessonsErrorHandler.onFeatureDisabled = { + this.view?.showFeatureDisabled() + this.view?.showEmpty(true) + Timber.i("Completed lessons feature disabled by school") + } + reloadView(ofEpochDay(date ?: baseDate.toEpochDay())) + loadData() + if (currentDate.isHolidays) setBaseDateOnHolidays() + } + + fun onPreviousDay() { + reloadView(currentDate.previousSchoolDay) + loadData() + } + + fun onNextDay() { + reloadView(currentDate.nextSchoolDay) + loadData() + } + + fun onPickDate() { + view?.showDatePickerDialog(currentDate) + } + + fun onDateSet(year: Int, month: Int, day: Int) { + reloadView(LocalDate.of(year, month, day)) + loadData() + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the completed lessons") + loadData(true) + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + fun onCompletedLessonsItemSelected(completedLesson: CompletedLesson) { + Timber.i("Select completed lessons item ${completedLesson.id}") + view?.showCompletedLessonDialog(completedLesson) + } + + private fun setBaseDateOnHolidays() { + flow { + val student = studentRepository.getCurrentStudent() + emit(semesterRepository.getCurrentSemester(student)) + }.catch { + Timber.i("Loading semester result: An exception occurred") + }.onEach { + baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear) + currentDate = baseDate + reloadNavigation() + }.launch("holidays") + } + + private fun loadData(forceRefresh: Boolean = false) { + flatResourceFlow { + val student = studentRepository.getCurrentStudent() + val semester = semesterRepository.getCurrentSemester(student) + completedLessonsRepository.getCompletedLessons( + student = student, + semester = semester, + start = currentDate, + end = currentDate, + forceRefresh = forceRefresh + ) + } + .logResourceStatus("load completed lessons") + .mapResourceData { it.sortedBy { lesson -> lesson.number } } + .onResourceData { + view?.run { + enableSwipe(true) + showProgress(false) + showErrorView(false) + showContent(it.isNotEmpty()) + showEmpty(it.isEmpty()) + updateData(it) + } + } + .onResourceIntermediate { view?.showRefresh(true) } + .onResourceSuccess { + analytics.logEvent( + "load_data", + "type" to "completed_lessons", + "items" to it.size + ) + } + .onResourceNotLoading { + view?.run { + enableSwipe(true) + showProgress(false) + showRefresh(false) + } + } + .onResourceError(errorHandler::dispatch) + .launch() + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + + private fun reloadView(date: LocalDate) { + currentDate = date + + Timber.i("Reload completed lessons view with the date ${currentDate.toFormattedString()}") + view?.apply { + showProgress(true) + enableSwipe(false) + showContent(false) + showEmpty(false) + showErrorView(false) + clearData() + reloadNavigation() + } + } + + @SuppressLint("DefaultLocale") + private fun reloadNavigation() { + view?.apply { + showPreButton(!currentDate.minusDays(1).isHolidays) + showNextButton(!currentDate.plusDays(1).isHolidays) + updateNavigationDay(currentDate.toFormattedString("EEEE, dd.MM").capitalise()) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt new file mode 100644 index 000000000..715ce01fc --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt @@ -0,0 +1,42 @@ +package io.github.wulkanowy.ui.modules.timetable.completed + +import io.github.wulkanowy.data.db.entities.CompletedLesson +import io.github.wulkanowy.ui.base.BaseView +import java.time.LocalDate + +interface CompletedLessonsView : BaseView { + + val isViewEmpty: Boolean + + fun initView() + + fun updateData(data: List) + + fun clearData() + + fun updateNavigationDay(date: String) + + fun showRefresh(show: Boolean) + + fun showEmpty(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun showFeatureDisabled() + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun showPreButton(show: Boolean) + + fun showNextButton(show: Boolean) + + fun showCompletedLessonDialog(completedLesson: CompletedLesson) + + fun showDatePickerDialog(selectedDate: LocalDate) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt new file mode 100644 index 000000000..a27dba882 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt @@ -0,0 +1,118 @@ +package io.github.wulkanowy.ui.modules.timetablewidget + +import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS +import android.content.Intent +import android.os.Build +import android.os.Bundle +import android.widget.Toast +import android.widget.Toast.LENGTH_LONG +import androidx.appcompat.app.AlertDialog +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.databinding.ActivityWidgetConfigureBinding +import io.github.wulkanowy.ui.base.BaseActivity +import io.github.wulkanowy.ui.base.WidgetConfigureAdapter +import io.github.wulkanowy.ui.modules.login.LoginActivity +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.EXTRA_FROM_PROVIDER +import io.github.wulkanowy.utils.AppInfo +import javax.inject.Inject + +@AndroidEntryPoint +class TimetableWidgetConfigureActivity : + BaseActivity(), + TimetableWidgetConfigureView { + + @Inject + lateinit var configureAdapter: WidgetConfigureAdapter + + @Inject + override lateinit var presenter: TimetableWidgetConfigurePresenter + + @Inject + lateinit var appInfo: AppInfo + + private var dialog: AlertDialog? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setResult(RESULT_CANCELED) + setContentView( + ActivityWidgetConfigureBinding.inflate(layoutInflater).apply { binding = this }.root + ) + + intent.extras.let { + presenter.onAttachView( + this, + it?.getInt(EXTRA_APPWIDGET_ID), + it?.getBoolean(EXTRA_FROM_PROVIDER) + ) + } + } + + override fun initView() { + with(binding.widgetConfigureRecycler) { + adapter = configureAdapter + layoutManager = LinearLayoutManager(context) + } + + configureAdapter.onClickListener = presenter::onItemSelect + } + + override fun showThemeDialog() { + var items = arrayOf( + getString(R.string.widget_timetable_theme_light), + getString(R.string.widget_timetable_theme_dark) + ) + + if (appInfo.systemVersion >= Build.VERSION_CODES.Q) items += getString(R.string.widget_timetable_theme_system) + + dialog = AlertDialog.Builder(this, R.style.WulkanowyTheme_WidgetAccountSwitcher) + .setTitle(R.string.widget_timetable_theme_title) + .setOnDismissListener { presenter.onDismissThemeView() } + .setSingleChoiceItems(items, -1) { _, which -> + presenter.onThemeSelect(which) + } + .show() + } + + override fun updateData(data: List, selectedStudentId: Long) { + with(configureAdapter) { + selectedId = selectedStudentId + items = data + notifyDataSetChanged() + } + } + + override fun updateTimetableWidget(widgetId: Int) { + sendBroadcast(Intent(this, TimetableWidgetProvider::class.java) + .apply { + action = ACTION_APPWIDGET_UPDATE + putExtra(EXTRA_APPWIDGET_IDS, intArrayOf(widgetId)) + }) + } + + override fun setSuccessResult(widgetId: Int) { + setResult(RESULT_OK, Intent().apply { putExtra(EXTRA_APPWIDGET_ID, widgetId) }) + } + + override fun showError(text: String, error: Throwable) { + Toast.makeText(this, text, LENGTH_LONG).show() + } + + override fun finishView() { + finish() + } + + override fun openLoginView() { + startActivity(LoginActivity.getStartIntent(this)) + } + + override fun onDestroy() { + super.onDestroy() + dialog?.dismiss() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt new file mode 100644 index 000000000..dc2a7c6c7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt @@ -0,0 +1,92 @@ +package io.github.wulkanowy.ui.modules.timetablewidget + +import io.github.wulkanowy.data.Resource +import io.github.wulkanowy.data.db.SharedPrefProvider +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.resourceFlow +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getThemeWidgetKey +import kotlinx.coroutines.flow.onEach +import timber.log.Timber +import javax.inject.Inject + +class TimetableWidgetConfigurePresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val sharedPref: SharedPrefProvider +) : BasePresenter(errorHandler, studentRepository) { + + private var appWidgetId: Int? = null + + private var isFromProvider = false + + private var selectedStudent: Student? = null + + fun onAttachView( + view: TimetableWidgetConfigureView, + appWidgetId: Int?, + isFromProvider: Boolean? + ) { + super.onAttachView(view) + this.appWidgetId = appWidgetId + this.isFromProvider = isFromProvider ?: false + view.initView() + loadData() + } + + fun onItemSelect(student: Student) { + selectedStudent = student + + if (isFromProvider) registerStudent(selectedStudent) + else view?.showThemeDialog() + } + + fun onThemeSelect(index: Int) { + appWidgetId?.let { + sharedPref.putLong(getThemeWidgetKey(it), index.toLong()) + } + registerStudent(selectedStudent) + } + + fun onDismissThemeView() { + view?.finishView() + } + + private fun loadData() { + resourceFlow { studentRepository.getSavedStudents(false) }.onEach { + when (it) { + is Resource.Loading -> Timber.d("Timetable widget configure students data load") + is Resource.Success -> { + val selectedStudentId = appWidgetId?.let { id -> + sharedPref.getLong(getStudentWidgetKey(id), 0) + } ?: -1 + when { + it.data.isEmpty() -> view?.openLoginView() + it.data.size == 1 && !isFromProvider -> { + selectedStudent = it.data.single().student + view?.showThemeDialog() + } + else -> view?.updateData(it.data, selectedStudentId) + } + } + is Resource.Error -> errorHandler.dispatch(it.error) + } + }.launch() + } + + private fun registerStudent(student: Student?) { + requireNotNull(student) + + appWidgetId?.let { id -> + sharedPref.putLong(getStudentWidgetKey(id), student.id) + view?.run { + updateTimetableWidget(id) + setSuccessResult(id) + } + } + view?.finishView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt new file mode 100644 index 000000000..accdc28dc --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt @@ -0,0 +1,21 @@ +package io.github.wulkanowy.ui.modules.timetablewidget + +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.ui.base.BaseView + +interface TimetableWidgetConfigureView : BaseView { + + fun initView() + + fun updateData(data: List, selectedStudentId: Long) + + fun updateTimetableWidget(widgetId: Int) + + fun showThemeDialog() + + fun setSuccessResult(widgetId: Int) + + fun finishView() + + fun openLoginView() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt new file mode 100644 index 000000000..664086bca --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt @@ -0,0 +1,264 @@ +package io.github.wulkanowy.ui.modules.timetablewidget + +import android.annotation.SuppressLint +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID +import android.content.Context +import android.content.Intent +import android.graphics.Paint.ANTI_ALIAS_FLAG +import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG +import android.view.View.GONE +import android.view.View.VISIBLE +import android.widget.AdapterView.INVALID_POSITION +import android.widget.RemoteViews +import android.widget.RemoteViewsService +import io.github.wulkanowy.R +import io.github.wulkanowy.data.dataOrNull +import io.github.wulkanowy.data.db.SharedPrefProvider +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.data.enums.TimetableMode +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.repositories.TimetableRepository +import io.github.wulkanowy.data.toFirstResult +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getCurrentThemeWidgetKey +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getDateWidgetKey +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getTodayLastLessonEndDateTimeWidgetKey +import io.github.wulkanowy.utils.getCompatColor +import io.github.wulkanowy.utils.toFormattedString +import kotlinx.coroutines.runBlocking +import timber.log.Timber +import java.time.LocalDate + +class TimetableWidgetFactory( + private val timetableRepository: TimetableRepository, + private val studentRepository: StudentRepository, + private val semesterRepository: SemesterRepository, + private val prefRepository: PreferencesRepository, + private val sharedPref: SharedPrefProvider, + private val context: Context, + private val intent: Intent? +) : RemoteViewsService.RemoteViewsFactory { + + private var lessons = emptyList() + + private var savedCurrentTheme: Long? = null + + private var primaryColor: Int? = null + + private var textColor: Int? = null + + private var timetableChangeColor: Int? = null + + override fun getLoadingView() = null + + override fun hasStableIds() = true + + override fun getCount() = lessons.size + + override fun getViewTypeCount() = 2 + + override fun getItemId(position: Int) = position.toLong() + + override fun onCreate() {} + + override fun onDestroy() {} + + override fun onDataSetChanged() { + intent?.extras?.getInt(EXTRA_APPWIDGET_ID)?.let { appWidgetId -> + val date = LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(appWidgetId), 0)) + val studentId = sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0) + + updateTheme(appWidgetId) + lessons = getLessons(date, studentId) + + val todayLastLessonEndTimestamp = lessons.maxOfOrNull { it.end } + if (date == LocalDate.now() && todayLastLessonEndTimestamp != null) { + sharedPref.putLong( + key = getTodayLastLessonEndDateTimeWidgetKey(appWidgetId), + value = todayLastLessonEndTimestamp.epochSecond, + sync = true + ) + } + } + } + + private fun updateTheme(appWidgetId: Int) { + savedCurrentTheme = sharedPref.getLong(getCurrentThemeWidgetKey(appWidgetId), 0) + + if (savedCurrentTheme == 0L) { + primaryColor = R.color.colorPrimary + textColor = android.R.color.black + timetableChangeColor = R.color.timetable_change_dark + } else { + primaryColor = R.color.colorPrimaryLight + textColor = android.R.color.white + timetableChangeColor = R.color.timetable_change_light + } + } + + private fun getItemLayout(lesson: Timetable): Int { + return when { + prefRepository.showWholeClassPlan == TimetableMode.SMALL_OTHER_GROUP && !lesson.isStudentPlan -> { + if (savedCurrentTheme == 0L) R.layout.item_widget_timetable_small + else R.layout.item_widget_timetable_small_dark + } + savedCurrentTheme == 1L -> R.layout.item_widget_timetable_dark + else -> R.layout.item_widget_timetable + } + } + + private fun getLessons(date: LocalDate, studentId: Long) = try { + runBlocking { + if (!studentRepository.isStudentSaved()) return@runBlocking emptyList() + + val students = studentRepository.getSavedStudents() + val student = students.singleOrNull { it.student.id == studentId }?.student + ?: return@runBlocking emptyList() + + val semester = semesterRepository.getCurrentSemester(student) + timetableRepository.getTimetable(student, semester, date, date, false) + .toFirstResult().dataOrNull?.lessons.orEmpty() + .sortedWith(compareBy({ it.number }, { !it.isStudentPlan })) + .filter { + if (prefRepository.showWholeClassPlan == TimetableMode.ONLY_CURRENT_GROUP) { + it.isStudentPlan + } else true + } + } + } catch (e: Exception) { + Timber.e(e, "An error has occurred in timetable widget factory") + emptyList() + } + + @SuppressLint("DefaultLocale") + override fun getViewAt(position: Int): RemoteViews? { + if (position == INVALID_POSITION || lessons.getOrNull(position) == null) return null + + val lesson = lessons[position] + return RemoteViews(context.packageName, getItemLayout(lesson)).apply { + setTextViewText(R.id.timetableWidgetItemSubject, lesson.subject) + setTextViewText(R.id.timetableWidgetItemNumber, lesson.number.toString()) + setTextViewText( + R.id.timetableWidgetItemTimeStart, + lesson.start.toFormattedString("HH:mm") + ) + setTextViewText( + R.id.timetableWidgetItemTimeFinish, + lesson.end.toFormattedString("HH:mm") + ) + + updateDescription(this, lesson) + + if (lesson.canceled) { + updateStylesCanceled(this) + } else { + updateStylesNotCanceled(this, lesson) + } + + setOnClickFillInIntent(R.id.timetableWidgetItemContainer, Intent()) + } + } + + private fun updateDescription(remoteViews: RemoteViews, lesson: Timetable) { + with(remoteViews) { + if (lesson.info.isNotBlank() && !lesson.changes) { + setTextViewText(R.id.timetableWidgetItemDescription, lesson.info) + setViewVisibility(R.id.timetableWidgetItemDescription, VISIBLE) + setViewVisibility(R.id.timetableWidgetItemRoom, GONE) + setViewVisibility(R.id.timetableWidgetItemTeacher, GONE) + } else { + setViewVisibility(R.id.timetableWidgetItemDescription, GONE) + setViewVisibility(R.id.timetableWidgetItemRoom, VISIBLE) + setViewVisibility(R.id.timetableWidgetItemTeacher, VISIBLE) + } + } + } + + private fun updateStylesCanceled(remoteViews: RemoteViews) { + with(remoteViews) { + setInt( + R.id.timetableWidgetItemSubject, "setPaintFlags", + STRIKE_THRU_TEXT_FLAG or ANTI_ALIAS_FLAG + ) + setTextColor(R.id.timetableWidgetItemNumber, context.getCompatColor(primaryColor!!)) + setTextColor(R.id.timetableWidgetItemSubject, context.getCompatColor(primaryColor!!)) + setTextColor( + R.id.timetableWidgetItemDescription, + context.getCompatColor(primaryColor!!) + ) + } + } + + private fun updateStylesNotCanceled(remoteViews: RemoteViews, lesson: Timetable) { + with(remoteViews) { + setInt(R.id.timetableWidgetItemSubject, "setPaintFlags", ANTI_ALIAS_FLAG) + setTextColor(R.id.timetableWidgetItemSubject, context.getCompatColor(textColor!!)) + setTextColor( + R.id.timetableWidgetItemDescription, + context.getCompatColor(timetableChangeColor!!) + ) + + updateNotCanceledLessonNumberColor(this, lesson) + updateNotCanceledSubjectColor(this, lesson) + + val teacherChange = lesson.teacherOld.isNotBlank() + updateNotCanceledRoom(this, lesson, teacherChange) + updateNotCanceledTeacher(this, lesson, teacherChange) + } + } + + private fun updateNotCanceledLessonNumberColor(remoteViews: RemoteViews, lesson: Timetable) { + remoteViews.setTextColor( + R.id.timetableWidgetItemNumber, context.getCompatColor( + if (lesson.changes || (lesson.info.isNotBlank() && !lesson.canceled)) timetableChangeColor!! + else textColor!! + ) + ) + } + + private fun updateNotCanceledSubjectColor(remoteViews: RemoteViews, lesson: Timetable) { + remoteViews.setTextColor( + R.id.timetableWidgetItemSubject, context.getCompatColor( + if (lesson.subjectOld.isNotBlank() && lesson.subject != lesson.subjectOld) timetableChangeColor!! + else textColor!! + ) + ) + } + + private fun updateNotCanceledRoom( + remoteViews: RemoteViews, + lesson: Timetable, + teacherChange: Boolean + ) { + with(remoteViews) { + if (lesson.room.isNotBlank()) { + setTextViewText( + R.id.timetableWidgetItemRoom, + if (teacherChange) lesson.room + else "${context.getString(R.string.timetable_room)} ${lesson.room}" + ) + + setTextColor( + R.id.timetableWidgetItemRoom, context.getCompatColor( + if (lesson.roomOld.isNotBlank() && lesson.room != lesson.roomOld) timetableChangeColor!! + else textColor!! + ) + ) + } else setTextViewText(R.id.timetableWidgetItemRoom, "") + } + } + + private fun updateNotCanceledTeacher( + remoteViews: RemoteViews, + lesson: Timetable, + teacherChange: Boolean + ) { + remoteViews.setTextViewText( + R.id.timetableWidgetItemTeacher, + if (teacherChange) lesson.teacher + else "" + ) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt new file mode 100644 index 000000000..07e717eaf --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt @@ -0,0 +1,292 @@ +package io.github.wulkanowy.ui.modules.timetablewidget + +import android.annotation.SuppressLint +import android.app.PendingIntent +import android.appwidget.AppWidgetManager +import android.appwidget.AppWidgetManager.* +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK +import android.content.Intent.FLAG_ACTIVITY_NEW_TASK +import android.content.res.Configuration +import android.graphics.Bitmap +import android.graphics.Canvas +import android.widget.RemoteViews +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.SharedPrefProvider +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.exceptions.NoCurrentStudentException +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.services.widgets.TimetableWidgetService +import io.github.wulkanowy.ui.modules.Destination +import io.github.wulkanowy.ui.modules.splash.SplashActivity +import io.github.wulkanowy.utils.* +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import timber.log.Timber +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.ZoneOffset +import javax.inject.Inject + +@AndroidEntryPoint +class TimetableWidgetProvider : BroadcastReceiver() { + + @Inject + lateinit var appWidgetManager: AppWidgetManager + + @Inject + lateinit var studentRepository: StudentRepository + + @Inject + lateinit var sharedPref: SharedPrefProvider + + @Inject + lateinit var analytics: AnalyticsHelper + + companion object { + + private const val TIMETABLE_PENDING_INTENT_ID = 201 + + private const val EXTRA_TOGGLED_WIDGET_ID = "extraToggledWidget" + + private const val EXTRA_BUTTON_TYPE = "extraButtonType" + + private const val BUTTON_NEXT = "buttonNext" + + private const val BUTTON_PREV = "buttonPrev" + + private const val BUTTON_RESET = "buttonReset" + + const val EXTRA_FROM_PROVIDER = "extraFromProvider" + + fun getDateWidgetKey(appWidgetId: Int) = "timetable_widget_date_$appWidgetId" + + fun getTodayLastLessonEndDateTimeWidgetKey(appWidgetId: Int) = + "timetable_widget_today_last_lesson_end_date_time_$appWidgetId" + + fun getStudentWidgetKey(appWidgetId: Int) = "timetable_widget_student_$appWidgetId" + + fun getThemeWidgetKey(appWidgetId: Int) = "timetable_widget_theme_$appWidgetId" + + fun getCurrentThemeWidgetKey(appWidgetId: Int) = + "timetable_widget_current_theme_$appWidgetId" + } + + @OptIn(DelicateCoroutinesApi::class) + override fun onReceive(context: Context, intent: Intent) { + GlobalScope.launch { + when (intent.action) { + ACTION_APPWIDGET_UPDATE -> onUpdate(context, intent) + ACTION_APPWIDGET_DELETED -> onDelete(intent) + } + } + } + + private suspend fun onUpdate(context: Context, intent: Intent) { + if (intent.getStringExtra(EXTRA_BUTTON_TYPE) === null) { + intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS)?.forEach { appWidgetId -> + val student = + getStudent(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), appWidgetId) + + updateWidget(context, appWidgetId, getWidgetDateToLoad(appWidgetId), student) + } + } else { + val buttonType = intent.getStringExtra(EXTRA_BUTTON_TYPE) + val toggledWidgetId = intent.getIntExtra(EXTRA_TOGGLED_WIDGET_ID, 0) + val student = getStudent( + sharedPref.getLong(getStudentWidgetKey(toggledWidgetId), 0), + toggledWidgetId + ) + val savedDate = + LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(toggledWidgetId), 0)) + val date = when (buttonType) { + BUTTON_RESET -> getWidgetDateToLoad(toggledWidgetId) + BUTTON_NEXT -> savedDate.nextSchoolDay + BUTTON_PREV -> savedDate.previousSchoolDay + else -> getWidgetDateToLoad(toggledWidgetId) + } + if (!buttonType.isNullOrBlank()) { + analytics.logEvent( + "changed_timetable_widget_day", + "button" to buttonType + ) + } + updateWidget(context, toggledWidgetId, date, student) + } + } + + private fun onDelete(intent: Intent) { + val appWidgetId = intent.getIntExtra(EXTRA_APPWIDGET_ID, 0) + + if (appWidgetId != 0) { + with(sharedPref) { + delete(getStudentWidgetKey(appWidgetId)) + delete(getDateWidgetKey(appWidgetId)) + delete(getThemeWidgetKey(appWidgetId)) + delete(getCurrentThemeWidgetKey(appWidgetId)) + } + } + } + + @SuppressLint("DefaultLocale") + private fun updateWidget( + context: Context, + appWidgetId: Int, + date: LocalDate, + student: Student? + ) { + val savedConfigureTheme = sharedPref.getLong(getThemeWidgetKey(appWidgetId), 0) + val isSystemDarkMode = + context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES + var currentTheme = 0L + var layoutId = R.layout.widget_timetable + + if (savedConfigureTheme == 1L || (savedConfigureTheme == 2L && isSystemDarkMode)) { + currentTheme = 1L + layoutId = R.layout.widget_timetable_dark + } + + val nextNavIntent = createNavIntent(context, appWidgetId, appWidgetId, BUTTON_NEXT) + val prevNavIntent = createNavIntent(context, -appWidgetId, appWidgetId, BUTTON_PREV) + val resetNavIntent = + createNavIntent(context, Int.MAX_VALUE - appWidgetId, appWidgetId, BUTTON_RESET) + val adapterIntent = Intent(context, TimetableWidgetService::class.java) + .apply { + putExtra(EXTRA_APPWIDGET_ID, appWidgetId) + //make Intent unique + action = appWidgetId.toString() + } + val accountIntent = PendingIntent.getActivity( + context, + -Int.MAX_VALUE + appWidgetId, + Intent(context, TimetableWidgetConfigureActivity::class.java).apply { + addFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK) + putExtra(EXTRA_APPWIDGET_ID, appWidgetId) + putExtra(EXTRA_FROM_PROVIDER, true) + }, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE + + ) + val appIntent = PendingIntent.getActivity( + context, + TIMETABLE_PENDING_INTENT_ID, + SplashActivity.getStartIntent(context, Destination.Timetable()), + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE + ) + + val remoteView = RemoteViews(context.packageName, layoutId).apply { + setEmptyView(R.id.timetableWidgetList, R.id.timetableWidgetEmpty) + setTextViewText( + R.id.timetableWidgetDate, + date.toFormattedString("EEEE, dd.MM").capitalise() + ) + setTextViewText( + R.id.timetableWidgetName, + student?.nickOrName ?: context.getString(R.string.all_no_data) + ) + + student?.let { + setImageViewBitmap(R.id.timetableWidgetAccount, context.createAvatarBitmap(it)) + } + + setRemoteAdapter(R.id.timetableWidgetList, adapterIntent) + setOnClickPendingIntent(R.id.timetableWidgetNext, nextNavIntent) + setOnClickPendingIntent(R.id.timetableWidgetPrev, prevNavIntent) + setOnClickPendingIntent(R.id.timetableWidgetDate, resetNavIntent) + setOnClickPendingIntent(R.id.timetableWidgetName, resetNavIntent) + setOnClickPendingIntent(R.id.timetableWidgetAccount, accountIntent) + setPendingIntentTemplate(R.id.timetableWidgetList, appIntent) + } + + with(sharedPref) { + putLong(getCurrentThemeWidgetKey(appWidgetId), currentTheme) + putLong(getDateWidgetKey(appWidgetId), date.toEpochDay(), true) + } + + with(appWidgetManager) { + updateAppWidget(appWidgetId, remoteView) + notifyAppWidgetViewDataChanged(appWidgetId, R.id.timetableWidgetList) + Timber.d("TimetableWidgetProvider updated") + } + } + + private fun createNavIntent( + context: Context, + code: Int, + appWidgetId: Int, + buttonType: String + ) = PendingIntent.getBroadcast( + context, + code, + Intent(context, TimetableWidgetProvider::class.java).apply { + action = ACTION_APPWIDGET_UPDATE + putExtra(EXTRA_BUTTON_TYPE, buttonType) + putExtra(EXTRA_TOGGLED_WIDGET_ID, appWidgetId) + }, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE + ) + + private suspend fun getStudent(studentId: Long, appWidgetId: Int) = try { + val students = studentRepository.getSavedStudents(false) + val student = students.singleOrNull { it.student.id == studentId }?.student + when { + student != null -> student + studentId != 0L && studentRepository.isCurrentStudentSet() -> { + studentRepository.getCurrentStudent(false).also { + sharedPref.putLong(getStudentWidgetKey(appWidgetId), it.id) + } + } + else -> null + } + } catch (e: Exception) { + if (e.cause !is NoCurrentStudentException) { + Timber.e(e, "An error has occurred in timetable widget provider") + } + null + } + + private fun Context.createAvatarBitmap(student: Student): Bitmap { + val avatarColor = if (student.avatarColor == -2937041L) { + getCompatColor(R.color.colorPrimaryLight).toLong() + } else { + student.avatarColor + } + val avatarDrawable = createNameInitialsDrawable(student.nickOrName, avatarColor, 0.5f) + + val avatarBitmap = + if (avatarDrawable.intrinsicWidth <= 0 || avatarDrawable.intrinsicHeight <= 0) { + Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888) + } else { + Bitmap.createBitmap( + avatarDrawable.intrinsicWidth, + avatarDrawable.intrinsicHeight, + Bitmap.Config.ARGB_8888 + ) + } + + val canvas = Canvas(avatarBitmap) + avatarDrawable.setBounds(0, 0, canvas.width, canvas.height) + avatarDrawable.draw(canvas) + return avatarBitmap + } + + private fun getWidgetDateToLoad(appWidgetId: Int): LocalDate { + val lastLessonEndTimestamp = + sharedPref.getLong(getTodayLastLessonEndDateTimeWidgetKey(appWidgetId), 0) + val lastLessonEndDateTime = + LocalDateTime.ofEpochSecond(lastLessonEndTimestamp, 0, ZoneOffset.UTC) + + val todayDate = LocalDate.now() + val isLastLessonEndDateNow = lastLessonEndDateTime.toLocalDate() == todayDate + val isLastLessonEndDateAfterNowTime = LocalDateTime.now() > lastLessonEndDateTime + + return if (isLastLessonEndDateNow && isLastLessonEndDateAfterNowTime) { + todayDate.nextSchoolDay + } else { + todayDate.nextOrSameSchoolDay + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/splash/SplashActivity.java b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashActivity.java deleted file mode 100644 index c9f98d5ee..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/splash/SplashActivity.java +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.wulkanowy.ui.splash; - -import android.os.Bundle; - -import javax.inject.Inject; - -import butterknife.ButterKnife; -import io.github.wulkanowy.services.SyncJob; -import io.github.wulkanowy.ui.base.BaseActivity; -import io.github.wulkanowy.ui.login.LoginActivity; -import io.github.wulkanowy.ui.main.MainActivity; - -public class SplashActivity extends BaseActivity implements SplashContract.View { - - @Inject - SplashContract.Presenter presenter; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - getActivityComponent().inject(this); - setButterKnife(ButterKnife.bind(this)); - - presenter.onStart(this); - } - - @Override - protected void onDestroy() { - presenter.onDestroy(); - super.onDestroy(); - } - - @Override - public void openLoginActivity() { - startActivity(LoginActivity.getStartIntent(this)); - finish(); - } - - @Override - public void openMainActivity() { - startActivity(MainActivity.getStartIntent(this)); - finish(); - } - - @Override - public void startSyncService() { - SyncJob.start(getApplicationContext()); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/splash/SplashContract.java b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashContract.java deleted file mode 100644 index a69deb032..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/splash/SplashContract.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.wulkanowy.ui.splash; - - -import io.github.wulkanowy.di.annotations.PerActivity; -import io.github.wulkanowy.ui.base.BaseContract; - -public interface SplashContract { - - interface View extends BaseContract.View { - - void openLoginActivity(); - - void openMainActivity(); - - void startSyncService(); - } - - @PerActivity - interface Presenter extends BaseContract.Presenter { - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/splash/SplashPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashPresenter.java deleted file mode 100644 index c3d81215c..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/splash/SplashPresenter.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.wulkanowy.ui.splash; - -import android.support.annotation.NonNull; - -import javax.inject.Inject; - -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.ui.base.BasePresenter; - -public class SplashPresenter extends BasePresenter - implements SplashContract.Presenter { - - @Inject - SplashPresenter(RepositoryContract repository) { - super(repository); - } - - @Override - public void onStart(@NonNull SplashContract.View activity) { - super.onStart(activity); - getView().startSyncService(); - - if (getRepository().getCurrentUserId() == 0) { - getView().openLoginActivity(); - } else { - getView().openMainActivity(); - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/widgets/DividerItemDecoration.kt b/app/src/main/java/io/github/wulkanowy/ui/widgets/DividerItemDecoration.kt new file mode 100644 index 000000000..933301317 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/widgets/DividerItemDecoration.kt @@ -0,0 +1,31 @@ +package io.github.wulkanowy.ui.widgets + +import android.content.Context +import android.graphics.Canvas +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.RecyclerView + +class DividerItemDecoration( + context: Context, + private val showDividerWithFirstItem: Boolean = true +) : DividerItemDecoration(context, VERTICAL) { + + override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) { + canvas.save() + val dividerLeft = parent.paddingLeft + val dividerRight = parent.width - parent.paddingRight + + for (i in 0..parent.childCount - 2) { + if (!showDividerWithFirstItem && i == 0) continue + + val child = parent.getChildAt(i) + val params = child.layoutParams as RecyclerView.LayoutParams + val dividerTop = child.bottom + params.bottomMargin + val dividerBottom = dividerTop + drawable!!.intrinsicHeight + + drawable?.setBounds(dividerLeft, dividerTop, dividerRight, dividerBottom) + drawable?.draw(canvas) + } + canvas.restore() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/widgets/FittedScrollableTabLayout.kt b/app/src/main/java/io/github/wulkanowy/ui/widgets/FittedScrollableTabLayout.kt new file mode 100644 index 000000000..6b7fb4aa9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/widgets/FittedScrollableTabLayout.kt @@ -0,0 +1,31 @@ +package io.github.wulkanowy.ui.widgets + +import android.content.Context +import android.util.AttributeSet +import android.view.ViewGroup +import com.google.android.material.tabs.TabLayout + +/** + * @see Tabs don't fit to screen with tabmode=scrollable, Even with a Custom Tab Layout + */ +class FittedScrollableTabLayout @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null +) : TabLayout(context, attrs) { + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + setMeasuredDimension(widthMeasureSpec, heightMeasureSpec) + val tabLayout = getChildAt(0) as ViewGroup + val childCount = tabLayout.childCount + + if (childCount == 0) return + + val tabMinWidth = context.resources.displayMetrics.widthPixels / childCount + + for (i in 0 until childCount) { + tabLayout.getChildAt(i).minimumWidth = tabMinWidth + } + + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/widgets/MaterialLinearLayout.kt b/app/src/main/java/io/github/wulkanowy/ui/widgets/MaterialLinearLayout.kt new file mode 100644 index 000000000..4e1ca1a97 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/widgets/MaterialLinearLayout.kt @@ -0,0 +1,26 @@ +package io.github.wulkanowy.ui.widgets + +import android.content.Context +import android.util.AttributeSet +import android.widget.LinearLayout +import androidx.core.view.ViewCompat +import com.google.android.material.shape.MaterialShapeDrawable + +class MaterialLinearLayout @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null +) : LinearLayout(context, attrs) { + + init { + val drawable = + MaterialShapeDrawable.createWithElevationOverlay(context, ViewCompat.getElevation(this)) + ViewCompat.setBackground(this, drawable) + } + + override fun setElevation(elevation: Float) { + super.setElevation(elevation) + if (background is MaterialShapeDrawable) { + (background as MaterialShapeDrawable).elevation = elevation + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/ActivityExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ActivityExtension.kt new file mode 100644 index 000000000..e9cac72ea --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/ActivityExtension.kt @@ -0,0 +1,15 @@ +package io.github.wulkanowy.utils + +import android.app.Activity +import android.view.inputmethod.InputMethodManager +import androidx.core.content.getSystemService + +fun Activity.showSoftInput() { + getSystemService()?.let { manager -> + currentFocus?.let { manager.showSoftInput(it, 0) } + } +} + +fun Activity.hideSoftInput() { + getSystemService()?.hideSoftInputFromWindow(window.decorView.applicationWindowToken, 0) +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/AppConstant.java b/app/src/main/java/io/github/wulkanowy/utils/AppConstant.java deleted file mode 100644 index 27d76e756..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/AppConstant.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.utils; - -public final class AppConstant { - - public static final String APP_NAME = "Wulkanowy"; - - public static final String DATABASE_NAME = "wulkanowy_db"; - - public static final String SHARED_PREFERENCES_NAME = "user_data"; - - - public static final String VULCAN_CREATE_ACCOUNT_URL = - "https://cufs.vulcan.net.pl/Default/AccountManage/CreateAccount"; - - public static final String VULCAN_FORGOT_PASS_URL = - "https://cufs.vulcan.net.pl/Default/AccountManage/UnlockAccount"; - - public static final String DEFAULT_SYMBOL = "Default"; - - public static final String DATE_PATTERN = "yyyy-MM-dd"; - - private AppConstant() { - throw new IllegalStateException("Utility class"); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/AppInfo.kt b/app/src/main/java/io/github/wulkanowy/utils/AppInfo.kt new file mode 100644 index 000000000..962e5b208 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/AppInfo.kt @@ -0,0 +1,38 @@ +package io.github.wulkanowy.utils + +import android.content.res.Resources +import android.os.Build +import io.github.wulkanowy.BuildConfig +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +open class AppInfo @Inject constructor() { + + open val isDebug get() = BuildConfig.DEBUG + + open val versionCode get() = BuildConfig.VERSION_CODE + + open val buildTimestamp get() = BuildConfig.BUILD_TIMESTAMP + + open val buildFlavor get() = BuildConfig.FLAVOR + + open val versionName get() = BuildConfig.VERSION_NAME + + open val systemVersion get() = Build.VERSION.SDK_INT + + open val systemManufacturer: String get() = Build.MANUFACTURER + + open val systemModel: String get() = Build.MODEL + + open val messagesBaseUrl = BuildConfig.MESSAGES_BASE_URL + + @Suppress("DEPRECATION") + open val systemLanguage: String + get() = Resources.getSystem().configuration.locale.language + + val defaultColorsForAvatar = listOf( + 0xd32f2f, 0xE64A19, 0xFFA000, 0xAFB42B, 0x689F38, 0x388E3C, 0x00796B, 0x0097A7, + 0x1976D2, 0x3647b5, 0x6236c9, 0x9225c1, 0xC2185B, 0x616161, 0x455A64, 0x7a5348 + ).map { (it and 0x00ffffff or (255 shl 24)).toLong() } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt new file mode 100644 index 000000000..397c95953 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt @@ -0,0 +1,43 @@ +package io.github.wulkanowy.utils + +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Attendance +import io.github.wulkanowy.data.db.entities.AttendanceSummary +import io.github.wulkanowy.sdk.scrapper.attendance.AttendanceCategory + +/** + * [UONET+ - Zasady tworzenia podsumowań liczb uczniów obecnych i nieobecnych w tabeli frekwencji] + * (https://www.vulcan.edu.pl/vulcang_files/user/AABW/AABW-PDF/uonetplus/uonetplus_Frekwencja-liczby-obecnych-nieobecnych.pdf) + */ + +private inline val AttendanceSummary.allPresences: Double + get() = presence.toDouble() + absenceForSchoolReasons + lateness + latenessExcused + +private inline val AttendanceSummary.allAbsences: Double + get() = absence.toDouble() + absenceExcused + +inline val Attendance.isExcusableOrNotExcused: Boolean + get() = (excusable || ((absence || lateness) && !excused)) && excuseStatus == null + +fun AttendanceSummary.calculatePercentage() = calculatePercentage(allPresences, allAbsences) + +fun List.calculatePercentage(): Double { + return calculatePercentage(sumOf { it.allPresences }, sumOf { it.allAbsences }) +} + +private fun calculatePercentage(presence: Double, absence: Double): Double { + return if ((presence + absence) == 0.0) 0.0 else (presence / (presence + absence)) * 100 +} + +inline val Attendance.descriptionRes + get() = when (AttendanceCategory.getCategoryByName(name)) { + AttendanceCategory.PRESENCE -> R.string.attendance_present + AttendanceCategory.ABSENCE_UNEXCUSED -> R.string.attendance_absence_unexcused + AttendanceCategory.ABSENCE_EXCUSED -> R.string.attendance_absence_excused + AttendanceCategory.UNEXCUSED_LATENESS -> R.string.attendance_unexcused_lateness + AttendanceCategory.EXCUSED_LATENESS -> R.string.attendance_excused_lateness + AttendanceCategory.ABSENCE_FOR_SCHOOL_REASONS -> R.string.attendance_absence_school + AttendanceCategory.EXEMPTION -> R.string.attendance_exemption + AttendanceCategory.DELETED -> R.string.attendance_deleted + else -> R.string.attendance_unknown + } diff --git a/app/src/main/java/io/github/wulkanowy/utils/AverageCalculator.java b/app/src/main/java/io/github/wulkanowy/utils/AverageCalculator.java deleted file mode 100644 index cc33e65a9..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/AverageCalculator.java +++ /dev/null @@ -1,61 +0,0 @@ -package io.github.wulkanowy.utils; - -import java.util.List; - -import io.github.wulkanowy.data.db.dao.entities.Grade; - -public final class AverageCalculator { - - private AverageCalculator() { - throw new IllegalStateException("Utility class"); - } - - public static float calculate(List gradeList) { - - float counter = 0f; - float denominator = 0f; - - for (Grade grade : gradeList) { - int integerWeight = getIntegerForWeightOfGrade(grade.getWeight()); - float floatValue = getMathematicalValueOfGrade(grade.getValue()); - - if (floatValue != -1f) { - counter += floatValue * integerWeight; - denominator += integerWeight; - } - } - - if (counter == 0f) { - return -1f; - } else { - return counter / denominator; - } - } - - private static float getMathematicalValueOfGrade(String valueOfGrade) { - if (valueOfGrade.matches("[-|+|=]{0,2}[0-6]") - || valueOfGrade.matches("[0-6][-|+|=]{0,2}")) { - if (valueOfGrade.matches("[-][0-6]") - || valueOfGrade.matches("[0-6][-]")) { - String replacedValue = valueOfGrade.replaceAll("[-]", ""); - return Float.valueOf(replacedValue) - 0.33f; - } else if (valueOfGrade.matches("[+][0-6]") - || valueOfGrade.matches("[0-6][+]")) { - String replacedValue = valueOfGrade.replaceAll("[+]", ""); - return Float.valueOf((replacedValue)) + 0.33f; - } else if (valueOfGrade.matches("[-|=]{1,2}[0-6]") - || valueOfGrade.matches("[0-6][-|=]{1,2}")) { - String replacedValue = valueOfGrade.replaceAll("[-|=]{1,2}", ""); - return Float.valueOf((replacedValue)) - 0.5f; - } else { - return Float.valueOf(valueOfGrade); - } - } else { - return -1; - } - } - - private static int getIntegerForWeightOfGrade(String weightOfGrade) { - return Integer.valueOf(weightOfGrade.substring(0, weightOfGrade.length() - 3)); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/CommonUtils.java b/app/src/main/java/io/github/wulkanowy/utils/CommonUtils.java deleted file mode 100644 index 8ec73faf7..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/CommonUtils.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.wulkanowy.utils; - -import android.app.Activity; -import android.net.Uri; -import android.support.customtabs.CustomTabsIntent; - -import io.github.wulkanowy.R; - -public final class CommonUtils { - - private CommonUtils() { - throw new IllegalStateException("Utility class"); - } - - public static void openInternalBrowserViewer(Activity activity, String url) { - CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); - builder.setToolbarColor(activity.getResources().getColor(R.color.colorPrimary)); - CustomTabsIntent customTabsIntent = builder.build(); - customTabsIntent.launchUrl(activity, Uri.parse(url)); - } - - public static int colorHexToColorName(String hexColor) { - switch (hexColor) { - case "000000": - return R.string.color_black_text; - - case "F04C4C": - return R.string.color_red_text; - - case "20A4F7": - return R.string.color_blue_text; - - case "6ECD07": - return R.string.color_green_text; - - default: - return R.string.noColor_text; - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt new file mode 100644 index 000000000..323e1e477 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt @@ -0,0 +1,86 @@ +package io.github.wulkanowy.utils + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.* +import android.text.TextPaint +import android.util.DisplayMetrics.DENSITY_DEFAULT +import androidx.annotation.* +import androidx.core.content.ContextCompat +import androidx.core.graphics.ColorUtils +import androidx.core.graphics.applyCanvas +import androidx.core.graphics.drawable.RoundedBitmapDrawable +import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory +import androidx.core.graphics.drawable.toBitmap + +@ColorInt +fun Context.getThemeAttrColor(@AttrRes colorAttr: Int): Int { + val array = obtainStyledAttributes(null, intArrayOf(colorAttr)) + return try { + array.getColor(0, 0) + } finally { + array.recycle() + } +} + +@ColorInt +fun Context.getThemeAttrColor(@AttrRes colorAttr: Int, alpha: Int): Int { + return ColorUtils.setAlphaComponent(getThemeAttrColor(colorAttr), alpha) +} + +@ColorInt +fun Context.getCompatColor(@ColorRes colorRes: Int) = ContextCompat.getColor(this, colorRes) + +fun Context.getCompatDrawable(@DrawableRes drawableRes: Int) = + ContextCompat.getDrawable(this, drawableRes) + +fun Context.getCompatDrawable(@DrawableRes drawableRes: Int, @ColorRes colorRes: Int) = + getCompatDrawable(drawableRes)?.mutate()?.apply { + colorFilter = PorterDuffColorFilter( + getCompatColor(colorRes), PorterDuff.Mode.MULTIPLY + ) + } + +fun Context.getCompatBitmap(@DrawableRes drawableRes: Int, @ColorRes colorRes: Int) = + getCompatDrawable(drawableRes, colorRes)?.toBitmap() + +fun Context.getPlural(@PluralsRes pluralRes: Int, quantity: Int, vararg arguments: Any) = + resources.getQuantityString(pluralRes, quantity, *arguments) + +fun Context.dpToPx(dp: Float) = dp * resources.displayMetrics.densityDpi / DENSITY_DEFAULT + +@SuppressLint("DefaultLocale") +fun Context.createNameInitialsDrawable( + text: String, + backgroundColor: Long, + scaleFactory: Float = 1f +): RoundedBitmapDrawable { + val words = text.split(" ") + val firstCharFirstWord = words.getOrNull(0)?.firstOrNull() ?: "" + val firstCharSecondWord = words.getOrNull(1)?.firstOrNull() ?: "" + + val initials = "$firstCharFirstWord$firstCharSecondWord".uppercase() + + val bounds = Rect() + val dimension = this.dpToPx(64f * scaleFactory).toInt() + val textPaint = TextPaint().apply { + typeface = Typeface.SANS_SERIF + color = Color.WHITE + textAlign = Paint.Align.CENTER + isAntiAlias = true + textSize = this@createNameInitialsDrawable.dpToPx(30f * scaleFactory) + getTextBounds(initials, 0, initials.length, bounds) + } + + val xCoordinate = (dimension / 2).toFloat() + val yCoordinate = (dimension / 2 + (bounds.bottom - bounds.top) / 2).toFloat() + + val bitmap = Bitmap.createBitmap(dimension, dimension, Bitmap.Config.ARGB_8888) + .applyCanvas { + drawColor(backgroundColor.toInt()) + drawText(initials, 0, initials.length, xCoordinate, yCoordinate, textPaint) + } + + return RoundedBitmapDrawableFactory.create(this.resources, bitmap) + .apply { isCircular = true } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java b/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java deleted file mode 100644 index a22e4d8b2..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java +++ /dev/null @@ -1,131 +0,0 @@ -package io.github.wulkanowy.utils; - - -import java.util.ArrayList; -import java.util.List; - -import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; -import io.github.wulkanowy.data.db.dao.entities.Day; -import io.github.wulkanowy.data.db.dao.entities.Grade; -import io.github.wulkanowy.data.db.dao.entities.Subject; -import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; -import io.github.wulkanowy.data.db.dao.entities.Week; - -public final class DataObjectConverter { - - private DataObjectConverter() { - throw new IllegalStateException("Utility class"); - } - - public static List subjectsToSubjectEntities(List subjectList) { - - List subjectEntityList = new ArrayList<>(); - - for (io.github.wulkanowy.api.grades.Subject subject : subjectList) { - Subject subjectEntity = new Subject() - .setName(subject.getName()) - .setPredictedRating(subject.getPredictedRating()) - .setFinalRating(subject.getFinalRating()); - subjectEntityList.add(subjectEntity); - } - - return subjectEntityList; - } - - public static List gradesToGradeEntities(List gradeList) { - - List gradeEntityList = new ArrayList<>(); - - for (io.github.wulkanowy.api.grades.Grade grade : gradeList) { - Grade gradeEntity = new Grade() - .setSubject(grade.getSubject()) - .setValue(grade.getValue()) - .setColor(grade.getColor()) - .setSymbol(grade.getSymbol()) - .setDescription(grade.getDescription()) - .setWeight(grade.getWeight()) - .setDate(grade.getDate()) - .setTeacher(grade.getTeacher()) - .setSemester(grade.getSemester()); - - gradeEntityList.add(gradeEntity); - } - return gradeEntityList; - } - - public static Week weekToWeekEntity(io.github.wulkanowy.api.generic.Week week) { - return new Week().setStartDayDate(week.getStartDayDate()); - } - - public static Day dayToDayEntity(io.github.wulkanowy.api.generic.Day day) { - return new Day() - .setDate(day.getDate()) - .setDayName(day.getDayName()) - .setIsFreeDay(day.isFreeDay()) - .setFreeDayName(day.getFreeDayName()); - } - - - public static List daysToDaysEntities(List dayList) { - - List dayEntityList = new ArrayList<>(); - - for (io.github.wulkanowy.api.generic.Day day : dayList) { - dayEntityList.add(dayToDayEntity(day)); - } - return dayEntityList; - } - - public static TimetableLesson lessonToTimetableLessonEntity(io.github.wulkanowy.api.generic.Lesson lesson) { - return new TimetableLesson() - .setNumber(lesson.getNumber()) - .setSubject(lesson.getSubject()) - .setTeacher(lesson.getTeacher()) - .setRoom(lesson.getRoom()) - .setDescription(lesson.getDescription()) - .setGroupName(lesson.getGroupName()) - .setStartTime(lesson.getStartTime()) - .setEndTime(lesson.getEndTime()) - .setDate(lesson.getDate()) - .setEmpty(lesson.isEmpty()) - .setDivisionIntoGroups(lesson.isDivisionIntoGroups()) - .setPlanning(lesson.isPlanning()) - .setRealized(lesson.isRealized()) - .setMovedOrCanceled(lesson.isMovedOrCanceled()) - .setNewMovedInOrChanged(lesson.isNewMovedInOrChanged()); - } - - public static AttendanceLesson lessonToAttendanceLessonEntity(io.github.wulkanowy.api.generic.Lesson lesson) { - return new AttendanceLesson() - .setNumber(Integer.valueOf(lesson.getNumber())) - .setSubject(lesson.getSubject()) - .setDate(lesson.getDate()) - .setIsPresence(lesson.isPresence()) - .setIsAbsenceUnexcused(lesson.isAbsenceUnexcused()) - .setIsAbsenceExcused(lesson.isAbsenceExcused()) - .setIsUnexcusedLateness(lesson.isUnexcusedLateness()) - .setIsAbsenceForSchoolReasons(lesson.isAbsenceForSchoolReasons()) - .setIsExcusedLateness(lesson.isExcusedLateness()) - .setIsExemption(lesson.isExemption()); - } - - public static List lessonsToTimetableLessonsEntities(List lessonList) { - - List lessonEntityList = new ArrayList<>(); - - for (io.github.wulkanowy.api.generic.Lesson lesson : lessonList) { - lessonEntityList.add(lessonToTimetableLessonEntity(lesson)); - } - return lessonEntityList; - } - - public static List lessonsToAttendanceLessonsEntities(List lessonList) { - - List lessonEntityList = new ArrayList<>(); - - for (io.github.wulkanowy.api.generic.Lesson lesson : lessonList) { - lessonEntityList.add(lessonToAttendanceLessonEntity(lesson)); - } - return lessonEntityList; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/DispatchersProvider.kt b/app/src/main/java/io/github/wulkanowy/utils/DispatchersProvider.kt new file mode 100644 index 000000000..8aaa57f4f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/DispatchersProvider.kt @@ -0,0 +1,8 @@ +package io.github.wulkanowy.utils + +import kotlinx.coroutines.Dispatchers + +open class DispatchersProvider { + + open val io get() = Dispatchers.IO +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/EditTextExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/EditTextExtension.kt new file mode 100644 index 000000000..58c93729b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/EditTextExtension.kt @@ -0,0 +1,10 @@ +package io.github.wulkanowy.utils + +import android.view.inputmethod.EditorInfo +import android.widget.EditText + +fun EditText.setOnEditorDoneSignIn(callback: () -> Boolean) { + setOnEditorActionListener { _, id, _ -> + if (id == EditorInfo.IME_ACTION_DONE || id == EditorInfo.IME_NULL) callback() else false + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/EntitiesCompare.java b/app/src/main/java/io/github/wulkanowy/utils/EntitiesCompare.java deleted file mode 100644 index 5681bad17..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/EntitiesCompare.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.github.wulkanowy.utils; - -import org.apache.commons.collections4.CollectionUtils; - -import java.util.ArrayList; -import java.util.List; - -import io.github.wulkanowy.data.db.dao.entities.Grade; - -public final class EntitiesCompare { - - private EntitiesCompare() { - throw new IllegalStateException("Utility class"); - } - - public static List compareGradeList(List newList, List oldList) { - - List addedOrUpdatedGradeList = new ArrayList<>(CollectionUtils - .removeAll(newList, oldList)); - List updatedList = new ArrayList<>(CollectionUtils - .removeAll(newList, addedOrUpdatedGradeList)); - List lastList = new ArrayList<>(); - - for (Grade grade : addedOrUpdatedGradeList) { - if (!oldList.isEmpty()) { - grade.setRead(false); - } - grade.setIsNew(true); - updatedList.add(grade); - } - - for (Grade updateGrade : updatedList) { - for (Grade oldGrade : oldList) { - if (updateGrade.equals(oldGrade)) { - updateGrade.setRead(oldGrade.getRead()); - } - } - lastList.add(updateGrade); - } - return lastList; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt new file mode 100644 index 000000000..43cecd400 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt @@ -0,0 +1,74 @@ +package io.github.wulkanowy.utils + +import android.content.res.Resources +import io.github.wulkanowy.R +import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException +import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException +import io.github.wulkanowy.sdk.scrapper.exception.ScrapperException +import io.github.wulkanowy.sdk.scrapper.exception.ServiceUnavailableException +import io.github.wulkanowy.sdk.scrapper.exception.VulcanException +import io.github.wulkanowy.sdk.scrapper.login.NotLoggedInException +import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException +import okhttp3.internal.http2.StreamResetException +import java.io.InterruptedIOException +import java.net.ConnectException +import java.net.SocketException +import java.net.SocketTimeoutException +import java.net.UnknownHostException +import java.security.cert.CertificateExpiredException +import java.security.cert.CertificateNotYetValidException +import javax.net.ssl.SSLHandshakeException + +fun Resources.getErrorString(error: Throwable): String = when (error) { + is UnknownHostException -> R.string.error_no_internet + is SocketException, + is SocketTimeoutException, + is InterruptedIOException, + is ConnectException, + is StreamResetException -> R.string.error_timeout + is NotLoggedInException -> R.string.error_login_failed + is PasswordChangeRequiredException -> R.string.error_password_change_required + is ServiceUnavailableException -> R.string.error_service_unavailable + is FeatureDisabledException -> R.string.error_feature_disabled + is FeatureNotAvailableException -> R.string.error_feature_not_available + is VulcanException -> R.string.error_unknown_uonet + is ScrapperException -> R.string.error_unknown_app + is SSLHandshakeException -> when { + error.isCausedByCertificateNotValidNow() -> R.string.error_invalid_device_datetime + else -> R.string.error_timeout + } + else -> R.string.error_unknown +}.let { getString(it) } + +fun Throwable.isShouldBeReported(): Boolean = when (this) { + is UnknownHostException, + is SocketException, + is SocketTimeoutException, + is InterruptedIOException, + is ConnectException, + is StreamResetException, + is ServiceUnavailableException, + is FeatureDisabledException, + is FeatureNotAvailableException -> false + is SSLHandshakeException -> when { + isCausedByCertificateNotValidNow() -> false + else -> true + } + else -> true +} + +private fun Throwable?.isCausedByCertificateNotValidNow(): Boolean { + var exception = this + do { + if (exception.isCertificateNotValidNow()) return true + + exception = exception?.cause + } while (exception != null) + return false +} + +private fun Throwable?.isCertificateNotValidNow(): Boolean { + val isNotYetValid = this is CertificateNotYetValidException + val isExpired = this is CertificateExpiredException + return isNotYetValid || isExpired +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/FragNavControlerExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/FragNavControlerExtension.kt new file mode 100644 index 000000000..01c876dd2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/FragNavControlerExtension.kt @@ -0,0 +1,24 @@ +package io.github.wulkanowy.utils + +import androidx.fragment.app.Fragment +import com.ncapdevi.fragnav.FragNavController +import io.github.wulkanowy.ui.base.BaseView + +inline fun FragNavController.setOnViewChangeListener(crossinline listener: (view: BaseView) -> Unit) { + transactionListener = object : FragNavController.TransactionListener { + override fun onFragmentTransaction( + fragment: Fragment?, + transactionType: FragNavController.TransactionType + ) { + fragment?.let { listener(it as BaseView) } + } + + override fun onTabTransaction(fragment: Fragment?, index: Int) { + fragment?.let { listener(it as BaseView) } + } + } +} + +fun FragNavController.safelyPopFragments(depth: Int) { + if (!isRootFragment) popFragments(depth) +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt new file mode 100644 index 000000000..ff65d6376 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt @@ -0,0 +1,86 @@ +package io.github.wulkanowy.utils + +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.data.db.entities.GradeSummary +import io.github.wulkanowy.data.enums.GradeColorTheme +import io.github.wulkanowy.sdk.scrapper.grades.isGradeValid + +fun List.calcAverage(isOptionalArithmeticAverage: Boolean): Double { + val isArithmeticAverage = isOptionalArithmeticAverage && !any { it.weightValue != .0 } + var counter = 0.0 + var denominator = 0.0 + + forEach { + val weight = if (isArithmeticAverage && isGradeValid(it.entry)) 1.0 else it.weightValue + counter += (it.value + it.modifier) * weight + denominator += weight + } + return if (denominator != 0.0) counter / denominator else 0.0 +} + +fun List.calcFinalAverage(plusModifier: Double, minusModifier: Double) = asSequence() + .mapNotNull { + if (it.finalGrade.matches("[0-6][+-]?".toRegex())) { + when { + it.finalGrade.endsWith('+') -> { + it.finalGrade.removeSuffix("+").toDouble() + plusModifier + } + it.finalGrade.endsWith('-') -> { + it.finalGrade.removeSuffix("-").toDouble() - minusModifier + } + else -> { + it.finalGrade.toDouble() + } + } + } else null + } + .average() + .let { if (it.isNaN()) 0.0 else it } + +fun Grade.getGradeColor() = when (color) { + "000000" -> R.color.grade_black + "F04C4C" -> R.color.grade_red + "20A4F7" -> R.color.grade_blue + "6ECD07" -> R.color.grade_green + "B16CF1" -> R.color.grade_purple + else -> R.color.grade_material_default +} + +inline val Grade.colorStringId: Int + get() = when (color) { + "000000" -> R.string.all_black + "F04C4C" -> R.string.all_red + "20A4F7" -> R.string.all_blue + "6ECD07" -> R.string.all_green + "B16CF1" -> R.string.all_purple + else -> R.string.all_empty_color + } + +fun Grade.changeModifier(plusModifier: Double, minusModifier: Double) = when { + modifier > 0 -> copy(modifier = plusModifier) + modifier < 0 -> copy(modifier = -minusModifier) + else -> this +} + +fun Grade.getBackgroundColor(theme: GradeColorTheme) = when (theme) { + GradeColorTheme.GRADE_COLOR -> getGradeColor() + GradeColorTheme.MATERIAL -> when (value.toInt()) { + 6 -> R.color.grade_material_six + 5 -> R.color.grade_material_five + 4 -> R.color.grade_material_four + 3 -> R.color.grade_material_three + 2 -> R.color.grade_material_two + 1 -> R.color.grade_material_one + else -> R.color.grade_material_default + } + GradeColorTheme.VULCAN -> when (value.toInt()) { + 6 -> R.color.grade_vulcan_six + 5 -> R.color.grade_vulcan_five + 4 -> R.color.grade_vulcan_four + 3 -> R.color.grade_vulcan_three + 2 -> R.color.grade_vulcan_two + 1 -> R.color.grade_vulcan_one + else -> R.color.grade_vulcan_default + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/IntentUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/IntentUtils.kt new file mode 100644 index 000000000..1ef03f2e6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/IntentUtils.kt @@ -0,0 +1,100 @@ +package io.github.wulkanowy.utils + +import android.content.ActivityNotFoundException +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.provider.CalendarContract +import io.github.wulkanowy.BuildConfig +import java.time.LocalDateTime +import java.time.ZoneId + +fun Context.openInternetBrowser(uri: String, onActivityNotFound: (uri: String) -> Unit = {}) { + Intent.parseUri(uri, 0).let { + try { + startActivity(it) + } catch (e: ActivityNotFoundException) { + onActivityNotFound(uri) + } + } +} + +fun Context.openAppInMarket(onActivityNotFound: (uri: String) -> Unit) { + openInternetBrowser("market://details?id=${BuildConfig.APPLICATION_ID}") { + openInternetBrowser("https://github.com/wulkanowy/wulkanowy/releases", onActivityNotFound) + } +} + +fun Context.openEmailClient( + chooserTitle: String, + email: String, + subject: String, + body: String, + onActivityNotFound: () -> Unit = {} +) { + val intent = Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:")).apply { + putExtra(Intent.EXTRA_EMAIL, arrayOf(email)) + putExtra(Intent.EXTRA_SUBJECT, subject) + putExtra(Intent.EXTRA_TEXT, body) + } + + if (intent.resolveActivity(packageManager) != null) { + startActivity(Intent.createChooser(intent, chooserTitle)) + } else onActivityNotFound() +} + +fun Context.openCalendarEventAdd( + title: String, + description: String, + start: LocalDateTime, + end: LocalDateTime? = null, + isAllDay: Boolean = false, + onActivityNotFound: (uri: String?) -> Unit = {}, +) { + val beginTime = start.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() + val endTime = end?.atZone(ZoneId.systemDefault())?.toInstant()?.toEpochMilli() + + val intent = Intent(Intent.ACTION_INSERT) + .setData(CalendarContract.Events.CONTENT_URI) + .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime) + .putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime) + .putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, isAllDay) + .putExtra(CalendarContract.Events.TITLE, title) + .putExtra(CalendarContract.Events.DESCRIPTION, description) + .putExtra(CalendarContract.Events.AVAILABILITY, CalendarContract.Events.AVAILABILITY_BUSY) + + try { + startActivity(intent) + } catch (e: ActivityNotFoundException) { + onActivityNotFound(intent.dataString) + } +} + +fun Context.openNavigation(location: String) { + val intentUri = Uri.parse("geo:0,0?q=${Uri.encode(location)}") + val intent = Intent(Intent.ACTION_VIEW, intentUri) + if (intent.resolveActivity(packageManager) != null) { + startActivity(intent) + } +} + +fun Context.openDialer(phone: String) { + val intentUri = Uri.parse("tel:$phone") + val intent = Intent(Intent.ACTION_DIAL, intentUri) + if (intent.resolveActivity(packageManager) != null) { + startActivity(intent) + } +} + +fun Context.shareText(text: String, subject: String?) { + val sendIntent: Intent = Intent().apply { + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_TEXT, text) + if (subject != null) { + putExtra(Intent.EXTRA_SUBJECT, subject) + } + type = "text/plain" + } + val shareIntent = Intent.createChooser(sendIntent, null) + startActivity(shareIntent) +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/KeyboardUtils.java b/app/src/main/java/io/github/wulkanowy/utils/KeyboardUtils.java deleted file mode 100644 index 6be1b76f4..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/KeyboardUtils.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.utils; - -import android.app.Activity; -import android.content.Context; -import android.view.inputmethod.InputMethodManager; -import android.widget.EditText; - -public final class KeyboardUtils { - - private KeyboardUtils() { - throw new IllegalStateException("Utility class"); - } - - public static void hideSoftInput(Activity activity) { - InputMethodManager manager = (InputMethodManager) - activity.getSystemService(Context.INPUT_METHOD_SERVICE); - if (manager != null) { - manager.hideSoftInputFromWindow(activity.getWindow() - .getDecorView().getApplicationWindowToken(), 0); - } - } - - public static void showSoftInput(EditText editText, Context context) { - editText.setFocusable(true); - editText.setFocusableInTouchMode(true); - editText.requestFocus(); - InputMethodManager inputMethodManager = (InputMethodManager) context - .getSystemService(Context.INPUT_METHOD_SERVICE); - if (inputMethodManager != null) { - inputMethodManager.showSoftInput(editText, 0); - } - } -} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/utils/LifecycleAwareVariable.kt b/app/src/main/java/io/github/wulkanowy/utils/LifecycleAwareVariable.kt new file mode 100644 index 000000000..032e2d28a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/LifecycleAwareVariable.kt @@ -0,0 +1,56 @@ +package io.github.wulkanowy.utils + +import android.os.Handler +import android.os.Looper +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +class LifecycleAwareVariable : ReadWriteProperty, DefaultLifecycleObserver { + + private var _value: T? = null + + override fun setValue(thisRef: Fragment, property: KProperty<*>, value: T) { + thisRef.viewLifecycleOwner.lifecycle.removeObserver(this) + _value = value + thisRef.viewLifecycleOwner.lifecycle.addObserver(this) + } + + override fun getValue(thisRef: Fragment, property: KProperty<*>) = _value + ?: throw IllegalStateException("Trying to call an lifecycle-aware value outside of the view lifecycle, or the value has not been initialized") + + override fun onDestroy(owner: LifecycleOwner) { + Handler(Looper.getMainLooper()).post { + _value = null + } + } +} + +class LifecycleAwareVariableActivity : ReadWriteProperty, + DefaultLifecycleObserver { + + private var _value: T? = null + + override fun setValue(thisRef: AppCompatActivity, property: KProperty<*>, value: T) { + thisRef.lifecycle.removeObserver(this) + _value = value + thisRef.lifecycle.addObserver(this) + } + + override fun getValue(thisRef: AppCompatActivity, property: KProperty<*>) = _value + ?: throw IllegalStateException("Trying to call an lifecycle-aware value outside of the view lifecycle, or the value has not been initialized") + + override fun onDestroy(owner: LifecycleOwner) { + Handler(Looper.getMainLooper()).post { + _value = null + } + } +} + +@Suppress("unused") +fun Fragment.lifecycleAwareVariable() = LifecycleAwareVariable() + +fun lifecycleAwareVariable() = LifecycleAwareVariableActivity() diff --git a/app/src/main/java/io/github/wulkanowy/utils/ListExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ListExtension.kt new file mode 100644 index 000000000..4374aeb41 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/ListExtension.kt @@ -0,0 +1,9 @@ +package io.github.wulkanowy.utils + +infix fun List.uniqueSubtract(other: List): List { + val list = toMutableList() + other.forEach { + list.remove(it) + } + return list.toList() +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/LogUtils.java b/app/src/main/java/io/github/wulkanowy/utils/LogUtils.java deleted file mode 100644 index f59bbf641..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/LogUtils.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.utils; - -import android.util.Log; - -public final class LogUtils { - - private LogUtils() { - throw new IllegalStateException("Utility class"); - } - - public static void debug(String message) { - Log.d(AppConstant.APP_NAME, message); - } - - public static void error(String message, Throwable throwable) { - Log.e(AppConstant.APP_NAME, message, throwable); - } - - public static void error(String message) { - Log.e(AppConstant.APP_NAME, message); - } - - public static void info(String message) { - Log.i(AppConstant.APP_NAME, message); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt new file mode 100644 index 000000000..1e9f49a66 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt @@ -0,0 +1,137 @@ +package io.github.wulkanowy.utils + +import android.app.Activity +import android.app.Application +import android.content.Context +import android.os.Bundle +import android.view.View +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import fr.bipi.tressence.common.filters.Filter +import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException +import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException +import timber.log.Timber +import java.io.InterruptedIOException +import java.net.SocketTimeoutException +import java.net.UnknownHostException +import javax.inject.Inject +import javax.inject.Singleton + +class DebugLogTree : Timber.DebugTree() { + + override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { + super.log(priority, "Wulkanowy", message, t) + } +} + +object ExceptionFilter : Filter { + + override fun isLoggable(priority: Int, tag: String?) = true + + override fun skipLog(priority: Int, tag: String?, message: String, t: Throwable?) = + when (t) { + is FeatureDisabledException, + is FeatureNotAvailableException, + is UnknownHostException, + is SocketTimeoutException, + is InterruptedIOException -> true + else -> false + } +} + +class ActivityLifecycleLogger : Application.ActivityLifecycleCallbacks { + + override fun onActivityPaused(activity: Activity) { + Timber.d("${activity::class.java.simpleName} PAUSED") + } + + override fun onActivityResumed(activity: Activity) { + Timber.d("${activity::class.java.simpleName} RESUMED") + } + + override fun onActivityStarted(activity: Activity) { + Timber.d("${activity::class.java.simpleName} STARTED") + } + + override fun onActivityDestroyed(activity: Activity) { + Timber.d("${activity::class.java.simpleName} DESTROYED") + } + + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) { + Timber.d("${activity::class.java.simpleName} SAVED INSTANCE STATE") + } + + override fun onActivityStopped(activity: Activity) { + Timber.d("${activity::class.java.simpleName} STOPPED") + } + + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { + Timber.d("${activity::class.java.simpleName} CREATED ${savedInstanceState.checkSavedState()}") + } +} + +@Singleton +class FragmentLifecycleLogger @Inject constructor() : + FragmentManager.FragmentLifecycleCallbacks() { + + override fun onFragmentViewCreated( + fm: FragmentManager, + f: Fragment, + v: View, + savedInstanceState: Bundle? + ) { + Timber.d("${f::class.java.simpleName} VIEW CREATED ${savedInstanceState.checkSavedState()}") + } + + override fun onFragmentStopped(fm: FragmentManager, f: Fragment) { + Timber.d("${f::class.java.simpleName} STOPPED") + } + + override fun onFragmentCreated( + fm: FragmentManager, + f: Fragment, + savedInstanceState: Bundle? + ) { + Timber.d("${f::class.java.simpleName} CREATED ${savedInstanceState.checkSavedState()}") + } + + override fun onFragmentResumed(fm: FragmentManager, f: Fragment) { + Timber.d("${f::class.java.simpleName} RESUMED") + } + + override fun onFragmentAttached(fm: FragmentManager, f: Fragment, context: Context) { + Timber.d("${f::class.java.simpleName} ATTACHED") + } + + override fun onFragmentDestroyed(fm: FragmentManager, f: Fragment) { + Timber.d("${f::class.java.simpleName} DESTROYED") + } + + override fun onFragmentSaveInstanceState( + fm: FragmentManager, + f: Fragment, + outState: Bundle + ) { + Timber.d("${f::class.java.simpleName} SAVED INSTANCE STATE") + } + + override fun onFragmentStarted(fm: FragmentManager, f: Fragment) { + Timber.d("${f::class.java.simpleName} STARTED") + } + + override fun onFragmentViewDestroyed(fm: FragmentManager, f: Fragment) { + Timber.d("${f::class.java.simpleName} VIEW DESTROYED") + } + + override fun onFragmentPaused(fm: FragmentManager, f: Fragment) { + Timber.d("${f::class.java.simpleName} PAUSED") + } + + override fun onFragmentDetached(fm: FragmentManager, f: Fragment) { + Timber.d("${f::class.java.simpleName} DETACHED") + } +} + +private fun Bundle?.checkSavedState() = + if (this == null) "(STATE IS NULL)" else "(RESTORE STATE)" + diff --git a/app/src/main/java/io/github/wulkanowy/utils/MaterialDatePickerUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/MaterialDatePickerUtils.kt new file mode 100644 index 000000000..09ccda899 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/MaterialDatePickerUtils.kt @@ -0,0 +1,50 @@ +package io.github.wulkanowy.utils + +import androidx.fragment.app.Fragment +import com.google.android.material.datepicker.CalendarConstraints +import com.google.android.material.datepicker.MaterialDatePicker +import kotlinx.parcelize.Parcelize +import java.time.LocalDate +import java.time.temporal.ChronoUnit + +fun Fragment.openMaterialDatePicker( + selected: LocalDate, + rangeStart: LocalDate, + rangeEnd: LocalDate, + onDateSelected: (LocalDate) -> Unit, +) { + val constraintsBuilder = CalendarConstraints.Builder().apply { + setValidator(CalendarDayRangeValidator(rangeStart, rangeEnd)) + setStart(rangeStart.toTimestamp()) + setEnd(rangeEnd.toTimestamp()) + } + + val datePicker = MaterialDatePicker.Builder.datePicker() + .setCalendarConstraints(constraintsBuilder.build()) + .setSelection(selected.toTimestamp()) + .build() + + datePicker.addOnPositiveButtonClickListener { + val date = it.toLocalDateTime().toLocalDate() + onDateSelected(date) + } + + if (!parentFragmentManager.isStateSaved) { + datePicker.show(parentFragmentManager, null) + } +} + +@Parcelize +private class CalendarDayRangeValidator( + val start: LocalDate, + val end: LocalDate, +) : CalendarConstraints.DateValidator { + + override fun isValid(dateLong: Long): Boolean { + val date = dateLong.toLocalDateTime().toLocalDate() + val daysUntilEnd = date.until(end, ChronoUnit.DAYS) + val daysUntilStart = date.until(start, ChronoUnit.DAYS) + + return daysUntilStart <= 0 && daysUntilEnd >= 0 + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/NetworkUtils.java b/app/src/main/java/io/github/wulkanowy/utils/NetworkUtils.java deleted file mode 100644 index a1484eb9e..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/NetworkUtils.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.wulkanowy.utils; - -import android.content.Context; -import android.net.ConnectivityManager; - -public final class NetworkUtils { - - private NetworkUtils() { - throw new IllegalStateException("Utility class"); - } - - public static boolean isOnline(Context context) { - ConnectivityManager connectivityManager = - (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - return connectivityManager != null && connectivityManager.getActiveNetworkInfo() != null - && connectivityManager.getActiveNetworkInfo().isConnectedOrConnecting(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/PendingIntentCompat.kt b/app/src/main/java/io/github/wulkanowy/utils/PendingIntentCompat.kt new file mode 100644 index 000000000..45ee431a2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/PendingIntentCompat.kt @@ -0,0 +1,11 @@ +package io.github.wulkanowy.utils + +import android.app.PendingIntent +import android.os.Build + +object PendingIntentCompat { + + val FLAG_IMMUTABLE = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + PendingIntent.FLAG_IMMUTABLE + } else 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt new file mode 100644 index 000000000..c69fec65c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt @@ -0,0 +1,51 @@ +package io.github.wulkanowy.utils + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.SharedPrefProvider +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.enums.MessageFolder +import timber.log.Timber +import java.time.Duration.ofMinutes +import java.time.Instant +import java.time.LocalDate +import javax.inject.Inject + +fun getRefreshKey(name: String, semester: Semester, start: LocalDate, end: LocalDate): String { + return "${name}_${semester.studentId}_${semester.semesterId}_${start.monday}_${end.sunday}" +} + +fun getRefreshKey(name: String, semester: Semester): String { + return "${name}_${semester.studentId}_${semester.semesterId}" +} + +fun getRefreshKey(name: String, student: Student): String { + return "${name}_${student.userLoginId}" +} + +fun getRefreshKey(name: String, student: Student, folder: MessageFolder): String { + return "${name}_${student.id}_${folder.id}" +} + +class AutoRefreshHelper @Inject constructor( + @ApplicationContext private val context: Context, + private val sharedPref: SharedPrefProvider +) { + + fun shouldBeRefreshed(key: String): Boolean { + val timestamp = sharedPref.getLong(key, 0).let(Instant::ofEpochMilli) + val servicesInterval = sharedPref.getString(context.getString(R.string.pref_key_services_interval), context.getString(R.string.pref_default_services_interval)).toLong() + + val shouldBeRefreshed = timestamp < Instant.now().minus(ofMinutes(servicesInterval)) + + Timber.d("Check if $key need to be refreshed: $shouldBeRefreshed (last refresh: $timestamp, interval: $servicesInterval min)") + + return shouldBeRefreshed + } + + fun updateLastRefreshTimestamp(key: String) { + sharedPref.putLong(key, Instant.now().toEpochMilli()) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/RootChecker.java b/app/src/main/java/io/github/wulkanowy/utils/RootChecker.java deleted file mode 100644 index 9c23c217f..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/RootChecker.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.wulkanowy.utils; - -import android.os.Build; - -import java.io.File; - -public final class RootChecker { - - private RootChecker() { - throw new IllegalStateException("Utility class"); - } - - public static boolean isRooted() { - return checkOne() || checkTwo() || checkThree(); - } - - private static boolean checkOne() { - return Build.TAGS != null && Build.TAGS.contains("test-keys"); - } - - private static boolean checkTwo() { - return new File("/system/app/Superuser.apk").exists(); - } - - private static boolean checkThree() { - String[] commands = {"/system/xbin/which su", "/system/bin/which su", "which su"}; - for (String command : commands) { - try { - Runtime.getRuntime().exec(command); - return true; - } catch (Exception e) { - // ignore - } - } - return false; - } - -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/SdkExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/SdkExtension.kt new file mode 100644 index 000000000..63a30db8c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/SdkExtension.kt @@ -0,0 +1,31 @@ +package io.github.wulkanowy.utils + +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.sdk.Sdk +import timber.log.Timber + +fun Sdk.init(student: Student): Sdk { + email = student.email + password = student.password + symbol = student.symbol + schoolSymbol = student.schoolSymbol + studentId = student.studentId + classId = student.classId + + if (Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { + scrapperBaseUrl = student.scrapperBaseUrl + loginType = Sdk.ScrapperLoginType.valueOf(student.loginType) + } + loginId = student.userLoginId + + mode = Sdk.Mode.valueOf(student.loginMode) + mobileBaseUrl = student.mobileBaseUrl + certKey = student.certificateKey + privateKey = student.privateKey + + emptyCookieJarInterceptor = true + + Timber.d("Sdk in ${student.loginMode} mode reinitialized") + + return this +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/SemesterExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/SemesterExtension.kt new file mode 100644 index 000000000..6e11a8b2c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/SemesterExtension.kt @@ -0,0 +1,19 @@ +package io.github.wulkanowy.utils + +import io.github.wulkanowy.data.db.entities.Semester +import java.time.LocalDate.now + +inline val Semester.isCurrent: Boolean + get() = now() in start..end + +fun List.getCurrentOrLast(): Semester { + if (isEmpty()) throw RuntimeException("Empty semester list") + + // when there is only one current semester + singleOrNull { it.isCurrent }?.let { return it } + + // when there is more than one current semester - find one with higher id + singleOrNull { semester -> semester.semesterId == maxByOrNull { it.semesterId }?.semesterId }?.let { return it } + + throw IllegalArgumentException("Duplicated last semester! Semesters: ${joinToString(separator = "\n")}") +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/SpinnerExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/SpinnerExtension.kt new file mode 100644 index 000000000..1f7216906 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/SpinnerExtension.kt @@ -0,0 +1,23 @@ +package io.github.wulkanowy.utils + +import android.view.View +import android.widget.AdapterView +import android.widget.Spinner + +/** + * @see How to keep onItemSelected from firing off on a newly instantiated Spinner? + */ +@Suppress("UNCHECKED_CAST") +inline fun Spinner.setOnItemSelectedListener(crossinline listener: (view: T?) -> Unit) { + onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onNothingSelected(parent: AdapterView<*>?) {} + override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { + onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onNothingSelected(parent: AdapterView<*>?) {} + override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { + listener(view as T?) + } + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/StringExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/StringExtension.kt new file mode 100644 index 000000000..bddd7df4c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/StringExtension.kt @@ -0,0 +1,7 @@ +package io.github.wulkanowy.utils + +inline fun String?.ifNullOrBlank(defaultValue: () -> String) = + if (isNullOrBlank()) defaultValue() else this + +fun String.capitalise() = + replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() } \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/utils/StudentExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/StudentExtension.kt new file mode 100644 index 000000000..fdd0610a0 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/StudentExtension.kt @@ -0,0 +1,5 @@ +package io.github.wulkanowy.utils + +import io.github.wulkanowy.data.db.entities.Student + +inline val Student.nickOrName get() = if (nick.isBlank()) studentName else nick diff --git a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt new file mode 100644 index 000000000..e7a50d0c3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt @@ -0,0 +1,125 @@ +package io.github.wulkanowy.utils + +import java.text.SimpleDateFormat +import java.time.* +import java.time.DayOfWeek.* +import java.time.format.DateTimeFormatter +import java.time.temporal.TemporalAdjusters.* +import java.util.* + +private const val DEFAULT_DATE_PATTERN = "dd.MM.yyyy" + +fun LocalDate.toTimestamp(): Long = atStartOfDay() + .toInstant(ZoneOffset.UTC) + .toEpochMilli() + +fun Long.toLocalDateTime(): LocalDateTime = LocalDateTime.ofInstant( + Instant.ofEpochMilli(this), ZoneOffset.UTC +) + +fun Instant.toLocalDate(): LocalDate = atZone(ZoneOffset.UTC).toLocalDate() + +fun String.toLocalDate(format: String = DEFAULT_DATE_PATTERN): LocalDate = + LocalDate.parse(this, DateTimeFormatter.ofPattern(format)) + +fun LocalDate.toFormattedString(pattern: String = DEFAULT_DATE_PATTERN): String = + format(DateTimeFormatter.ofPattern(pattern)) + +fun Instant.toFormattedString( + pattern: String = DEFAULT_DATE_PATTERN, + tz: ZoneId = ZoneId.systemDefault() +): String = atZone(tz).format(DateTimeFormatter.ofPattern(pattern)) + +fun Month.getFormattedName(): String { + val formatter = SimpleDateFormat("LLLL", Locale.getDefault()) + + val date = LocalDateTime.now().withMonth(value) + return formatter.format(date.toInstant(ZoneOffset.UTC).toEpochMilli()).capitalise() +} + +inline val LocalDate.nextSchoolDay: LocalDate + get() { + return when (dayOfWeek) { + FRIDAY, SATURDAY, SUNDAY -> with(next(MONDAY)) + else -> plusDays(1) + } + } + +inline val LocalDate.previousSchoolDay: LocalDate + get() { + return when (dayOfWeek) { + SATURDAY, SUNDAY, MONDAY -> with(previous(FRIDAY)) + else -> minusDays(1) + } + } + +inline val LocalDate.nextOrSameSchoolDay: LocalDate + get() { + return when (dayOfWeek) { + SATURDAY, SUNDAY -> with(next(MONDAY)) + else -> this + } + } + +inline val LocalDate.startExamsDay: LocalDate + get() = nextOrSameSchoolDay.monday + +inline val LocalDate.endExamsDay: LocalDate + get() = nextOrSameSchoolDay.monday.plusWeeks(4).minusDays(1) + +inline val LocalDate.previousOrSameSchoolDay: LocalDate + get() { + return when (dayOfWeek) { + SATURDAY, SUNDAY -> with(previous(FRIDAY)) + else -> this + } + } + +inline val LocalDate.weekDayName: String + get() = format(DateTimeFormatter.ofPattern("EEEE", Locale.getDefault())) + +inline val LocalDate.monday: LocalDate get() = with(MONDAY) + +inline val LocalDate.sunday: LocalDate get() = with(SUNDAY) + +/** + * [Dz.U. 2016 poz. 1335](http://prawo.sejm.gov.pl/isap.nsf/DocDetails.xsp?id=WDU20160001335) + */ +val LocalDate.isHolidays: Boolean + get() = isBefore(firstSchoolDayInCalendarYear) && isAfter(lastSchoolDayInCalendarYear) + +val LocalDate.firstSchoolDayInSchoolYear: LocalDate + get() = withYear(if (this.monthValue <= 6) this.year - 1 else this.year).firstSchoolDayInCalendarYear + +val LocalDate.lastSchoolDayInSchoolYear: LocalDate + get() = withYear(if (this.monthValue > 6) this.year + 1 else this.year).lastSchoolDayInCalendarYear + +fun LocalDate.getLastSchoolDayIfHoliday(schoolYear: Int): LocalDate { + val date = LocalDate.of(schoolYear.getSchoolYearByMonth(monthValue), monthValue, dayOfMonth) + + if (date.isHolidays) { + return date.lastSchoolDayInCalendarYear + } + + return date +} + +private fun Int.getSchoolYearByMonth(monthValue: Int): Int { + return when (monthValue) { + in 9..12 -> this + else -> this + 1 + } +} + +private inline val LocalDate.firstSchoolDayInCalendarYear: LocalDate + get() = LocalDate.of(year, 9, 1).run { + when (dayOfWeek) { + FRIDAY, SATURDAY, SUNDAY -> with(firstInMonth(MONDAY)) + else -> this + } + } + +private inline val LocalDate.lastSchoolDayInCalendarYear: LocalDate + get() = LocalDate.of(year, 6, 20) + .with(next(FRIDAY)) + diff --git a/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.java b/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.java deleted file mode 100644 index 717ed42a9..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.java +++ /dev/null @@ -1,89 +0,0 @@ -package io.github.wulkanowy.utils; - -import org.joda.time.DateTime; -import org.joda.time.DateTimeConstants; -import org.joda.time.LocalDate; - -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.List; -import java.util.Locale; -import java.util.TimeZone; - -import static io.github.wulkanowy.utils.AppConstant.DATE_PATTERN; - -public final class TimeUtils { - - private static final long TICKS_AT_EPOCH = 621355968000000000L; - - private static final long TICKS_PER_MILLISECOND = 10000; - - private TimeUtils() { - throw new IllegalStateException("Utility class"); - } - - public static long getNetTicks(Date date) { - Calendar calendar = Calendar.getInstance(); - calendar.setTime(date); - - return (calendar.getTimeInMillis() * TICKS_PER_MILLISECOND) + TICKS_AT_EPOCH; - } - - public static long getNetTicks(String dateString) throws ParseException { - return getNetTicks(dateString, DATE_PATTERN); - } - - public static long getNetTicks(String dateString, String dateFormat) throws ParseException { - SimpleDateFormat format = new SimpleDateFormat(dateFormat, Locale.ROOT); - format.setTimeZone(TimeZone.getTimeZone("UTC")); - Date dateObject = format.parse(dateString); - - return getNetTicks(dateObject); - } - - public static Date getDate(long netTicks) { - return new Date((netTicks - TICKS_AT_EPOCH) / TICKS_PER_MILLISECOND); - } - - public static List getMondaysFromCurrentSchoolYear() { - LocalDate startDate = new LocalDate(getCurrentSchoolYear(), 9, 1); - LocalDate endDate = new LocalDate(getCurrentSchoolYear() + 1, 8, 31); - - List dateList = new ArrayList<>(); - - LocalDate thisMonday = startDate.withDayOfWeek(DateTimeConstants.MONDAY); - - if (startDate.isAfter(thisMonday)) { - startDate = thisMonday.plusWeeks(1); - } else { - startDate = thisMonday; - } - - while (startDate.isBefore(endDate)) { - dateList.add(startDate.toString(DATE_PATTERN)); - startDate = startDate.plusWeeks(1); - } - return dateList; - } - - public static int getCurrentSchoolYear() { - DateTime dateTime = new DateTime(); - return dateTime.getMonthOfYear() <= 8 ? dateTime.getYear() - 1 : dateTime.getYear(); - } - - public static String getDateOfCurrentMonday(boolean normalize) { - DateTime currentDate = new DateTime(); - - if (currentDate.getDayOfWeek() == DateTimeConstants.SATURDAY && normalize) { - currentDate = currentDate.plusDays(2); - } else if (currentDate.getDayOfWeek() == DateTimeConstants.SUNDAY && normalize) { - currentDate = currentDate.plusDays(1); - } else { - currentDate = currentDate.withDayOfWeek(DateTimeConstants.MONDAY); - } - return currentDate.toString(DATE_PATTERN); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/TimetableExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/TimetableExtension.kt new file mode 100644 index 000000000..3e94463b9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/TimetableExtension.kt @@ -0,0 +1,29 @@ +package io.github.wulkanowy.utils + +import io.github.wulkanowy.data.db.entities.Timetable +import java.time.Duration +import java.time.Duration.between +import java.time.Instant +import java.time.Instant.now + +fun Timetable.isShowTimeUntil(previousLessonEnd: Instant?) = when { + !isStudentPlan -> false + canceled -> false + now().isAfter(start) -> false + previousLessonEnd != null && now().isBefore(previousLessonEnd) -> false + else -> between(now(), start) <= Duration.ofMinutes(60) +} + +inline val Timetable.left: Duration? + get() = when { + canceled -> null + !isStudentPlan -> null + end >= now() && start <= now() -> between(now(), end) + else -> null + } + +inline val Timetable.until: Duration + get() = between(now(), start) + +inline val Timetable.isJustFinished: Boolean + get() = end.isBefore(now()) && end.plusSeconds(15).isAfter(now()) && !canceled diff --git a/app/src/main/java/io/github/wulkanowy/utils/ViewPagerExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ViewPagerExtension.kt new file mode 100644 index 000000000..700ac2f1d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/ViewPagerExtension.kt @@ -0,0 +1,11 @@ +package io.github.wulkanowy.utils + +import androidx.viewpager2.widget.ViewPager2 + +inline fun ViewPager2.setOnSelectPageListener(crossinline selectListener: (position: Int) -> Unit) { + registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + selectListener(position) + } + }) +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/async/AbstractTask.java b/app/src/main/java/io/github/wulkanowy/utils/async/AbstractTask.java deleted file mode 100644 index 5106d6417..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/async/AbstractTask.java +++ /dev/null @@ -1,63 +0,0 @@ -package io.github.wulkanowy.utils.async; - -import android.os.AsyncTask; - -import io.github.wulkanowy.utils.LogUtils; - -public class AbstractTask extends AsyncTask { - - private Exception exception; - - private AsyncListeners.OnRefreshListener onRefreshListener; - - private AsyncListeners.OnFirstLoadingListener onFirstLoadingListener; - - public void setOnFirstLoadingListener(AsyncListeners.OnFirstLoadingListener onFirstLoadingListener) { - this.onFirstLoadingListener = onFirstLoadingListener; - } - - public void setOnRefreshListener(AsyncListeners.OnRefreshListener onRefreshListener) { - this.onRefreshListener = onRefreshListener; - } - - @Override - protected Boolean doInBackground(Void... voids) { - try { - if (onFirstLoadingListener != null) { - onFirstLoadingListener.onDoInBackgroundLoading(); - } else if (onRefreshListener != null) { - onRefreshListener.onDoInBackgroundRefresh(); - } else { - LogUtils.error("AbstractTask does not have a listener assigned"); - } - return true; - } catch (Exception e) { - exception = e; - return false; - } - } - - @Override - protected void onCancelled() { - super.onCancelled(); - if (onFirstLoadingListener != null) { - onFirstLoadingListener.onCanceledLoadingAsync(); - } else if (onRefreshListener != null) { - onRefreshListener.onCanceledRefreshAsync(); - } else { - LogUtils.error("AbstractTask does not have a listener assigned"); - } - } - - @Override - protected void onPostExecute(Boolean result) { - super.onPostExecute(result); - if (onFirstLoadingListener != null) { - onFirstLoadingListener.onEndLoadingAsync(result, exception); - } else if (onRefreshListener != null) { - onRefreshListener.onEndRefreshAsync(result, exception); - } else { - LogUtils.error("AbstractTask does not have a listener assigned"); - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/async/AsyncListeners.java b/app/src/main/java/io/github/wulkanowy/utils/async/AsyncListeners.java deleted file mode 100644 index 0d33c57cf..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/async/AsyncListeners.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.wulkanowy.utils.async; - -public interface AsyncListeners { - - interface OnRefreshListener { - - void onDoInBackgroundRefresh() throws Exception; - - void onCanceledRefreshAsync(); - - void onEndRefreshAsync(boolean result, Exception exception); - - } - - interface OnFirstLoadingListener { - - void onDoInBackgroundLoading() throws Exception; - - void onCanceledLoadingAsync(); - - void onEndLoadingAsync(boolean result, Exception exception); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/security/CryptoException.java b/app/src/main/java/io/github/wulkanowy/utils/security/CryptoException.java deleted file mode 100644 index 1ee4a9fa8..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/security/CryptoException.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.github.wulkanowy.utils.security; - - -public class CryptoException extends Exception { - - public CryptoException(String message) { - super(message); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.java b/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.java deleted file mode 100644 index 93da1d0bd..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.java +++ /dev/null @@ -1,184 +0,0 @@ -package io.github.wulkanowy.utils.security; - - -import android.annotation.TargetApi; -import android.content.Context; -import android.os.Build; -import android.security.KeyPairGeneratorSpec; -import android.security.keystore.KeyGenParameterSpec; -import android.security.keystore.KeyProperties; -import android.util.Base64; - -import org.apache.commons.lang3.ArrayUtils; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.math.BigInteger; -import java.security.KeyPairGenerator; -import java.security.KeyStore; -import java.security.interfaces.RSAPublicKey; -import java.security.spec.AlgorithmParameterSpec; -import java.util.ArrayList; -import java.util.Calendar; - -import javax.crypto.Cipher; -import javax.crypto.CipherInputStream; -import javax.crypto.CipherOutputStream; -import javax.security.auth.x500.X500Principal; - -import io.github.wulkanowy.utils.LogUtils; -import io.github.wulkanowy.utils.RootChecker; - -public final class Scrambler { - - private static final String ANDROID_KEYSTORE = "AndroidKeyStore"; - - private static KeyStore keyStore; - - private Scrambler() { - throw new IllegalStateException("Utility class"); - } - - public static String encrypt(String email, String plainText, Context context) - throws CryptoException { - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - loadKeyStore(); - generateNewKey(email, context); - return encryptString(email, plainText); - } else { - if (RootChecker.isRooted()) { - return new String(Base64.encode(plainText.getBytes(), Base64.DEFAULT)); - } else { - throw new UnsupportedOperationException("Stored data in this devices " + - "isn't safe because android is rooted"); - } - } - } - - public static String decrypt(String email, String encryptedText) throws CryptoException { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - loadKeyStore(); - return decryptString(email, encryptedText); - } else { - return new String(Base64.decode(encryptedText, Base64.DEFAULT)); - } - } - - private static void loadKeyStore() throws CryptoException { - try { - keyStore = KeyStore.getInstance(ANDROID_KEYSTORE); - keyStore.load(null); - } catch (Exception e) { - throw new CryptoException(e.getMessage()); - } - } - - @SuppressWarnings("deprecation") - @TargetApi(18) - private static void generateNewKey(String alias, Context context) throws CryptoException { - - Calendar start = Calendar.getInstance(); - Calendar end = Calendar.getInstance(); - - AlgorithmParameterSpec spec; - - end.add(Calendar.YEAR, 10); - if (!"".equals(alias)) { - try { - if (!keyStore.containsAlias(alias)) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - spec = new KeyGenParameterSpec.Builder(alias, - KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) - .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1) - .setDigests(KeyProperties.DIGEST_SHA256) - .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1) - .setCertificateNotBefore(start.getTime()) - .setCertificateNotAfter(end.getTime()) - .build(); - - } else { - spec = new KeyPairGeneratorSpec.Builder(context) - .setAlias(alias) - .setSubject(new X500Principal("CN=" + alias)) - .setSerialNumber(BigInteger.TEN) - .setStartDate(start.getTime()) - .setEndDate(end.getTime()) - .build(); - } - - KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", - ANDROID_KEYSTORE); - keyPairGenerator.initialize(spec); - keyPairGenerator.generateKeyPair(); - } - } catch (Exception e) { - throw new CryptoException(e.getMessage()); - } - } else { - throw new CryptoException("GenerateNewKey - String is empty"); - } - - LogUtils.debug("Key pair are create"); - - } - - private static String encryptString(String alias, String text) throws CryptoException { - - if (!alias.isEmpty() && !text.isEmpty()) { - try { - KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, null); - RSAPublicKey publicKey = (RSAPublicKey) privateKeyEntry.getCertificate().getPublicKey(); - - Cipher input = Cipher.getInstance("RSA/ECB/PKCS1Padding"); - input.init(Cipher.ENCRYPT_MODE, publicKey); - - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - CipherOutputStream cipherOutputStream = new CipherOutputStream( - outputStream, input); - cipherOutputStream.write(text.getBytes("UTF-8")); - cipherOutputStream.close(); - - byte[] values = outputStream.toByteArray(); - - return Base64.encodeToString(values, Base64.DEFAULT); - - } catch (Exception e) { - throw new CryptoException(e.getMessage()); - } - } else { - throw new CryptoException("EncryptString - String is empty"); - } - } - - private static String decryptString(String alias, String text) throws CryptoException { - - if (!alias.isEmpty() && !text.isEmpty()) { - try { - KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, null); - - Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding"); - output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey()); - - CipherInputStream cipherInputStream = new CipherInputStream( - new ByteArrayInputStream(Base64.decode(text, Base64.DEFAULT)), output); - - ArrayList values = new ArrayList<>(); - - int nextByte; - - while ((nextByte = cipherInputStream.read()) != -1) { - values.add((byte) nextByte); - } - - Byte[] bytes = values.toArray(new Byte[values.size()]); - - return new String(ArrayUtils.toPrimitive(bytes), 0, bytes.length, "UTF-8"); - } catch (Exception e) { - throw new CryptoException(e.getMessage()); - } - } else { - throw new CryptoException("EncryptString - String is empty"); - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.kt b/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.kt new file mode 100644 index 000000000..c994ebab6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.kt @@ -0,0 +1,140 @@ +@file:Suppress("DEPRECATION") + +package io.github.wulkanowy.utils.security + +import android.content.Context +import android.os.Build.VERSION.SDK_INT +import android.os.Build.VERSION_CODES.M +import android.security.KeyPairGeneratorSpec +import android.security.keystore.KeyGenParameterSpec +import android.security.keystore.KeyProperties.DIGEST_SHA256 +import android.security.keystore.KeyProperties.DIGEST_SHA512 +import android.security.keystore.KeyProperties.ENCRYPTION_PADDING_RSA_OAEP +import android.security.keystore.KeyProperties.PURPOSE_DECRYPT +import android.security.keystore.KeyProperties.PURPOSE_ENCRYPT +import android.util.Base64.DEFAULT +import android.util.Base64.decode +import android.util.Base64.encode +import android.util.Base64.encodeToString +import timber.log.Timber +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.math.BigInteger +import java.nio.charset.Charset +import java.security.KeyPairGenerator +import java.security.KeyStore +import java.security.spec.MGF1ParameterSpec.SHA1 +import java.util.Calendar +import java.util.Calendar.YEAR +import javax.crypto.Cipher +import javax.crypto.Cipher.DECRYPT_MODE +import javax.crypto.Cipher.ENCRYPT_MODE +import javax.crypto.CipherInputStream +import javax.crypto.CipherOutputStream +import javax.crypto.spec.OAEPParameterSpec +import javax.crypto.spec.PSource.PSpecified +import javax.security.auth.x500.X500Principal + +private const val KEYSTORE_NAME = "AndroidKeyStore" + +private const val KEY_ALIAS = "wulkanowy_password" + +private val KEY_CHARSET = Charset.forName("UTF-8") + +private val isKeyPairExists: Boolean + get() = keyStore.getKey(KEY_ALIAS, null) != null + +private val keyStore: KeyStore + get() = KeyStore.getInstance(KEYSTORE_NAME).apply { load(null) } + +private val cipher: Cipher + get() { + return if (SDK_INT >= M) Cipher.getInstance( + "RSA/ECB/OAEPWithSHA-256AndMGF1Padding", + "AndroidKeyStoreBCWorkaround" + ) + else Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL") + } + +fun encrypt(plainText: String, context: Context): String { + if (plainText.isEmpty()) throw ScramblerException("Text to be encrypted is empty") + + return try { + if (!isKeyPairExists) generateKeyPair(context) + + cipher.let { + if (SDK_INT >= M) { + OAEPParameterSpec("SHA-256", "MGF1", SHA1, PSpecified.DEFAULT).let { spec -> + it.init(ENCRYPT_MODE, keyStore.getCertificate(KEY_ALIAS).publicKey, spec) + } + } else it.init(ENCRYPT_MODE, keyStore.getCertificate(KEY_ALIAS).publicKey) + + ByteArrayOutputStream().let { output -> + CipherOutputStream(output, it).apply { + write(plainText.toByteArray(KEY_CHARSET)) + close() + } + encodeToString(output.toByteArray(), DEFAULT) + } + } + } catch (exception: Exception) { + Timber.e(exception, "An error occurred while encrypting text") + String(encode(plainText.toByteArray(KEY_CHARSET), DEFAULT), KEY_CHARSET) + } +} + +fun decrypt(cipherText: String): String { + if (cipherText.isEmpty()) throw ScramblerException("Text to be encrypted is empty") + + return try { + if (!isKeyPairExists) throw ScramblerException("KeyPair doesn't exist") + + cipher.let { + if (SDK_INT >= M) { + OAEPParameterSpec("SHA-256", "MGF1", SHA1, PSpecified.DEFAULT).let { spec -> + it.init(DECRYPT_MODE, keyStore.getKey(KEY_ALIAS, null), spec) + } + } else it.init(DECRYPT_MODE, keyStore.getKey(KEY_ALIAS, null)) + + CipherInputStream(ByteArrayInputStream(decode(cipherText, DEFAULT)), it).let { input -> + val values = ArrayList() + var nextByte: Int + while (run { nextByte = input.read(); nextByte } != -1) { + values.add(nextByte.toByte()) + } + val bytes = ByteArray(values.size) + for (i in bytes.indices) { + bytes[i] = values[i] + } + String(bytes, 0, bytes.size, KEY_CHARSET) + } + } + } catch (e: Exception) { + throw ScramblerException("An error occurred while decrypting text", e) + } +} + +private fun generateKeyPair(context: Context) { + (if (SDK_INT >= M) { + KeyGenParameterSpec.Builder(KEY_ALIAS, PURPOSE_DECRYPT or PURPOSE_ENCRYPT) + .setDigests(DIGEST_SHA256, DIGEST_SHA512) + .setEncryptionPaddings(ENCRYPTION_PADDING_RSA_OAEP) + .setCertificateSerialNumber(BigInteger.TEN) + .setCertificateSubject(X500Principal("CN=Wulkanowy")) + .build() + } else { + KeyPairGeneratorSpec.Builder(context) + .setAlias(KEY_ALIAS) + .setSubject(X500Principal("CN=Wulkanowy")) + .setSerialNumber(BigInteger.TEN) + .setStartDate(Calendar.getInstance().time) + .setEndDate(Calendar.getInstance().apply { add(YEAR, 99) }.time) + .build() + }).let { + KeyPairGenerator.getInstance("RSA", KEYSTORE_NAME).apply { + initialize(it) + genKeyPair() + } + } + Timber.i("A new KeyPair has been generated") +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/security/ScramblerException.kt b/app/src/main/java/io/github/wulkanowy/utils/security/ScramblerException.kt new file mode 100644 index 000000000..59f830fad --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/security/ScramblerException.kt @@ -0,0 +1,6 @@ +package io.github.wulkanowy.utils.security + +class ScramblerException : Exception { + constructor(message: String, cause: Throwable) : super(message, cause) + constructor(message: String) : super(message) +} diff --git a/app/src/main/play/contact-email.txt b/app/src/main/play/contact-email.txt new file mode 100644 index 000000000..851521eb7 --- /dev/null +++ b/app/src/main/play/contact-email.txt @@ -0,0 +1 @@ +wulkanowyinc@gmail.com diff --git a/app/src/main/play/contact-phone.txt b/app/src/main/play/contact-phone.txt new file mode 100644 index 000000000..552e53a58 --- /dev/null +++ b/app/src/main/play/contact-phone.txt @@ -0,0 +1 @@ ++48733393622 diff --git a/app/src/main/play/contact-website.txt b/app/src/main/play/contact-website.txt new file mode 100644 index 000000000..53f7bee6b --- /dev/null +++ b/app/src/main/play/contact-website.txt @@ -0,0 +1 @@ +https://wulkanowy.github.io/ diff --git a/app/src/main/play/default-language.txt b/app/src/main/play/default-language.txt new file mode 100644 index 000000000..1134c4d5c --- /dev/null +++ b/app/src/main/play/default-language.txt @@ -0,0 +1 @@ +pl-PL diff --git a/app/src/main/play/listings/cs-CZ/full-description.txt b/app/src/main/play/listings/cs-CZ/full-description.txt new file mode 100644 index 000000000..1420f5d67 --- /dev/null +++ b/app/src/main/play/listings/cs-CZ/full-description.txt @@ -0,0 +1,14 @@ +Aplikace je určena pro uživatele deníku VULCAN UONET+. + +Zvýrazněné vlastnosti a funkce: +- výpočet váženého průměru, +- procentuální zobrazení docházky, +- šťastné číslo, +- náhled na další a dokončené lekce, +- tmavý motiv, +- žádné reklamy, +- offline režim, +- upozornění. + +GitHub: https://github.com/wulkanowy/wulkanowy +Discord: https://discord.gg/vccAQBr diff --git a/app/src/main/play/listings/cs-CZ/short-description.txt b/app/src/main/play/listings/cs-CZ/short-description.txt new file mode 100644 index 000000000..0f29ab1b5 --- /dev/null +++ b/app/src/main/play/listings/cs-CZ/short-description.txt @@ -0,0 +1 @@ +Neoficiální aplikace žáka a rodiče pro deníku VULCAN UONET+ diff --git a/app/src/main/play/listings/cs-CZ/title.txt b/app/src/main/play/listings/cs-CZ/title.txt new file mode 100644 index 000000000..b7f42a5b0 --- /dev/null +++ b/app/src/main/play/listings/cs-CZ/title.txt @@ -0,0 +1 @@ +Wulkanowy Deníček diff --git a/app/src/main/play/listings/pl-PL/full-description.txt b/app/src/main/play/listings/pl-PL/full-description.txt new file mode 100644 index 000000000..7da51da2d --- /dev/null +++ b/app/src/main/play/listings/pl-PL/full-description.txt @@ -0,0 +1,14 @@ +Aplikacja jest przeznaczona dla użytkowników dziennika VULCAN UONET+. + +Wyróżnione cechy i funkcje: +- obliczanie średniej ważonej, +- procentowy podgląd frekwencji, +- szczęśliwy numerek, +- podgląd lekcji dodatkowych i zrealizowanych, +- ciemny motyw. +- brak reklam, +- tryb offline, +- powiadomienia. + +GitHub: https://github.com/wulkanowy/wulkanowy +Discord: https://discord.gg/vccAQBr diff --git a/app/src/main/play/listings/pl-PL/graphics/feature-graphic/feature.png b/app/src/main/play/listings/pl-PL/graphics/feature-graphic/feature.png new file mode 100644 index 000000000..094b25b6c Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/feature-graphic/feature.png differ diff --git a/app/src/main/play/listings/pl-PL/graphics/icon/icon.png b/app/src/main/play/listings/pl-PL/graphics/icon/icon.png new file mode 100644 index 000000000..8c33c2f92 Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/icon/icon.png differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/1-start.jpg b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/1-start.jpg new file mode 100644 index 000000000..0ed20c04d Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/1-start.jpg differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/2.jpg b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/2.jpg new file mode 100644 index 000000000..f70e2c43b Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/2.jpg differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/3-timetable-dialog.jpg b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/3-timetable-dialog.jpg new file mode 100644 index 000000000..968fccdbe Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/3-timetable-dialog.jpg differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/4-exams.jpg b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/4-exams.jpg new file mode 100644 index 000000000..3f49e774a Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/4-exams.jpg differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/5-timetable-widget.jpg b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/5-timetable-widget.jpg new file mode 100644 index 000000000..f68daaf1a Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/5-timetable-widget.jpg differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/6-class-grades.jpg b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/6-class-grades.jpg new file mode 100644 index 000000000..ca5446a24 Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/6-class-grades.jpg differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/7-account-switcher.jpg b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/7-account-switcher.jpg new file mode 100644 index 000000000..ca747aff0 Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/7-account-switcher.jpg differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/8-themes.jpg b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/8-themes.jpg new file mode 100644 index 000000000..ce3af9bbf Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/8-themes.jpg differ diff --git a/app/src/main/play/listings/pl-PL/short-description.txt b/app/src/main/play/listings/pl-PL/short-description.txt new file mode 100644 index 000000000..c850c159a --- /dev/null +++ b/app/src/main/play/listings/pl-PL/short-description.txt @@ -0,0 +1 @@ +Nieoficjalna aplikacja ucznia i rodzica dla dziennika VULCAN UONET+ diff --git a/app/src/main/play/listings/pl-PL/title.txt b/app/src/main/play/listings/pl-PL/title.txt new file mode 100644 index 000000000..2d53a62a8 --- /dev/null +++ b/app/src/main/play/listings/pl-PL/title.txt @@ -0,0 +1 @@ +Wulkanowy Dzienniczek diff --git a/app/src/main/play/listings/sk/full-description.txt b/app/src/main/play/listings/sk/full-description.txt new file mode 100644 index 000000000..2a4787d2d --- /dev/null +++ b/app/src/main/play/listings/sk/full-description.txt @@ -0,0 +1,14 @@ +Aplikácia je určená pre užívateľov denníka VULCAN UONET+. + +Zvýraznené vlastnosti a funkcie: +- výpočet váženého priemeru, +- percentuálne zobrazenie dochádzky, +- šťastné číslo, +- náhľad na ďalšie a dokončené lekcie, +- tmavý motív, +- žiadne reklamy, +- offline režim, +- upozornenia. + +GitHub: https://github.com/wulkanowy/wulkanowy +Discord: https://discord.gg/vccAQBr diff --git a/app/src/main/play/listings/sk/short-description.txt b/app/src/main/play/listings/sk/short-description.txt new file mode 100644 index 000000000..645ebbb6a --- /dev/null +++ b/app/src/main/play/listings/sk/short-description.txt @@ -0,0 +1 @@ +Neoficiálna aplikácia žiaka a rodiča pre denníka VULCAN UONET+ diff --git a/app/src/main/play/listings/sk/title.txt b/app/src/main/play/listings/sk/title.txt new file mode 100644 index 000000000..aa50ce77a --- /dev/null +++ b/app/src/main/play/listings/sk/title.txt @@ -0,0 +1 @@ +Wulkanowy Denníček diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt new file mode 100644 index 000000000..2dba56ddd --- /dev/null +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -0,0 +1,9 @@ +Wersja 1.6.3 + +- dodaliśmy możliwość usuwania wielu wiadomości jednocześnie +- dodaliśmy opcję szybkiego dodawania sprawdzianów do kalendarza +- dodaliśmy średnią ucznia w wykresach ocen klasy +- naprawiliśmy rzadki błąd dotyczący problemów z automatycznym odświeżaniem ekranu startowego +- naprawiliśmy błąd z liczeniem średniej w drugim semestrze + +Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases diff --git a/app/src/main/res/drawable-hdpi/ic_shortcut_attendance.png b/app/src/main/res/drawable-hdpi/ic_shortcut_attendance.png new file mode 100644 index 000000000..0b5feff2d Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_shortcut_attendance.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_shortcut_exam.png b/app/src/main/res/drawable-hdpi/ic_shortcut_exam.png new file mode 100644 index 000000000..e5af0d086 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_shortcut_exam.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_shortcut_grade.png b/app/src/main/res/drawable-hdpi/ic_shortcut_grade.png new file mode 100644 index 000000000..095a8228c Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_shortcut_grade.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_shortcut_message.png b/app/src/main/res/drawable-hdpi/ic_shortcut_message.png new file mode 100644 index 000000000..7bcd79e01 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_shortcut_message.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_shortcut_timetable.png b/app/src/main/res/drawable-hdpi/ic_shortcut_timetable.png new file mode 100644 index 000000000..2808559a5 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_shortcut_timetable.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_stat_all.png b/app/src/main/res/drawable-hdpi/ic_stat_all.png new file mode 100644 index 000000000..84578183f Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_stat_all.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_stat_notify.png b/app/src/main/res/drawable-hdpi/ic_stat_notify.png deleted file mode 100644 index 34544980a..000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_stat_notify.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_widget_account.png b/app/src/main/res/drawable-hdpi/ic_widget_account.png new file mode 100644 index 000000000..4cb5ac89e Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_widget_account.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_shortcut_attendance.png b/app/src/main/res/drawable-mdpi/ic_shortcut_attendance.png new file mode 100644 index 000000000..e81e7ad92 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_shortcut_attendance.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_shortcut_exam.png b/app/src/main/res/drawable-mdpi/ic_shortcut_exam.png new file mode 100644 index 000000000..3bdb5297f Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_shortcut_exam.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_shortcut_grade.png b/app/src/main/res/drawable-mdpi/ic_shortcut_grade.png new file mode 100644 index 000000000..e35135071 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_shortcut_grade.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_shortcut_message.png b/app/src/main/res/drawable-mdpi/ic_shortcut_message.png new file mode 100644 index 000000000..392c45d24 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_shortcut_message.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_shortcut_timetable.png b/app/src/main/res/drawable-mdpi/ic_shortcut_timetable.png new file mode 100644 index 000000000..7d61306a4 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_shortcut_timetable.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_stat_all.png b/app/src/main/res/drawable-mdpi/ic_stat_all.png new file mode 100644 index 000000000..d1e954b0c Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_stat_all.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_stat_notify.png b/app/src/main/res/drawable-mdpi/ic_stat_notify.png deleted file mode 100644 index 5564049af..000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_stat_notify.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_widget_account.png b/app/src/main/res/drawable-mdpi/ic_widget_account.png new file mode 100644 index 000000000..237a6b6c2 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_widget_account.png differ diff --git a/app/src/main/res/drawable-night/background_header_note.xml b/app/src/main/res/drawable-night/background_header_note.xml new file mode 100644 index 000000000..6b594e7c6 --- /dev/null +++ b/app/src/main/res/drawable-night/background_header_note.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable-v15/splash_background.xml b/app/src/main/res/drawable-v15/splash_background.xml deleted file mode 100644 index c58c374a1..000000000 --- a/app/src/main/res/drawable-v15/splash_background.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - diff --git a/app/src/main/res/drawable-v23/compat_splash_screen_no_icon_background.xml b/app/src/main/res/drawable-v23/compat_splash_screen_no_icon_background.xml new file mode 100644 index 000000000..dad56a171 --- /dev/null +++ b/app/src/main/res/drawable-v23/compat_splash_screen_no_icon_background.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable-v23/splash_background.xml b/app/src/main/res/drawable-v23/splash_background.xml deleted file mode 100644 index 611bb2a7a..000000000 --- a/app/src/main/res/drawable-v23/splash_background.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - diff --git a/app/src/main/res/drawable-xhdpi/ic_shortcut_attendance.png b/app/src/main/res/drawable-xhdpi/ic_shortcut_attendance.png new file mode 100644 index 000000000..302b9e0ee Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_shortcut_attendance.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_shortcut_exam.png b/app/src/main/res/drawable-xhdpi/ic_shortcut_exam.png new file mode 100644 index 000000000..9f36ca47a Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_shortcut_exam.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_shortcut_grade.png b/app/src/main/res/drawable-xhdpi/ic_shortcut_grade.png new file mode 100644 index 000000000..281bc7a31 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_shortcut_grade.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_shortcut_message.png b/app/src/main/res/drawable-xhdpi/ic_shortcut_message.png new file mode 100644 index 000000000..184929a3c Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_shortcut_message.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_shortcut_timetable.png b/app/src/main/res/drawable-xhdpi/ic_shortcut_timetable.png new file mode 100644 index 000000000..9a40fe61c Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_shortcut_timetable.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_stat_all.png b/app/src/main/res/drawable-xhdpi/ic_stat_all.png new file mode 100644 index 000000000..79b38e63f Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_stat_all.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_stat_notify.png b/app/src/main/res/drawable-xhdpi/ic_stat_notify.png deleted file mode 100644 index fabb8760c..000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_stat_notify.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_widget_account.png b/app/src/main/res/drawable-xhdpi/ic_widget_account.png new file mode 100644 index 000000000..0ea798a8a Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_widget_account.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_shortcut_attendance.png b/app/src/main/res/drawable-xxhdpi/ic_shortcut_attendance.png new file mode 100644 index 000000000..9b4ef2daf Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_shortcut_attendance.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_shortcut_exam.png b/app/src/main/res/drawable-xxhdpi/ic_shortcut_exam.png new file mode 100644 index 000000000..c2677a139 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_shortcut_exam.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_shortcut_grade.png b/app/src/main/res/drawable-xxhdpi/ic_shortcut_grade.png new file mode 100644 index 000000000..8b51021cb Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_shortcut_grade.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_shortcut_message.png b/app/src/main/res/drawable-xxhdpi/ic_shortcut_message.png new file mode 100644 index 000000000..250c290aa Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_shortcut_message.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_shortcut_timetable.png b/app/src/main/res/drawable-xxhdpi/ic_shortcut_timetable.png new file mode 100644 index 000000000..c530153a1 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_shortcut_timetable.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_stat_all.png b/app/src/main/res/drawable-xxhdpi/ic_stat_all.png new file mode 100644 index 000000000..bc33cb5b1 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_stat_all.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_stat_notify.png b/app/src/main/res/drawable-xxhdpi/ic_stat_notify.png deleted file mode 100644 index c1d78d465..000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_stat_notify.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_widget_account.png b/app/src/main/res/drawable-xxhdpi/ic_widget_account.png new file mode 100644 index 000000000..0f8933b17 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_widget_account.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_shortcut_attendance.png b/app/src/main/res/drawable-xxxhdpi/ic_shortcut_attendance.png new file mode 100644 index 000000000..7b9a68a70 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_shortcut_attendance.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_shortcut_exam.png b/app/src/main/res/drawable-xxxhdpi/ic_shortcut_exam.png new file mode 100644 index 000000000..519c50abc Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_shortcut_exam.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_shortcut_grade.png b/app/src/main/res/drawable-xxxhdpi/ic_shortcut_grade.png new file mode 100644 index 000000000..13c793d5e Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_shortcut_grade.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_shortcut_message.png b/app/src/main/res/drawable-xxxhdpi/ic_shortcut_message.png new file mode 100644 index 000000000..eaffa2dde Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_shortcut_message.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_shortcut_timetable.png b/app/src/main/res/drawable-xxxhdpi/ic_shortcut_timetable.png new file mode 100644 index 000000000..03522e9a5 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_shortcut_timetable.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_stat_all.png b/app/src/main/res/drawable-xxxhdpi/ic_stat_all.png new file mode 100644 index 000000000..b354bd06c Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_stat_all.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_stat_notify.png b/app/src/main/res/drawable-xxxhdpi/ic_stat_notify.png deleted file mode 100644 index ee89e6015..000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_stat_notify.png and /dev/null differ diff --git a/app/src/main/res/drawable/background_header_note.xml b/app/src/main/res/drawable/background_header_note.xml new file mode 100644 index 000000000..c21e55c6b --- /dev/null +++ b/app/src/main/res/drawable/background_header_note.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/background_luckynumber_widget.xml b/app/src/main/res/drawable/background_luckynumber_widget.xml new file mode 100644 index 000000000..367c55275 --- /dev/null +++ b/app/src/main/res/drawable/background_luckynumber_widget.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/drawable/background_luckynumber_widget_dark.xml b/app/src/main/res/drawable/background_luckynumber_widget_dark.xml new file mode 100644 index 000000000..cb094b57e --- /dev/null +++ b/app/src/main/res/drawable/background_luckynumber_widget_dark.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/drawable/background_timetable_time_left.xml b/app/src/main/res/drawable/background_timetable_time_left.xml new file mode 100644 index 000000000..0f3326112 --- /dev/null +++ b/app/src/main/res/drawable/background_timetable_time_left.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/background_widget_header_timetable.xml b/app/src/main/res/drawable/background_widget_header_timetable.xml new file mode 100644 index 000000000..98eec700d --- /dev/null +++ b/app/src/main/res/drawable/background_widget_header_timetable.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/background_widget_header_timetable_dark.xml b/app/src/main/res/drawable/background_widget_header_timetable_dark.xml new file mode 100644 index 000000000..616a91279 --- /dev/null +++ b/app/src/main/res/drawable/background_widget_header_timetable_dark.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/background_widget_item_timetable.xml b/app/src/main/res/drawable/background_widget_item_timetable.xml new file mode 100644 index 000000000..08854fba2 --- /dev/null +++ b/app/src/main/res/drawable/background_widget_item_timetable.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/background_widget_item_timetable_dark.xml b/app/src/main/res/drawable/background_widget_item_timetable_dark.xml new file mode 100644 index 000000000..e432a648c --- /dev/null +++ b/app/src/main/res/drawable/background_widget_item_timetable_dark.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/background_widget_timetable.xml b/app/src/main/res/drawable/background_widget_timetable.xml new file mode 100644 index 000000000..2267587d9 --- /dev/null +++ b/app/src/main/res/drawable/background_widget_timetable.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/background_widget_timetable_dark.xml b/app/src/main/res/drawable/background_widget_timetable_dark.xml new file mode 100644 index 000000000..6fe7d0ab2 --- /dev/null +++ b/app/src/main/res/drawable/background_widget_timetable_dark.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_about_creator.xml b/app/src/main/res/drawable/ic_about_creator.xml new file mode 100644 index 000000000..c3daf609d --- /dev/null +++ b/app/src/main/res/drawable/ic_about_creator.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_about_discord.xml b/app/src/main/res/drawable/ic_about_discord.xml new file mode 100644 index 000000000..bcca42bf1 --- /dev/null +++ b/app/src/main/res/drawable/ic_about_discord.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_about_facebook.xml b/app/src/main/res/drawable/ic_about_facebook.xml new file mode 100644 index 000000000..a1b7b46eb --- /dev/null +++ b/app/src/main/res/drawable/ic_about_facebook.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_about_faq.xml b/app/src/main/res/drawable/ic_about_faq.xml new file mode 100644 index 000000000..d6ab255b2 --- /dev/null +++ b/app/src/main/res/drawable/ic_about_faq.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_about_feedback.xml b/app/src/main/res/drawable/ic_about_feedback.xml new file mode 100644 index 000000000..a348cb6a3 --- /dev/null +++ b/app/src/main/res/drawable/ic_about_feedback.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_about_licenses.xml b/app/src/main/res/drawable/ic_about_licenses.xml new file mode 100644 index 000000000..1a052734c --- /dev/null +++ b/app/src/main/res/drawable/ic_about_licenses.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_about_privacy.xml b/app/src/main/res/drawable/ic_about_privacy.xml new file mode 100644 index 000000000..109f05df8 --- /dev/null +++ b/app/src/main/res/drawable/ic_about_privacy.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_about_twitter.xml b/app/src/main/res/drawable/ic_about_twitter.xml new file mode 100644 index 000000000..7b7cf6d5c --- /dev/null +++ b/app/src/main/res/drawable/ic_about_twitter.xml @@ -0,0 +1,7 @@ + + + + diff --git a/app/src/main/res/drawable/ic_account_details_family.xml b/app/src/main/res/drawable/ic_account_details_family.xml new file mode 100644 index 000000000..363b4f58b --- /dev/null +++ b/app/src/main/res/drawable/ic_account_details_family.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_alert_24dp.xml b/app/src/main/res/drawable/ic_alert_24dp.xml deleted file mode 100644 index 1c41b556d..000000000 --- a/app/src/main/res/drawable/ic_alert_24dp.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_all_about.xml b/app/src/main/res/drawable/ic_all_about.xml new file mode 100644 index 000000000..3868f85c6 --- /dev/null +++ b/app/src/main/res/drawable/ic_all_about.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res/drawable/ic_all_account.xml b/app/src/main/res/drawable/ic_all_account.xml new file mode 100644 index 000000000..0e917d5f9 --- /dev/null +++ b/app/src/main/res/drawable/ic_all_account.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_all_add.xml b/app/src/main/res/drawable/ic_all_add.xml new file mode 100644 index 000000000..e45662935 --- /dev/null +++ b/app/src/main/res/drawable/ic_all_add.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_all_clock.xml b/app/src/main/res/drawable/ic_all_clock.xml new file mode 100644 index 000000000..4b98ed233 --- /dev/null +++ b/app/src/main/res/drawable/ic_all_clock.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_all_close_circle.xml b/app/src/main/res/drawable/ic_all_close_circle.xml new file mode 100644 index 000000000..3f89a8fa3 --- /dev/null +++ b/app/src/main/res/drawable/ic_all_close_circle.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_all_divider.xml b/app/src/main/res/drawable/ic_all_divider.xml new file mode 100644 index 000000000..922882c21 --- /dev/null +++ b/app/src/main/res/drawable/ic_all_divider.xml @@ -0,0 +1,5 @@ + + + + diff --git a/app/src/main/res/drawable/ic_all_done.xml b/app/src/main/res/drawable/ic_all_done.xml new file mode 100644 index 000000000..bb657f6ec --- /dev/null +++ b/app/src/main/res/drawable/ic_all_done.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_all_done_all.xml b/app/src/main/res/drawable/ic_all_done_all.xml new file mode 100644 index 000000000..e27672efa --- /dev/null +++ b/app/src/main/res/drawable/ic_all_done_all.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_all_home.xml b/app/src/main/res/drawable/ic_all_home.xml new file mode 100644 index 000000000..1ec608120 --- /dev/null +++ b/app/src/main/res/drawable/ic_all_home.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_exclamation_24dp.xml b/app/src/main/res/drawable/ic_all_mark.xml similarity index 55% rename from app/src/main/res/drawable/ic_exclamation_24dp.xml rename to app/src/main/res/drawable/ic_all_mark.xml index 4d3c91cfb..127216490 100644 --- a/app/src/main/res/drawable/ic_exclamation_24dp.xml +++ b/app/src/main/res/drawable/ic_all_mark.xml @@ -1,10 +1,9 @@ - + android:viewportWidth="24" + android:viewportHeight="24"> - \ No newline at end of file + diff --git a/app/src/main/res/drawable/ic_all_phone.xml b/app/src/main/res/drawable/ic_all_phone.xml new file mode 100644 index 000000000..7e3d7991e --- /dev/null +++ b/app/src/main/res/drawable/ic_all_phone.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_all_round_check.xml b/app/src/main/res/drawable/ic_all_round_check.xml new file mode 100644 index 000000000..aecaf234a --- /dev/null +++ b/app/src/main/res/drawable/ic_all_round_check.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_all_round_mark.xml b/app/src/main/res/drawable/ic_all_round_mark.xml new file mode 100644 index 000000000..91afb2334 --- /dev/null +++ b/app/src/main/res/drawable/ic_all_round_mark.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_attachment.xml b/app/src/main/res/drawable/ic_attachment.xml new file mode 100644 index 000000000..c18714f5c --- /dev/null +++ b/app/src/main/res/drawable/ic_attachment.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_border.xml b/app/src/main/res/drawable/ic_border.xml deleted file mode 100644 index dc33b8aa2..000000000 --- a/app/src/main/res/drawable/ic_border.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_calendar_all.xml b/app/src/main/res/drawable/ic_calendar_all.xml new file mode 100644 index 000000000..5908035ed --- /dev/null +++ b/app/src/main/res/drawable/ic_calendar_all.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_check.xml b/app/src/main/res/drawable/ic_check.xml new file mode 100644 index 000000000..3c728c59f --- /dev/null +++ b/app/src/main/res/drawable/ic_check.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_chevron_left.xml b/app/src/main/res/drawable/ic_chevron_left.xml new file mode 100644 index 000000000..ee3ff4be8 --- /dev/null +++ b/app/src/main/res/drawable/ic_chevron_left.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_chevron_right.xml b/app/src/main/res/drawable/ic_chevron_right.xml new file mode 100644 index 000000000..a6d734973 --- /dev/null +++ b/app/src/main/res/drawable/ic_chevron_right.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_dashboard_warning.xml b/app/src/main/res/drawable/ic_dashboard_warning.xml new file mode 100644 index 000000000..e7a5dc5a4 --- /dev/null +++ b/app/src/main/res/drawable/ic_dashboard_warning.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_error.xml b/app/src/main/res/drawable/ic_error.xml new file mode 100644 index 000000000..bb4cb3025 --- /dev/null +++ b/app/src/main/res/drawable/ic_error.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_excuse_denied.xml b/app/src/main/res/drawable/ic_excuse_denied.xml new file mode 100644 index 000000000..218cfbdc4 --- /dev/null +++ b/app/src/main/res/drawable/ic_excuse_denied.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_excuse_waiting.xml b/app/src/main/res/drawable/ic_excuse_waiting.xml new file mode 100644 index 000000000..863418b7b --- /dev/null +++ b/app/src/main/res/drawable/ic_excuse_waiting.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_fullscreen.xml b/app/src/main/res/drawable/ic_fullscreen.xml new file mode 100644 index 000000000..86b7649b6 --- /dev/null +++ b/app/src/main/res/drawable/ic_fullscreen.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_fullscreen_exit.xml b/app/src/main/res/drawable/ic_fullscreen_exit.xml new file mode 100644 index 000000000..bb7140f29 --- /dev/null +++ b/app/src/main/res/drawable/ic_fullscreen_exit.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_help.xml b/app/src/main/res/drawable/ic_help.xml new file mode 100644 index 000000000..9c6ba2925 --- /dev/null +++ b/app/src/main/res/drawable/ic_help.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml index c5ecc7474..ee11f89d1 100644 --- a/app/src/main/res/drawable/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -1,17 +1,15 @@ - - - + android:viewportWidth="1926" + android:viewportHeight="1926"> + + + diff --git a/app/src/main/res/drawable/ic_main_attendance.xml b/app/src/main/res/drawable/ic_main_attendance.xml new file mode 100644 index 000000000..c42a17b3d --- /dev/null +++ b/app/src/main/res/drawable/ic_main_attendance.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_main_dashboard.xml b/app/src/main/res/drawable/ic_main_dashboard.xml new file mode 100644 index 000000000..ed2269256 --- /dev/null +++ b/app/src/main/res/drawable/ic_main_dashboard.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_main_exam.xml b/app/src/main/res/drawable/ic_main_exam.xml new file mode 100644 index 000000000..a705c0a4b --- /dev/null +++ b/app/src/main/res/drawable/ic_main_exam.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_main_grade.xml b/app/src/main/res/drawable/ic_main_grade.xml new file mode 100644 index 000000000..bf4c2cf20 --- /dev/null +++ b/app/src/main/res/drawable/ic_main_grade.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_main_more.xml b/app/src/main/res/drawable/ic_main_more.xml new file mode 100644 index 000000000..f57a72456 --- /dev/null +++ b/app/src/main/res/drawable/ic_main_more.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_main_timetable.xml b/app/src/main/res/drawable/ic_main_timetable.xml new file mode 100644 index 000000000..e33aae165 --- /dev/null +++ b/app/src/main/res/drawable/ic_main_timetable.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_attendance_24dp.xml b/app/src/main/res/drawable/ic_menu_attendance_24dp.xml deleted file mode 100644 index fdef73225..000000000 --- a/app/src/main/res/drawable/ic_menu_attendance_24dp.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_menu_attendance_summary.xml b/app/src/main/res/drawable/ic_menu_attendance_summary.xml new file mode 100644 index 000000000..f0dab549d --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_attendance_summary.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_dashboard_24dp.xml b/app/src/main/res/drawable/ic_menu_dashboard_24dp.xml deleted file mode 100644 index 50726abd1..000000000 --- a/app/src/main/res/drawable/ic_menu_dashboard_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_menu_grade_26dp.xml b/app/src/main/res/drawable/ic_menu_grade_26dp.xml deleted file mode 100644 index 61506dada..000000000 --- a/app/src/main/res/drawable/ic_menu_grade_26dp.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_menu_grade_semester.xml b/app/src/main/res/drawable/ic_menu_grade_semester.xml new file mode 100644 index 000000000..8eb86922a --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_grade_semester.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_message_delete.xml b/app/src/main/res/drawable/ic_menu_message_delete.xml new file mode 100644 index 000000000..e60947c89 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_message_delete.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_message_forward.xml b/app/src/main/res/drawable/ic_menu_message_forward.xml new file mode 100644 index 000000000..97e34ddac --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_message_forward.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_message_print.xml b/app/src/main/res/drawable/ic_menu_message_print.xml new file mode 100644 index 000000000..204b0f6e3 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_message_print.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res/drawable/ic_menu_message_reply.xml b/app/src/main/res/drawable/ic_menu_message_reply.xml new file mode 100644 index 000000000..d9c667114 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_message_reply.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_message_send.xml b/app/src/main/res/drawable/ic_menu_message_send.xml new file mode 100644 index 000000000..fe72265f6 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_message_send.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_message_share.xml b/app/src/main/res/drawable/ic_menu_message_share.xml new file mode 100644 index 000000000..67a8ee494 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_message_share.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_message_write.xml b/app/src/main/res/drawable/ic_menu_message_write.xml new file mode 100644 index 000000000..7068aa5fe --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_message_write.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_other_24dp.xml b/app/src/main/res/drawable/ic_menu_other_24dp.xml deleted file mode 100644 index 9fed586b0..000000000 --- a/app/src/main/res/drawable/ic_menu_other_24dp.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_menu_timetable_24dp.xml b/app/src/main/res/drawable/ic_menu_timetable_24dp.xml deleted file mode 100644 index a689dc064..000000000 --- a/app/src/main/res/drawable/ic_menu_timetable_24dp.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_menu_timetable_lessons_additional.xml b/app/src/main/res/drawable/ic_menu_timetable_lessons_additional.xml new file mode 100644 index 000000000..ddb6d2f57 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_timetable_lessons_additional.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_timetable_lessons_completed.xml b/app/src/main/res/drawable/ic_menu_timetable_lessons_completed.xml new file mode 100644 index 000000000..a521adeea --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_timetable_lessons_completed.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_message_select_all.xml b/app/src/main/res/drawable/ic_message_select_all.xml new file mode 100644 index 000000000..eab195d94 --- /dev/null +++ b/app/src/main/res/drawable/ic_message_select_all.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_message_unselect_all.xml b/app/src/main/res/drawable/ic_message_unselect_all.xml new file mode 100644 index 000000000..c388522e4 --- /dev/null +++ b/app/src/main/res/drawable/ic_message_unselect_all.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_more_conferences.xml b/app/src/main/res/drawable/ic_more_conferences.xml new file mode 100644 index 000000000..65d6b6e83 --- /dev/null +++ b/app/src/main/res/drawable/ic_more_conferences.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_more_homework.xml b/app/src/main/res/drawable/ic_more_homework.xml new file mode 100644 index 000000000..9641ff70f --- /dev/null +++ b/app/src/main/res/drawable/ic_more_homework.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_more_lucky_number.xml b/app/src/main/res/drawable/ic_more_lucky_number.xml new file mode 100644 index 000000000..d644288c0 --- /dev/null +++ b/app/src/main/res/drawable/ic_more_lucky_number.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_more_messages.xml b/app/src/main/res/drawable/ic_more_messages.xml new file mode 100644 index 000000000..20c54e01f --- /dev/null +++ b/app/src/main/res/drawable/ic_more_messages.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_more_mobile_devices.xml b/app/src/main/res/drawable/ic_more_mobile_devices.xml new file mode 100644 index 000000000..6adeb6cff --- /dev/null +++ b/app/src/main/res/drawable/ic_more_mobile_devices.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_more_note.xml b/app/src/main/res/drawable/ic_more_note.xml new file mode 100644 index 000000000..47ba29cbe --- /dev/null +++ b/app/src/main/res/drawable/ic_more_note.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_more_schoolandteachers.xml b/app/src/main/res/drawable/ic_more_schoolandteachers.xml new file mode 100644 index 000000000..9cb9aee0e --- /dev/null +++ b/app/src/main/res/drawable/ic_more_schoolandteachers.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_more_settings.xml b/app/src/main/res/drawable/ic_more_settings.xml new file mode 100644 index 000000000..334eb11f7 --- /dev/null +++ b/app/src/main/res/drawable/ic_more_settings.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_refresh.xml b/app/src/main/res/drawable/ic_refresh.xml new file mode 100644 index 000000000..cc2d1e04f --- /dev/null +++ b/app/src/main/res/drawable/ic_refresh.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_school_directions.xml b/app/src/main/res/drawable/ic_school_directions.xml new file mode 100644 index 000000000..c48db1da0 --- /dev/null +++ b/app/src/main/res/drawable/ic_school_directions.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_search.xml b/app/src/main/res/drawable/ic_search.xml new file mode 100644 index 000000000..cd9985cb1 --- /dev/null +++ b/app/src/main/res/drawable/ic_search.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_settings_ads.xml b/app/src/main/res/drawable/ic_settings_ads.xml new file mode 100644 index 000000000..c333ea763 --- /dev/null +++ b/app/src/main/res/drawable/ic_settings_ads.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_settings_advanced.xml b/app/src/main/res/drawable/ic_settings_advanced.xml new file mode 100644 index 000000000..2fd7e5907 --- /dev/null +++ b/app/src/main/res/drawable/ic_settings_advanced.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res/drawable/ic_settings_appearance.xml b/app/src/main/res/drawable/ic_settings_appearance.xml new file mode 100644 index 000000000..afea27f27 --- /dev/null +++ b/app/src/main/res/drawable/ic_settings_appearance.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_settings_notifications.xml b/app/src/main/res/drawable/ic_settings_notifications.xml new file mode 100644 index 000000000..f4ff247f4 --- /dev/null +++ b/app/src/main/res/drawable/ic_settings_notifications.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_settings_sync.xml b/app/src/main/res/drawable/ic_settings_sync.xml new file mode 100644 index 000000000..3697ac0ba --- /dev/null +++ b/app/src/main/res/drawable/ic_settings_sync.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_share.xml b/app/src/main/res/drawable/ic_share.xml new file mode 100644 index 000000000..045bbc0c0 --- /dev/null +++ b/app/src/main/res/drawable/ic_share.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_splash_logo.xml b/app/src/main/res/drawable/ic_splash_logo.xml new file mode 100644 index 000000000..e2e747316 --- /dev/null +++ b/app/src/main/res/drawable/ic_splash_logo.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_stat_grade.xml b/app/src/main/res/drawable/ic_stat_grade.xml new file mode 100644 index 000000000..21b406fdc --- /dev/null +++ b/app/src/main/res/drawable/ic_stat_grade.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_stat_luckynumber.xml b/app/src/main/res/drawable/ic_stat_luckynumber.xml new file mode 100644 index 000000000..bee1c7072 --- /dev/null +++ b/app/src/main/res/drawable/ic_stat_luckynumber.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_stat_message.xml b/app/src/main/res/drawable/ic_stat_message.xml new file mode 100644 index 000000000..89a8aef29 --- /dev/null +++ b/app/src/main/res/drawable/ic_stat_message.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_stat_note.xml b/app/src/main/res/drawable/ic_stat_note.xml new file mode 100644 index 000000000..0f335336b --- /dev/null +++ b/app/src/main/res/drawable/ic_stat_note.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_stat_timetable.xml b/app/src/main/res/drawable/ic_stat_timetable.xml new file mode 100644 index 000000000..9efc03489 --- /dev/null +++ b/app/src/main/res/drawable/ic_stat_timetable.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_swap_30dp.xml b/app/src/main/res/drawable/ic_timetable_swap.xml similarity index 85% rename from app/src/main/res/drawable/ic_swap_30dp.xml rename to app/src/main/res/drawable/ic_timetable_swap.xml index 01eee893c..4055623a7 100644 --- a/app/src/main/res/drawable/ic_swap_30dp.xml +++ b/app/src/main/res/drawable/ic_timetable_swap.xml @@ -4,6 +4,6 @@ android:viewportHeight="24.0" android:viewportWidth="24.0"> diff --git a/app/src/main/res/drawable/ic_widget_chevron.png b/app/src/main/res/drawable/ic_widget_chevron.png new file mode 100644 index 000000000..34345521a Binary files /dev/null and b/app/src/main/res/drawable/ic_widget_chevron.png differ diff --git a/app/src/main/res/drawable/ic_widget_clover.png b/app/src/main/res/drawable/ic_widget_clover.png new file mode 100644 index 000000000..b1a7832a2 Binary files /dev/null and b/app/src/main/res/drawable/ic_widget_clover.png differ diff --git a/app/src/main/res/drawable/ic_wrench_24dp.xml b/app/src/main/res/drawable/ic_wrench_24dp.xml deleted file mode 100644 index 8f3a86faa..000000000 --- a/app/src/main/res/drawable/ic_wrench_24dp.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/img_luckynumber_widget_preview.png b/app/src/main/res/drawable/img_luckynumber_widget_preview.png new file mode 100644 index 000000000..539b0a598 Binary files /dev/null and b/app/src/main/res/drawable/img_luckynumber_widget_preview.png differ diff --git a/app/src/main/res/drawable/img_splash_512px.png b/app/src/main/res/drawable/img_splash_512px.png deleted file mode 100644 index 9d8f403c2..000000000 Binary files a/app/src/main/res/drawable/img_splash_512px.png and /dev/null differ diff --git a/app/src/main/res/drawable/img_timetable_widget_preview.png b/app/src/main/res/drawable/img_timetable_widget_preview.png new file mode 100755 index 000000000..849430187 Binary files /dev/null and b/app/src/main/res/drawable/img_timetable_widget_preview.png differ diff --git a/app/src/main/res/drawable/splash_background.xml b/app/src/main/res/drawable/splash_background.xml deleted file mode 100644 index 0b79b6729..000000000 --- a/app/src/main/res/drawable/splash_background.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 670959826..912792638 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -1,138 +1,20 @@ - + android:orientation="vertical"> - - + android:layout_height="wrap_content" + android:background="@android:color/transparent" /> - - - - - - + android:layout_height="0dp" + android:layout_weight="1" + tools:layout="@layout/fragment_login_form" /> - - - - - - - - - - - - - - - - - - - - - - -