Skip to content

Commit

Permalink
Add timetable endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
MaximMaximS committed Mar 4, 2024
1 parent c8b7ee9 commit 29145bd
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 9 deletions.
69 changes: 61 additions & 8 deletions rezvrh_api/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use axum::{
extract::Query,
extract::{Path, Query},
http::{HeaderMap, StatusCode},
response::{IntoResponse, Response},
routing::get,
Json, Router,
};
use base64::prelude::*;
use rezvrh_scraper::{Bakalari, Error as BakalariError};
use rezvrh_scraper::{Bakalari, Error as BakalariError, Timetable, Type, Which};
use thiserror::Error;

// Extract basic auth from headers
Expand All @@ -26,6 +26,8 @@ enum ApiError {
BadUrl,
#[error("Scrape error: {0}")]
ScrapeError(#[from] BakalariError),
#[error("invalid or missing selector")]
InvalidSelector,
}

impl IntoResponse for ApiError {
Expand All @@ -35,6 +37,9 @@ impl IntoResponse for ApiError {
(StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into_response()
}
Self::BadUrl => (StatusCode::BAD_REQUEST, "Invalid or missing URL").into_response(),
Self::InvalidSelector => {
(StatusCode::BAD_REQUEST, "Invalid or missing selector").into_response()
}
}
}
}
Expand All @@ -44,8 +49,8 @@ struct GetQuery {
url: String,
}

async fn get_api(headers: &HeaderMap, query: &Query<GetQuery>) -> Result<Bakalari, ApiError> {
let url = query.url.parse().map_err(|_| ApiError::BadUrl)?;
async fn get_api(headers: &HeaderMap, url: &str) -> Result<Bakalari, ApiError> {
let url = url.parse().map_err(|_| ApiError::BadUrl)?;
match auth(headers) {
Some((username, password)) => {
Ok(Bakalari::from_creds_no_store((&username, &password), url).await?)
Expand All @@ -58,7 +63,7 @@ async fn get_rooms(
headers: HeaderMap,
query: Query<GetQuery>,
) -> Result<Json<Vec<String>>, ApiError> {
let bakalari = get_api(&headers, &query).await?;
let bakalari = get_api(&headers, &query.url).await?;
let classes = bakalari.get_objects(rezvrh_scraper::Type::Room);
Ok(Json(classes))
}
Expand All @@ -67,7 +72,7 @@ async fn get_classes(
headers: HeaderMap,
query: Query<GetQuery>,
) -> Result<Json<Vec<String>>, ApiError> {
let bakalari = get_api(&headers, &query).await?;
let bakalari = get_api(&headers, &query.url).await?;
let classes = bakalari.get_objects(rezvrh_scraper::Type::Class);
Ok(Json(classes))
}
Expand All @@ -76,11 +81,50 @@ async fn get_teachers(
headers: HeaderMap,
query: Query<GetQuery>,
) -> Result<Json<Vec<String>>, ApiError> {
let bakalari = get_api(&headers, &query).await?;
let bakalari = get_api(&headers, &query.url).await?;
let classes = bakalari.get_objects(rezvrh_scraper::Type::Teacher);
Ok(Json(classes))
}

async fn get_class_timetable(
Path((class_name, which)): Path<(String, Which)>,
headers: HeaderMap,
query: Query<GetQuery>,
) -> Result<Json<Timetable>, ApiError> {
let bakalari = get_api(&headers, &query.url).await?;
let selector = bakalari
.get_selector(Type::Class, &class_name)
.ok_or(ApiError::InvalidSelector)?;
let timetable = bakalari.get_timetable(which, &selector).await?;
Ok(Json(timetable))
}

async fn get_teacher_timetable(
Path((teacher_name, which)): Path<(String, Which)>,
headers: HeaderMap,
query: Query<GetQuery>,
) -> Result<Json<Timetable>, ApiError> {
let bakalari = get_api(&headers, &query.url).await?;
let selector = bakalari
.get_selector(Type::Teacher, &teacher_name)
.ok_or(ApiError::InvalidSelector)?;
let timetable = bakalari.get_timetable(which, &selector).await?;
Ok(Json(timetable))
}

async fn get_room_timetable(
Path((room_name, which)): Path<(String, Which)>,
headers: HeaderMap,
query: Query<GetQuery>,
) -> Result<Json<Timetable>, ApiError> {
let bakalari = get_api(&headers, &query.url).await?;
let selector = bakalari
.get_selector(Type::Room, &room_name)
.ok_or(ApiError::InvalidSelector)?;
let timetable = bakalari.get_timetable(which, &selector).await?;
Ok(Json(timetable))
}

/*
async fn get_timetable(
headers: HeaderMap,
Expand All @@ -98,7 +142,16 @@ async fn main() -> anyhow::Result<()> {
.route("/", get(|| async { "Hello, World!" }))
.route("/classes", get(get_classes))
.route("/rooms", get(get_rooms))
.route("/teachers", get(get_teachers));
.route("/teachers", get(get_teachers))
.route(
"/timetable/class/:class_name/:which",
get(get_class_timetable),
)
.route(
"/timetable/teacher/:teacher_name/:which",
get(get_teacher_timetable),
)
.route("/timetable/room/:room_name/:which", get(get_room_timetable));

// run our app with hyper, listening globally on port 3000
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
Expand Down
1 change: 1 addition & 0 deletions rezvrh_scraper/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ mod modules;
pub use modules::bakalari::Bakalari;
pub use modules::bakalari::RequestError as Error;
pub use modules::timetable::RawType as Type;
pub use modules::timetable::Type as Selector;
pub use modules::timetable::Which;
pub use modules::timetable::Timetable;
3 changes: 2 additions & 1 deletion rezvrh_scraper/src/modules/timetable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ mod lesson;
mod util;

/// Which timetable to get
#[derive(Debug, Display, PartialEq, Eq, Hash, Clone, Copy)]
#[derive(Debug, Display, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Which {
/// Permanent timetable
Permanent,
Expand Down
1 change: 1 addition & 0 deletions rezvrh_scraper/src/modules/timetable/lesson.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod parser;

/// Struct that hold one lesson of timetable
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Lesson {
Regular {
class: String,
Expand Down

0 comments on commit 29145bd

Please sign in to comment.