diff --git a/Cargo.lock b/Cargo.lock index 19ff500..743943a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2410,28 +2410,6 @@ dependencies = [ "proc-macro2 1.0.59", ] -[[package]] -name = "r2d2" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" -dependencies = [ - "log 0.4.18", - "parking_lot", - "scheduled-thread-pool", -] - -[[package]] -name = "r2d2_sqlite" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99f31323d6161385f385046738df520e0e8694fa74852d35891fc0be08348ddc" -dependencies = [ - "r2d2", - "rusqlite", - "uuid", -] - [[package]] name = "rand" version = "0.8.5" @@ -2907,15 +2885,6 @@ dependencies = [ "windows-sys 0.42.0", ] -[[package]] -name = "scheduled-thread-pool" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" -dependencies = [ - "parking_lot", -] - [[package]] name = "scoped-tls" version = "1.0.1" @@ -3277,8 +3246,6 @@ dependencies = [ "dotenv", "gettext", "poise", - "r2d2", - "r2d2_sqlite", "reqwest", "rocket 0.5.0-rc.3", "rocket_contrib", @@ -3863,16 +3830,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" -[[package]] -name = "uuid" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be" -dependencies = [ - "getrandom", - "rand", -] - [[package]] name = "valuable" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index c068c0f..09a886a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,6 @@ derive_more = "0.99.17" dotenv = "0.15.0" gettext = "0.4.0" poise = "0.5.5" -r2d2 = "0.8.10" -r2d2_sqlite = "0.22.0" reqwest = "0.11.18" rocket = { version = "=0.5.0-rc.3", features = ["secrets", "json"] } rocket_contrib = { version = "0.4.11", features = ["templates"] } diff --git a/src/main.rs b/src/main.rs index aaec564..9125773 100644 --- a/src/main.rs +++ b/src/main.rs @@ -52,7 +52,8 @@ async fn main() { return; } let http = http(); - load_database() + Database::new(false) + .expect("Failed to load database") .load_legacy(&http).await .expect("Failed to load legacy submissions"); }, @@ -63,10 +64,6 @@ async fn main() { } } -fn load_database() -> Database { - Database::new(false).expect("Failed to load database") -} - fn http() -> Http { let token = env::var("DISCORD_TOKEN").expect("Expected a token in the environment"); Http::new(&token) @@ -84,13 +81,11 @@ async fn rocket() -> Result, rocket::Error> { rocket::custom(config) .manage(Settings::new(&http).await.unwrap()) .manage(http) - .manage(load_database()) .mount( "/", routes![get_challenge, get_guilds, login, post_login, success, logout, testing], ) .mount("/css", FileServer::from(relative!("styles/css"))) - .mount("/", FileServer::from(relative!("assets")).rank(2)) .mount("/", FileServer::from(relative!("static")).rank(1)) .attach(Template::custom(move |engines| { use tera::Value; diff --git a/src/models/database.rs b/src/models/database.rs index 71623e2..8553f0b 100644 --- a/src/models/database.rs +++ b/src/models/database.rs @@ -1,32 +1,18 @@ -use std::collections::HashSet; use std::{fs::File, io::Read, collections::HashMap, path::Path}; use poise::serenity_prelude::Http; -use r2d2::{Pool, PooledConnection}; -use r2d2_sqlite::SqliteConnectionManager; -use r2d2_sqlite::rusqlite::{self, params}; -use derive_more::From; +use rusqlite::{Connection, params}; use crate::{utils::get_challenge_number, models::User}; use super::{LegacySubmission, Submission}; pub struct Database { - // Must be Arc because Connection contains RefCell, - // which cannot be shared between threads safely - connection_pool: Pool, + conn: Connection, } const DATABASE_FILENAME: &str = "database.db"; -#[derive(From, Debug)] -pub enum DatabaseError { - Rusqlite(rusqlite::Error), - Pool(r2d2::Error), -} - -type Result = std::result::Result; - impl Database { pub fn file_exists() -> bool { Path::new(DATABASE_FILENAME).exists() @@ -34,14 +20,12 @@ impl Database { pub fn new( testing: bool, - ) -> Result { - let connection_manager = if testing { - SqliteConnectionManager::memory() + ) -> rusqlite::Result { + let conn = if testing { + Connection::open_in_memory() } else { - SqliteConnectionManager::file(DATABASE_FILENAME) - }; - let connection_pool = Pool::new(connection_manager)?; - let conn = connection_pool.get()?; + Connection::open(DATABASE_FILENAME) + }?; conn.execute( "CREATE TABLE IF NOT EXISTS Submission ( id INTEGER PRIMARY KEY, @@ -62,26 +46,21 @@ impl Database { )", params![], )?; - Ok(Self { connection_pool }) + Ok(Self { conn }) } - fn conn(&self) -> std::result::Result, r2d2::Error> { - self.connection_pool.get() - } - - pub fn has_submitted(&self, user_id: u64) -> Result { - Ok(self.conn()? + pub fn has_submitted(&self, user_id: u64) -> rusqlite::Result { + Ok(self.conn .prepare("SELECT 1 FROM User WHERE id = ?1 LIMIT 1")? .query_row(params![user_id], |_row| Ok(())) .is_ok()) } - pub async fn load_legacy(&self, http: &Http) -> Result<()> { + pub async fn load_legacy(&self, http: &Http) -> rusqlite::Result<()> { let latest_challenge = get_challenge_number(); // HashMap of archived users that are no longer sharing a server with 字ちゃん // Their historical usernames and discriminators will be used let mut archived_users = HashMap::new(); - let conn = self.conn()?; for n in 1..=latest_challenge { println!("Loading legacy challenge {n}/{latest_challenge}..."); let mut file = File::open(format!("data/challenges/{n}.json")).unwrap(); @@ -93,7 +72,7 @@ impl Database { .map(|legacy| (legacy, legacy.parse().unwrap())) { let mut already_updated = false; for submission in submissions { - conn.execute( + self.conn.execute( "INSERT INTO Submission(author_id, timestamp, image, challenge) VALUES (?1, ?2, ?3, ?4)", params![ &submission.author_id, @@ -141,7 +120,7 @@ impl Database { } else { match User::fetch(http, submission.author_id).await { Ok(user) => { - conn.execute( + self.conn.execute( "INSERT INTO User(id, name, discriminator, avatar) VALUES (?1, ?2, ?3, ?4)", params![user.id, user.name, user.discriminator, user.avatar] )?; @@ -158,53 +137,6 @@ impl Database { } Ok(()) } - - pub fn get_challenge_user_data(&self, challenge: u32) -> Result<(Vec, HashMap)> { - let submissions = self.get_submissions(challenge)?; - let users = self.get_users({ - let mut user_ids = HashSet::new(); - for submission in &submissions { - user_ids.insert(submission.author_id); - } - user_ids - })?; - Ok((submissions, users)) - } - - pub fn get_submissions(&self, challenge: u32) -> Result> { - Ok(self.conn()? - .prepare("SELECT author_id, timestamp, image FROM Submission WHERE challenge = ?1")? - .query_map(params![challenge], |row| { - Ok(Submission { - author_id: row.get(0)?, - timestamp: row.get(1)?, - image: row.get(2)?, - }) - })? - .collect::, rusqlite::Error>>()?) - } - - fn get_users(&self, users: HashSet) -> Result> { - let conn = self.conn()?; - // Not sure why derive_more::From is unable to convert these errors - users - .iter() - // u64 must be converted to String for templates - .map(|id| -> Result<(String, User)> { - match conn.prepare("SELECT name, discriminator, avatar FROM User WHERE id = ?1") { - Ok(mut statement) => statement.query_row(params![id], |row| { - Ok((id.to_string(), User { - id: *id, - name: row.get(0)?, - discriminator: row.get(1)?, - avatar: row.get(2)?, - })) - }).map_err(DatabaseError::Rusqlite), - Err(error) => Err(DatabaseError::Rusqlite(error)), - } - }) - .collect() - } #[allow(dead_code)] pub fn refresh_users(&self) -> rusqlite::Result<()> { diff --git a/src/models/submission/submission.rs b/src/models/submission/submission.rs index 1c1a4c2..0ce5ccf 100644 --- a/src/models/submission/submission.rs +++ b/src/models/submission/submission.rs @@ -1,11 +1,8 @@ use chrono::{Utc, DateTime}; -use serde::Serialize; // Challenge submission // In the legacy site version, one submission held 1 or more images. // Now, 1 submission = 1 image, and the leaderboard count will be labeled as "participations" - -#[derive(Serialize, Debug)] pub struct Submission { pub author_id: u64, // Some fields might be empty for legacy submissions diff --git a/src/models/user/mod.rs b/src/models/user/mod.rs index 073449c..c0eeef4 100644 --- a/src/models/user/mod.rs +++ b/src/models/user/mod.rs @@ -18,7 +18,7 @@ pub trait Username { fn username(&self) -> String; } -#[derive(Default, Deserialize, Debug)] +#[derive(Default, Deserialize)] pub struct User { #[serde(deserialize_with = "deserialize_id")] pub id: u64, diff --git a/src/routes/get_challenge.rs b/src/routes/get_challenge.rs index b042546..4d3dea2 100644 --- a/src/routes/get_challenge.rs +++ b/src/routes/get_challenge.rs @@ -6,7 +6,7 @@ use rocket_dyn_templates::{context, Template}; use crate::{ cookies::LANG_COOKIE, i18n::DEFAULT as DEFAULT_LANG, - models::{Challenge, Settings, SessionUser, Database}, + models::{Challenge, Settings, SessionUser}, utils::AcceptLanguage, }; @@ -15,16 +15,12 @@ pub async fn get_challenge( challenge: u32, cookies: &CookieJar<'_>, settings: &State, - database: &State, accept_language: AcceptLanguage, ) -> Template { - let (submissions, users) = database.get_challenge_user_data(challenge).unwrap(); Template::render( "index", context! { challenge, - submissions, - users, settings: settings.deref(), lang: cookies .get(LANG_COOKIE) diff --git a/styles/sass/style.scss b/styles/sass/style.scss index a1be5d2..fd6da3f 100644 --- a/styles/sass/style.scss +++ b/styles/sass/style.scss @@ -42,10 +42,10 @@ input[type=number] { #content { width: 100%; - height: 100%; - display: flex; - flex-direction: column; overflow: hidden; + & > div { + padding: 1em; + } nav { --bg: #{$fg}; @@ -97,58 +97,6 @@ input[type=number] { border-radius: 100%; } } - - & > div:first-of-type { - display: flex; - height: 100%; - & > :first-child { - min-width: 40em; - padding: 1em; - } - & > * { - overflow: scroll; - } - } -} - -* { - box-sizing: border-box !important; -} - -#submissions { - display: flex; - flex-direction: column; - height: calc(100% - 2em); - background: rgba(0, 0, 0, 0.125); - $gap: 0.5em; - padding: $gap; - & > div { - width: 100%; - column-count: 3; - margin: 0; - - gap: $gap; - - & > figure { - background: rgba(0, 0, 0, 0.125); - padding: $gap; - width: 100%; - box-sizing: border-box; - margin: 0; - margin-bottom: $gap; - break-inside: avoid-column; - text-align: center; - - & > img { - width: 100%; - cursor: pointer; - &:hover { - border: none; - filter: brightness(1.25); - } - } - } - } } .dropdown:hover > .link, nav > .link:hover { diff --git a/templates/index.html.tera b/templates/index.html.tera index 790e832..ef3d19e 100644 --- a/templates/index.html.tera +++ b/templates/index.html.tera @@ -65,88 +65,54 @@
-
-

Welcome to Tegaki Tuesday #{{ challenge }}!

-
- - - {% for line in content.japanese %} - {% for subline in line %} -

- {%- for word in subline -%} - {%- if word.dictionary -%} - - {%- endif -%} - {% for segment in word.text -%} - {{ segment.kanji }}({{ segment.furigana | safe }}) - {%- endfor -%} - {%- if word.dictionary -%} - - {%- endif -%} - {%- endfor -%} -

- {% endfor %} - {% endfor %} -
- {% if content.translation %} -

- Translation - {% if content.translation.author %}by {{ content.translation.author }}{% endif %} - {% if content.translation.site %} via - {% if content.translation.site.link %} - {{ content.translation.site.name }} - {% else %} - {{ content.translation.site.name }} - {% endif %} - {%- endif -%} -

- {%- endif -%} - {%- if content.suggester -%} -

This challenge was suggested by {{ content.suggester }} using the -h suggest command.

- {%- endif -%} -
-
+

Welcome to Tegaki Tuesday #{{ challenge }}!

+
-
- {% for submission in submissions %} - {% set author = users[submission.author_id] %} -
- {{ author.username }}'s submission -
{{ author.username }}
-
+ + {% for line in content.japanese %} + {% for subline in line %} +

+ {%- for word in subline -%} + {%- if word.dictionary -%} + + {%- endif -%} + {% for segment in word.text -%} + {{ segment.kanji }}({{ segment.furigana | safe }}) + {%- endfor -%} + {%- if word.dictionary -%} + + {%- endif -%} + {%- endfor -%} +

{% endfor %} -
-
- some random shit goes here -
-
+ {% endfor %} +
+ {% if content.translation %} +

+ Translation + {% if content.translation.author %}by {{ content.translation.author }}{% endif %} + {% if content.translation.site %} via + {% if content.translation.site.link %} + {{ content.translation.site.name }} + {% else %} + {{ content.translation.site.name }} + {% endif %} + {%- endif -%} +

+ {%- endif -%} + {%- if content.suggester -%} +

This challenge was suggested by {{ content.suggester }} using the -h suggest command.

+ {%- endif -%}