2023-07-28 23:15:27 +00:00
use std ::time ::{ Duration , Instant } ;
use julid ::Julid ;
2023-07-08 20:16:05 +00:00
use sqlx ::{ query_scalar , SqlitePool } ;
2023-07-06 15:48:42 +00:00
2023-07-28 23:15:27 +00:00
use crate ::{ util ::year_to_epoch , ShowKind , User , Watch , WatchQuest } ;
2023-07-01 22:42:01 +00:00
2023-07-09 04:21:12 +00:00
const USER_EXISTS_QUERY : & str = " select count(*) from users where id = $1 " ;
2023-07-01 22:42:01 +00:00
2023-10-22 20:35:41 +00:00
const MOVIE_QUERY : & str = " select * from movie_titles order by year, title asc limit ? " ;
2023-07-06 15:48:42 +00:00
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-28 23:15:27 +00:00
const OMEGA_ID : Julid = Julid ::omega ( ) ;
2023-07-08 20:16:05 +00:00
const BULK_INSERT : usize = 2_000 ;
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 > ,
2023-10-22 20:35:41 +00:00
pub runtime : Option < String > ,
2023-07-03 22:20:19 +00:00
}
impl From < ImportMovieOmega > for Watch {
fn from ( value : ImportMovieOmega ) -> Self {
Watch {
2023-07-29 19:27:12 +00:00
id : OMEGA_ID , // this is ignored by the inserter
2023-07-03 22:20:19 +00:00
title : value . title ,
release_date : year_to_epoch ( value . year . as_deref ( ) ) ,
2023-10-22 20:35:41 +00:00
length : value . runtime . and_then ( | v | v . parse ::< i64 > ( ) . ok ( ) ) ,
2023-07-03 22:20:19 +00:00
kind : ShowKind ::Movie ,
metadata_url : None ,
2023-07-28 23:15:27 +00:00
added_by : OMEGA_ID ,
2023-07-03 22:20:19 +00:00
}
}
}
2023-07-04 18:30:42 +00:00
impl From < & ImportMovieOmega > for Watch {
fn from ( value : & ImportMovieOmega ) -> Self {
Watch {
2023-07-29 19:27:12 +00:00
id : OMEGA_ID ,
2023-07-04 18:30:42 +00:00
title : value . title . to_string ( ) ,
release_date : year_to_epoch ( value . year . as_deref ( ) ) ,
2023-10-22 20:35:41 +00:00
length : value . runtime . as_ref ( ) . and_then ( | v | v . parse ::< i64 > ( ) . ok ( ) ) ,
2023-07-04 18:30:42 +00:00
kind : ShowKind ::Movie ,
metadata_url : None ,
2023-07-28 23:15:27 +00:00
added_by : OMEGA_ID ,
2023-07-04 18:30:42 +00:00
}
}
}
2023-07-01 22:42:01 +00:00
//-************************************************************************
// utility functions for building CLI tools, currently just for benchmarking
//-************************************************************************
2023-07-08 20:16:05 +00:00
pub async fn add_watch_quests ( pool : & SqlitePool , quests : & [ WatchQuest ] ) -> Result < ( ) , ( ) > {
2023-07-09 05:00:26 +00:00
let mut builder = sqlx ::QueryBuilder ::new ( " insert into watch_quests (user, watch) " ) ;
2023-07-08 20:16:05 +00:00
builder . push_values ( quests , | mut b , quest | {
let user = quest . user ;
let watch = quest . watch ;
//eprintln!("{user}, {watch}");
b . push_bind ( user ) . push_bind ( watch ) ;
} ) ;
2023-07-01 22:42:01 +00:00
2023-07-08 20:16:05 +00:00
let q = builder . build ( ) ;
q . execute ( pool ) . await . map_err ( | e | {
dbg! ( e ) ;
} ) ? ;
Ok ( ( ) )
2023-07-05 23:26:20 +00:00
}
2023-07-28 23:15:27 +00:00
pub async fn add_users ( db_pool : & SqlitePool , users : & [ User ] ) -> Result < Duration , ( ) > {
2023-07-06 19:24:25 +00:00
let mut builder =
2023-07-28 23:15:27 +00:00
sqlx ::QueryBuilder ::new ( " insert into users (username, displayname, email, pwhash) " ) ;
2023-07-08 20:16:05 +00:00
2023-07-28 23:15:27 +00:00
let start = Instant ::now ( ) ;
2023-07-08 20:16:05 +00:00
builder . push_values ( users . iter ( ) , | mut b , user | {
2023-07-28 23:15:27 +00:00
b . push_bind ( & user . username )
2023-07-08 20:16:05 +00:00
. push_bind ( & user . displayname )
. push_bind ( & user . email )
. push_bind ( & user . pwhash ) ;
2023-07-06 19:24:25 +00:00
} ) ;
let q = builder . build ( ) ;
2023-07-08 20:16:05 +00:00
q . execute ( db_pool ) . await . map_err ( | _ | ( ) ) ? ;
2023-07-28 23:15:27 +00:00
let end = Instant ::now ( ) ;
let dur = end - start ;
Ok ( dur )
2023-07-06 19:24:25 +00:00
}
2023-07-09 05:00:26 +00:00
pub async fn add_omega_watches (
w2w_db : & SqlitePool ,
movie_db : & SqlitePool ,
2023-07-28 23:15:27 +00:00
num : u32 ,
) -> Result < Duration , ( ) > {
let omega = ensure_omega ( w2w_db ) . await ;
2023-07-05 23:26:20 +00:00
2023-07-06 15:48:42 +00:00
let movies : Vec < ImportMovieOmega > = sqlx ::query_as ( MOVIE_QUERY )
2023-07-28 23:15:27 +00:00
. bind ( num )
2023-07-06 15:48:42 +00:00
. fetch_all ( movie_db )
. await
. unwrap ( ) ;
2023-07-28 23:15:27 +00:00
let start = Instant ::now ( ) ;
2023-07-08 20:16:05 +00:00
for movies in movies . as_slice ( ) . chunks ( BULK_INSERT ) {
let mut builder = sqlx ::QueryBuilder ::new (
2023-07-28 23:15:27 +00:00
" insert into watches (kind, title, length, release_date, added_by) " ,
2023-07-08 20:16:05 +00:00
) ;
builder . push_values ( movies , | mut b , movie | {
let title = & movie . title ;
2023-07-28 23:15:27 +00:00
b . push_bind ( ShowKind ::Movie )
2023-07-08 20:16:05 +00:00
. push_bind ( title )
2023-10-22 20:35:41 +00:00
. push_bind ( movie . runtime . as_ref ( ) . and_then ( | l | l . parse ::< i64 > ( ) . ok ( ) ) )
2023-07-08 20:16:05 +00:00
. push_bind ( year_to_epoch ( movie . year . as_deref ( ) ) )
. push_bind ( omega ) ;
2023-07-05 23:26:20 +00:00
} ) ;
2023-07-08 20:16:05 +00:00
let q = builder . build ( ) ;
2023-07-09 05:00:26 +00:00
q . execute ( w2w_db ) . await . map_err ( | _ | ( ) ) ? ;
2023-07-05 23:26:20 +00:00
}
2023-07-28 23:15:27 +00:00
let end = Instant ::now ( ) ;
let dur = end - start ;
2023-07-08 20:16:05 +00:00
2023-07-28 23:15:27 +00:00
Ok ( dur )
2023-07-05 23:26:20 +00:00
}
2023-07-28 23:15:27 +00:00
pub async fn ensure_omega ( db_pool : & SqlitePool ) -> Julid {
2023-07-01 22:42:01 +00:00
if ! check_omega_exists ( db_pool ) . await {
2023-07-29 19:27:12 +00:00
sqlx ::query ( " insert into users (id, username, pwhash) values (?, 'the omega user', 'you shall not password') " ) . bind ( OMEGA_ID ) . execute ( db_pool ) . await . unwrap ( ) ;
2023-07-01 22:42:01 +00:00
}
2023-07-28 23:15:27 +00:00
OMEGA_ID
2023-07-01 22:42:01 +00:00
}
async fn check_omega_exists ( db_pool : & SqlitePool ) -> bool {
let count = query_scalar ( USER_EXISTS_QUERY )
2023-07-29 19:27:12 +00:00
. bind ( OMEGA_ID )
2023-07-01 22:42:01 +00:00
. fetch_one ( db_pool )
. await
. unwrap_or ( 0 ) ;
count > 0
}
//-************************************************************************
//tests
//-************************************************************************
#[ cfg(test) ]
mod test {
2023-07-21 22:15:47 +00:00
use tokio ::runtime ::Runtime ;
2023-07-01 22:42:01 +00:00
use super ::* ;
2023-07-21 22:15:47 +00:00
#[ test ]
fn ensure_omega_user ( ) {
2023-07-19 00:37:24 +00:00
let p = crate ::db ::get_db_pool ( ) ;
2023-07-21 22:15:47 +00:00
let rt = Runtime ::new ( ) . unwrap ( ) ;
rt . block_on ( async {
assert! ( ! check_omega_exists ( & p ) . await ) ;
ensure_omega ( & p ) . await ;
} ) ;
2023-07-28 23:15:27 +00:00
assert! ( rt . block_on ( check_omega_exists ( & p ) ) ) ;
2023-07-01 22:42:01 +00:00
}
}