quickpeep/quickpeep/src/web/seed_collector.rs

133 lines
3.6 KiB
Rust

use crate::webutil::{internal_error, TemplatedHtml};
use crate::WebConfig;
use askama::Template;
use axum::extract::{Extension, Form};
use axum::response::IntoResponse;
use itertools::Itertools;
use serde::Deserialize;
use sqlx::SqlitePool;
use std::collections::{BTreeSet, HashMap};
use std::time::{SystemTime, UNIX_EPOCH};
pub type SeedTagColumn = Vec<SeedTagGroup>;
#[derive(Debug, Clone, Deserialize)]
pub struct SeedTagGroup {
name: String,
tags: Vec<SeedTag>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct SeedTag {
name: String,
id: String,
}
#[derive(Debug, Clone, Template)]
#[template(path = "seed_collection.html.askama")]
pub struct SeedCollectionFormTemplate {
column1: SeedTagColumn,
column2: SeedTagColumn,
column3: SeedTagColumn,
thanks_for_submitting: bool,
contact: Vec<(String, String)>,
}
pub async fn seed_collection_root(
Extension(web_config): Extension<WebConfig>,
) -> impl IntoResponse {
// TODO(perf): cloning is a bit ugly; can we do better?
let seed_config = &web_config.seed_collection;
TemplatedHtml(SeedCollectionFormTemplate {
column1: seed_config.column1.clone(),
column2: seed_config.column2.clone(),
column3: seed_config.column3.clone(),
thanks_for_submitting: false,
contact: seed_config.contact.clone(),
})
}
#[derive(Deserialize, Debug, Clone)]
pub struct SeedCollectionPostForm {
pub url: String,
pub opendata_agree: bool,
pub extratags: String,
pub remarks_private: String,
/// Mostly expecting `tag_xyz` bools to be here.
#[serde(flatten)]
pub leftovers: HashMap<String, String>,
}
pub async fn seed_collection_root_post_inner(
Form(form): Form<SeedCollectionPostForm>,
Extension(web_config): Extension<WebConfig>,
Extension(pool): Extension<SqlitePool>,
) -> anyhow::Result<TemplatedHtml<SeedCollectionFormTemplate>> {
// TODO(perf): cloning is a bit ugly; can we do better?
eprintln!("{:?}", form);
let mut tags = BTreeSet::new();
let seed_config = &web_config.seed_collection;
for tag_group in itertools::chain!(
&seed_config.column1,
&seed_config.column2,
&seed_config.column3
) {
for tag in &tag_group.tags {
if form.leftovers.contains_key(&format!("tag_{}", tag.id)) {
tags.insert(tag.id.clone());
}
}
}
let tags_str = tags.iter().join(",");
let extra_tags = form
.extratags
.trim()
.replace(',', " ")
.split_whitespace()
.join(",");
let remarks_private: String = form.remarks_private.trim().chars().take(512).collect();
let ts = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Duration since Unix Epoch failed.")
.as_secs() as i64;
let url = form.url.trim();
sqlx::query!(
"INSERT INTO collected_seeds (
collected_ts, url, tags, extra_tags, remarks_private
) VALUES (?, ?, ?, ?, ?)",
ts,
url,
tags_str,
extra_tags,
remarks_private
)
.execute(&pool)
.await?;
Ok(TemplatedHtml(SeedCollectionFormTemplate {
column1: seed_config.column1.clone(),
column2: seed_config.column2.clone(),
column3: seed_config.column3.clone(),
thanks_for_submitting: true,
contact: seed_config.contact.clone(),
}))
}
pub async fn seed_collection_root_post(
form: Form<SeedCollectionPostForm>,
web_config: Extension<WebConfig>,
pool: Extension<SqlitePool>,
) -> impl IntoResponse {
seed_collection_root_post_inner(form, web_config, pool)
.await
.map_err(internal_error)
}