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, approved: bool, } #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct Vote { ip: String, rating: String, quote_id: String, timestamp: String, } fn import<'a, I: Iterator>(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 { s.lines() .filter(|s| !s.is_empty()) .map(|s| { debug!("deserializing this: {:?}", s); serde_json::from_str(s).unwrap() }) .collect::>() } let quotes: Vec = parse_all("es); let mut quotes: HashMap = quotes .into_iter() .filter(|q| q.approved) .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 = 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 = 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(); }