Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sigarra files api #963

Merged
merged 43 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
be39832
Download feature
DGoiana Sep 23, 2023
30844be
Merge branch 'develop' into feature/sigarra-files
DGoiana Sep 24, 2023
cb4cc3c
Files page redesign
DGoiana Sep 24, 2023
80b4ec0
Merging
DGoiana Sep 24, 2023
560588f
Merge branch 'develop' into feature/sigarra-files
DGoiana Sep 25, 2023
3e057de
Pdf icon and open file support
DGoiana Sep 28, 2023
2c93601
Merge branch 'develop' into feature/sigarra-files
DGoiana Sep 28, 2023
40c2924
Merge branch 'develop' into feature/sigarra-files
DGoiana Sep 30, 2023
b3bbc32
Lint fix
DGoiana Oct 1, 2023
f2e2ae8
Merge branch 'develop' into feature/sigarra-files
DGoiana Oct 1, 2023
776f8a7
Merge
DGoiana Oct 1, 2023
7f2b715
Merge branch 'develop' into feature/sigarra-files
DGoiana Oct 4, 2023
041e365
Merge branch 'develop' into feature/sigarra-files
DGoiana Oct 5, 2023
3fccae6
Opening file fix and dependency update
DGoiana Oct 5, 2023
8ec07b6
Local storage integration
DGoiana Oct 5, 2023
22dba72
Card redesign
DGoiana Oct 5, 2023
9b0cbf5
Merge branch 'develop' into feature/sigarra-files
DGoiana Oct 11, 2023
4613ceb
File name changes
DGoiana Oct 13, 2023
c4b8d9c
Cache clean up flow
DGoiana Oct 20, 2023
6a23a9c
Merge branch 'develop' into feature/sigarra-files
DGoiana Oct 20, 2023
68ef599
Merge branch 'develop' into feature/sigarra-files
DGoiana Nov 18, 2023
c8608ad
Implementing text animation and improving cleaning flow
DGoiana Dec 1, 2023
32325d0
Fixing format
DGoiana Dec 1, 2023
c8c97b4
Catching a timeout error while opening files
DGoiana Dec 8, 2023
ad4991a
Merge branch 'develop' into feature/sigarra-files
DGoiana Dec 8, 2023
5a2df58
Lint fix
DGoiana Dec 8, 2023
d0d8703
Merge branch 'develop' into feature/sigarra-files
DGoiana Dec 9, 2023
7fc226b
Merge branch 'develop' into feature/sigarra-files
DGoiana Dec 24, 2023
ebbc3fe
Translating and abstracting code
DGoiana Jan 4, 2024
b889aba
Merging
DGoiana Jan 5, 2024
172483e
Merging
DGoiana Jan 6, 2024
de8ef9b
Fixing minor url launcher problem
DGoiana Jan 6, 2024
4da11d3
Formatting
DGoiana Jan 6, 2024
39109db
Lint fix
DGoiana Jan 6, 2024
633f182
Typo and synchronous getter
DGoiana Jan 8, 2024
7b70b72
Merge branch 'develop' into feature/sigarra-files
DGoiana Jan 26, 2024
bb86a22
Merge branch 'develop' into feature/sigarra-files
DGoiana Jan 29, 2024
2ebe982
Merge branch 'develop' into feature/sigarra-files
DGoiana Feb 2, 2024
5bf36c6
Solving files format
DGoiana Feb 2, 2024
9d64ed0
Merge branch 'develop' into feature/sigarra-files
DGoiana Feb 8, 2024
c6b8334
Encapsulating pulse animation
DGoiana Feb 9, 2024
87df88e
Handling different opening results
DGoiana Feb 12, 2024
fd5f6de
Merge branch 'develop' into feature/sigarra-files
DGoiana Feb 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions uni/lib/controller/cleanup.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,38 @@ Future<void> cleanupStoredData(BuildContext context) async {
directory.deleteSync(recursive: true);
}
}

Future<void> cleanupCachedFiles() async {
final lastCleanupDate = await PreferencesController.getLastCleanUpDate();
final daysSinceLastCleanup =
DateTime.now().difference(lastCleanupDate).inDays;

if (daysSinceLastCleanup < 14) {
return;
}

final toCleanDirectory = await getApplicationDocumentsDirectory();
final treshold = DateTime.now().subtract(const Duration(days: 30));
DGoiana marked this conversation as resolved.
Show resolved Hide resolved
final directories = toCleanDirectory.listSync(followLinks: false);

for (final directory in directories) {
if (directory is Directory) {
final files = directory.listSync(recursive: true, followLinks: false);

final oldFiles = files.where((file) {
try {
final fileDate = File(file.path).lastModifiedSync();
return fileDate.isBefore(treshold);
} catch (e) {
return false;
}
});

for (final file in oldFiles) {
await File(file.path).delete();
}
}
}

await PreferencesController.setLastCleanUpDate(DateTime.now());
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ 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_directory.dart';
import 'package:uni/model/entities/course_units/course_unit_sheet.dart';
import 'package:uni/model/entities/session.dart';

Expand All @@ -26,6 +27,27 @@ class CourseUnitsInfoFetcher implements SessionDependantFetcher {
return parseCourseUnitSheet(response);
}

Future<List<CourseUnitFileDirectory>> fetchCourseUnitFiles(
Session session,
int occurId,
) async {
final url = '${getEndpoints(session)[0]}mob_ucurr_geral.conteudos';
final response = await NetworkRouter.getWithCookies(
url,
{
'pv_ocorrencia_id': occurId.toString(),
},
session,
);
return parseFiles(response, session);
}

Future<String> getDownloadLink(
Session session,
) async {
return '${getEndpoints(session)[0]}conteudos_service.conteudos_cont';
}

Future<List<CourseUnitClass>> fetchCourseUnitClasses(
Session session,
int occurrId,
Expand Down
6 changes: 4 additions & 2 deletions uni/lib/controller/local_storage/file_offline_storage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import 'package:uni/model/entities/session.dart';

/// The offline image storage location on the device.
Future<String> get _localPath async {
final directory = await getTemporaryDirectory();
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}

Expand Down Expand Up @@ -67,9 +67,11 @@ Future<File?> _downloadAndSaveFile(
Session? session,
Map<String, String>? headers,
) async {
final header = headers ?? <String, String>{};

final response = session == null
? await http.get(url.toUri(), headers: headers)
: await NetworkRouter.getWithCookies(url, {}, session);
: await NetworkRouter.getWithCookies(url, header, session);

if (response.statusCode == 200) {
return File(filePath).writeAsBytes(response.bodyBytes);
Expand Down
13 changes: 13 additions & 0 deletions uni/lib/controller/local_storage/preferences_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class PreferencesController {
static const String _isDataCollectionBannerViewedKey =
'data_collection_banner';
static const String _locale = 'app_locale';
static const String _lastCacheCleanUpDate = 'last_clean';
static const String _favoriteCards = 'favorite_cards';
static final List<FavoriteWidgetType> _defaultFavoriteCards = [
FavoriteWidgetType.schedule,
Expand Down Expand Up @@ -139,6 +140,18 @@ class PreferencesController {
);
}

static Future<void> setLastCleanUpDate(DateTime date) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_lastCacheCleanUpDate, date.toString());
}

static Future<DateTime> getLastCleanUpDate() async {
final prefs = await SharedPreferences.getInstance();
DGoiana marked this conversation as resolved.
Show resolved Hide resolved
final date =
prefs.getString(_lastCacheCleanUpDate) ?? DateTime.now().toString();
return DateTime.parse(date);
}

/// Deletes the user's student number and password.
static Future<void> removePersistentUserInfo() async {
await prefs.remove(_userNumber);
Expand Down
36 changes: 36 additions & 0 deletions uni/lib/controller/parsers/parser_course_unit_info.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,43 @@
import 'dart:convert';

import 'package:html/parser.dart';
import 'package:http/http.dart' as http;
import 'package:uni/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart';
import 'package:uni/model/entities/course_units/course_unit_class.dart';
import 'package:uni/model/entities/course_units/course_unit_directory.dart';
import 'package:uni/model/entities/course_units/course_unit_file.dart';
import 'package:uni/model/entities/course_units/course_unit_sheet.dart';
import 'package:uni/model/entities/session.dart';

Future<List<CourseUnitFileDirectory>> parseFiles(
http.Response response,
Session session,
) async {
final json = jsonDecode(response.body) as List<dynamic>;
final dirs = <CourseUnitFileDirectory>[];
if (json.isEmpty) return [];

for (var item in json) {
item = item as Map<String, dynamic>;
final files = <CourseUnitFile>[];
for (final file in item['ficheiros'] as List<dynamic>) {
if (file is Map<String, dynamic>) {
final fileName = file['nome'].toString();
final fileDate = file['data_actualizacao'].toString();
final fileCode = file['codigo'].toString();
final url = await CourseUnitsInfoFetcher().getDownloadLink(session);
final courseUnitFile = CourseUnitFile(
'${fileName}_$fileDate',
url,
fileCode,
);
files.add(courseUnitFile);
}
}
dirs.add(CourseUnitFileDirectory(item['nome'].toString(), files));
}
return dirs;
}

Future<CourseUnitSheet> parseCourseUnitSheet(http.Response response) async {
final document = parse(response.body);
Expand Down
7 changes: 5 additions & 2 deletions uni/lib/generated/intl/messages_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("Deadline for next fee:"),
"fee_notification":
MessageLookupByLibrary.simpleMessage("Fee deadline"),
"files": MessageLookupByLibrary.simpleMessage("Files"),
"first_year_registration": MessageLookupByLibrary.simpleMessage(
"Year of first registration: "),
"floor": MessageLookupByLibrary.simpleMessage("Floor"),
Expand Down Expand Up @@ -185,12 +186,14 @@ class MessageLookup extends MessageLookupByLibrary {
"Looks like you are on vacation!"),
"no_favorite_restaurants":
MessageLookupByLibrary.simpleMessage("No favorite restaurants"),
"no_files_found":
MessageLookupByLibrary.simpleMessage("No files found"),
"no_info": MessageLookupByLibrary.simpleMessage(
"There is no information to display"),
"no_link":
MessageLookupByLibrary.simpleMessage("We couldn\'t open the link"),
"no_library_info": MessageLookupByLibrary.simpleMessage(
"No library occupation information available"),
"no_link":
MessageLookupByLibrary.simpleMessage("We couldn\'t open the link"),
"no_menu_info": MessageLookupByLibrary.simpleMessage(
"There is no information available about meals"),
"no_menus": MessageLookupByLibrary.simpleMessage(
Expand Down
7 changes: 5 additions & 2 deletions uni/lib/generated/intl/messages_pt_PT.dart
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Data limite próxima prestação:"),
"fee_notification":
MessageLookupByLibrary.simpleMessage("Data limite de propina"),
"files": MessageLookupByLibrary.simpleMessage("Ficheiros"),
"first_year_registration":
MessageLookupByLibrary.simpleMessage("Ano da primeira inscrição: "),
"floor": MessageLookupByLibrary.simpleMessage("Piso"),
Expand Down Expand Up @@ -186,12 +187,14 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("Parece que estás de férias!"),
"no_favorite_restaurants":
MessageLookupByLibrary.simpleMessage("Sem restaurantes favoritos"),
"no_files_found":
MessageLookupByLibrary.simpleMessage("Nenhum ficheiro encontrado"),
"no_info": MessageLookupByLibrary.simpleMessage(
"Não existem informações para apresentar"),
"no_link": MessageLookupByLibrary.simpleMessage(
"Não conseguimos abrir o link"),
"no_library_info":
MessageLookupByLibrary.simpleMessage("Sem informação de ocupação"),
"no_link": MessageLookupByLibrary.simpleMessage(
"Não conseguimos abrir o link"),
"no_menu_info": MessageLookupByLibrary.simpleMessage(
"Não há informação disponível sobre refeições"),
"no_menus": MessageLookupByLibrary.simpleMessage(
Expand Down
20 changes: 20 additions & 0 deletions uni/lib/generated/l10n.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions uni/lib/l10n/intl_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@
"@of_month": {},
"no_link": "We couldn't open the link",
"@no_link": {},
"no_files_found": "No files found",
"@no_files_found": {},
"other_links": "Other links",
"@other_links": {},
"pass_change_request": "For security reasons, passwords must be changed periodically.",
Expand Down Expand Up @@ -252,6 +254,8 @@
"@restaurant_main_page": {},
"room": "Room",
"@room": {},
"files": "Files",
"@files": {},
"school_calendar": "School Calendar",
"@school_calendar": {},
"semester": "Semester",
Expand Down
4 changes: 4 additions & 0 deletions uni/lib/l10n/intl_pt_PT.arb
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@
"@of_month": {},
"no_link": "Não conseguimos abrir o link",
"@no_link": {},
"no_files_found": "Nenhum ficheiro encontrado",
"@no_files_found": {},
"other_links": "Outros links",
"@other_links": {},
"pass_change_request": "Por razões de segurança, as palavras-passe têm de ser alteradas periodicamente.",
Expand Down Expand Up @@ -252,6 +254,8 @@
"@restaurant_main_page": {},
"room": "Sala",
"@room": {},
"files": "Ficheiros",
"@files": {},
"school_calendar": "Calendário Escolar",
"@school_calendar": {},
"semester": "Semestre",
Expand Down
3 changes: 3 additions & 0 deletions uni/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:provider/provider.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uni/controller/background_workers/background_callback.dart';
import 'package:uni/controller/cleanup.dart';
import 'package:uni/controller/fetchers/terms_and_conditions_fetcher.dart';
import 'package:uni/controller/local_storage/preferences_controller.dart';
import 'package:uni/generated/l10n.dart';
Expand Down Expand Up @@ -77,6 +78,8 @@ Future<void> main() async {
ReferenceProvider(),
);

unawaited(cleanupCachedFiles());

// Initialize WorkManager for background tasks
await Workmanager().initialize(
workerStartCallback,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import 'package:uni/model/entities/course_units/course_unit_file.dart';

class CourseUnitFileDirectory {
CourseUnitFileDirectory(this.folderName, this.files);

final String folderName;
final List<CourseUnitFile> files;
}
10 changes: 10 additions & 0 deletions uni/lib/model/entities/course_units/course_unit_file.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class CourseUnitFile {
CourseUnitFile(
this.name,
this.url,
this.fileCode,
);
String fileCode;
String name;
String url;
}
25 changes: 19 additions & 6 deletions uni/lib/model/providers/lazy/course_units_info_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,25 @@ import 'package:tuple/tuple.dart';
import 'package:uni/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart';
import 'package:uni/model/entities/course_units/course_unit.dart';
import 'package:uni/model/entities/course_units/course_unit_class.dart';
import 'package:uni/model/entities/course_units/course_unit_directory.dart';
import 'package:uni/model/entities/course_units/course_unit_sheet.dart';
import 'package:uni/model/entities/session.dart';
import 'package:uni/model/providers/state_provider_notifier.dart';
import 'package:uni/model/providers/state_providers.dart';

typedef SheetsMap = Map<CourseUnit, CourseUnitSheet>;
typedef ClassesMap = Map<CourseUnit, List<CourseUnitClass>>;
typedef FilesMap = Map<CourseUnit, List<CourseUnitFileDirectory>>;

class CourseUnitsInfoProvider
extends StateProviderNotifier<Tuple2<SheetsMap, ClassesMap>> {
extends StateProviderNotifier<Tuple3<SheetsMap, ClassesMap, FilesMap>> {
CourseUnitsInfoProvider()
: super(
cacheDuration: null,
// Const constructor is not allowed here because of the
// need for mutable maps
// ignore: prefer_const_constructors
initialState: Tuple2({}, {}),
initialState: Tuple3({}, {}, {}),
);

UnmodifiableMapView<CourseUnit, CourseUnitSheet> get courseUnitsSheets =>
Expand All @@ -29,6 +31,9 @@ class CourseUnitsInfoProvider
UnmodifiableMapView<CourseUnit, List<CourseUnitClass>>
get courseUnitsClasses => UnmodifiableMapView(state!.item2);

UnmodifiableMapView<CourseUnit, List<CourseUnitFileDirectory>>
get courseUnitsFiles => UnmodifiableMapView(state!.item3);

Future<void> fetchCourseUnitSheet(
CourseUnit courseUnit,
Session session,
Expand All @@ -46,17 +51,25 @@ class CourseUnitsInfoProvider
.fetchCourseUnitClasses(session, courseUnit.occurrId);
}

Future<void> fetchCourseUnitFiles(
CourseUnit courseUnit,
Session session,
) async {
state!.item3[courseUnit] = await CourseUnitsInfoFetcher()
.fetchCourseUnitFiles(session, courseUnit.occurrId);
}

@override
Future<Tuple2<SheetsMap, ClassesMap>> loadFromRemote(
Future<Tuple3<SheetsMap, ClassesMap, FilesMap>> loadFromRemote(
StateProviders stateProviders,
) async {
return const Tuple2({}, {});
return const Tuple3({}, {}, {});
}

@override
Future<Tuple2<SheetsMap, ClassesMap>> loadFromStorage(
Future<Tuple3<SheetsMap, ClassesMap, FilesMap>> loadFromStorage(
StateProviders stateProviders,
) async {
return const Tuple2({}, {});
return const Tuple3({}, {}, {});
}
}
Loading
Loading