Skip to content
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
2ea4096
modified ScheduleFetcherNewApi to become an abstract class and create…
maluviieira Dec 15, 2025
b49089e
CourseUnitClassesView now receives a list of professors
maluviieira Dec 15, 2025
0a28fe3
created _fetchClassProfessors in course_unit_classes (also pasing cou…
maluviieira Dec 15, 2025
a1b5123
added _buildClassProfessor and the professor name now appears in each…
maluviieira Dec 15, 2025
9ddf2a8
added InstructorCard styling form professors in class page
maluviieira Dec 15, 2025
15ffeb6
changed professor position to 'Class Teacher' and made the card a squ…
maluviieira Dec 15, 2025
9acb437
formatted code
maluviieira Dec 15, 2025
178391f
formatted code again
maluviieira Dec 15, 2025
e989af4
added portuguese translation for 'class professor'
maluviieira Dec 16, 2025
402855f
fixed: class professor not appearing in joint classes
maluviieira Dec 16, 2025
8e8dbfd
changed logic so that professores are fetched before loading the clas…
maluviieira Dec 17, 2025
d0e94b7
classes with two professors now show both in the page
maluviieira Dec 17, 2025
a78c721
formatted code
maluviieira Dec 17, 2025
5991099
fixed problem with lint (loading classes separated from info)
maluviieira Dec 17, 2025
0c8195a
Merge branch 'develop' into feat/class-teacher
pedroafmonteiro Jan 22, 2026
c14918c
fixed: professors not appearing for past course units
maluviieira Jan 28, 2026
e3ef5b3
removed unnecessary period parameter
maluviieira Jan 28, 2026
cde7712
Merge branch 'feat/class-teacher' of github.com:NIAEFEUP/uni into fea…
maluviieira Jan 28, 2026
328faec
Merge branch 'develop' into feat/class-teacher
pedroafmonteiro Jan 29, 2026
4392135
fixed: professor not appearing in old course units for conjoined classes
maluviieira Jan 29, 2026
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 @@ -4,14 +4,18 @@ import 'package:uni/controller/parsers/schedule/new_api/parser.dart';
import 'package:uni/model/entities/lecture.dart';
import 'package:uni/session/flows/base/session.dart';

/// Class for fetching the user's lectures from the schedule's HTML page.
class ScheduleFetcherNewApi extends ScheduleFetcher {
/// Abstract base class for fetching lectures from the schedule's HTML page.
abstract class ScheduleFetcherNewApiBase extends ScheduleFetcher {
String getEndpointView();

Map<String, String> getQueryParams(Session session);

@override
List<String> getEndpoints(Session session) {
final urls =
NetworkRouter.getBaseUrlsFromSession(
session,
).map((url) => '${url}hor_geral.estudantes_view').toList();
).map((url) => '${url}hor_geral.${getEndpointView()}').toList();
return urls;
}

Expand All @@ -22,7 +26,7 @@ class ScheduleFetcherNewApi extends ScheduleFetcher {
final lectiveYear = getLectiveYear(DateTime.now());

final scheduleResponse = await NetworkRouter.getWithCookies(url, {
'pv_num_unico': session.username,
...getQueryParams(session),
'pv_ano_lectivo': lectiveYear.toString(),
'pv_periodos': '1',
}, session);
Expand All @@ -47,3 +51,28 @@ class ScheduleFetcherNewApi extends ScheduleFetcher {
return lectures;
}
}

/// Class for fetching student lectures from the schedule's HTML page.
class ScheduleFetcherNewApi extends ScheduleFetcherNewApiBase {
@override
String getEndpointView() => 'estudantes_view';

@override
Map<String, String> getQueryParams(Session session) => {
'pv_num_unico': session.username,
};
}

/// Class for fetching professor lectures from the schedule's HTML page.
class ScheduleFetcherNewApiProfessor extends ScheduleFetcherNewApiBase {
ScheduleFetcherNewApiProfessor({required this.professorCode});
final String professorCode;

@override
String getEndpointView() => 'docentes_view';

@override
Map<String, String> getQueryParams(Session session) => {
'pv_doc_codigo': professorCode,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,7 @@ List<Lecture> getLecturesFromApiResponse(http.Response response) {
lecture.persons.map((person) => person.acronym).join('+'),
_filterTeacherName(lecture.persons.first.name),
_filterTeacherCode(lecture.persons.first.name),
lecture.classes.length > 1
? '${lecture.classes.first.acronym} + ${lecture.classes.length - 1}'
: lecture.classes.first.acronym,
lecture.classes.first.acronym,
lecture.units.first.sigarraId,
),
)
Expand Down
1 change: 1 addition & 0 deletions packages/uni_app/lib/generated/intl/messages_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ class MessageLookup extends MessageLookupByLibrary {
"check_internet": MessageLookupByLibrary.simpleMessage(
"Check your internet connection",
),
"classProfessor": MessageLookupByLibrary.simpleMessage("Class Professor"),
"class_registration": MessageLookupByLibrary.simpleMessage(
"Class Registration",
),
Expand Down
3 changes: 3 additions & 0 deletions packages/uni_app/lib/generated/intl/messages_pt_PT.dart
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ class MessageLookup extends MessageLookupByLibrary {
"check_internet": MessageLookupByLibrary.simpleMessage(
"Verifica a tua ligação à internet",
),
"classProfessor": MessageLookupByLibrary.simpleMessage(
"Professor da Turma",
),
"class_registration": MessageLookupByLibrary.simpleMessage(
"Inscrição de Turmas",
),
Expand Down
10 changes: 10 additions & 0 deletions packages/uni_app/lib/generated/l10n.dart

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

2 changes: 2 additions & 0 deletions packages/uni_app/lib/l10n/intl_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,8 @@
"@courseRegent": {},
"instructor": "Instructor",
"@instructor": {},
"classProfessor": "Class Professor",
"@classProfessor": {},
"lectures": "Lectures",
"@lectures": {},
"exams": "Exams",
Expand Down
2 changes: 2 additions & 0 deletions packages/uni_app/lib/l10n/intl_pt_PT.arb
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,8 @@
"@courseRegent": {},
"instructor": "Docente",
"@instructor": {},
"classProfessor": "Professor da Turma",
"@classProfessor": {},
"lectures": "Aulas",
"@lectures": {},
"exams": "Exames",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:collection';

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:uni/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart';
import 'package:uni/controller/fetchers/schedule_fetcher/schedule_fetcher_new_api.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';
Expand All @@ -12,7 +13,9 @@ import 'package:uni/model/providers/riverpod/session_provider.dart';
typedef SheetsMap = Map<CourseUnit, Sheet>;
typedef ClassesMap = Map<CourseUnit, List<CourseUnitClass>>;
typedef FilesMap = Map<CourseUnit, List<CourseUnitFileDirectory>>;
typedef CourseUnitsInfoState = (SheetsMap, ClassesMap, FilesMap);
typedef ClassProfessorsMap = Map<CourseUnit, Map<String, List<Professor>>>;
typedef CourseUnitsInfoState =
(SheetsMap, ClassesMap, FilesMap, ClassProfessorsMap);

final courseUnitsInfoProvider =
AsyncNotifierProvider<CourseUnitsInfoNotifier, CourseUnitsInfoState?>(
Expand Down Expand Up @@ -45,12 +48,21 @@ class CourseUnitsInfoNotifier
);
}

UnmodifiableMapView<CourseUnit, Map<String, List<Professor>>>
get courseUnitsClassProfessors {
final currentState = state.value;
return UnmodifiableMapView(
currentState?.$4 ?? <CourseUnit, Map<String, List<Professor>>>{},
);
}

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

Expand All @@ -60,6 +72,7 @@ class CourseUnitsInfoNotifier
<CourseUnit, Sheet>{},
<CourseUnit, List<CourseUnitClass>>{},
<CourseUnit, List<CourseUnitFileDirectory>>{},
<CourseUnit, Map<String, List<Professor>>>{},
);
}

Expand All @@ -82,11 +95,17 @@ class CourseUnitsInfoNotifier
<CourseUnit, Sheet>{},
<CourseUnit, List<CourseUnitClass>>{},
<CourseUnit, List<CourseUnitFileDirectory>>{},
<CourseUnit, Map<String, List<Professor>>>{},
);

final updatedSheetsMap = Map<CourseUnit, Sheet>.from(currentState.$1);
updatedSheetsMap[courseUnit] = sheet;
updateState((updatedSheetsMap, currentState.$2, currentState.$3));
updateState((
updatedSheetsMap,
currentState.$2,
currentState.$3,
currentState.$4,
));
}

Future<void> fetchCourseUnitClasses(CourseUnit courseUnit) async {
Expand All @@ -111,13 +130,19 @@ class CourseUnitsInfoNotifier
<CourseUnit, Sheet>{},
<CourseUnit, List<CourseUnitClass>>{},
<CourseUnit, List<CourseUnitFileDirectory>>{},
<CourseUnit, Map<String, List<Professor>>>{},
);

final updatedClassesMap = Map<CourseUnit, List<CourseUnitClass>>.from(
currentState.$2,
);
updatedClassesMap[courseUnit] = classes;
updateState((currentState.$1, updatedClassesMap, currentState.$3));
updateState((
currentState.$1,
updatedClassesMap,
currentState.$3,
currentState.$4,
));
}

Future<void> fetchCourseUnitFiles(CourseUnit courseUnit) async {
Expand All @@ -142,12 +167,73 @@ class CourseUnitsInfoNotifier
<CourseUnit, Sheet>{},
<CourseUnit, List<CourseUnitClass>>{},
<CourseUnit, List<CourseUnitFileDirectory>>{},
<CourseUnit, Map<String, List<Professor>>>{},
);

final updatedFilesMap = Map<CourseUnit, List<CourseUnitFileDirectory>>.from(
currentState.$3,
);
updatedFilesMap[courseUnit] = files;
updateState((currentState.$1, currentState.$2, updatedFilesMap));
updateState((
currentState.$1,
currentState.$2,
updatedFilesMap,
currentState.$4,
));
}

Future<void> fetchClassProfessors(CourseUnit courseUnit) async {
final session = await ref.read(sessionProvider.future);
if (session == null) {
return;
}

final sheet = courseUnitsSheets[courseUnit];
if (sheet == null) {
return;
}

final professors = sheet.professors;
final Map<String, List<Professor>> classProfessors = {};
final courseAcronym = courseUnit.abbreviation;

for (final professor in professors) {
final fetcher = ScheduleFetcherNewApiProfessor(
professorCode: professor.code,
);
final lectures = await fetcher.getLectures(session);

for (final lecture in lectures) {
if (lecture.classNumber.isNotEmpty &&
lecture.acronym == courseAcronym &&
lecture.typeClass != 'T') {
if (!classProfessors.containsKey(lecture.classNumber)) {
classProfessors[lecture.classNumber] = [];
}
if (!classProfessors[lecture.classNumber]!.contains(professor)) {
classProfessors[lecture.classNumber]!.add(professor);
}
}
}
}

final currentState =
state.value ??
(
<CourseUnit, Sheet>{},
<CourseUnit, List<CourseUnitClass>>{},
<CourseUnit, List<CourseUnitFileDirectory>>{},
<CourseUnit, Map<String, List<Professor>>>{},
);

final updatedClassProfessorsMap =
Map<CourseUnit, Map<String, List<Professor>>>.from(currentState.$4);
updatedClassProfessorsMap[courseUnit] = classProfessors;
updateState((
currentState.$1,
currentState.$2,
currentState.$3,
updatedClassProfessorsMap,
));
}
}
59 changes: 50 additions & 9 deletions packages/uni_app/lib/view/course_unit_info/course_unit_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ class CourseUnitDetailPageViewState
void initState() {
super.initState();
tabController = TabController(vsync: this, length: 3);
tabController.addListener(_onTabChanged);
}

void _onTabChanged() {
if (tabController.index == 1) {
loadClasses(force: false);
}
}

Future<void> loadInfo({required bool force}) async {
Expand All @@ -52,17 +59,30 @@ class CourseUnitDetailPageViewState
if (courseUnitFiles == null || force) {
await courseUnitsProvider.fetchCourseUnitFiles(widget.courseUnit);
}
}

Future<void> loadClasses({required bool force}) async {
final courseUnitsProvider = ref.read(courseUnitsInfoProvider.notifier);

final courseUnitClasses =
courseUnitsProvider.courseUnitsClasses[widget.courseUnit];
if (courseUnitClasses == null || force) {
await courseUnitsProvider.fetchCourseUnitClasses(widget.courseUnit);
}

final courseUnitClassProfessors =
courseUnitsProvider.courseUnitsClassProfessors[widget.courseUnit];
if (courseUnitClassProfessors == null || force) {
await courseUnitsProvider.fetchClassProfessors(widget.courseUnit);
}
}

@override
Future<void> onRefresh() async {
await loadInfo(force: true);
if (tabController.index == 1) {
await loadClasses(force: true);
}
}

@override
Expand Down Expand Up @@ -151,17 +171,38 @@ class CourseUnitDetailPageViewState
}

Widget _courseUnitClassesView(BuildContext context) {
final classes =
ref.read(courseUnitsInfoProvider.notifier).courseUnitsClasses[widget
.courseUnit];
return Consumer(
builder: (context, ref, _) {
ref.watch(courseUnitsInfoProvider);
final provider = ref.read(courseUnitsInfoProvider.notifier);

if (classes == null || classes.isEmpty) {
return Center(
child: Text(S.of(context).no_class, textAlign: TextAlign.center),
);
}
final classes = provider.courseUnitsClasses[widget.courseUnit];
final sheet = provider.courseUnitsSheets[widget.courseUnit];
final classProfessors =
provider.courseUnitsClassProfessors[widget.courseUnit];

if (classes == null) {
return const Center(child: CircularProgressIndicator());
}

if (classes.isEmpty) {
return Center(
child: Text(S.of(context).no_class, textAlign: TextAlign.center),
);
}

if (classProfessors == null) {
return const Center(child: CircularProgressIndicator());
}

return CourseUnitClassesView(classes);
return CourseUnitClassesView(
classes,
sheet?.professors ?? [],
widget.courseUnit,
classProfessors: classProfessors,
);
},
);
}

@override
Expand Down
Loading