OrangeGuidanceTomestone/server/src/web/get_location.rs

125 lines
4.0 KiB
Rust
Raw Normal View History

2022-09-05 01:48:43 +00:00
use std::collections::HashMap;
2022-09-03 10:35:15 +00:00
use std::sync::Arc;
use anyhow::Context;
2022-09-05 04:02:36 +00:00
use chrono::{Duration, Utc};
2022-09-05 01:48:43 +00:00
use rand::Rng;
2022-09-03 10:35:15 +00:00
use warp::{Filter, Rejection, Reply};
use warp::filters::BoxedFilter;
use crate::message::RetrievedMessage;
use crate::State;
use crate::web::AnyhowRejection;
pub fn get_location(state: Arc<State>) -> BoxedFilter<(impl Reply, )> {
warp::get()
.and(warp::path("messages"))
.and(warp::path::param())
.and(warp::path::end())
2022-09-05 01:48:43 +00:00
.and(warp::query::<HashMap<String, String>>())
2022-09-04 03:50:30 +00:00
.and(super::get_id(Arc::clone(&state)))
2022-09-05 04:02:36 +00:00
.and_then(move |location: u32, query: HashMap<String, String>, (id, _)| logic(Arc::clone(&state), id, location, query))
2022-09-03 10:35:15 +00:00
.boxed()
}
2022-09-05 01:48:43 +00:00
async fn logic(state: Arc<State>, id: i64, location: u32, query: HashMap<String, String>) -> Result<impl Reply, Rejection> {
2022-09-04 03:50:30 +00:00
// TODO: when we're not just returning all results, make sure own messages are always present
2022-09-05 01:48:43 +00:00
let filter = query.contains_key("filter");
2022-09-04 07:12:51 +00:00
let location = location as i64;
2022-09-05 01:48:43 +00:00
let mut messages = sqlx::query_as!(
2022-09-03 10:35:15 +00:00
RetrievedMessage,
// language=sqlite
r#"
select m.id,
m.x,
m.y,
m.z,
2022-09-04 21:04:44 +00:00
m.yaw,
2022-09-03 10:35:15 +00:00
m.message,
coalesce(sum(v.vote between 0 and 1), 0) as positive_votes,
2022-09-04 07:12:51 +00:00
coalesce(sum(v.vote between -1 and 0), 0) as negative_votes,
2022-09-05 04:02:36 +00:00
v2.vote as user_vote,
m.created
2022-09-03 10:35:15 +00:00
from messages m
left join votes v on m.id = v.message
2022-09-04 07:12:51 +00:00
left join votes v2 on m.id = v2.message and v2.user = ?
2022-09-03 10:35:15 +00:00
where m.territory = ?
group by m.id"#,
id,
2022-09-04 07:12:51 +00:00
location,
2022-09-03 10:35:15 +00:00
)
.fetch_all(&state.db)
.await
.context("could not get messages from database")
.map_err(AnyhowRejection)
.map_err(warp::reject::custom)?;
2022-09-05 01:48:43 +00:00
if filter {
filter_messages(&mut messages);
}
2022-09-03 10:35:15 +00:00
Ok(warp::reply::json(&messages))
}
2022-09-05 01:48:43 +00:00
fn filter_messages(messages: &mut Vec<RetrievedMessage>) {
// just count nearby messages. this is O(n^2) but alternatives are hard
let mut ids = Vec::with_capacity(messages.len());
for a in messages.iter() {
let mut nearby = 0;
for b in messages.iter() {
if a.id == b.id {
continue;
}
let distance = (a.x - b.x).powi(2)
+ (a.y - b.y).powi(2)
+ (a.z - b.z).powi(2);
// 7.5 squared
if distance >= 56.25 {
continue;
}
nearby += 1;
}
2022-09-05 04:02:36 +00:00
println!("{} ({} nearby)", a.id, nearby);
2022-09-05 01:48:43 +00:00
if nearby <= 2 {
// always include groups of three or fewer
ids.push(a.id.clone());
continue;
}
let score = (a.positive_votes - a.negative_votes).max(0);
2022-09-05 04:02:36 +00:00
let time_since_creation = Utc::now().naive_utc().signed_duration_since(a.created) - Duration::weeks(score as i64);
println!(" time_since_creation: {}", Utc::now().naive_utc().signed_duration_since(a.created));
println!(" modified: {}", time_since_creation);
if time_since_creation > Duration::weeks(1) {
continue;
}
2022-09-05 01:48:43 +00:00
2022-09-05 08:45:41 +00:00
// originally thresholds were 6 hours and 2 days
let brand_new = time_since_creation < Duration::minutes(30);
let new = time_since_creation < Duration::hours(2);
2022-09-05 01:48:43 +00:00
2022-09-05 04:02:36 +00:00
let mut numerator = 1;
if brand_new {
numerator = nearby;
} else if new {
numerator += (nearby / 3).min(1);
2022-09-05 01:48:43 +00:00
}
2022-09-05 04:02:36 +00:00
if score > 0 {
let pad = score as f32 / nearby as f32;
let rounded = pad.round() as u32;
numerator += rounded.max(nearby / 2);
}
2022-09-05 08:45:41 +00:00
println!(" chance: {}/{}", numerator, nearby * 2);
if rand::thread_rng().gen_ratio(numerator.min(nearby), nearby * 2) {
2022-09-05 04:02:36 +00:00
ids.push(a.id.clone());
}
}
2022-09-05 01:48:43 +00:00
2022-09-05 08:23:10 +00:00
messages.drain_filter(|msg| !ids.contains(&msg.id));
2022-09-05 01:48:43 +00:00
}