mensa-upb-api/scraper/src/util.rs

75 lines
2.5 KiB
Rust

use std::env;
use anyhow::Result;
use chrono::NaiveDate;
use futures::{Stream, StreamExt as _};
use shared::{Canteen, DishType};
use sqlx::{postgres::PgPoolOptions, types::Decimal, PgPool, PgTransaction};
use crate::{scrape_menu, Dish};
pub fn get_db() -> Result<PgPool> {
Ok(PgPoolOptions::new()
.connect_lazy(&env::var("DATABASE_URL").expect("missing DATABASE_URL env variable"))?)
}
pub fn scrape_canteens_at_days<'a>(
date_canteen_combinations: &'a [(NaiveDate, Canteen)],
) -> impl Stream<Item = Result<(NaiveDate, Canteen, Vec<Dish>)>> + 'a {
futures::stream::iter(date_canteen_combinations).then(|(date, canteen)| async move {
scrape_menu(date, *canteen)
.await
.map(|menu| (*date, *canteen, menu))
})
}
pub async fn add_menu_to_db(
db: &mut PgTransaction<'_>,
date: &NaiveDate,
canteen: Canteen,
menu: Vec<Dish>,
) -> Result<(), sqlx::Error> {
if !menu.is_empty() {
let mut query = sqlx::QueryBuilder::new("INSERT INTO meals (date,canteen,name,dish_type,image_src,price_students,price_employees,price_guests,vegan,vegetarian,kjoules,proteins,carbohydrates,fats) ");
query
.push_values(menu, |mut sep, item| {
let vegan = item.is_vegan();
sep.push_bind(date)
.push_bind(canteen.get_identifier())
.push_bind(item.get_name().to_string())
.push_bind(item.get_type() as DishType)
.push_bind(item.get_image_src().map(str::to_string))
.push_bind(item.get_price_students().to_owned())
.push_bind(item.get_price_employees().to_owned())
.push_bind(item.get_price_guests().to_owned())
.push_bind(vegan)
.push_bind(vegan || item.is_vegetarian())
.push_bind(item.nutrition_values.kjoule)
.push_bind(item.nutrition_values.protein.to_owned())
.push_bind(item.nutrition_values.carbs.to_owned())
.push_bind(item.nutrition_values.fat.to_owned());
})
.build()
.execute(&mut **db)
.await?;
}
sqlx::query!(
"INSERT INTO canteens_scraped (scraped_for, canteen) VALUES ($1, $2)",
date,
canteen.get_identifier()
)
.execute(&mut **db)
.await?;
tracing::trace!("Insert to DB successfull");
Ok(())
}
pub fn normalize_price_bigdecimal(price: Decimal) -> Decimal {
price.normalize().round_dp(2)
}