|
|
|
@ -1,13 +1,13 @@
|
|
|
|
|
use std::collections::HashSet;
|
|
|
|
|
use std::{fs::File, io::Read, collections::HashMap, path::Path};
|
|
|
|
|
use std::{collections::HashMap, fs::File, io::Read, path::Path};
|
|
|
|
|
|
|
|
|
|
use poise::serenity_prelude::{SerenityError, Http};
|
|
|
|
|
use derive_more::From;
|
|
|
|
|
use poise::serenity_prelude::{Http, SerenityError};
|
|
|
|
|
use r2d2::{Pool, PooledConnection};
|
|
|
|
|
use r2d2_sqlite::SqliteConnectionManager;
|
|
|
|
|
use r2d2_sqlite::rusqlite::{self, params};
|
|
|
|
|
use derive_more::From;
|
|
|
|
|
use r2d2_sqlite::SqliteConnectionManager;
|
|
|
|
|
|
|
|
|
|
use crate::{utils::get_challenge_number, models::User};
|
|
|
|
|
use crate::{models::User, utils::get_challenge_number};
|
|
|
|
|
|
|
|
|
|
use super::{LegacySubmission, Submission};
|
|
|
|
|
|
|
|
|
@ -38,9 +38,7 @@ impl Database {
|
|
|
|
|
Path::new(DATABASE_FILENAME).exists()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn new(
|
|
|
|
|
testing: bool,
|
|
|
|
|
) -> Result<Self> {
|
|
|
|
|
pub fn new(testing: bool) -> Result<Self> {
|
|
|
|
|
let connection_manager = if testing {
|
|
|
|
|
SqliteConnectionManager::memory()
|
|
|
|
|
} else {
|
|
|
|
@ -77,7 +75,8 @@ impl Database {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn has_submitted(&self, user_id: u64) -> Result<bool> {
|
|
|
|
|
Ok(self.conn()?
|
|
|
|
|
Ok(self
|
|
|
|
|
.conn()?
|
|
|
|
|
.prepare("SELECT 1 FROM User WHERE id = ?1 LIMIT 1")?
|
|
|
|
|
.query_row(params![user_id], |_row| Ok(()))
|
|
|
|
|
.is_ok())
|
|
|
|
@ -103,38 +102,47 @@ impl Database {
|
|
|
|
|
// their username/discriminator since their previous submission
|
|
|
|
|
match archived_users.get(&id) {
|
|
|
|
|
Some(User { deleted, .. }) => {
|
|
|
|
|
archived_users.insert(id, User {
|
|
|
|
|
archived_users.insert(
|
|
|
|
|
id,
|
|
|
|
|
User {
|
|
|
|
|
id,
|
|
|
|
|
avatar: None,
|
|
|
|
|
deleted: *deleted,
|
|
|
|
|
..User::from_username(&legacy.username)
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
None => match User::fetch(http, id).await {
|
|
|
|
|
Ok(User { deleted: true, .. }) => {
|
|
|
|
|
archived_users.insert(id, User {
|
|
|
|
|
archived_users.insert(
|
|
|
|
|
id,
|
|
|
|
|
User {
|
|
|
|
|
id,
|
|
|
|
|
avatar: None,
|
|
|
|
|
deleted: true,
|
|
|
|
|
..User::from_username(&legacy.username)
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
Ok(user) => {
|
|
|
|
|
conn.execute(
|
|
|
|
|
"INSERT INTO User(id, name, discriminator, avatar, deleted) VALUES (?1, ?2, ?3, ?4, ?5)",
|
|
|
|
|
params![user.id, user.name, user.discriminator, user.avatar, user.deleted]
|
|
|
|
|
).map_err(DatabaseError::Rusqlite)?;
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
Err(error) if error.to_string().eq("Unknown User") => {
|
|
|
|
|
// This will also be called in the case of an invalid user ID
|
|
|
|
|
println!("Failed to fetch user {id}, adding to archive");
|
|
|
|
|
archived_users.insert(id, User {
|
|
|
|
|
archived_users.insert(
|
|
|
|
|
id,
|
|
|
|
|
User {
|
|
|
|
|
id,
|
|
|
|
|
avatar: None,
|
|
|
|
|
deleted: false,
|
|
|
|
|
..User::from_username(&legacy.username)
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
Err(error) => return Err(LoadLegacyError::Serenity(error)),
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
@ -161,7 +169,10 @@ impl Database {
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_challenge_user_data(&self, challenge: u32) -> Result<(Vec<Submission>, HashMap<String, User>)> {
|
|
|
|
|
pub fn get_challenge_user_data(
|
|
|
|
|
&self,
|
|
|
|
|
challenge: u32,
|
|
|
|
|
) -> Result<(Vec<Submission>, HashMap<String, User>)> {
|
|
|
|
|
let submissions = self.get_submissions(challenge)?;
|
|
|
|
|
let users = self.get_users({
|
|
|
|
|
let mut user_ids = HashSet::new();
|
|
|
|
@ -174,7 +185,8 @@ impl Database {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_submissions(&self, challenge: u32) -> Result<Vec<Submission>> {
|
|
|
|
|
Ok(self.conn()?
|
|
|
|
|
Ok(self
|
|
|
|
|
.conn()?
|
|
|
|
|
.prepare("SELECT author_id, timestamp, image FROM Submission WHERE challenge = ?1")?
|
|
|
|
|
.query_map(params![challenge], |row| {
|
|
|
|
|
Ok(Submission {
|
|
|
|
@ -188,7 +200,8 @@ impl Database {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_user_by_name(&self, name: &str) -> Result<Option<User>> {
|
|
|
|
|
Ok(self.conn()?
|
|
|
|
|
Ok(self
|
|
|
|
|
.conn()?
|
|
|
|
|
.prepare("SELECT id, discriminator, avatar, deleted FROM User where name = ?1")?
|
|
|
|
|
.query_row(params![name], |row| {
|
|
|
|
|
Ok(Some(User {
|
|
|
|
@ -202,8 +215,10 @@ impl Database {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_submissions_by_user_name(&self, name: &str) -> Result<Vec<Submission>> {
|
|
|
|
|
Ok(self.conn()?
|
|
|
|
|
.prepare("
|
|
|
|
|
Ok(self
|
|
|
|
|
.conn()?
|
|
|
|
|
.prepare(
|
|
|
|
|
"
|
|
|
|
|
SELECT author_id, timestamp, image, challenge
|
|
|
|
|
FROM Submission s
|
|
|
|
|
JOIN User u ON s.author_id = u.id
|
|
|
|
@ -227,16 +242,23 @@ impl Database {
|
|
|
|
|
.iter()
|
|
|
|
|
// u64 must be converted to String for templates
|
|
|
|
|
.map(|id| -> Result<(String, User)> {
|
|
|
|
|
match conn.prepare("SELECT name, discriminator, avatar, deleted FROM User WHERE id = ?1") {
|
|
|
|
|
Ok(mut statement) => statement.query_row(params![id], |row| {
|
|
|
|
|
Ok((id.to_string(), User {
|
|
|
|
|
match conn
|
|
|
|
|
.prepare("SELECT name, discriminator, avatar, deleted 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)?,
|
|
|
|
|
deleted: row.get(3)?,
|
|
|
|
|
}))
|
|
|
|
|
}).map_err(DatabaseError::Rusqlite),
|
|
|
|
|
},
|
|
|
|
|
))
|
|
|
|
|
})
|
|
|
|
|
.map_err(DatabaseError::Rusqlite),
|
|
|
|
|
Err(error) => Err(DatabaseError::Rusqlite(error)),
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|