diff options
Diffstat (limited to 'migration/src/main.rs')
-rw-r--r-- | migration/src/main.rs | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/migration/src/main.rs b/migration/src/main.rs new file mode 100644 index 0000000..72252a9 --- /dev/null +++ b/migration/src/main.rs @@ -0,0 +1,131 @@ +extern crate clap; +extern crate serde; +extern crate serde_json; +#[macro_use] +extern crate serde_derive; +#[macro_use] +extern crate log; +extern crate env_logger; +extern crate rusqlite; +extern crate regex; + +use std::fs::File; +use std::io::Read; +use clap::{App, Arg}; +use std::collections::HashMap; +use regex::Regex; + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +struct Key { + id: String, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +struct Quote { + timestamp: String, + author: String, + ip: String, + content: String, + quote_date: String, + #[serde(rename = "__key__")] + key: Key, + #[serde(default = "Vec::new")] + votes: Vec<Vote>, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +struct Vote { + ip: String, + rating: String, + quote_id: String, + timestamp: String, +} + +fn import<'a, I: Iterator<Item = &'a Quote>>(conn: &rusqlite::Connection, quotes: I) { + let r = Regex::new(r".*(\d{4})-(\d{2})-(\d{2}).*").unwrap(); + for quote in quotes { + let quote_date = r.replace("e.quote_date, "$1-$2-$3").to_string(); + info!("Inserting: {:?}, quote_date({})", quote, quote_date); + conn.execute("INSERT INTO quotes (approved, quote_date, author, content, ip, timestamp) VALUES (1, ?1, ?2, ?3, ?4, ?5)", + &["e_date, "e.author, "e.content, "e.ip, "e.timestamp]).unwrap(); + + let quote_id = conn.last_insert_rowid(); + let mut user_id = 1; + for vote in quote.votes.iter() { + info!("quote({}): Add vote {:?}", quote_id, vote); + let score: i32 = vote.rating.parse().unwrap(); + conn.execute(r"INSERT INTO votes (user_id, quote_id, score, ip, timestamp) VALUES (?1, ?2, ?3, ?4, ?5)", + &[&user_id, "e_id, &score, &vote.ip, &vote.timestamp]).unwrap(); + user_id += 1; + } + } +} + +fn main() { + env_logger::init().unwrap(); + + let matches = App::new("migration") + .arg(Arg::with_name("db").required(true)) + .arg(Arg::with_name("quotes_json").required(true)) + .arg(Arg::with_name("votes_json").required(true)) + .get_matches(); + + let mut quotes = "".to_string(); + File::open(matches.value_of("quotes_json").unwrap()) + .unwrap() + .read_to_string(&mut quotes) + .unwrap(); + + fn parse_all<'a, T: serde::Deserialize<'a>>(s: &'a str) -> Vec<T> { + s.lines() + .filter(|s| !s.is_empty()) + .map(|s| { + debug!("deserializing this: {:?}", s); + serde_json::from_str(s).unwrap() + }) + .collect::<Vec<T>>() + } + let quotes: Vec<Quote> = parse_all("es); + let mut quotes: HashMap<String, Quote> = + quotes.into_iter().map(|q| (q.key.id.clone(), q)).collect(); + + let mut votes = "".to_string(); + File::open(matches.value_of("votes_json").unwrap()) + .unwrap() + .read_to_string(&mut votes) + .unwrap(); + let votes: Vec<Vote> = parse_all(&votes); + + let mut v1 = 0; + for vote in votes.into_iter() { + if let Some(quote) = quotes.get_mut(&vote.quote_id) { + quote.votes.push(vote); + } + v1 += 1; + } + + let mut v2 = 0; + for (_, quote) in quotes.iter() { + println!( + "{}/{}: {} votes", + quote.key.id, + quote.quote_date, + quote.votes.len() + ); + v2 += quote.votes.len(); + } + println!("votes before {} after {}", v1, v2); + + let mut quotes: Vec<Quote> = quotes.into_iter().map(|(_, v)| v).collect(); + quotes.sort_by_key(|q| { + (q.quote_date.clone(), q.timestamp.clone(), q.key.id.clone()) + }); + + let mut conn = rusqlite::Connection::open(matches.value_of("db").unwrap()).unwrap(); + let tx = conn.transaction().unwrap(); + import(&tx, quotes.iter()); + tx.commit().unwrap(); +} |