Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
Expand Up @@ -18,7 +18,7 @@ class CourseUnitsInfoFetcher implements SessionDependantFetcher {
}

Future<Sheet> fetchSheet(Session session, int occurId) async {
//TODO: Through this link we can't retrieve the sheet of a course unit in english
// TODO: Through this link we can't retrieve the sheet of a course unit in english
final responses = await Future.wait(
getEndpoints(session)
.map(
Expand Down Expand Up @@ -71,47 +71,63 @@ class CourseUnitsInfoFetcher implements SessionDependantFetcher {
Session session,
int occurrId,
) async {
var courseUnitClasses = <CourseUnitClass>[];
final endpoints = getEndpoints(session);
final allCourseChoices = await Future.wait(
endpoints.map((endpoint) {
final url =
'$endpoint'
'it_listagem.lista_cursos_disciplina?pv_ocorrencia_id=$occurrId';
return NetworkRouter.getWithCookies(url, {}, session)
.then((res) => (endpoint, res))
.catchError((_) => (endpoint, Response('', 500)));
}),
);

final classUrls = <(String, String)>{};
for (final (endpoint, response) in allCourseChoices) {
if (response.statusCode != 200) {
continue;
}

for (final endpoint in getEndpoints(session)) {
// Crawl classes from all courses that the course unit is offered in
final courseChoiceUrl =
'$endpoint'
'it_listagem.lista_cursos_disciplina?pv_ocorrencia_id=$occurrId';
final courseChoiceResponse = await NetworkRouter.getWithCookies(
courseChoiceUrl,
{},
session,
);
final courseChoiceDocument = parse(courseChoiceResponse.body);
final urls = courseChoiceDocument
final document = parse(response.body);
final links = document
.querySelectorAll('a')
.where(
(element) =>
element.attributes['href'] != null &&
element.attributes['href']!.contains(
(e) =>
e.attributes['href']?.contains(
'it_listagem.lista_turma_disciplina',
),
)
.map((e) {
var url = e.attributes['href']!;
if (!url.contains('sigarra.up.pt')) {
url = endpoint + url;
}
return url;
})
.toList();
) ??
false,
);

for (final url in urls) {
try {
final response = await NetworkRouter.getWithCookies(url, {}, session);
courseUnitClasses += parseCourseUnitClasses(response, endpoint);
} catch (_) {
continue;
for (final link in links) {
var url = link.attributes['href']!;
if (!url.contains('sigarra.up.pt')) {
url = endpoint + url;
}
classUrls.add((endpoint, url));
}
}

final classResponses = await Future.wait(
classUrls.map(
(item) => NetworkRouter.getWithCookies(item.$2, {}, session)
.then((res) => (item.$1, res))
.catchError((_) => (item.$1, Response('', 500))),
),
);

final Map<String, CourseUnitClass> classesByName = {};
for (final (endpoint, response) in classResponses) {
if (response.statusCode != 200) {
continue;
}
final parsedClasses = parseCourseUnitClasses(response, endpoint);
for (final c in parsedClasses) {
classesByName[c.className] = c;
}
}

return courseUnitClasses;
return classesByName.values.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -142,28 +142,53 @@ List<CourseUnitClass> parseCourseUnitClasses(
) {
final classes = <CourseUnitClass>[];
final document = parse(response.body);
final titles = document.querySelectorAll('#conteudoinner h3').sublist(1);
final titles = document.querySelectorAll('#conteudoinner h3');

if (titles.isEmpty) {
return [];
}

for (final title in titles) {
final titleText = title.text.trim();
if (!titleText.contains('Turma') && !titleText.contains('Class')) {
continue;
}

final parts = titleText.split(RegExp(r'\s+'));
if (parts.length < 2) {
continue;
}
final className = parts[1].replaceAll('&nbsp;', '').trim();

final table = title.nextElementSibling;
final className = title.innerHtml.substring(
title.innerHtml.indexOf(' ') + 1,
title.innerHtml.indexOf('&'),
);
if (table == null || table.localName != 'table') {
continue;
}

final rows = table?.querySelectorAll('tr');
if (rows == null || rows.length < 2) {
final allRows = table.querySelectorAll('tr');
if (allRows.length < 2) {
continue;
}

final studentRows = rows.sublist(1);
final studentRows = allRows.sublist(1);
final students = <CourseUnitStudent>[];

for (final row in studentRows) {
final columns = row.querySelectorAll('td.k.t');
final studentName = columns[0].children[0].innerHtml;
final studentNumber = int.tryParse(columns[1].innerHtml.trim()) ?? 0;
final studentMail = columns[2].innerHtml;
final columns = row.querySelectorAll('td');
if (columns.length < 3) {
continue;
}

final nameLink = columns[0].querySelector('a');
final studentName = nameLink != null
? nameLink.text.trim()
: columns[0].text.trim();
final studentNumber = int.tryParse(columns[1].text.trim()) ?? 0;
final studentMail = columns[2].text.trim();

if (studentNumber == 0) {
continue;
}

final studentPhoto = Uri.parse(
'${baseUrl}fotografias_service.foto?pct_cod=$studentNumber',
Expand All @@ -182,7 +207,9 @@ List<CourseUnitClass> parseCourseUnitClasses(
);
}

classes.add(CourseUnitClass(className, students));
if (students.isNotEmpty) {
classes.add(CourseUnitClass(className, students));
}
}

return classes;
Expand Down
17 changes: 17 additions & 0 deletions packages/uni_app/lib/model/entities/course_units/course_unit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,23 @@ class CourseUnit {

Map<String, dynamic> toJson() => _$CourseUnitToJson(this);

@override
bool operator ==(Object other) {
if (identical(this, other)) {
return true;
}

return other is CourseUnit &&
other.occurrId == occurrId &&
other.name == name &&
other.schoolYear == schoolYear;
}

@override
int get hashCode {
return Object.hash(occurrId, name, schoolYear);
}

bool enrollmentIsValid() {
return status == 'V' || status == 'C';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@ class CourseUnitsInfoNotifier
);
}

@override
Future<CourseUnitsInfoState?> build() async {
return (
<CourseUnit, Sheet>{},
<CourseUnit, List<CourseUnitClass>>{},
<CourseUnit, List<CourseUnitFileDirectory>>{},
<CourseUnit, Map<String, List<Professor>>>{},
);
}

@override
Future<CourseUnitsInfoState?> loadFromStorage() async {
return (
Expand Down
7 changes: 3 additions & 4 deletions packages/uni_app/lib/view/academic_path/academic_path.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class AcademicPathPageViewState
S.of(context).nav_title(NavigationItem.navAcademicPath.route);

late TabController tabController;
late final List<Widget> _tabs;

@override
void initState() {
Expand All @@ -38,6 +39,7 @@ class AcademicPathPageViewState
length: 3,
initialIndex: widget.initialTabIndex,
);
_tabs = [const CoursesPage(), SchedulePage(), const ExamsPage()];
}

@override
Expand All @@ -61,10 +63,7 @@ class AcademicPathPageViewState

@override
Widget getBody(BuildContext context) {
return TabBarView(
controller: tabController,
children: [const CoursesPage(), SchedulePage(), const ExamsPage()],
);
return TabBarView(controller: tabController, children: _tabs);
}

@override
Expand Down
11 changes: 8 additions & 3 deletions packages/uni_app/lib/view/academic_path/courses_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@ class CoursesPage extends ConsumerStatefulWidget {
ConsumerState<CoursesPage> createState() => CoursesPageState();
}

class CoursesPageState extends ConsumerState<CoursesPage> {
class CoursesPageState extends ConsumerState<CoursesPage>
with AutomaticKeepAliveClientMixin {
static Locale? _lastLocale;

@override
bool get wantKeepAlive => true;

@override
void didChangeDependencies() {
super.didChangeDependencies();
Expand Down Expand Up @@ -123,8 +127,8 @@ class CoursesPageState extends ConsumerState<CoursesPage> {
return '???';
}

//TODO: This fix(finished courses the abbreviation is null) works when the
//app is in portuguese, but not in english. Where instead of LEIC it will be BICE.
// TODO: This fix(finished courses the abbreviation is null) works when the
// app is in portuguese, but not in english. Where instead of LEIC it will be BICE.
return course.name!
.replaceAll('Licenciatura', 'Licenciatura.')
.replaceAll('Mestrado', 'Mestrado.')
Expand All @@ -134,6 +138,7 @@ class CoursesPageState extends ConsumerState<CoursesPage> {

@override
Widget build(BuildContext context) {
super.build(context);
return DefaultConsumer<Profile>(
provider: profileProvider,
builder: (context, ref, profile) {
Expand Down
7 changes: 6 additions & 1 deletion packages/uni_app/lib/view/academic_path/exam_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,18 @@ class ExamsPage extends ConsumerStatefulWidget {
ConsumerState<ExamsPage> createState() => _ExamsPageState();
}

class _ExamsPageState extends ConsumerState<ExamsPage> {
class _ExamsPageState extends ConsumerState<ExamsPage>
with AutomaticKeepAliveClientMixin {
List<String> hiddenExams = PreferencesController.getHiddenExams();
Map<String, bool> filteredExamTypes =
PreferencesController.getFilteredExams();

@override
bool get wantKeepAlive => true;

@override
Widget build(BuildContext context) {
super.build(context);
/*
If we want to filters exams again
filteredExamTypes[Exam.getExamTypeLong(exam.examType)] ??
Expand Down
24 changes: 19 additions & 5 deletions packages/uni_app/lib/view/academic_path/schedule_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,36 @@ import 'package:uni/view/academic_path/widgets/no_classes_widget.dart';
import 'package:uni/view/academic_path/widgets/schedule_page_shimmer.dart';
import 'package:uni/view/academic_path/widgets/schedule_page_view.dart';

class SchedulePage extends ConsumerWidget {
class SchedulePage extends ConsumerStatefulWidget {
SchedulePage({super.key, DateTime? now}) : now = now ?? DateTime.now();

final DateTime now;

@override
Widget build(BuildContext context, WidgetRef ref) {
ConsumerState<SchedulePage> createState() => _SchedulePageState();
}

class _SchedulePageState extends ConsumerState<SchedulePage>
with AutomaticKeepAliveClientMixin {
@override
bool get wantKeepAlive => true;

@override
Widget build(BuildContext context) {
super.build(context);
return MediaQuery.removePadding(
context: context,
removeBottom: true,
child: DefaultConsumer<List<Lecture>>(
provider: lectureProvider,
builder: (context, ref, lectures) {
final startOfWeek = _getStartOfWeek(now, lectures);
final startOfWeek = _getStartOfWeek(widget.now, lectures);

return SchedulePageView(lectures, startOfWeek: startOfWeek, now: now);
return SchedulePageView(
lectures,
startOfWeek: startOfWeek,
now: widget.now,
);
},
nullContentWidget: LayoutBuilder(
builder: (context, constraints) => SingleChildScrollView(
Expand All @@ -36,7 +50,7 @@ class SchedulePage extends ConsumerWidget {
),
hasContent: (lectures) => lectures.isNotEmpty,
mapper: (lectures) {
final startOfWeek = _getStartOfWeek(now, lectures);
final startOfWeek = _getStartOfWeek(widget.now, lectures);
final endOfNextWeek = startOfWeek.add(const Duration(days: 14));

return lectures
Expand Down
Loading