From 11dab8a049001dc576f7b09b7857199871f50bf1 Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 10 Oct 2019 17:39:34 +0200 Subject: [PATCH] store events in db --- schema.sql | 13 +++++++++++++ src/ics/mod.rs | 26 +++++++++++++++++++++++-- src/main.rs | 51 ++++++++++++++++++++++++++++++++++++++++++-------- src/model.rs | 18 +++++++++++++++--- src/schema.rs | 4 ++-- 5 files changed, 97 insertions(+), 15 deletions(-) diff --git a/schema.sql b/schema.sql index 9c928a0..a8d4a74 100644 --- a/schema.sql +++ b/schema.sql @@ -9,3 +9,16 @@ CREATE TABLE calendars ( etag TEXT, last_modified TEXT ); + +CREATE TABLE events ( + calendar TEXT NOT NULL, + id TEXT NOT NULL, + PRIMARY KEY (calendar, id), + + dtstart TIMESTAMP NOT NULL, + dtend TIMESTAMP, + + summary TEXT NOT NULL, + location TEXT, + url TEXT +); diff --git a/src/ics/mod.rs b/src/ics/mod.rs index e3f1032..4fa9d54 100644 --- a/src/ics/mod.rs +++ b/src/ics/mod.rs @@ -1,9 +1,14 @@ use std::collections::HashMap; +use chrono::NaiveDateTime; mod tokenizer; mod parser; pub use parser::Parser; +pub trait GetValue<'a, R: 'a> { + fn get(&'a self, key: &'_ str) -> Option; +} + pub type Props = Vec<(String, String)>; #[derive(Debug, PartialEq)] @@ -12,9 +17,26 @@ pub struct Object { pub content: HashMap, } -impl Object { - pub fn get(&self, key: &'_ str) -> Option<&str> { +impl<'a> GetValue<'a, &'a str> for Object { + fn get(&'a self, key: &'_ str) -> Option<&'a str> { self.content.get(key) .map(|(_props, value)| value.as_ref()) } } + +impl<'a> GetValue<'a, String> for Object { + fn get(&self, key: &'_ str) -> Option { + self.content.get(key) + .map(|(_props, value)| value.clone()) + } +} + +impl<'a> GetValue<'a, NaiveDateTime> for Object { + fn get(&self, key: &'_ str) -> Option { + let s = self.get(key)?; + NaiveDateTime::parse_from_str(s, "%Y%m%dT%H%M%SZ") + .or_else(|_| NaiveDateTime::parse_from_str(s, "%Y%m%dT%H%M%S")) + .or_else(|_| NaiveDateTime::parse_from_str(s, "%Y%m%d")) + .ok() + } +} diff --git a/src/main.rs b/src/main.rs index b60e86f..d7cd5f1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,11 +12,34 @@ use diesel::{Connection, pg::PgConnection, prelude::*}; mod config; use config::{Config, CalendarOptions}; mod schema; -use schema::{calendars::dsl::calendars, events}; +use schema::{calendars::dsl::calendars}; mod model; -use model::{Calendar, NewCalendar}; +use model::{Calendar, NewCalendar, Event}; mod ics; -use ics::Parser; +use ics::{Parser, Object, GetValue}; + +fn obj_to_event(calendar: String, obj: Object) -> Option { + if obj.name != "VEVENT" { + return None; + } + + let dtstart = obj.get("DTSTART")?; + let summary = obj.get("SUMMARY")?; + let id = format!("{}{}{}{}", + obj.get("UID").unwrap_or(""), + dtstart, + obj.get("DTSTAMP").unwrap_or(""), + obj.get("RECURRENCE-ID").unwrap_or("")); + // TODO: DESCRIPTION + Some(Event { + calendar, id, + dtstart, + dtend: obj.get("DTEND"), + summary, + location: obj.get("LOCATION"), + url: obj.get("URL"), + }) +} pub struct Resources { db_url: String, @@ -84,9 +107,9 @@ impl Resources { } let mut res = req.send()?; - println!("{} {}", res.status(), cal_opts.url); if res.status() != 200 { let msg = format!("HTTP {}", res.status()); + println!("{} {}", res.status(), cal_opts.url); diesel::update(calendars) .filter(schema::calendars::dsl::id.eq(id.clone())) .set((schema::calendars::dsl::last_success.eq(Utc::now().naive_utc()), @@ -112,20 +135,32 @@ impl Resources { let mut p = Parser::new(); let mut buf = [0; 1024]; + let mut events = vec![]; loop { match res.read(&mut buf)? { len if len > 0 => { let data = &buf[..len]; p.feed(data, |obj| { - println!("- [{}] {}", obj.get("DTSTART").unwrap_or("?"), obj.get("SUMMARY").unwrap_or("?")); - print!(" {}", obj.get("LOCATION").unwrap_or("?")); - obj.get("URL").map(|url| print!(" <{}>", url)); - println!(""); + if let Some(event) = obj_to_event(cal_opts.url.clone(), obj) { + events.push(event); + } }); } _ => break, } } + drop(res); + + println!("{} events {}", events.len(), cal_opts.url); + diesel::delete(schema::events::dsl::events) + .filter(schema::events::dsl::calendar.eq(cal_opts.url)) + .execute(&db)?; + for event in events { + // println!("insert {:?}", event); + diesel::insert_into(schema::events::dsl::events) + .values(&event) + .execute(&db)?; + } Ok(()) })?; diff --git a/src/model.rs b/src/model.rs index c9615df..556fddd 100644 --- a/src/model.rs +++ b/src/model.rs @@ -1,7 +1,7 @@ use chrono::NaiveDateTime; -use super::schema::calendars; +use super::schema::{calendars, events}; -#[derive(Queryable)] +#[derive(Debug, Queryable)] pub struct Calendar { pub id: String, pub url: String, @@ -14,7 +14,7 @@ pub struct Calendar { pub last_modified: Option, } -#[derive(Insertable)] +#[derive(Debug, Insertable)] #[table_name = "calendars"] pub struct NewCalendar { pub id: String, @@ -22,3 +22,15 @@ pub struct NewCalendar { pub last_fetch: Option, } + +#[derive(Debug, Queryable, Insertable)] +pub struct Event { + pub calendar: String, + pub id: String, + + pub dtstart: NaiveDateTime, + pub dtend: Option, + pub summary: String, + pub location: Option, + pub url: Option, +} diff --git a/src/schema.rs b/src/schema.rs index f0269e1..3048735 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -17,8 +17,8 @@ table! { calendar -> VarChar, id -> VarChar, - start -> Timestamp, - end -> Nullable, + dtstart -> Timestamp, + dtend -> Nullable, summary -> VarChar, location -> Nullable, url -> Nullable,