Skip to content

Commit

Permalink
feat: new restaurants api (#1393)
Browse files Browse the repository at this point in the history
  • Loading branch information
DGoiana authored Jan 2, 2025
2 parents d6df6ef + e17b8d1 commit 967097a
Show file tree
Hide file tree
Showing 20 changed files with 258 additions and 159 deletions.
120 changes: 61 additions & 59 deletions packages/uni_app/lib/controller/fetchers/restaurant_fetcher.dart
Original file line number Diff line number Diff line change
@@ -1,47 +1,73 @@
import 'package:uni/controller/networking/network_router.dart';
import 'package:uni/controller/parsers/parser_restaurants.dart';
import 'package:uni/model/entities/meal.dart';
import 'package:uni/model/entities/restaurant.dart';
import 'package:uni/model/utils/day_of_week.dart';
import 'package:uni/session/flows/base/session.dart';
import 'package:up_menus/up_menus.dart';

/// Class for fetching the menu
class RestaurantFetcher {
final String spreadSheetUrl = 'https://docs.google.com/spreadsheets/d/'
'1TJauM0HwIf2RauQU2GmhdZZ1ZicFLMHuBkxWwVOw3Q4';
final String jsonEndpoint = '/gviz/tq?tqx=out:json';

// Format: Date(dd/mm/yyyy), Meal("Almoço", "Jantar), Dish("Sopa", "Carne",
// "Peixe", "Dieta", "Vegetariano", "Salada"), Description(String)
final String sheetsColumnRange = 'A:D';

// List the Restaurant sheet names in the Google Sheets Document
final List<String> restaurantSheets = ['Cantina'];

// Generate the Gsheets endpoints list based on a list of sheets
String buildGSheetsEndpoint(String sheet) {
return Uri.encodeFull(
'$spreadSheetUrl$jsonEndpoint&sheet=$sheet&range=$sheetsColumnRange',
Restaurant convertToRestaurant(
Establishment establishment,
Iterable<DayMenu> dayMenus,
String period,
) {
final meals = <Meal>[];
for (final dayMenu in dayMenus) {
for (final dish in dayMenu.dishes) {
// Extract the information about the meal.
meals.add(
Meal(
dish.dishType.namePt,
dish.dish.namePt,
dish.dish.nameEn ?? dish.dish.namePt,
parseDateTime(dayMenu.day),
dayMenu.day,
),
);
}
}
return Restaurant(
establishment.id,
establishment.namePt,
establishment.nameEn,
period,
'',
meals: meals,
);
}

String getRestaurantGSheetName(Restaurant restaurant) {
return restaurantSheets.firstWhere(
(sheetName) =>
restaurant.name.toLowerCase().contains(sheetName.toLowerCase()),
orElse: () => '',
);
}
Future<List<Restaurant>> fetchSASUPRestaurants() async {
// TODO: change the implementation to accomodate changes for the new UI.
final upMenus = UPMenusApi();
final establishments = await upMenus.establishments.list();
final restaurants = <Restaurant>[];

Future<Restaurant> fetchGSheetsRestaurant(
String url,
String restaurantName,
Session session, {
bool isDinner = false,
}) async {
return getRestaurantFromGSheets(
await NetworkRouter.getWithCookies(url, {}, session),
restaurantName,
isDinner: isDinner,
);
const periods = [
{'period': Period.lunch, 'meal': 'lunch'},
{'period': Period.dinner, 'meal': 'dinner'},
{'period': Period.snackBar, 'meal': 'snackbar'},
{'period': Period.breakfast, 'meal': 'breakfast'},
];

for (final establishment in establishments) {
if (establishment.dayMenu == false) {
continue;
}

for (final period in periods) {
restaurants.add(
convertToRestaurant(
establishment,
await upMenus.dayMenus
.get(establishment.id, period['period']! as Period),
period['meal']! as String,
),
);
}
}
return restaurants;
}

final List<String> sigarraMenuEndpoints = [
Expand All @@ -64,32 +90,8 @@ class RestaurantFetcher {
}

Future<List<Restaurant>> getRestaurants(Session session) async {
final restaurants = await fetchSigarraRestaurants(session);

// Check for restaurants without associated meals and attempt to parse them
// from GSheets
final restaurantsWithoutMeals =
restaurants.where((restaurant) => restaurant.meals.isEmpty).toList();

for (final restaurant in restaurantsWithoutMeals) {
final sheetName = getRestaurantGSheetName(restaurant);
if (sheetName.isEmpty) {
continue;
}

final gSheetsRestaurant = await fetchGSheetsRestaurant(
buildGSheetsEndpoint(sheetName),
restaurant.name,
session,
isDinner: restaurant.name.toLowerCase().contains('jantar'),
);

restaurants
..removeWhere(
(restaurant) => restaurant.name == gSheetsRestaurant.name,
)
..insert(0, gSheetsRestaurant);
}
final restaurants =
await fetchSASUPRestaurants() + await fetchSigarraRestaurants(session);

return restaurants;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,17 @@ class RestaurantDatabase extends AppDatabase<List<Restaurant>> {
CREATE TABLE RESTAURANTS(
id INTEGER PRIMARY KEY,
ref TEXT,
name TEXT)
namePt TEXT
namePt TEXT)
''',
'''
CREATE TABLE MEALS(
id INTEGER PRIMARY KEY AUTOINCREMENT,
day TEXT,
type TEXT,
date TEXT,
name TEXT,
namePt TEXT,
nameEn TEXT,
id_restaurant INTEGER,
FOREIGN KEY (id_restaurant) REFERENCES RESTAURANTS(id))
'''
Expand All @@ -45,7 +47,9 @@ class RestaurantDatabase extends AppDatabase<List<Restaurant>> {

return Restaurant(
restaurantId,
map['name'] as String,
map['namePt'] as String,
map['nameEn'] as String,
map['period'] as String,
map['ref'] as String,
meals: meals,
);
Expand All @@ -70,7 +74,7 @@ class RestaurantDatabase extends AppDatabase<List<Restaurant>> {
}
});

return filterPastMeals(restaurants);
return restaurants;
}

Future<List<Meal>> getRestaurantMeals(
Expand All @@ -93,10 +97,11 @@ class RestaurantDatabase extends AppDatabase<List<Restaurant>> {
final meals = mealsMaps.map((map) {
final day = parseDayOfWeek(map['day'] as String);
final type = map['type'] as String;
final name = map['name'] as String;
final namePt = map['namePt'] as String;
final nameEn = map['nameEn'] as String;
final format = DateFormat('d-M-y');
final date = format.parseUtc(map['date'] as String);
return Meal(type, name, day!, date);
return Meal(type, namePt, nameEn, day!, date);
}).toList();

return meals;
Expand Down Expand Up @@ -129,22 +134,3 @@ class RestaurantDatabase extends AppDatabase<List<Restaurant>> {
});
}
}

List<Restaurant> filterPastMeals(List<Restaurant> restaurants) {
final restaurantsCopy = List<Restaurant>.from(restaurants);
// Hide past and next weeks' meals
// (To replicate sigarra's behaviour for the GSheets meals)
final now = DateTime.now().toUtc();
final today = DateTime.utc(now.year, now.month, now.day);
final nextSunday = today.add(Duration(days: DateTime.sunday - now.weekday));

for (final restaurant in restaurantsCopy) {
for (final meals in restaurant.meals.values) {
meals.removeWhere(
(meal) => meal.date.isBefore(today) || meal.date.isAfter(nextSunday),
);
}
}

return restaurantsCopy;
}
50 changes: 3 additions & 47 deletions packages/uni_app/lib/controller/parsers/parser_restaurants.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import 'dart:convert';

import 'package:html/parser.dart';
import 'package:http/http.dart';
import 'package:intl/intl.dart';
Expand Down Expand Up @@ -59,7 +57,7 @@ List<Restaurant> getRestaurantsFromHtml(Response response) {
}
} else {
type = document.querySelector('#$header')?.text;
final meal = Meal(type ?? '', value, dayOfWeek!, date!);
final meal = Meal(type ?? '', value, value, dayOfWeek!, date!);
meals.add(meal);
}
}
Expand All @@ -70,53 +68,11 @@ List<Restaurant> getRestaurantsFromHtml(Response response) {
return Restaurant(
null,
restaurantTuple.$2,
restaurantTuple.$2,
restaurantTuple.$1,
'',
meals: meals,
);
}).toList();
return restaurants;
}

Restaurant getRestaurantFromGSheets(
Response response,
String restaurantName, {
bool isDinner = false,
}) {
// Ignore beginning of response: "/*O_o*/\ngoogle.visualization.Query.setResponse("
// Ignore the end of the response: ");"
// Check the structure by accessing the link:
// https://docs.google.com/spreadsheets/d/1TJauM0HwIf2RauQU2GmhdZZ1ZicFLMHuBkxWwVOw3Q4/gviz/tq?tqx=out:json&sheet=Cantina%20de%20Engenharia&range=A:D
final jsonString = response.body.substring(
response.body.indexOf('(') + 1,
response.body.lastIndexOf(')'),
);
final parsedJson = jsonDecode(jsonString) as Map<String, dynamic>;

final mealsList = <Meal>[];

final format = DateFormat('d/M/y');

final table = parsedJson['table'] as Map<String, dynamic>;
final rows = table['rows'] as List<dynamic>;

for (final row in rows) {
final cellList = (row as Map<String, dynamic>)['c'] as List<dynamic>;
if (((cellList[1] as Map<String, dynamic>)['v'] == 'Almoço' && isDinner) ||
((cellList[1] as Map<String, dynamic>)['v'] != 'Almoço' && !isDinner)) {
continue;
}

final meal = Meal(
(cellList[2] as Map<String, dynamic>)['v'] as String,
(cellList[3] as Map<String, dynamic>)['v'] as String,
DayOfWeek.values[format
.parseUtc((cellList[0] as Map<String, dynamic>)['f'] as String)
.weekday -
1],
format.parseUtc((cellList[0] as Map<String, dynamic>)['f'] as String),
);
mealsList.add(meal);
}

return Restaurant(null, restaurantName, '', meals: mealsList);
}
4 changes: 4 additions & 0 deletions packages/uni_app/lib/generated/intl/messages_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class MessageLookup extends MessageLookupByLibrary {
"banner_info": MessageLookupByLibrary.simpleMessage(
"We do now collect anonymous usage statistics in order to improve your experience. You can change it in settings."),
"bibliography": MessageLookupByLibrary.simpleMessage("Bibliography"),
"breakfast": MessageLookupByLibrary.simpleMessage("Breakfast"),
"bs_description": MessageLookupByLibrary.simpleMessage(
"Did you find any bugs in the application?\nDo you have any suggestions for the app?\nTell us so we can improve!"),
"bug_description": MessageLookupByLibrary.simpleMessage(
Expand Down Expand Up @@ -110,6 +111,7 @@ class MessageLookup extends MessageLookupByLibrary {
"description": MessageLookupByLibrary.simpleMessage("Description"),
"desired_email": MessageLookupByLibrary.simpleMessage(
"Email where you want to be contacted"),
"dinner": MessageLookupByLibrary.simpleMessage("Dinner"),
"dona_bia": MessageLookupByLibrary.simpleMessage(
"D. Beatriz\'s stationery store"),
"dona_bia_building": MessageLookupByLibrary.simpleMessage(
Expand Down Expand Up @@ -167,6 +169,7 @@ class MessageLookup extends MessageLookupByLibrary {
"login_with_credentials":
MessageLookupByLibrary.simpleMessage("Login with credentials"),
"logout": MessageLookupByLibrary.simpleMessage("Log out"),
"lunch": MessageLookupByLibrary.simpleMessage("Lunch"),
"menus": MessageLookupByLibrary.simpleMessage("Menus"),
"min_value_reference":
MessageLookupByLibrary.simpleMessage("Minimum value: 1,00 €"),
Expand Down Expand Up @@ -272,6 +275,7 @@ class MessageLookup extends MessageLookupByLibrary {
"sent_error": MessageLookupByLibrary.simpleMessage(
"An error occurred in sending"),
"settings": MessageLookupByLibrary.simpleMessage("Settings"),
"snackbar": MessageLookupByLibrary.simpleMessage("Snackbar"),
"some_error": MessageLookupByLibrary.simpleMessage("Some error!"),
"stcp_stops":
MessageLookupByLibrary.simpleMessage("STCP - Upcoming Trips"),
Expand Down
4 changes: 4 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 @@ -63,6 +63,7 @@ class MessageLookup extends MessageLookupByLibrary {
"banner_info": MessageLookupByLibrary.simpleMessage(
"Agora recolhemos estatísticas de uso anónimas para melhorar a tua experiência. Podes alterá-lo nas definições."),
"bibliography": MessageLookupByLibrary.simpleMessage("Bibliografia"),
"breakfast": MessageLookupByLibrary.simpleMessage("Pequeno Almoço"),
"bs_description": MessageLookupByLibrary.simpleMessage(
"Encontraste algum bug na aplicação?\nTens alguma sugestão para a app?\nConta-nos para que possamos melhorar!"),
"bug_description": MessageLookupByLibrary.simpleMessage(
Expand Down Expand Up @@ -109,6 +110,7 @@ class MessageLookup extends MessageLookupByLibrary {
"description": MessageLookupByLibrary.simpleMessage("Descrição"),
"desired_email": MessageLookupByLibrary.simpleMessage(
"Email em que desejas ser contactado"),
"dinner": MessageLookupByLibrary.simpleMessage("Jantar"),
"dona_bia":
MessageLookupByLibrary.simpleMessage("Papelaria D. Beatriz"),
"dona_bia_building": MessageLookupByLibrary.simpleMessage(
Expand Down Expand Up @@ -166,6 +168,7 @@ class MessageLookup extends MessageLookupByLibrary {
"login_with_credentials": MessageLookupByLibrary.simpleMessage(
"Iniciar sessão com credenciais"),
"logout": MessageLookupByLibrary.simpleMessage("Terminar sessão"),
"lunch": MessageLookupByLibrary.simpleMessage("Almoço"),
"menus": MessageLookupByLibrary.simpleMessage("Ementas"),
"min_value_reference":
MessageLookupByLibrary.simpleMessage("Valor mínimo: 1,00 €"),
Expand Down Expand Up @@ -273,6 +276,7 @@ class MessageLookup extends MessageLookupByLibrary {
"sent_error":
MessageLookupByLibrary.simpleMessage("Ocorreu um erro no envio"),
"settings": MessageLookupByLibrary.simpleMessage("Definições"),
"snackbar": MessageLookupByLibrary.simpleMessage("Snackbar"),
"some_error": MessageLookupByLibrary.simpleMessage("Algum erro!"),
"stcp_stops":
MessageLookupByLibrary.simpleMessage("STCP - Próximas Viagens"),
Expand Down
Loading

0 comments on commit 967097a

Please sign in to comment.