diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml
index 6289971b7..94a4a6309 100644
--- a/.github/workflows/deploy.yaml
+++ b/.github/workflows/deploy.yaml
@@ -46,10 +46,11 @@ jobs:
working-directory: ./uni
steps:
- uses: actions/checkout@v3
- - uses: actions/setup-java@v1
+ - uses: actions/setup-java@v3
with:
java-version: ${{env.JAVA_VERSION}}
- - uses: subosito/flutter-action@v1
+ distribution: 'zulu'
+ - uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
@@ -77,7 +78,7 @@ jobs:
flutter build appbundle
- name: Upload App Bundle
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
with:
name: appbundle
if-no-files-found: error
@@ -90,7 +91,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Get App Bundle
- uses: actions/download-artifact@v2
+ uses: actions/download-artifact@v3
with:
name: appbundle
diff --git a/.github/workflows/format_lint_test.yaml b/.github/workflows/format_lint_test.yaml
new file mode 100644
index 000000000..636ccc151
--- /dev/null
+++ b/.github/workflows/format_lint_test.yaml
@@ -0,0 +1,65 @@
+on: pull_request
+
+env:
+ FLUTTER_VERSION: 3.7.2
+ JAVA_VERSION: 11.x
+
+jobs:
+ format:
+ name: 'Format'
+ runs-on: ubuntu-latest
+ defaults:
+ run:
+ working-directory: ./uni
+ steps:
+ - uses: actions/checkout@v3
+ - uses: subosito/flutter-action@v2
+ with:
+ flutter-version: ${{ env.FLUTTER_VERSION }}
+
+ - run: dart format . --set-exit-if-changed
+
+ lint:
+ name: 'Lint'
+ runs-on: ubuntu-latest
+ needs: format
+ defaults:
+ run:
+ working-directory: ./uni
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions/setup-java@v3
+ with:
+ java-version: ${{ env.JAVA_VERSION }}
+ distribution: 'zulu'
+ - uses: subosito/flutter-action@v2
+ with:
+ flutter-version: ${{ env.FLUTTER_VERSION }}
+
+ - name: Cache pub dependencies
+ uses: actions/cache@v3
+ with:
+ path: ${{ env.FLUTTER_HOME }}/.pub-cache
+ key: ${{ runner.os }}-pub-${{ github.ref }}-${{ hashFiles('**/pubspec.lock') }}
+ restore-keys: ${{ runner.os }}-pub-${{ github.ref }}-
+
+ - run: flutter analyze .
+
+ test:
+ name: 'Test'
+ runs-on: ubuntu-latest
+ needs: lint
+ defaults:
+ run:
+ working-directory: ./uni
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions/setup-java@v3
+ with:
+ java-version: ${{ env.JAVA_VERSION }}
+ distribution: 'zulu'
+ - uses: subosito/flutter-action@v2
+ with:
+ flutter-version: ${{ env.FLUTTER_VERSION }}
+
+ - run: flutter test --no-sound-null-safety
diff --git a/.github/workflows/test_lint.yaml b/.github/workflows/test_lint.yaml
deleted file mode 100644
index ffb255569..000000000
--- a/.github/workflows/test_lint.yaml
+++ /dev/null
@@ -1,45 +0,0 @@
-on: pull_request
-
-jobs:
- lint:
- name: 'Lint'
- runs-on: ubuntu-latest
- defaults:
- run:
- working-directory: ./uni
- steps:
- - uses: actions/checkout@v3
- - uses: actions/setup-java@v1
- with:
- java-version: '11.x'
- - uses: subosito/flutter-action@v1
- with:
- flutter-version: '3.7.2'
-
- - name: Cache pub dependencies
- uses: actions/cache@v2
- with:
- path: ${{ env.FLUTTER_HOME }}/.pub-cache
- key: ${{ runner.os }}-pub-${{ github.ref }}-${{ hashFiles('**/pubspec.lock') }}
- restore-keys: ${{ runner.os }}-pub-${{ github.ref }}-
-
- - run: flutter pub get
- - run: flutter analyze --no-pub --preamble .
-
- test:
- name: 'Test'
- runs-on: ubuntu-latest
- defaults:
- run:
- working-directory: ./uni
- steps:
- - uses: actions/checkout@v3
- - uses: actions/setup-java@v1
- with:
- java-version: '11.x'
- - uses: subosito/flutter-action@v1
- with:
- flutter-version: '3.7.2'
-
- - run: flutter pub get
- - run: flutter test --no-sound-null-safety
diff --git a/README.md b/README.md
index 88107466a..13787ab54 100644
--- a/README.md
+++ b/README.md
@@ -7,9 +7,9 @@
-[![Build badge](https://img.shields.io/github/actions/workflow/status/NIAEFEUP/project-schrodinger/test_lint.yaml?style=for-the-badge)](https://github.com/NIAEFEUP/project-schrodinger/actions)
-[![Deploy badge](https://img.shields.io/github/actions/workflow/status/NIAEFEUP/project-schrodinger/deploy.yaml?label=Deploy&style=for-the-badge)](https://github.com/NIAEFEUP/project-schrodinger/actions)
-[![License badge](https://img.shields.io/github/license/NIAEFEUP/project-schrodinger?style=for-the-badge)](https://github.com/NIAEFEUP/project-schrodinger/blob/master/LICENSE)
+[![Build badge](https://img.shields.io/github/actions/workflow/status/NIAEFEUP/uni/format_lint_test.yaml?style=for-the-badge)](https://github.com/NIAEFEUP/uni/actions)
+[![Deploy badge](https://img.shields.io/github/actions/workflow/status/NIAEFEUP/uni/deploy.yaml?label=Deploy&style=for-the-badge)](https://github.com/NIAEFEUP/uni/actions)
+[![License badge](https://img.shields.io/github/license/NIAEFEUP/uni?style=for-the-badge)](https://github.com/NIAEFEUP/uni/blob/develop/LICENSE)
@@ -31,4 +31,4 @@ This application is licensed under the [GNU General Public License v3.0](./LICEN
Contributions are welcome, and can be made by opening a pull request. Please note, however, that a university's account is required to access most of the app's features.
-For further information about the project structure, please refer to [the app's README file](./uni/README.md).
\ No newline at end of file
+For further information about the project structure, please refer to [the app's README file](./uni/README.md).
diff --git a/uni/android/app/build.gradle b/uni/android/app/build.gradle
index b8daaceff..1e13ce5bc 100644
--- a/uni/android/app/build.gradle
+++ b/uni/android/app/build.gradle
@@ -32,7 +32,7 @@ if (keystorePropertiesFile.exists()) {
}
android {
- compileSdkVersion 33
+ compileSdkVersion 33
ndkVersion flutter.ndkVersion
compileOptions {
@@ -52,7 +52,7 @@ android {
applicationId "pt.up.fe.ni.uni"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
- minSdkVersion 19
+ minSdkVersion 21 // default is flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
diff --git a/uni/android/build.gradle b/uni/android/build.gradle
index 96de58432..8dd3ad838 100644
--- a/uni/android/build.gradle
+++ b/uni/android/build.gradle
@@ -1,12 +1,14 @@
buildscript {
ext.kotlin_version = '1.7.21'
+ ext.android_plugin_version = '7.2.0'
+
repositories {
google()
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:7.4.2'
+ classpath "com.android.tools.build:gradle:$android_plugin_version"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
@@ -26,6 +28,6 @@ subprojects {
project.evaluationDependsOn(':app')
}
-task clean(type: Delete) {
+tasks.register("clean", Delete) {
delete rootProject.buildDir
-}
\ No newline at end of file
+}
diff --git a/uni/app_version.txt b/uni/app_version.txt
index 87a049733..eb9cb27b0 100644
--- a/uni/app_version.txt
+++ b/uni/app_version.txt
@@ -1 +1 @@
-1.5.31+149
\ No newline at end of file
+1.5.43+161
\ No newline at end of file
diff --git a/uni/assets/images/bus.png b/uni/assets/images/bus.png
new file mode 100644
index 000000000..1313c483f
Binary files /dev/null and b/uni/assets/images/bus.png differ
diff --git a/uni/assets/images/logo_ni.svg b/uni/assets/images/logo_ni.svg
new file mode 100644
index 000000000..1b3f6776d
--- /dev/null
+++ b/uni/assets/images/logo_ni.svg
@@ -0,0 +1,15 @@
+
+
diff --git a/uni/assets/images/logo_ni_original.svg b/uni/assets/images/logo_ni_original.svg
deleted file mode 100644
index 3130746fe..000000000
--- a/uni/assets/images/logo_ni_original.svg
+++ /dev/null
@@ -1,53 +0,0 @@
-
-
-
diff --git a/uni/assets/images/ni_logo.svg b/uni/assets/images/ni_logo.svg
deleted file mode 100644
index a382c6f0e..000000000
--- a/uni/assets/images/ni_logo.svg
+++ /dev/null
@@ -1,58 +0,0 @@
-
-
-
-
diff --git a/uni/assets/images/outline_red.svg b/uni/assets/images/outline_red.svg
deleted file mode 100644
index 0ca3d1ce9..000000000
--- a/uni/assets/images/outline_red.svg
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
diff --git a/uni/assets/images/schedule.png b/uni/assets/images/schedule.png
new file mode 100644
index 000000000..b409c6d65
Binary files /dev/null and b/uni/assets/images/schedule.png differ
diff --git a/uni/assets/images/vacation.png b/uni/assets/images/vacation.png
new file mode 100644
index 000000000..b047c93df
Binary files /dev/null and b/uni/assets/images/vacation.png differ
diff --git a/uni/lib/controller/background_workers/notifications.dart b/uni/lib/controller/background_workers/notifications.dart
index a270fa644..d614116db 100644
--- a/uni/lib/controller/background_workers/notifications.dart
+++ b/uni/lib/controller/background_workers/notifications.dart
@@ -60,15 +60,20 @@ class NotificationManager {
}
static Future updateAndTriggerNotifications() async {
- //first we get the .json file that contains the last time that the notification have ran
- _initFlutterNotificationsPlugin();
- final notificationStorage = await NotificationTimeoutStorage.create();
final userInfo = await AppSharedPreferences.getPersistentUserInfo();
final faculties = await AppSharedPreferences.getUserFaculties();
- final Session session = await NetworkRouter.login(
+ final Session? session = await NetworkRouter.login(
userInfo.item1, userInfo.item2, faculties, false);
+ if (session == null) {
+ return;
+ }
+
+ // Get the .json file that contains the last time that the notification has ran
+ _initFlutterNotificationsPlugin();
+ final notificationStorage = await NotificationTimeoutStorage.create();
+
for (Notification Function() value in notificationMap.values) {
final Notification notification = value();
final DateTime lastRan = notificationStorage
@@ -83,7 +88,7 @@ class NotificationManager {
}
void initializeNotifications() async {
- //guarentees that the execution is only done once in the lifetime of the app.
+ // guarantees that the execution is only done once in the lifetime of the app.
if (_initialized) return;
_initialized = true;
_initFlutterNotificationsPlugin();
diff --git a/uni/lib/controller/background_workers/notifications/tuition_notification.dart b/uni/lib/controller/background_workers/notifications/tuition_notification.dart
index 777084d75..db6e43abd 100644
--- a/uni/lib/controller/background_workers/notifications/tuition_notification.dart
+++ b/uni/lib/controller/background_workers/notifications/tuition_notification.dart
@@ -45,9 +45,12 @@ class TuitionNotification extends Notification {
!(await AppSharedPreferences.getTuitionNotificationToggle());
if (notificationsAreDisabled) return false;
final FeesFetcher feesFetcher = FeesFetcher();
- final String nextDueDate = await parseFeesNextLimit(
+ final DateTime? dueDate = await parseFeesNextLimit(
await feesFetcher.getUserFeesResponse(session));
- _dueDate = DateTime.parse(nextDueDate);
+
+ if (dueDate == null) return false;
+
+ _dueDate = dueDate;
return DateTime.now().difference(_dueDate).inDays >= -3;
}
diff --git a/uni/lib/controller/fetchers/all_course_units_fetcher.dart b/uni/lib/controller/fetchers/course_units_fetcher/all_course_units_fetcher.dart
similarity index 92%
rename from uni/lib/controller/fetchers/all_course_units_fetcher.dart
rename to uni/lib/controller/fetchers/course_units_fetcher/all_course_units_fetcher.dart
index baa454ebf..4cb53205e 100644
--- a/uni/lib/controller/fetchers/all_course_units_fetcher.dart
+++ b/uni/lib/controller/fetchers/course_units_fetcher/all_course_units_fetcher.dart
@@ -2,14 +2,14 @@ import 'package:logger/logger.dart';
import 'package:uni/controller/networking/network_router.dart';
import 'package:uni/controller/parsers/parser_course_units.dart';
import 'package:uni/model/entities/course.dart';
-import 'package:uni/model/entities/course_unit.dart';
+import 'package:uni/model/entities/course_units/course_unit.dart';
import 'package:uni/model/entities/session.dart';
class AllCourseUnitsFetcher {
Future> getAllCourseUnitsAndCourseAverages(
List courses, Session session) async {
final List allCourseUnits = [];
- for (var course in courses) {
+ for (Course course in courses) {
try {
final List courseUnits =
await _getAllCourseUnitsAndCourseAveragesFromCourse(
diff --git a/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart b/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart
new file mode 100644
index 000000000..f2e39788c
--- /dev/null
+++ b/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart
@@ -0,0 +1,63 @@
+import 'package:html/parser.dart';
+import 'package:http/http.dart';
+import 'package:uni/controller/fetchers/session_dependant_fetcher.dart';
+import 'package:uni/controller/networking/network_router.dart';
+import 'package:uni/controller/parsers/parser_course_unit_info.dart';
+import 'package:uni/model/entities/course_units/course_unit_class.dart';
+import 'package:uni/model/entities/course_units/course_unit_sheet.dart';
+import 'package:uni/model/entities/session.dart';
+
+class CourseUnitsInfoFetcher implements SessionDependantFetcher {
+ @override
+ List getEndpoints(Session session) {
+ return NetworkRouter.getBaseUrlsFromSession(session).toList();
+ }
+
+ Future fetchCourseUnitSheet(
+ Session session, int occurrId) async {
+ // if course unit is not from the main faculty, Sigarra redirects
+ final String url = '${getEndpoints(session)[0]}ucurr_geral.ficha_uc_view';
+ final Response response = await NetworkRouter.getWithCookies(
+ url, {'pv_ocorrencia_id': occurrId.toString()}, session);
+ return parseCourseUnitSheet(response);
+ }
+
+ Future> fetchCourseUnitClasses(
+ Session session, int occurrId) async {
+ List courseUnitClasses = [];
+
+ for (String endpoint in getEndpoints(session)) {
+ // Crawl classes from all courses that the course unit is offered in
+ final String courseChoiceUrl =
+ '${endpoint}it_listagem.lista_cursos_disciplina?pv_ocorrencia_id=$occurrId';
+ final Response courseChoiceResponse =
+ await NetworkRouter.getWithCookies(courseChoiceUrl, {}, session);
+ final courseChoiceDocument = parse(courseChoiceResponse.body);
+ final List urls = courseChoiceDocument
+ .querySelectorAll('a')
+ .where((element) =>
+ element.attributes['href'] != null &&
+ element.attributes['href']!
+ .contains('it_listagem.lista_turma_disciplina'))
+ .map((e) {
+ String? url = e.attributes['href']!;
+ if (!url.contains('sigarra.up.pt')) {
+ url = endpoint + url;
+ }
+ return url;
+ }).toList();
+
+ for (String url in urls) {
+ try {
+ final Response response =
+ await NetworkRouter.getWithCookies(url, {}, session);
+ courseUnitClasses += parseCourseUnitClasses(response, endpoint);
+ } catch (_) {
+ continue;
+ }
+ }
+ }
+
+ return courseUnitClasses;
+ }
+}
diff --git a/uni/lib/controller/fetchers/current_course_units_fetcher.dart b/uni/lib/controller/fetchers/course_units_fetcher/current_course_units_fetcher.dart
similarity index 78%
rename from uni/lib/controller/fetchers/current_course_units_fetcher.dart
rename to uni/lib/controller/fetchers/course_units_fetcher/current_course_units_fetcher.dart
index 95f23004d..7160c5369 100644
--- a/uni/lib/controller/fetchers/current_course_units_fetcher.dart
+++ b/uni/lib/controller/fetchers/course_units_fetcher/current_course_units_fetcher.dart
@@ -1,23 +1,24 @@
import 'dart:convert';
+import 'package:http/http.dart';
import 'package:uni/controller/fetchers/session_dependant_fetcher.dart';
import 'package:uni/controller/networking/network_router.dart';
-import 'package:uni/model/entities/course_unit.dart';
+import 'package:uni/model/entities/course_units/course_unit.dart';
import 'package:uni/model/entities/session.dart';
class CurrentCourseUnitsFetcher implements SessionDependantFetcher {
@override
List getEndpoints(Session session) {
// all faculties list user course units on all faculties
- final url =
+ final String url =
'${NetworkRouter.getBaseUrlsFromSession(session)[0]}mob_fest_geral.ucurr_inscricoes_corrente';
return [url];
}
Future> getCurrentCourseUnits(Session session) async {
- final url = getEndpoints(session)[0];
- final response = await NetworkRouter.getWithCookies(
- url, {'pv_codigo': session.studentNumber}, session);
+ final String url = getEndpoints(session)[0];
+ final Response response = await NetworkRouter.getWithCookies(
+ url, {'pv_codigo': session.username}, session);
if (response.statusCode == 200) {
final responseBody = json.decode(response.body);
final List ucs = [];
diff --git a/uni/lib/controller/fetchers/courses_fetcher.dart b/uni/lib/controller/fetchers/courses_fetcher.dart
index d56ed3347..2e0f50808 100644
--- a/uni/lib/controller/fetchers/courses_fetcher.dart
+++ b/uni/lib/controller/fetchers/courses_fetcher.dart
@@ -1,7 +1,7 @@
import 'package:http/http.dart';
import 'package:uni/controller/fetchers/session_dependant_fetcher.dart';
import 'package:uni/controller/networking/network_router.dart';
-import 'package:uni/model/entities/course_unit.dart';
+import 'package:uni/model/entities/course_units/course_unit.dart';
import 'package:uni/model/entities/session.dart';
/// Returns the user's current list of [CourseUnit].
@@ -18,7 +18,7 @@ class CoursesFetcher implements SessionDependantFetcher {
final urls = getEndpoints(session);
return urls
.map((url) => NetworkRouter.getWithCookies(
- url, {'pv_num_unico': session.studentNumber}, session))
+ url, {'pv_num_unico': session.username}, session))
.toList();
}
}
diff --git a/uni/lib/controller/fetchers/exam_fetcher.dart b/uni/lib/controller/fetchers/exam_fetcher.dart
index c297eea23..4ddd49076 100644
--- a/uni/lib/controller/fetchers/exam_fetcher.dart
+++ b/uni/lib/controller/fetchers/exam_fetcher.dart
@@ -2,7 +2,7 @@ import 'package:uni/controller/fetchers/session_dependant_fetcher.dart';
import 'package:uni/controller/networking/network_router.dart';
import 'package:uni/controller/parsers/parser_exams.dart';
import 'package:uni/model/entities/course.dart';
-import 'package:uni/model/entities/course_unit.dart';
+import 'package:uni/model/entities/course_units/course_unit.dart';
import 'package:uni/model/entities/exam.dart';
import 'package:uni/model/entities/session.dart';
diff --git a/uni/lib/controller/fetchers/fees_fetcher.dart b/uni/lib/controller/fetchers/fees_fetcher.dart
index a7c3c7f4d..edd225aae 100644
--- a/uni/lib/controller/fetchers/fees_fetcher.dart
+++ b/uni/lib/controller/fetchers/fees_fetcher.dart
@@ -15,7 +15,7 @@ class FeesFetcher implements SessionDependantFetcher {
Future getUserFeesResponse(Session session) {
final String url = getEndpoints(session)[0];
- final Map query = {'pct_cod': session.studentNumber};
+ final Map query = {'pct_cod': session.username};
return NetworkRouter.getWithCookies(url, query, session);
}
}
diff --git a/uni/lib/controller/fetchers/print_fetcher.dart b/uni/lib/controller/fetchers/print_fetcher.dart
index 763bea773..9907dac03 100644
--- a/uni/lib/controller/fetchers/print_fetcher.dart
+++ b/uni/lib/controller/fetchers/print_fetcher.dart
@@ -13,7 +13,7 @@ class PrintFetcher implements SessionDependantFetcher {
getUserPrintsResponse(Session session) {
final String url = getEndpoints(session)[0];
- final Map query = {'p_codigo': session.studentNumber};
+ final Map query = {'p_codigo': session.username};
return NetworkRouter.getWithCookies(url, query, session);
}
@@ -26,7 +26,7 @@ class PrintFetcher implements SessionDependantFetcher {
final Map data = {
'p_tipo_id': '3',
- 'pct_codigo': session.studentNumber,
+ 'pct_codigo': session.username,
'p_valor': '1',
'p_valor_livre': amount.toStringAsFixed(2).trim().replaceAll('.', ',')
};
diff --git a/uni/lib/controller/fetchers/profile_fetcher.dart b/uni/lib/controller/fetchers/profile_fetcher.dart
index c57bed68e..d15a4ef1d 100644
--- a/uni/lib/controller/fetchers/profile_fetcher.dart
+++ b/uni/lib/controller/fetchers/profile_fetcher.dart
@@ -21,7 +21,7 @@ class ProfileFetcher implements SessionDependantFetcher {
final url =
'${NetworkRouter.getBaseUrlsFromSession(session)[0]}mob_fest_geral.perfil?';
final response = await NetworkRouter.getWithCookies(
- url, {'pv_codigo': session.studentNumber}, session);
+ url, {'pv_codigo': session.username}, session);
if (response.statusCode == 200) {
final Profile profile = Profile.fromResponse(response);
diff --git a/uni/lib/controller/fetchers/reference_fetcher.dart b/uni/lib/controller/fetchers/reference_fetcher.dart
index 8e54ae85d..f1258cc6a 100644
--- a/uni/lib/controller/fetchers/reference_fetcher.dart
+++ b/uni/lib/controller/fetchers/reference_fetcher.dart
@@ -4,11 +4,11 @@ import 'package:uni/controller/networking/network_router.dart';
import 'package:uni/model/entities/session.dart';
class ReferenceFetcher implements SessionDependantFetcher {
-
@override
List getEndpoints(Session session) {
- final List baseUrls = NetworkRouter.getBaseUrlsFromSession(session)
- + [NetworkRouter.getBaseUrl('sasup')];
+ final List baseUrls =
+ NetworkRouter.getBaseUrlsFromSession(session) +
+ [NetworkRouter.getBaseUrl('sasup')];
final List urls = baseUrls
.map((url) => '${url}gpag_ccorrente_geral.conta_corrente_view')
.toList();
@@ -18,7 +18,7 @@ class ReferenceFetcher implements SessionDependantFetcher {
Future getUserReferenceResponse(Session session) {
final List urls = getEndpoints(session);
final String url = urls[0];
- final Map query = {'pct_cod': session.studentNumber};
+ final Map query = {'pct_cod': session.username};
return NetworkRouter.getWithCookies(url, query, session);
}
-}
\ No newline at end of file
+}
diff --git a/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_api.dart b/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_api.dart
index f6ac80f8d..71b60501f 100644
--- a/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_api.dart
+++ b/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_api.dart
@@ -25,7 +25,7 @@ class ScheduleFetcherApi extends ScheduleFetcher {
final response = await NetworkRouter.getWithCookies(
url,
{
- 'pv_codigo': session.studentNumber,
+ 'pv_codigo': session.username,
'pv_semana_ini': dates.beginWeek,
'pv_semana_fim': dates.endWeek
},
diff --git a/uni/lib/controller/load_info.dart b/uni/lib/controller/load_info.dart
deleted file mode 100644
index 5c958e4d0..000000000
--- a/uni/lib/controller/load_info.dart
+++ /dev/null
@@ -1,149 +0,0 @@
-import 'dart:async';
-import 'dart:io';
-
-import 'package:connectivity_plus/connectivity_plus.dart';
-import 'package:logger/logger.dart';
-import 'package:tuple/tuple.dart';
-import 'package:uni/controller/local_storage/app_shared_preferences.dart';
-import 'package:uni/controller/local_storage/file_offline_storage.dart';
-import 'package:uni/controller/parsers/parser_exams.dart';
-import 'package:uni/model/entities/session.dart';
-import 'package:uni/model/providers/state_providers.dart';
-
-Future loadReloginInfo(StateProviders stateProviders) async {
- final Tuple2 userPersistentCredentials =
- await AppSharedPreferences.getPersistentUserInfo();
- final String userName = userPersistentCredentials.item1;
- final String password = userPersistentCredentials.item2;
- final List faculties = await AppSharedPreferences.getUserFaculties();
-
- if (userName != '' && password != '') {
- final action = Completer();
- stateProviders.sessionProvider
- .reLogin(userName, password, faculties, stateProviders, action: action);
- return action.future;
- }
- return Future.error('No credentials stored');
-}
-
-Future loadRemoteUserInfoToState(StateProviders stateProviders) async {
- if (await Connectivity().checkConnectivity() == ConnectivityResult.none) {
- return;
- }
-
- Logger().i('Loading remote info');
-
- final session = stateProviders.sessionProvider.session;
- if (!session.authenticated && session.persistentSession) {
- await loadReloginInfo(stateProviders);
- }
-
- final Completer userInfo = Completer(),
- ucs = Completer(),
- exams = Completer(),
- schedule = Completer(),
- printBalance = Completer(),
- fees = Completer(),
- trips = Completer(),
- lastUpdate = Completer(),
- restaurants = Completer(),
- libraryOccupation = Completer(),
- calendar = Completer(),
- references = Completer();
-
- stateProviders.profileStateProvider.getUserInfo(userInfo, session);
- stateProviders.busStopProvider.getUserBusTrips(trips);
- stateProviders.restaurantProvider
- .getRestaurantsFromFetcher(restaurants, session);
- stateProviders.calendarProvider.getCalendarFromFetcher(session, calendar);
- stateProviders.libraryOccupationProvider
- .getLibraryOccupation(session, libraryOccupation);
-
- final Tuple2 userPersistentInfo =
- await AppSharedPreferences.getPersistentUserInfo();
-
- userInfo.future.then((value) {
- final profile = stateProviders.profileStateProvider.profile;
- final currUcs = stateProviders.profileStateProvider.currUcs;
- stateProviders.examProvider.getUserExams(
- exams, ParserExams(), userPersistentInfo, profile, session, currUcs);
- stateProviders.lectureProvider
- .getUserLectures(schedule, userPersistentInfo, session, profile);
- stateProviders.profileStateProvider
- .getCourseUnitsAndCourseAverages(session, ucs);
- stateProviders.profileStateProvider
- .getUserPrintBalance(printBalance, session);
- stateProviders.profileStateProvider.getUserFees(fees, session);
- stateProviders.referenceProvider.getUserReferences(references, userPersistentInfo, session);
- });
-
- final allRequests = Future.wait([
- ucs.future,
- exams.future,
- schedule.future,
- printBalance.future,
- fees.future,
- userInfo.future,
- trips.future,
- restaurants.future,
- libraryOccupation.future,
- calendar.future,
- references.future,
- ]);
- allRequests.then((futures) {
- stateProviders.lastUserInfoProvider
- .setLastUserInfoUpdateTimestamp(lastUpdate);
- });
- return lastUpdate.future;
-}
-
-void loadLocalUserInfoToState(StateProviders stateProviders,
- {skipDatabaseLookup = false}) async {
- final Tuple2 userPersistentInfo =
- await AppSharedPreferences.getPersistentUserInfo();
-
- Logger().i('Setting up user preferences');
- stateProviders.favoriteCardsProvider
- .setFavoriteCards(await AppSharedPreferences.getFavoriteCards());
- stateProviders.examProvider.setFilteredExams(
- await AppSharedPreferences.getFilteredExams(), Completer());
- stateProviders.examProvider
- .setHiddenExams(await AppSharedPreferences.getHiddenExams(), Completer());
- stateProviders.userFacultiesProvider
- .setUserFaculties(await AppSharedPreferences.getUserFaculties());
-
- if (userPersistentInfo.item1 != '' &&
- userPersistentInfo.item2 != '' &&
- !skipDatabaseLookup) {
- Logger().i('Fetching local info from database');
- stateProviders.examProvider.updateStateBasedOnLocalUserExams();
- stateProviders.lectureProvider.updateStateBasedOnLocalUserLectures();
- stateProviders.examProvider.updateStateBasedOnLocalUserExams();
- stateProviders.lectureProvider.updateStateBasedOnLocalUserLectures();
- stateProviders.busStopProvider.updateStateBasedOnLocalUserBusStops();
- stateProviders.profileStateProvider.updateStateBasedOnLocalProfile();
- stateProviders.profileStateProvider.updateStateBasedOnLocalRefreshTimes();
- stateProviders.restaurantProvider.updateStateBasedOnLocalRestaurants();
- stateProviders.lastUserInfoProvider.updateStateBasedOnLocalTime();
- stateProviders.calendarProvider.updateStateBasedOnLocalCalendar();
- stateProviders.profileStateProvider.updateStateBasedOnLocalCourseUnits();
- stateProviders.referenceProvider.updateStateBasedOnLocalUserReferences();
- }
-
- stateProviders.facultyLocationsProvider.getFacultyLocations(Completer());
-}
-
-Future handleRefresh(StateProviders stateProviders) async {
- await loadRemoteUserInfoToState(stateProviders);
-}
-
-Future loadProfilePicture(Session session, {forceRetrieval = false}) {
- final String studentNumber = session.studentNumber;
- final String faculty = session.faculties[0];
- final String url =
- 'https://sigarra.up.pt/$faculty/pt/fotografias_service.foto?pct_cod=$studentNumber';
- final Map headers = {};
- headers['cookie'] = session.cookies;
- return loadFileFromStorageOrRetrieveNew('user_profile_picture', url, headers,
- forceRetrieval: forceRetrieval);
-}
diff --git a/uni/lib/controller/load_static/terms_and_conditions.dart b/uni/lib/controller/load_static/terms_and_conditions.dart
index 6aa23ae88..dd1cecb17 100644
--- a/uni/lib/controller/load_static/terms_and_conditions.dart
+++ b/uni/lib/controller/load_static/terms_and_conditions.dart
@@ -7,14 +7,15 @@ import 'package:http/http.dart' as http;
import 'package:logger/logger.dart';
import 'package:uni/controller/local_storage/app_shared_preferences.dart';
-/// Returns the content of the Terms and Conditions file.
+/// Returns the content of the Terms and Conditions remote file,
+/// or the local one if the remote file is not available.
///
/// If this operation is unsuccessful, an error message is returned.
-Future readTermsAndConditions() async {
+Future fetchTermsAndConditions() async {
if (await Connectivity().checkConnectivity() != ConnectivityResult.none) {
try {
- const String url =
- 'https://raw.githubusercontent.com/NIAEFEUP/project-schrodinger/develop/uni/assets/text/TermsAndConditions.md';
+ const String url = 'https://raw.githubusercontent.com/NIAEFEUP/'
+ 'uni/develop/uni/assets/text/TermsAndConditions.md';
final http.Response response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
return response.body;
@@ -23,6 +24,7 @@ Future readTermsAndConditions() async {
Logger().e('Failed to fetch Terms and Conditions: ${e.toString()}');
}
}
+
try {
return await rootBundle.loadString('assets/text/TermsAndConditions.md');
} catch (e) {
@@ -32,16 +34,17 @@ Future readTermsAndConditions() async {
}
}
-/// Checks if the current Terms and Conditions have been accepted by the user.
-///
-/// Returns true if the current Terms and Conditions have been accepted,
-/// false otherwise.
+/// Checks if the current Terms and Conditions have been accepted by the user,
+/// by fetching the current terms, hashing them and comparing with the stored hash.
+/// Sets the acceptance to false if the terms have changed, or true if they haven't.
+/// Returns the updated value.
Future updateTermsAndConditionsAcceptancePreference() async {
final hash = await AppSharedPreferences.getTermsAndConditionHash();
- final acceptance = await AppSharedPreferences.areTermsAndConditionsAccepted();
- final termsAndConditions = await readTermsAndConditions();
+ final termsAndConditions = await fetchTermsAndConditions();
final currentHash = md5.convert(utf8.encode(termsAndConditions)).toString();
+
if (hash == null) {
+ await AppSharedPreferences.setTermsAndConditionsAcceptance(true);
await AppSharedPreferences.setTermsAndConditionHash(currentHash);
return true;
}
@@ -49,14 +52,16 @@ Future updateTermsAndConditionsAcceptancePreference() async {
if (currentHash != hash) {
await AppSharedPreferences.setTermsAndConditionsAcceptance(false);
await AppSharedPreferences.setTermsAndConditionHash(currentHash);
+ return false;
}
- return currentHash != hash || !acceptance;
+ await AppSharedPreferences.setTermsAndConditionsAcceptance(true);
+ return true;
}
/// Accepts the current Terms and Conditions.
Future acceptTermsAndConditions() async {
- final termsAndConditions = await readTermsAndConditions();
+ final termsAndConditions = await fetchTermsAndConditions();
final currentHash = md5.convert(utf8.encode(termsAndConditions)).toString();
await AppSharedPreferences.setTermsAndConditionHash(currentHash);
await AppSharedPreferences.setTermsAndConditionsAcceptance(true);
diff --git a/uni/lib/controller/local_storage/app_course_units_database.dart b/uni/lib/controller/local_storage/app_course_units_database.dart
index 0c992777c..29bb850dc 100644
--- a/uni/lib/controller/local_storage/app_course_units_database.dart
+++ b/uni/lib/controller/local_storage/app_course_units_database.dart
@@ -2,7 +2,7 @@ import 'dart:async';
import 'package:sqflite/sqflite.dart';
import 'package:uni/controller/local_storage/app_database.dart';
-import 'package:uni/model/entities/course_unit.dart';
+import 'package:uni/model/entities/course_units/course_unit.dart';
class AppCourseUnitsDatabase extends AppDatabase {
static const String createScript =
diff --git a/uni/lib/controller/local_storage/app_references_database.dart b/uni/lib/controller/local_storage/app_references_database.dart
index d73b0d3c5..daf8682bf 100644
--- a/uni/lib/controller/local_storage/app_references_database.dart
+++ b/uni/lib/controller/local_storage/app_references_database.dart
@@ -10,11 +10,11 @@ import 'package:uni/model/entities/reference.dart';
/// See the [Reference] class to see what data is stored in this database.
class AppReferencesDatabase extends AppDatabase {
static const String createScript =
- '''CREATE TABLE refs(description TEXT, entity INTEGER, '''
- '''reference INTEGER, amount REAL, limitDate TEXT)''';
+ '''CREATE TABLE refs(description TEXT, entity INTEGER, '''
+ '''reference INTEGER, amount REAL, limitDate TEXT)''';
- AppReferencesDatabase() :
- super('refs.db', [createScript], onUpgrade: migrate, version: 2);
+ AppReferencesDatabase()
+ : super('refs.db', [createScript], onUpgrade: migrate, version: 2);
/// Replaces all of the data in this database with the data from [references].
Future saveNewReferences(List references) async {
@@ -48,11 +48,8 @@ class AppReferencesDatabase extends AppDatabase {
/// If a row with the same data is present, it will be replaced.
Future insertReferences(List references) async {
for (Reference reference in references) {
- await insertInDatabase(
- 'refs',
- reference.toMap(),
- conflictAlgorithm: ConflictAlgorithm.replace
- );
+ await insertInDatabase('refs', reference.toMap(),
+ conflictAlgorithm: ConflictAlgorithm.replace);
}
}
@@ -67,4 +64,4 @@ class AppReferencesDatabase extends AppDatabase {
batch.execute(createScript);
batch.commit();
}
-}
\ No newline at end of file
+}
diff --git a/uni/lib/controller/local_storage/app_restaurant_database.dart b/uni/lib/controller/local_storage/app_restaurant_database.dart
index cbc636dc9..4c0892bc3 100644
--- a/uni/lib/controller/local_storage/app_restaurant_database.dart
+++ b/uni/lib/controller/local_storage/app_restaurant_database.dart
@@ -117,12 +117,13 @@ List filterPastMeals(List restaurants) {
// (To replicate sigarra's behaviour for the GSheets meals)
final DateTime now = DateTime.now().toUtc();
final DateTime today = DateTime.utc(now.year, now.month, now.day);
- final DateTime nextSunday = today.add(Duration(days: DateTime.sunday - now.weekday));
+ final DateTime nextSunday =
+ today.add(Duration(days: DateTime.sunday - now.weekday));
for (var restaurant in restaurantsCopy) {
for (var meals in restaurant.meals.values) {
meals.removeWhere(
- (meal) => meal.date.isBefore(today) || meal.date.isAfter(nextSunday));
+ (meal) => meal.date.isBefore(today) || meal.date.isAfter(nextSunday));
}
}
diff --git a/uni/lib/controller/local_storage/app_shared_preferences.dart b/uni/lib/controller/local_storage/app_shared_preferences.dart
index 9134e9ef4..6db64b9ed 100644
--- a/uni/lib/controller/local_storage/app_shared_preferences.dart
+++ b/uni/lib/controller/local_storage/app_shared_preferences.dart
@@ -12,12 +12,14 @@ import 'package:uni/utils/favorite_widget_type.dart';
/// This database stores the user's student number, password and favorite
/// widgets.
class AppSharedPreferences {
+ static const lastUpdateTimeKeySuffix = "_last_update_time";
static const String userNumber = 'user_number';
static const String userPw = 'user_password';
static const String userFaculties = 'user_faculties';
static const String termsAndConditions = 'terms_and_conditions';
static const String areTermsAndConditionsAcceptedKey = 'is_t&c_accepted';
- static const String tuitionNotificationsToggleKey = "tuition_notification_toogle";
+ static const String tuitionNotificationsToggleKey =
+ "tuition_notification_toogle";
static const String themeMode = 'theme_mode';
static const int keyLength = 32;
static const int ivLength = 16;
@@ -33,6 +35,20 @@ class AppSharedPreferences {
static const String filteredExamsTypes = 'filtered_exam_types';
static final List defaultFilteredExamTypes = Exam.displayedTypes;
+ /// Returns the last time the data with given key was updated.
+ static Future getLastDataClassUpdateTime(String dataKey) async {
+ final prefs = await SharedPreferences.getInstance();
+ final lastUpdateTime = prefs.getString(dataKey + lastUpdateTimeKeySuffix);
+ return lastUpdateTime != null ? DateTime.parse(lastUpdateTime) : null;
+ }
+
+ /// Sets the last time the data with given key was updated.
+ static Future setLastDataClassUpdateTime(
+ String dataKey, DateTime dateTime) async {
+ final prefs = await SharedPreferences.getInstance();
+ prefs.setString(dataKey + lastUpdateTimeKeySuffix, dateTime.toString());
+ }
+
/// Saves the user's student number, password and faculties.
static Future savePersistentUserInfo(user, pass, faculties) async {
final prefs = await SharedPreferences.getInstance();
@@ -203,14 +219,13 @@ class AppSharedPreferences {
return encrypt.Encrypter(encrypt.AES(key));
}
- static Future getTuitionNotificationToggle() async{
+ static Future getTuitionNotificationToggle() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getBool(tuitionNotificationsToggleKey) ?? true;
}
- static setTuitionNotificationToggle(bool value) async{
+ static setTuitionNotificationToggle(bool value) async {
final prefs = await SharedPreferences.getInstance();
prefs.setBool(tuitionNotificationsToggleKey, value);
}
-
}
diff --git a/uni/lib/controller/local_storage/app_user_database.dart b/uni/lib/controller/local_storage/app_user_database.dart
index a8c728bc6..b734359ca 100644
--- a/uni/lib/controller/local_storage/app_user_database.dart
+++ b/uni/lib/controller/local_storage/app_user_database.dart
@@ -31,13 +31,16 @@ class AppUserDataDatabase extends AppDatabase {
final List