2023-07-06 15:48:42 +00:00
|
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
|
2023-07-01 22:42:01 +00:00
|
|
|
use sqlx::{query, query_scalar, SqlitePool};
|
2023-07-05 23:26:20 +00:00
|
|
|
use tokio::task::JoinSet;
|
|
|
|
use tokio_retry::Retry;
|
2023-07-01 22:42:01 +00:00
|
|
|
|
2023-07-03 22:20:19 +00:00
|
|
|
use crate::{
|
2023-07-06 15:48:42 +00:00
|
|
|
db_id::DbId,
|
|
|
|
util::year_to_epoch,
|
|
|
|
watches::handlers::{add_new_watch_impl, add_watch_quest_impl},
|
|
|
|
ShowKind, Watch, WatchQuest,
|
2023-07-03 22:20:19 +00:00
|
|
|
};
|
2023-07-01 22:42:01 +00:00
|
|
|
|
|
|
|
const USER_EXISTS_QUERY: &str = "select count(*) from witches where id = $1";
|
|
|
|
|
2023-07-06 15:48:42 +00:00
|
|
|
const MOVIE_QUERY: &str = "select * from movies order by random() limit 10000";
|
|
|
|
|
2023-07-05 23:26:20 +00:00
|
|
|
//-************************************************************************
|
|
|
|
// the omega user is the system ID, but has no actual power in the app
|
|
|
|
//-************************************************************************
|
2023-07-01 22:42:01 +00:00
|
|
|
const OMEGA_ID: u128 = u128::MAX;
|
|
|
|
|
2023-07-04 23:24:28 +00:00
|
|
|
#[derive(Debug, sqlx::FromRow, Clone)]
|
2023-07-03 22:20:19 +00:00
|
|
|
pub struct ImportMovieOmega {
|
|
|
|
pub title: String,
|
|
|
|
pub year: Option<String>,
|
|
|
|
pub length: Option<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<ImportMovieOmega> for Watch {
|
|
|
|
fn from(value: ImportMovieOmega) -> Self {
|
|
|
|
Watch {
|
|
|
|
title: value.title,
|
|
|
|
release_date: year_to_epoch(value.year.as_deref()),
|
|
|
|
length: value.length.and_then(|v| v.parse::<i64>().ok()),
|
|
|
|
id: DbId::new(),
|
|
|
|
kind: ShowKind::Movie,
|
|
|
|
metadata_url: None,
|
|
|
|
added_by: OMEGA_ID.into(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-04 18:30:42 +00:00
|
|
|
impl From<&ImportMovieOmega> for Watch {
|
|
|
|
fn from(value: &ImportMovieOmega) -> Self {
|
|
|
|
Watch {
|
|
|
|
title: value.title.to_string(),
|
|
|
|
release_date: year_to_epoch(value.year.as_deref()),
|
|
|
|
length: value.length.as_ref().and_then(|v| v.parse::<i64>().ok()),
|
|
|
|
id: DbId::new(),
|
|
|
|
kind: ShowKind::Movie,
|
|
|
|
metadata_url: None,
|
|
|
|
added_by: OMEGA_ID.into(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-01 22:42:01 +00:00
|
|
|
//-************************************************************************
|
|
|
|
// utility functions for building CLI tools, currently just for benchmarking
|
|
|
|
//-************************************************************************
|
2023-07-06 15:48:42 +00:00
|
|
|
pub async fn add_watch_omega(db_pool: &SqlitePool, movie: &ImportMovieOmega) -> Result<DbId, ()> {
|
2023-07-03 22:20:19 +00:00
|
|
|
let watch: Watch = movie.into();
|
|
|
|
if add_new_watch_impl(db_pool, &watch, None).await.is_ok() {
|
2023-07-06 15:48:42 +00:00
|
|
|
Ok(watch.id)
|
2023-07-01 22:42:01 +00:00
|
|
|
} else {
|
|
|
|
eprintln!("failed to add \"{}\"", watch.title);
|
2023-07-04 18:30:42 +00:00
|
|
|
Err(())
|
2023-07-01 22:42:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-06 15:48:42 +00:00
|
|
|
pub async fn add_watch_quest(db_pool: &SqlitePool, quest: WatchQuest) -> Result<(), ()> {
|
|
|
|
if add_watch_quest_impl(db_pool, &quest).await.is_ok() {
|
2023-07-05 23:26:20 +00:00
|
|
|
Ok(())
|
|
|
|
} else {
|
2023-07-06 15:48:42 +00:00
|
|
|
eprintln!("failed to add {}", quest.id);
|
2023-07-05 23:26:20 +00:00
|
|
|
Err(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-01 22:42:01 +00:00
|
|
|
pub async fn add_user(
|
|
|
|
db_pool: &SqlitePool,
|
|
|
|
username: &str,
|
|
|
|
displayname: Option<&str>,
|
|
|
|
email: Option<&str>,
|
|
|
|
id: Option<DbId>,
|
|
|
|
) {
|
|
|
|
let pwhash = "you shall not password";
|
|
|
|
let id: DbId = id.unwrap_or_else(DbId::new);
|
|
|
|
if query(crate::signup::CREATE_QUERY)
|
|
|
|
.bind(id)
|
|
|
|
.bind(username)
|
|
|
|
.bind(displayname)
|
|
|
|
.bind(email)
|
|
|
|
.bind(pwhash)
|
|
|
|
.execute(db_pool)
|
|
|
|
.await
|
|
|
|
.is_ok()
|
|
|
|
{
|
|
|
|
println!("{id}");
|
|
|
|
} else {
|
|
|
|
eprintln!("failed to add user \"{username}\"");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-06 15:48:42 +00:00
|
|
|
pub async fn add_omega_watches(ww_db: &SqlitePool, movie_db: &SqlitePool) -> Vec<DbId> {
|
2023-07-05 23:26:20 +00:00
|
|
|
ensure_omega(ww_db).await;
|
|
|
|
|
2023-07-06 15:48:42 +00:00
|
|
|
let movies: Vec<ImportMovieOmega> = sqlx::query_as(MOVIE_QUERY)
|
|
|
|
.fetch_all(movie_db)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-07-05 23:26:20 +00:00
|
|
|
let mut set = JoinSet::new();
|
2023-07-06 15:48:42 +00:00
|
|
|
let movie_set = Vec::with_capacity(10_000);
|
|
|
|
let movie_set = Arc::new(Mutex::new(movie_set));
|
2023-07-05 23:26:20 +00:00
|
|
|
|
|
|
|
let retry_strategy = tokio_retry::strategy::ExponentialBackoff::from_millis(100)
|
|
|
|
.map(tokio_retry::strategy::jitter)
|
|
|
|
.take(4);
|
|
|
|
|
|
|
|
for movie in movies {
|
|
|
|
let db = ww_db.clone();
|
|
|
|
let title = movie.title.as_str();
|
|
|
|
let year = movie.year.clone().unwrap();
|
|
|
|
let len = movie.length.clone().unwrap();
|
|
|
|
let retry_strategy = retry_strategy.clone();
|
2023-07-06 15:48:42 +00:00
|
|
|
let movie_set = movie_set.clone();
|
2023-07-05 23:26:20 +00:00
|
|
|
|
|
|
|
let key = format!("{title}{year}{len}");
|
|
|
|
set.spawn(async move {
|
|
|
|
(
|
|
|
|
key,
|
|
|
|
Retry::spawn(retry_strategy, || async {
|
2023-07-06 15:48:42 +00:00
|
|
|
if let Ok(id) = add_watch_omega(&db, &movie).await {
|
|
|
|
let mut mset = movie_set.lock().unwrap();
|
|
|
|
mset.push(id);
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err(())
|
|
|
|
}
|
2023-07-05 23:26:20 +00:00
|
|
|
})
|
|
|
|
.await,
|
|
|
|
)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
// stragglers
|
|
|
|
while (set.join_next().await).is_some() {}
|
2023-07-06 15:48:42 +00:00
|
|
|
let movies = movie_set.lock().unwrap().clone();
|
|
|
|
movies
|
2023-07-05 23:26:20 +00:00
|
|
|
}
|
|
|
|
|
2023-07-01 22:42:01 +00:00
|
|
|
pub async fn ensure_omega(db_pool: &SqlitePool) -> DbId {
|
|
|
|
if !check_omega_exists(db_pool).await {
|
|
|
|
add_user(
|
|
|
|
db_pool,
|
|
|
|
"The Omega User",
|
|
|
|
Some("I am the end of all watches."),
|
|
|
|
None,
|
|
|
|
Some(OMEGA_ID.into()),
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
OMEGA_ID.into()
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn check_omega_exists(db_pool: &SqlitePool) -> bool {
|
|
|
|
let id: DbId = OMEGA_ID.into();
|
|
|
|
let count = query_scalar(USER_EXISTS_QUERY)
|
|
|
|
.bind(id)
|
|
|
|
.fetch_one(db_pool)
|
|
|
|
.await
|
|
|
|
.unwrap_or(0);
|
|
|
|
count > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
//-************************************************************************
|
|
|
|
//tests
|
|
|
|
//-************************************************************************
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn ensure_omega_user() {
|
|
|
|
let p = crate::db::get_db_pool().await;
|
|
|
|
assert!(!check_omega_exists(&p).await);
|
|
|
|
ensure_omega(&p).await;
|
|
|
|
assert!(check_omega_exists(&p).await);
|
|
|
|
}
|
|
|
|
}
|