Skip to content

Commit

Permalink
moved all routes to a separate directory and made related changes to …
Browse files Browse the repository at this point in the history
…other files
  • Loading branch information
CommanderStorm committed Jan 2, 2025
1 parent c2cfc77 commit bc9ae12
Show file tree
Hide file tree
Showing 166 changed files with 3,802 additions and 308 deletions.
182 changes: 94 additions & 88 deletions server/src/calendar/models.rs → server/src/db/calendar.rs
Original file line number Diff line number Diff line change
@@ -1,52 +1,39 @@
use crate::external::connectum::ConnectumEvent;
use crate::limited::hash_map::LimitedHashMap;
use crate::limited::vec::LimitedVec;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::PgPool;
use std::collections::HashMap;
use std::fmt::{Debug, Display, Formatter};
use tracing::debug;
use tracing::error;
use tracing::warn;

#[derive(Serialize, Deserialize, Clone, utoipa::ToSchema)]
pub struct CalendarLocation {
/// Structured, globaly unique room code
///
/// Included to enable multi-room calendars.
/// Format: BUILDING.LEVEL.NUMBER
#[schema(examples("5602.EG.001", "5121.EG.003"))]
pub key: String,
/// name of the entry in a human-readable form
#[schema(examples(
"5602.EG.001 (MI HS 1, Friedrich L. Bauer Hörsaal)",
"5121.EG.003 (Computerraum)"
))]
pub name: String,
/// last time the calendar was scraped for this room
#[schema(examples("2039-01-19T03:14:07+01:00", "2042-01-07T00:00:00 UTC"))]
pub last_calendar_scrape_at: Option<DateTime<Utc>>,
/// Link to the calendar of the room
#[schema(examples(
"https://campus.tum.de/tumonline/tvKalender.wSicht?cOrg=19691&cRes=12543&cReadonly=J",
"https://campus.tum.de/tumonline/tvKalender.wSicht?cOrg=19691&cRes=12559&cReadonly=J"
))]
pub calendar_url: Option<String>,
/// Type of the entry in a human-readable form
#[schema(examples("Serverraum", "Büro"))]
pub type_common_name: String,
/// type of the entry
///
/// TODO document as a n enum with the following choices:
/// - `room`
/// - `building`
/// - `joined_building`
/// - `area`
/// - `site`
/// - `campus`
/// - `poi`
#[schema(examples("room", "building", "joined_building", "area", "site", "campus", "poi"))]
pub r#type: String,
}

impl CalendarLocation {
#[tracing::instrument(skip(pool))]
pub(crate) async fn get_locations(
pool: &PgPool,
ids: &[String],
) -> anyhow::Result<LimitedVec<CalendarLocation>> {
let res = sqlx::query_as!(
CalendarLocation,
"SELECT key,name,last_calendar_scrape_at,calendar_url,type,type_common_name FROM de WHERE key = ANY($1::text[])",
ids
)
.fetch_all(pool)
.await?;
Ok(LimitedVec(res))
}
}
impl Debug for CalendarLocation {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut base = f.debug_struct("CalendarLocation");
Expand All @@ -58,78 +45,64 @@ impl Debug for CalendarLocation {
}
}

#[derive(Serialize, Deserialize, Clone, utoipa::ToSchema)]
pub struct LocationEvents {
pub events: LimitedVec<Event>,
pub location: CalendarLocation,
}
impl LocationEvents {
#[tracing::instrument(skip(pool))]
pub(crate) async fn get_from_db(
pool: &PgPool,
locations: Vec<CalendarLocation>,
start_after: &DateTime<Utc>,
end_before: &DateTime<Utc>,
) -> anyhow::Result<LimitedHashMap<String, LocationEvents>> {
let mut located_events: HashMap<String, LocationEvents> = HashMap::new();
for location in locations.into_iter() {
let events = sqlx::query_as!(
Event,
r#"SELECT id,room_code,start_at,end_at,title_de,title_en,stp_type,entry_type,detailed_entry_type
FROM calendar
WHERE room_code = $1 AND start_at >= $2 AND end_at <= $3"#,
location.key,
start_after,
end_before
)
.fetch_all(pool)
.await?;
located_events.insert(
location.key.clone(),
LocationEvents {
location,
events: events.into(),
},
);
}
Ok(LimitedHashMap(located_events))
}
}

#[derive(Serialize, Deserialize, Clone, utoipa::ToSchema)]
pub struct Event {
/// ID of the calendar entry used in TUMonline internally
#[schema(examples(6424))]
pub id: i32,
/// Structured, globaly unique room code
///
/// Included to enable multi-room calendars.
/// Format: BUILDING.LEVEL.NUMBER
#[schema(examples("5602.EG.001", "5121.EG.003"))]
pub room_code: String,
/// start of the entry
#[schema(examples("2018-01-01T00:00:00"))]
pub start_at: DateTime<Utc>,
/// end of the entry
#[schema(examples("2019-01-01T00:00:00"))]
pub end_at: DateTime<Utc>,
/// German title of the Entry
#[schema(examples("Quantenteleportation"))]
pub title_de: String,
/// English title of the Entry
#[schema(examples("Quantum teleportation"))]
pub title_en: String,
/// Lecture-type
#[schema(examples("Vorlesung mit Zentralübung"))]
pub stp_type: Option<String>,
/// What this calendar entry means.
///
/// Each of these should be displayed in a different color
/// TODO document as an enum with these values via EventType:
/// - `lecture`
/// - `exercise`
/// - `exam`
/// - `barred`
/// - `other`
#[schema(examples("lecture", "exercise", "exam"))]
pub entry_type: String,
/// For some Entrys, we do have more information (what kind of a `lecture` is it? What kind of an other `entry` is it?)
#[schema(examples("Abhaltung"))]
pub detailed_entry_type: String,
}
impl Debug for Event {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let duration = (self.end_at - self.start_at).num_minutes();
f.debug_tuple("Event")
.field(&format!(
"{start} ({duration_h}h{duration_min:?}m): {title}",
start = self.start_at.naive_local(),
duration_min = duration % 60,
duration_h = duration / 60,
title = self.title_de
))
.finish()
}
}

impl Event {
#[tracing::instrument]
#[tracing::instrument(skip(pool))]
pub async fn store_all(
pool: &PgPool,
events: LimitedVec<Event>,
id: &str,
) -> anyhow::Result<()> {
// insert into db
let mut tx = pool.begin().await?;
if let Err(e) = Event::delete_events(&mut tx, id).await {
if let Err(e) = Event::delete(&mut tx, id).await {
error!("could not delete existing events because {e:?}");
tx.rollback().await?;
return Err(e.into());
Expand All @@ -155,7 +128,7 @@ impl Event {
Ok(())
}
#[tracing::instrument(skip(tx))]
async fn delete_events(
async fn delete(
tx: &mut sqlx::Transaction<'_, sqlx::Postgres>,
id: &str,
) -> Result<(), sqlx::Error> {
Expand Down Expand Up @@ -202,6 +175,7 @@ impl Event {
.await
}

#[tracing::instrument(skip(tx))]
pub async fn store(
&self,
tx: &mut sqlx::Transaction<'_, sqlx::Postgres>,
Expand Down Expand Up @@ -231,10 +205,39 @@ impl Event {
}
}

#[derive(Serialize, Deserialize, Clone, Debug, sqlx::Type, utoipa::ToSchema)]
impl Debug for Event {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let duration = (self.end_at - self.start_at).num_minutes();
f.debug_tuple("Event")
.field(&format!(
"{start} ({duration_h}h{duration_min:?}m): {title}",
start = self.start_at.naive_local(),
duration_min = duration % 60,
duration_h = duration / 60,
title = self.title_de
))
.finish()
}
}
impl From<ConnectumEvent> for Event {
fn from(value: ConnectumEvent) -> Self {
Event {
id: value.id,
room_code: value.room_code,
start_at: value.start_at,
end_at: value.end_at,
title_de: value.title_de,
title_en: value.title_en,
stp_type: value.stp_type,
entry_type: value.entry_type,
detailed_entry_type: value.detailed_entry_type,
}
}
}

#[derive(Clone, Debug, sqlx::Type)]
#[sqlx(type_name = "EventType")]
#[sqlx(rename_all = "lowercase")]
#[serde(rename_all = "lowercase")]
pub enum EventType {
Lecture,
Exercise,
Expand All @@ -244,10 +247,13 @@ pub enum EventType {
}

impl Display for EventType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut str = serde_json::to_string(self).map_err(|_| std::fmt::Error)?;
let _ = str.remove(0);
let _ = str.remove(str.len() - 1);
write!(f, "{str}")
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
EventType::Lecture => write!(f, "lecture"),
EventType::Exercise => write!(f, "exercise"),
EventType::Exam => write!(f, "exam"),
EventType::Barred => write!(f, "barred"),
EventType::Other => write!(f, "other"),
}
}
}
1 change: 1 addition & 0 deletions server/src/db/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod calendar;
29 changes: 21 additions & 8 deletions server/src/external/connectum.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
const KEEP_ALIVE: Duration = Duration::from_secs(30);
use crate::calendar::models::Event;
use chrono::{DateTime, Utc};
use oauth2::basic::{BasicClient, BasicTokenResponse};
use oauth2::reqwest::async_http_client;
use oauth2::url::Url;
use oauth2::{AuthUrl, ClientId, ClientSecret, Scope, TokenResponse, TokenUrl};
use std::fmt;
use serde::Deserialize;
use std::fmt::{Debug, Formatter};
use std::sync::Arc;
use std::sync::RwLock;
use std::time::{Duration, Instant};
Expand All @@ -16,8 +17,8 @@ pub struct APIRequestor {
client: reqwest::Client,
oauth_token: OauthAccessToken,
}
impl fmt::Debug for APIRequestor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
impl Debug for APIRequestor {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut base = f.debug_struct("APIRequestor");
if !self.oauth_token.should_refresh_token() {
base.field("token", &self.oauth_token);
Expand All @@ -41,7 +42,7 @@ impl Default for APIRequestor {
}
}
impl APIRequestor {
pub async fn list_events(&mut self, id: &str) -> anyhow::Result<Vec<Event>> {
pub async fn list_events(&mut self, id: &str) -> anyhow::Result<Vec<ConnectumEvent>> {
let token = self.oauth_token.get_possibly_refreshed_token().await;

let url = format!("https://campus.tum.de/tumonline/co/connectum/api/rooms/{id}/calendars");
Expand All @@ -52,12 +53,24 @@ impl APIRequestor {
.bearer_auth(token)
.send()
.await?
.json::<Vec<Event>>()
.json::<Vec<ConnectumEvent>>()
.await?;
Ok(events)
}
}

#[derive(Deserialize)]
pub struct ConnectumEvent {
pub id: i32,
pub room_code: String,
pub start_at: DateTime<Utc>,
pub end_at: DateTime<Utc>,
pub title_de: String,
pub title_en: String,
pub stp_type: Option<String>,
pub entry_type: String,
pub detailed_entry_type: String,
}
#[derive(Clone)]
struct OauthAccessToken(Arc<RwLock<Option<(Instant, BasicTokenResponse)>>>);

Expand Down Expand Up @@ -139,8 +152,8 @@ impl OauthAccessToken {
token.unwrap()
}
}
impl fmt::Debug for OauthAccessToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
impl Debug for OauthAccessToken {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let token = self.0.read().expect("not poisoned");
let start_elapsed = token.as_ref().map(|(start, _)| start.elapsed());
let mut base = f.debug_struct("Token");
Expand Down
8 changes: 4 additions & 4 deletions server/src/external/download_map_image.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use std::fmt::Display;
use std::fmt::{Display, Formatter};
use std::io;
use std::time::Duration;
use std::{fmt, io};

use tracing::{error, warn};

use crate::limited::vec::LimitedVec;
use crate::maps::overlay_map::OverlayMapTask;
use crate::overlays::map::OverlayMapTask;

#[derive(Hash, Debug, Copy, Clone)]
struct TileLocation {
Expand All @@ -15,7 +15,7 @@ struct TileLocation {
}

impl Display for TileLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("TileLocation")
.field(&self.x)
.field(&self.y)
Expand Down
2 changes: 1 addition & 1 deletion server/src/external/meilisearch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use meilisearch_sdk::search::{MultiSearchResponse, SearchQuery, Selectors};
use serde::Deserialize;
use std::fmt::{Debug, Formatter};

use crate::search::{Highlighting, Limits};
use crate::routes::search::{Highlighting, Limits};

#[derive(Deserialize, Default, Clone)]
#[allow(dead_code)]
Expand Down
1 change: 1 addition & 0 deletions server/src/external/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ pub mod connectum;
pub mod download_map_image;
pub mod github;
pub mod meilisearch;
pub mod nominatim;
Loading

0 comments on commit bc9ae12

Please sign in to comment.