Skip to content

Commit

Permalink
details
Browse files Browse the repository at this point in the history
  • Loading branch information
CommanderStorm committed Oct 8, 2024
1 parent 046e90e commit 9bee647
Showing 1 changed file with 179 additions and 11 deletions.
190 changes: 179 additions & 11 deletions server/src/locations/details.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,175 @@
use actix_web::http::header::{CacheControl, CacheDirective};
use crate::localisation;
use crate::models::LocationKeyAlias;
use actix_web::{get, web, HttpResponse};
use chrono::DateTime;
use chrono::Utc;
use serde::{Deserialize, Serialize};
use sqlx::Error::RowNotFound;
use sqlx::PgPool;
use tracing::error;

use crate::localisation;
use crate::models::LocationKeyAlias;
#[derive(Serialize, Debug, Clone)]
struct Usage {
id: i64,
name: String,
din_277: String,
din_277_desc: String,
}

#[derive(Serialize, Debug, Clone)]
struct Operator {
id: String,
url: String,
code: String,
name: String,
}

#[derive(Serialize, Debug, Clone)]
struct Url {
name: String,
url: String,
}

#[derive(Debug, Clone)]
struct DBLocationDetails {
key: String,
name: String,
last_calendar_scrape_at: Option<DateTime<Utc>>,
calendar_url: Option<String>,
r#type: String,
type_common_name: String,
lat: f64,
lon: f64,
coordinate_source: String,
coordinate_accuracy: String,
comment: Option<String>,
}

#[derive(Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
enum LocationType {
#[default]
Room,
Building,
JoinedBuilding,
Area,
Site,
Campus,
Poi,
}

#[derive(Serialize, Default)]
struct GetLocationDetails {
/// The id, that was requested
id: String,
/// The type of the entry
r#type: LocationType,
/// The type of the entry in a human-readable form
type_common_name: String,
/// The name of the entry in a human-readable form
name: String,
/// A list of alternative ids for this entry.
///
/// Not to be confused with
/// - [`id`] which is the unique identifier or
/// - [`visual-id`] which is an alternative identifier for the entry (only displayed in the URL).
aliases: Vec<String>,
/// The ids of the parents.
/// They are ordered as they would appear in a Breadcrumb menu.
/// See [`parent_names`] for their human names.
parents: Vec<String>,
/// The ids of the parents. They are ordered as they would appear in a Breadcrumb menu.
/// See [`parents`] for their actual ids.
parent_names: Vec<String>,
/// Data for the info-card table
props: LocationProps,
/// The information you need to request Images from the /cdn/{size}/{id}_{counter}.webp endpoint
imgs: Vec<LocationImage>,
ranking_factors: RankingFactors,
/// Where we got our data from, should be displayed at the bottom of any page containing this data
sources: Sources,
/// The url, this item should be displayed at. Present on both redirects and normal entries, to allow for the common /view/:id path
redirect_url: String,
coords: Coordinate,
maps: Maps,
sections: Sections,
}

#[derive(Serialize, Default)]
struct Sections {}

#[derive(Serialize, Default)]
struct Maps {}

#[derive(Serialize, Default)]
struct LocationProps {}

#[derive(Serialize, Default)]
struct Sources {}

#[derive(Serialize)]
struct LocationImage {}

#[derive(Serialize, Default)]
struct RankingFactors {
rank_combined: u32,
rank_type: u32,
rank_usage: u32,
rank_boost: Option<u32>,
rank_custom: Option<u32>,
}

#[derive(Serialize, Default)]
struct Coordinate {
lat: f64,
lon: f64,
source: CoordinateSource,
accuracy: Option<CoordinateAccuracy>,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
enum CoordinateAccuracy {
Buiding,
}

#[derive(Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
enum CoordinateSource {
Roomfinder,
#[default]
Navigatum,
Inferred,
}

impl TryFrom<DBLocationDetails> for GetLocationDetails {
type Error = anyhow::Error;

fn try_from(base: DBLocationDetails) -> anyhow::Result<Self> {
Ok(Self {
id: base.key,
name: base.name,
r#type: LocationType::des(base.r#type)?,
type_common_name: base.type_common_name,
coords: Coordinate {
lat: base.lat,
lon: base.lon,
source: serde_json::from_str(&base.coordinate_source)?,
accuracy: match base.coordinate_accuracy {
Some(a) => CoordinateAccuracy::try_from(a)?,
None => None,
},
},
props: LocationProps {
comment: base.comment,
last_calendar_scrape_at: base.last_calendar_scrape_at,
calendar_url: base.calendar_url,
},
ranking_factors: Default::default(),
})
}
}

#[get("/{id}")]
pub async fn get_handler(
Expand All @@ -20,30 +184,34 @@ pub async fn get_handler(
return HttpResponse::NotFound().body("Not found");
};
let result = if args.should_use_english() {
sqlx::query_scalar!("SELECT data FROM en WHERE key = $1", probable_id)
sqlx::query_as!(DBLocationDetails,
r#"SELECT key,name,last_calendar_scrape_at,calendar_url,type,type_common_name,lat,lon,coordinate_source,rank_type,rank_combined,rank_usage,comment
FROM en
WHERE key = $1"#r,
probable_id)
.fetch_optional(&data.pool)
.await
} else {
sqlx::query_scalar!("SELECT data FROM de WHERE key = $1", probable_id)
sqlx::query_as!(DBLocationDetails,
r#"SELECT key,name,last_calendar_scrape_at,calendar_url,type,type_common_name,lat,lon,coordinate_source,rank_type,rank_combined,rank_usage,comment
FROM de
WHERE key = $1"#r, probable_id)
.fetch_optional(&data.pool)
.await
};
match result {
Ok(d) => match d {
None => HttpResponse::NotFound().body("Not found"),
Some(d) => {
let mut response_json = serde_json::to_string(&d).unwrap();
// We don't want to serialise this data at any point in the server.
// This just flows through the server, but adding redirect_url to the response is necessary
response_json.pop(); // remove last }
response_json.push_str(&format!(",\"redirect_url\":\"{redirect_url}\"}}",));
let mut res = GetLocationDetails::from(d);
res.redirect_url = redirect_url;

HttpResponse::Ok()
.insert_header(CacheControl(vec![
CacheDirective::MaxAge(24 * 60 * 60), // valid for 1d
CacheDirective::Public,
]))
.content_type("application/json")
.body(response_json)
.json(res)
}
},
Err(e) => {
Expand Down

0 comments on commit 9bee647

Please sign in to comment.