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 5 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
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
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_file.dart';
import 'package:uni/model/entities/course_units/course_unit_sheet.dart';
import 'package:uni/model/entities/session.dart';

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

Future<List<Map<String, List<CourseUnitFile>>>> fetchCourseUnitFiles(
DGoiana marked this conversation as resolved.
Show resolved Hide resolved
Session session,
int occurId,
) async {
final urls =
getEndpoints(session).map((url) => '${url}mob_ucurr_geral.conteudos');
final responses = <Response>[];
for (final url in urls) {
final response = await NetworkRouter.getWithCookies(
url,
{
'pv_ocorrencia_id': occurId.toString(),
},
session,
);
responses.add(response);
}
return parseFilesMultipleRequests(responses, session);
}

Future<List<CourseUnitClass>> fetchCourseUnitClasses(
Session session,
int occurrId,
Expand Down
42 changes: 42 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,49 @@
import 'dart:convert';

import 'package:html/parser.dart';
import 'package:http/http.dart' as http;
import 'package:uni/model/entities/course_units/course_unit_class.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<Map<String, List<CourseUnitFile>>>> parseFilesMultipleRequests(
List<http.Response> responses,
Session session,
) async {
final files = <Map<String, List<CourseUnitFile>>>[];
for (final response in responses) {
files.add(await parseFiles(response, session));
}
return files;
}

Future<Map<String, List<CourseUnitFile>>> parseFiles(
http.Response response,
Session session,
) async {
final folders = <String, List<CourseUnitFile>>{};

final json = jsonDecode(response.body) as List<dynamic>;

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 Iterable) {
final fileName = file['nome'] as String;
final fileCode = file['codigo'];
final courseUnitFile = CourseUnitFile(
fileName,
'https://sigarra.up.pt/feup/pt/conteudos_service.conteudos_cont?pct_id=$fileCode',
);
files.add(courseUnitFile);
}
folders[item['nome'] as String] = files;
}
return folders;
}

Future<CourseUnitSheet> parseCourseUnitSheet(http.Response response) async {
final document = parse(response.body);
Expand Down
8 changes: 8 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,8 @@
class CourseUnitFile {
CourseUnitFile(
this.name,
this.url,
);
String name;
String url;
}
22 changes: 22 additions & 0 deletions uni/lib/model/providers/lazy/course_units_info_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:logger/logger.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_file.dart';
import 'package:uni/model/entities/course_units/course_unit_sheet.dart';
import 'package:uni/model/entities/profile.dart';
import 'package:uni/model/entities/session.dart';
Expand All @@ -15,13 +16,18 @@ class CourseUnitsInfoProvider extends StateProviderNotifier {
: super(dependsOnSession: true, cacheDuration: null, initialize: false);
final Map<CourseUnit, CourseUnitSheet> _courseUnitsSheets = {};
final Map<CourseUnit, List<CourseUnitClass>> _courseUnitsClasses = {};
final Map<CourseUnit, List<Map<String, List<CourseUnitFile>>>>
_courseUnitsFiles = {};

UnmodifiableMapView<CourseUnit, CourseUnitSheet> get courseUnitsSheets =>
UnmodifiableMapView(_courseUnitsSheets);

UnmodifiableMapView<CourseUnit, List<CourseUnitClass>>
get courseUnitsClasses => UnmodifiableMapView(_courseUnitsClasses);

UnmodifiableMapView<CourseUnit, List<Map<String, List<CourseUnitFile>>>>
get courseUnitsFiles => UnmodifiableMapView(_courseUnitsFiles);

Future<void> fetchCourseUnitSheet(
CourseUnit courseUnit,
Session session,
Expand Down Expand Up @@ -55,6 +61,22 @@ class CourseUnitsInfoProvider extends StateProviderNotifier {
updateStatus(RequestStatus.successful);
}

Future<void> fetchCourseUnitFiles(
CourseUnit courseUnit,
Session session,
) async {
updateStatus(RequestStatus.busy);
try {
_courseUnitsFiles[courseUnit] = await CourseUnitsInfoFetcher()
.fetchCourseUnitFiles(session, courseUnit.occurrId);
} catch (e) {
updateStatus(RequestStatus.failed);
Logger().e('Failed to get course unit files for ${courseUnit.name}: $e');
return;
}
updateStatus(RequestStatus.successful);
}

@override
Future<void> loadFromRemote(Session session, Profile profile) async {
// Course units info is loaded on demand by its detail page
Expand Down
42 changes: 40 additions & 2 deletions uni/lib/view/course_unit_info/course_unit_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:uni/view/common_widgets/page_title.dart';
import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart';
import 'package:uni/view/common_widgets/request_dependent_widget_builder.dart';
import 'package:uni/view/course_unit_info/widgets/course_unit_classes.dart';
import 'package:uni/view/course_unit_info/widgets/course_unit_files.dart';
import 'package:uni/view/course_unit_info/widgets/course_unit_sheet.dart';
import 'package:uni/view/lazy_consumer.dart';

Expand Down Expand Up @@ -37,6 +38,15 @@ class CourseUnitDetailPageViewState
);
}

final courseUnitFiles =
courseUnitsProvider.courseUnitsFiles[widget.courseUnit];
if (courseUnitFiles == null || force) {
await courseUnitsProvider.fetchCourseUnitFiles(
widget.courseUnit,
session,
);
}
DGoiana marked this conversation as resolved.
Show resolved Hide resolved

final courseUnitClasses =
courseUnitsProvider.courseUnitsClasses[widget.courseUnit];
if (courseUnitClasses == null || force) {
Expand All @@ -60,7 +70,7 @@ class CourseUnitDetailPageViewState
@override
Widget getBody(BuildContext context) {
return DefaultTabController(
length: 2,
length: 3,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expand All @@ -71,7 +81,10 @@ class CourseUnitDetailPageViewState
TabBar(
tabs: [
Tab(text: S.of(context).course_info),
Tab(text: S.of(context).course_class)
Tab(text: S.of(context).course_class),
Tab(
text: 'Ficheiros',
DGoiana marked this conversation as resolved.
Show resolved Hide resolved
)
],
),
Expanded(
Expand All @@ -81,6 +94,7 @@ class CourseUnitDetailPageViewState
children: [
_courseUnitSheetView(context),
_courseUnitClassesView(context),
_courseUnitFilesView(context),
],
),
),
Expand Down Expand Up @@ -114,6 +128,30 @@ class CourseUnitDetailPageViewState
);
}

Widget _courseUnitFilesView(BuildContext context) {
return LazyConsumer<CourseUnitsInfoProvider>(
builder: (context, courseUnitsInfoProvider) {
return RequestDependentWidgetBuilder(
onNullContent: const Center(
child: Text(
'Não existem informações para apresentar',
textAlign: TextAlign.center,
),
),
status: courseUnitsInfoProvider.status,
builder: () => CourseUnitFilesView(
courseUnitsInfoProvider.courseUnitsFiles[widget.courseUnit]!,
),
hasContentPredicate:
courseUnitsInfoProvider.courseUnitsFiles[widget.courseUnit] !=
null &&
courseUnitsInfoProvider
.courseUnitsFiles[widget.courseUnit]!.isNotEmpty,
);
},
);
}

Widget _courseUnitClassesView(BuildContext context) {
return LazyConsumer<CourseUnitsInfoProvider>(
builder: (context, courseUnitsInfoProvider) {
Expand Down
31 changes: 31 additions & 0 deletions uni/lib/view/course_unit_info/widgets/course_unit_files.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
import 'package:uni/model/entities/course_units/course_unit_file.dart';
import 'package:uni/view/course_unit_info/widgets/course_unit_info_card.dart';
import 'package:uni/view/course_unit_info/widgets/course_unit_files_row.dart';

class CourseUnitFilesView extends StatelessWidget {
const CourseUnitFilesView(this.files, {Key? key}) : super(key: key);
final List<Map<String, List<CourseUnitFile>>> files;

@override
Widget build(BuildContext context) {
final cards = files
.expand((file) =>
file.entries.map((item) => _buildCard(item.key, item.value)))
.toList();

return Container(
padding: const EdgeInsets.only(left: 10, right: 10),
child: ListView(children: cards),
);
}

CourseUnitInfoCard _buildCard(String folder, List<CourseUnitFile> files) {
DGoiana marked this conversation as resolved.
Show resolved Hide resolved
return CourseUnitInfoCard(
folder,
Column(
children: files.map((file) => CourseUnitFilesRow(file)).toList(),
),
);
}
}
43 changes: 43 additions & 0 deletions uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
import 'package:uni/model/entities/course_units/course_unit_file.dart';
import 'package:url_launcher/url_launcher.dart';

class CourseUnitFilesRow extends StatelessWidget {
const CourseUnitFilesRow(this.file, {Key? key}) : super(key: key);

final CourseUnitFile file;

@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.only(bottom: 10),
child: Row(
children: [
Expanded(
child: Container(
padding: const EdgeInsets.only(left: 10),
child: Text(
file.name,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodyLarge,
),
),
),
IconButton(
icon: Icon(Icons.download),
onPressed: () => _launchURL(file.url),
),
],
),
);
}

Future<void> _launchURL(String url) async {
if (await canLaunchUrl(Uri.parse(url))) {
await launchUrl(Uri.parse(url));
} else {
// Handle the case when the URL cannot be launched
// For example, show an error message
}
}
}
Loading