Skip to content

Commit 07d9e71

Browse files
authored
Merge pull request #33 from LEIC-ES-2021-22/release
Iteration 2
2 parents 8d2e182 + 9a687b9 commit 07d9e71

29 files changed

+1324
-96
lines changed

app_feup/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,4 @@ lib/generated_plugin_registrant.dart
8383

8484
# Exceptions to above rules.
8585
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
86+
assets/auth/key.json

app_feup/lib/controller/load_info.dart

+8-2
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,18 @@ Future loadRemoteUserInfoToState(Store<AppState> store) async {
5656
coursesStates = Completer(),
5757
trips = Completer(),
5858
lastUpdate = Completer(),
59-
restaurants = Completer();
59+
restaurants = Completer(),
60+
occupation = Completer(),
61+
reservations = Completer();
6062

6163
store.dispatch(getUserInfo(userInfo));
6264
store.dispatch(getUserPrintBalance(printBalance));
6365
store.dispatch(getUserFees(fees));
6466
store.dispatch(getUserCoursesState(coursesStates));
6567
store.dispatch(getUserBusTrips(trips));
6668
store.dispatch(getRestaurantsFromFetcher(restaurants));
69+
store.dispatch(getOccupationFromFetcher(occupation));
70+
store.dispatch(getReservationsFromFetcher(reservations));
6771

6872
final Tuple2<String, String> userPersistentInfo =
6973
await AppSharedPreferences.getPersistentUserInfo();
@@ -80,7 +84,9 @@ Future loadRemoteUserInfoToState(Store<AppState> store) async {
8084
coursesStates.future,
8185
userInfo.future,
8286
trips.future,
83-
restaurants.future
87+
restaurants.future,
88+
occupation.future,
89+
reservations.future
8490
]);
8591
allRequests.then((futures) {
8692
store.dispatch(setLastUserInfoUpdateTimestamp(lastUpdate));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import 'package:uni/model/entities/library.dart';
2+
3+
import 'app_database.dart';
4+
5+
class OccupationDatabase extends AppDatabase {
6+
OccupationDatabase()
7+
: super('occupation.db',
8+
[
9+
'''CREATE TABLE FLOOR_OCCUPATION(
10+
id INTEGER PRIMARY KEY AUTOINCREMENT,
11+
number INT,
12+
occupation INT,
13+
capacity INT
14+
)
15+
'''
16+
]);
17+
18+
void saveOccupation(LibraryOccupation occupation) async {
19+
final db = await this.getDatabase();
20+
db.transaction((txn) async {
21+
await txn.delete('FLOOR_OCCUPATION');
22+
occupation.getFloors().forEach((floor) async {
23+
await txn.insert('FLOOR_OCCUPATION', floor.toMap());
24+
});
25+
});
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import 'package:uni/model/entities/reservation.dart';
2+
3+
import 'app_database.dart';
4+
5+
class ReservationDatabase extends AppDatabase {
6+
ReservationDatabase()
7+
: super('reservations.db',
8+
[
9+
'''CREATE TABLE RESERVATION(
10+
id INTEGER PRIMARY KEY AUTOINCREMENT,
11+
room TEXT,
12+
startDate INT,
13+
duration INT
14+
)
15+
'''
16+
]);
17+
18+
void saveReservations(List<Reservation> reservations) async {
19+
final db = await this.getDatabase();
20+
db.transaction((txn) async {
21+
await txn.delete('RESERVATION');
22+
reservations.forEach((reservation) async {
23+
await txn.insert('RESERVATION', reservation.toMap());
24+
});
25+
});
26+
}
27+
}

app_feup/lib/controller/local_storage/app_shared_preferences.dart

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ class AppSharedPreferences {
2626
FAVORITE_WIDGET_TYPE.schedule,
2727
FAVORITE_WIDGET_TYPE.exams,
2828
FAVORITE_WIDGET_TYPE.busStops,
29-
FAVORITE_WIDGET_TYPE.libraryOccupation
29+
FAVORITE_WIDGET_TYPE.libraryOccupation,
30+
FAVORITE_WIDGET_TYPE.roomReservations
3031
];
3132
static final String filteredExamsTypes = 'filtered_exam_types';
3233
static final List<String> defaultFilteredExamTypes =
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import 'dart:convert';
2+
import 'package:flutter/services.dart';
3+
import 'package:uni/model/app_state.dart';
4+
import 'package:redux/redux.dart';
5+
import 'package:uni/model/entities/library.dart';
6+
import 'package:uni/controller/parsers/parser_occupation.dart';
7+
import 'package:gsheets/gsheets.dart';
8+
9+
Future<String> getJson() {
10+
return rootBundle.loadString('assets/auth/key.json');
11+
}
12+
13+
/// Fetch the school calendar from Google Sheets
14+
class OccupationFetcherSheets {
15+
Future<LibraryOccupation> getOccupationFromSheets (
16+
Store<AppState> store) async {
17+
final sheetId = '1gZRbEX4y8vNW7vrl15FCdAQ3pVNRJw_uRZtVL6ORP0g';
18+
19+
try {
20+
final googleCredentials = json.decode(await getJson());
21+
final gSheets = GSheets(googleCredentials);
22+
final ss = await gSheets.spreadsheet(sheetId);
23+
24+
final sheet = ss.worksheetByTitle('MANUAL');
25+
26+
return getOccupationFromSheet(sheet);
27+
} catch (FlutterError) {
28+
return LibraryOccupation();
29+
}
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import 'package:uni/model/entities/library.dart';
2+
import 'package:gsheets/gsheets.dart';
3+
4+
Future<LibraryOccupation> getOccupationFromSheet(Worksheet sheet) async {
5+
try {
6+
final occupation = LibraryOccupation();
7+
8+
for (int i = 1; i < 7; i++) {
9+
final occupationCell = await sheet.cells.cell(column: 3, row: i + 1);
10+
final capacityCell = await sheet.cells.cell(column: 5, row: i + 1);
11+
12+
occupation.addFloor(FloorOccupation(
13+
i, int.parse(occupationCell.value), int.parse(capacityCell.value)));
14+
}
15+
return occupation;
16+
} catch (FormatException) {
17+
return LibraryOccupation();
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import 'package:http/http.dart';
2+
import 'package:html/dom.dart';
3+
import 'package:html/parser.dart';
4+
import 'package:uni/model/entities/reservation.dart';
5+
6+
Future<List<Reservation>> getReservationsFromHtml(Response response) async {
7+
final document = parse(response.body);
8+
9+
final List<Element> reservationHtml =
10+
document.getElementsByClassName('d interior');
11+
12+
return reservationHtml.map( (element) {
13+
final String room = element.children[5].firstChild.text;
14+
final String date = element.children[0].firstChild.text;
15+
final String hour = element.children[2].firstChild.text;
16+
final DateTime startDate = DateTime.parse('$date $hour');
17+
final String durationHtml = element.children[4].firstChild.text;
18+
final Duration duration = Duration(
19+
hours: int.parse(durationHtml.substring(0,2)),
20+
minutes: int.parse(durationHtml.substring(3,5))
21+
);
22+
return Reservation(room, startDate, duration);
23+
}).toList();
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
import 'package:http/http.dart';
3+
import 'package:uni/controller/networking/network_router.dart';
4+
import 'package:uni/controller/parsers/parser_reservation.dart';
5+
import 'package:uni/model/app_state.dart';
6+
import 'package:uni/model/entities/reservation.dart';
7+
import 'package:uni/model/entities/session.dart';
8+
import 'package:redux/redux.dart';
9+
10+
/// Get the library rooms' reservations from the website
11+
class ReservationsFetcherHtml {
12+
Future<List<Reservation>> getReservations(Store<AppState> store) async {
13+
14+
final String baseUrl = NetworkRouter.getBaseUrlFromSession(
15+
store.state.content['session']) +
16+
'res_recursos_geral.pedidos_list?pct_tipo_grupo_id=3';
17+
final Session session = store.state.content['session'];
18+
final Future<Response> response = NetworkRouter.getWithCookies(baseUrl,
19+
{}, session);
20+
final List<Reservation> reservations =
21+
await response.then((response) => getReservationsFromHtml(response));
22+
23+
return reservations;
24+
}
25+
}

app_feup/lib/main.dart

+5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import 'package:uni/view/Pages/home_page_view.dart';
1818
import 'package:uni/view/Pages/logout_route.dart';
1919
import 'package:uni/view/Pages/splash_page_view.dart';
2020
import 'package:uni/view/Pages/uniteca_page_view.dart';
21+
import 'package:uni/view/Pages/room_reservations_page_view.dart';
2122
import 'package:uni/view/Widgets/page_transition.dart';
2223
import 'package:uni/view/navigation_service.dart';
2324
import 'package:uni/view/theme.dart';
@@ -106,6 +107,10 @@ class MyAppState extends State<MyApp> {
106107
return PageTransition.makePageTransition(
107108
page: UnitecaPageView(), settings: settings
108109
);
110+
case '/' + Constants.navRooms:
111+
return PageTransition.makePageTransition(
112+
page: RoomReservationsPageView(), settings: settings
113+
);
109114
case '/' + Constants.navLogOut:
110115
return LogoutRoute.buildLogoutRoute();
111116
}

app_feup/lib/model/app_state.dart

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
// enum should be placed somewhere else?
22
import 'package:uni/model/entities/bus_stop.dart';
3+
import 'package:uni/model/entities/reservation.dart';
34
import 'package:uni/model/entities/session.dart';
45
import 'package:uni/model/entities/trip.dart';
56
import 'package:uni/utils/constants.dart' as Constants;
67

78
import 'entities/exam.dart';
89
import 'entities/lecture.dart';
910
import 'entities/restaurant.dart';
11+
import 'entities/library.dart';
1012

1113
enum RequestStatus { none, busy, failed, successful }
1214

@@ -19,6 +21,7 @@ class AppState {
1921
'schedule': <Lecture>[],
2022
'exams': <Exam>[],
2123
'restaurants': <Restaurant>[],
24+
'library':LibraryOccupation,
2225
'filteredExam': Map<String, bool>(),
2326
'scheduleStatus': RequestStatus.none,
2427
'loginStatus': RequestStatus.none,
@@ -34,7 +37,11 @@ class AppState {
3437
'printBalanceStatus': RequestStatus.none,
3538
'feesStatus': RequestStatus.none,
3639
'coursesStateStatus': RequestStatus.none,
37-
'lastUserInfoUpdateTime': null
40+
'lastUserInfoUpdateTime': null,
41+
'reservations': <Reservation>[],
42+
'reservationsStatus': RequestStatus.none,
43+
'occupation': LibraryOccupation(),
44+
'occupationStatus': RequestStatus.none
3845
};
3946
}
4047

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/// Overall occupation of the library
2+
class LibraryOccupation {
3+
int occupation;
4+
int capacity;
5+
List<FloorOccupation> floors;
6+
7+
LibraryOccupation() {
8+
this.occupation = 0;
9+
this.capacity = 0;
10+
floors = [];
11+
}
12+
13+
void addFloor(FloorOccupation floor) {
14+
floors.add(floor);
15+
this.occupation += floor.getOccupation();
16+
this.capacity += floor.getCapacity();
17+
}
18+
19+
int getPercentage() {
20+
if (capacity == 0) return 0;
21+
return (occupation * 100 / capacity).round();
22+
}
23+
24+
int getOccupation() {
25+
return this.occupation;
26+
}
27+
28+
int getCapacity() {
29+
return this.capacity;
30+
}
31+
32+
List<FloorOccupation> getFloors() {
33+
return this.floors;
34+
}
35+
36+
FloorOccupation getFloor(int number) {
37+
if (floors.length < number || number < 0) return FloorOccupation(0, 0, 0);
38+
return floors[number - 1];
39+
}
40+
}
41+
42+
/// Occupation values of a single floor
43+
class FloorOccupation {
44+
int number;
45+
int occupation;
46+
int capacity;
47+
48+
FloorOccupation(this.number, this.occupation, this.capacity);
49+
50+
int getNumber() {return this.number;}
51+
int getOccupation() {return this.occupation;}
52+
int getCapacity() {return this.capacity;}
53+
void setCapacity(int occupation) {this.occupation = occupation;}
54+
55+
int getPercentage() {
56+
if (capacity == 0) return 0;
57+
return (occupation * 100 / capacity).round();
58+
}
59+
60+
Map<String, dynamic> toMap() {
61+
final Map<String, dynamic> map = {
62+
'number' : number,
63+
'occupation' : occupation,
64+
'capacity' : capacity,
65+
};
66+
return map;
67+
}
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import 'dart:ui';
2+
3+
/// Private room reservation from the library
4+
class Reservation {
5+
String room;
6+
DateTime startDate;
7+
Duration duration;
8+
9+
Reservation(this.room, this.startDate, this.duration);
10+
11+
Map<String, dynamic> toMap() {
12+
final Map<String, dynamic> map = {
13+
'room' : room,
14+
'startDate' : startDate.millisecondsSinceEpoch,
15+
'duration' : duration.inHours,
16+
};
17+
return map;
18+
}
19+
20+
@override
21+
String toString() {
22+
return '$room, $startDate, $duration';
23+
}
24+
25+
@override
26+
bool operator == (Object other){
27+
return other is Reservation
28+
&& room == other.room
29+
&& (startDate.compareTo(other.startDate) == 0)
30+
&& (duration.compareTo(other.duration) == 0);
31+
}
32+
33+
@override
34+
int get hashCode => hashValues(room, startDate, duration);
35+
}
+2-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
enum FAVORITE_WIDGET_TYPE
2-
{exams, schedule, printBalance, account, busStops,libraryOccupation}
2+
{exams, schedule, printBalance, account, busStops,libraryOccupation
3+
,roomReservations}

0 commit comments

Comments
 (0)