mentorenwahl/auth/src/users.rs

195 lines
5.4 KiB
Rust

use actix_web::{get, HttpResponse};
use anyhow::Result;
use deunicode::deunicode;
use fancy_regex::Regex;
use lazy_static::lazy_static;
use serde::Serialize;
use std::collections::HashMap;
use crate::config;
lazy_static! {
static ref CLASS_REGEX: Regex = Regex::new(r"(9|10)\s?(?!R)[a-z]").unwrap();
}
#[derive(Serialize, Debug)]
pub struct Class {
pub name: String,
pub students: Vec<User>,
}
#[derive(Serialize, Debug)]
pub struct User {
pub username: String,
pub first_name: String,
pub last_name: String,
}
#[derive(Serialize, Debug)]
pub struct GetStudentsResponse {
pub error: Option<String>,
pub classes: Vec<Class>,
pub teachers: Vec<User>,
}
fn escape_username(s: &str) -> String {
deunicode(
&s.to_lowercase()
.chars()
.filter(|c| !c.is_whitespace())
.collect::<String>(),
)
}
async fn students(
client: &mut untis::Client,
usernames: &mut HashMap<String, usize>,
) -> Result<Vec<Class>> {
Ok(futures::future::try_join_all(
client
.classes()
.await?
.iter()
.filter(|c| CLASS_REGEX.is_match(&c.name).unwrap())
.map(|c| async {
Ok::<_, anyhow::Error>(Class {
name: c.name.to_owned(),
students: client
.student_reports(c.id)
.await?
.into_iter()
.map(|s| User {
username: escape_username(&s.name),
first_name: s.fore_name,
last_name: s.long_name,
})
.collect(),
})
}),
)
.await?
.into_iter()
.map(|c| Class {
name: c.name,
students: c
.students
.into_iter()
.map(|s| User {
username: {
let escaped = escape_username(&s.username);
match usernames.get_mut(&escaped) {
Some(x) => {
*x += 1;
format!("{}{}", escaped, x)
}
None => {
usernames.insert(escaped.to_owned(), 1);
escaped
}
}
},
first_name: s.first_name,
last_name: s.last_name,
})
.collect(),
})
.collect())
}
async fn teachers(
client: &mut untis::Client,
usernames: &mut HashMap<String, usize>,
) -> Result<Vec<User>> {
Ok(client
.teacher_reports()
.await?
.into_iter()
.filter_map(|t| {
if t.long_name.starts_with("Abi-Aufsicht")
|| t.long_name == "SBBZ"
|| t.long_name == "N.N."
|| t.long_name == "Werkrealschule"
{
None
} else {
Some(User {
username: {
let escaped = escape_username(&t.name);
match usernames.get_mut(&escaped) {
Some(x) => {
*x += 1;
format!("{}{}", escaped, x)
}
None => {
usernames.insert(escaped.to_owned(), 1);
escaped
}
}
},
first_name: t.fore_name,
last_name: t.long_name,
})
}
})
.collect())
}
#[get("/v1/users")]
pub async fn get_users() -> HttpResponse {
let mut client = match config::untis_from_env() {
Ok(x) => x,
Err(x) => {
return HttpResponse::InternalServerError().json(GetStudentsResponse {
error: Some(x.to_string()),
classes: vec![],
teachers: vec![],
})
}
};
if let Err(x) = client.login().await {
return HttpResponse::InternalServerError().json(GetStudentsResponse {
error: Some(x.to_string()),
classes: vec![],
teachers: vec![],
});
}
let mut usernames = HashMap::<String, usize>::new();
let classes = match students(&mut client, &mut usernames).await {
Ok(x) => x,
Err(x) => {
return HttpResponse::InternalServerError().json(GetStudentsResponse {
error: Some(x.to_string()),
classes: vec![],
teachers: vec![],
})
}
};
let teachers = match teachers(&mut client, &mut usernames).await {
Ok(x) => x,
Err(x) => {
return HttpResponse::InternalServerError().json(GetStudentsResponse {
error: Some(x.to_string()),
classes: vec![],
teachers: vec![],
})
}
};
if let Err(x) = client.logout().await {
return HttpResponse::InternalServerError().json(GetStudentsResponse {
error: Some(x.to_string()),
classes: vec![],
teachers: vec![],
});
}
HttpResponse::Ok()
.content_type(mime::APPLICATION_JSON)
.json(GetStudentsResponse {
error: None,
classes,
teachers,
})
}