store events in db

This commit is contained in:
Astro 2019-10-10 17:39:34 +02:00
parent bb547dc222
commit 11dab8a049
5 changed files with 97 additions and 15 deletions

View File

@ -9,3 +9,16 @@ CREATE TABLE calendars (
etag TEXT, etag TEXT,
last_modified 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
);

View File

@ -1,9 +1,14 @@
use std::collections::HashMap; use std::collections::HashMap;
use chrono::NaiveDateTime;
mod tokenizer; mod tokenizer;
mod parser; mod parser;
pub use parser::Parser; pub use parser::Parser;
pub trait GetValue<'a, R: 'a> {
fn get(&'a self, key: &'_ str) -> Option<R>;
}
pub type Props = Vec<(String, String)>; pub type Props = Vec<(String, String)>;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -12,9 +17,26 @@ pub struct Object {
pub content: HashMap<String, (Props, String)>, pub content: HashMap<String, (Props, String)>,
} }
impl Object { impl<'a> GetValue<'a, &'a str> for Object {
pub fn get(&self, key: &'_ str) -> Option<&str> { fn get(&'a self, key: &'_ str) -> Option<&'a str> {
self.content.get(key) self.content.get(key)
.map(|(_props, value)| value.as_ref()) .map(|(_props, value)| value.as_ref())
} }
} }
impl<'a> GetValue<'a, String> for Object {
fn get(&self, key: &'_ str) -> Option<String> {
self.content.get(key)
.map(|(_props, value)| value.clone())
}
}
impl<'a> GetValue<'a, NaiveDateTime> for Object {
fn get(&self, key: &'_ str) -> Option<NaiveDateTime> {
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()
}
}

View File

@ -12,11 +12,34 @@ use diesel::{Connection, pg::PgConnection, prelude::*};
mod config; mod config;
use config::{Config, CalendarOptions}; use config::{Config, CalendarOptions};
mod schema; mod schema;
use schema::{calendars::dsl::calendars, events}; use schema::{calendars::dsl::calendars};
mod model; mod model;
use model::{Calendar, NewCalendar}; use model::{Calendar, NewCalendar, Event};
mod ics; mod ics;
use ics::Parser; use ics::{Parser, Object, GetValue};
fn obj_to_event(calendar: String, obj: Object) -> Option<Event> {
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 { pub struct Resources {
db_url: String, db_url: String,
@ -84,9 +107,9 @@ impl Resources {
} }
let mut res = req.send()?; let mut res = req.send()?;
println!("{} {}", res.status(), cal_opts.url);
if res.status() != 200 { if res.status() != 200 {
let msg = format!("HTTP {}", res.status()); let msg = format!("HTTP {}", res.status());
println!("{} {}", res.status(), cal_opts.url);
diesel::update(calendars) diesel::update(calendars)
.filter(schema::calendars::dsl::id.eq(id.clone())) .filter(schema::calendars::dsl::id.eq(id.clone()))
.set((schema::calendars::dsl::last_success.eq(Utc::now().naive_utc()), .set((schema::calendars::dsl::last_success.eq(Utc::now().naive_utc()),
@ -112,20 +135,32 @@ impl Resources {
let mut p = Parser::new(); let mut p = Parser::new();
let mut buf = [0; 1024]; let mut buf = [0; 1024];
let mut events = vec![];
loop { loop {
match res.read(&mut buf)? { match res.read(&mut buf)? {
len if len > 0 => { len if len > 0 => {
let data = &buf[..len]; let data = &buf[..len];
p.feed(data, |obj| { p.feed(data, |obj| {
println!("- [{}] {}", obj.get("DTSTART").unwrap_or("?"), obj.get("SUMMARY").unwrap_or("?")); if let Some(event) = obj_to_event(cal_opts.url.clone(), obj) {
print!(" {}", obj.get("LOCATION").unwrap_or("?")); events.push(event);
obj.get("URL").map(|url| print!(" <{}>", url)); }
println!("");
}); });
} }
_ => break, _ => 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(()) Ok(())
})?; })?;

View File

@ -1,7 +1,7 @@
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
use super::schema::calendars; use super::schema::{calendars, events};
#[derive(Queryable)] #[derive(Debug, Queryable)]
pub struct Calendar { pub struct Calendar {
pub id: String, pub id: String,
pub url: String, pub url: String,
@ -14,7 +14,7 @@ pub struct Calendar {
pub last_modified: Option<String>, pub last_modified: Option<String>,
} }
#[derive(Insertable)] #[derive(Debug, Insertable)]
#[table_name = "calendars"] #[table_name = "calendars"]
pub struct NewCalendar { pub struct NewCalendar {
pub id: String, pub id: String,
@ -22,3 +22,15 @@ pub struct NewCalendar {
pub last_fetch: Option<NaiveDateTime>, pub last_fetch: Option<NaiveDateTime>,
} }
#[derive(Debug, Queryable, Insertable)]
pub struct Event {
pub calendar: String,
pub id: String,
pub dtstart: NaiveDateTime,
pub dtend: Option<NaiveDateTime>,
pub summary: String,
pub location: Option<String>,
pub url: Option<String>,
}

View File

@ -17,8 +17,8 @@ table! {
calendar -> VarChar, calendar -> VarChar,
id -> VarChar, id -> VarChar,
start -> Timestamp, dtstart -> Timestamp,
end -> Nullable<Timestamp>, dtend -> Nullable<Timestamp>,
summary -> VarChar, summary -> VarChar,
location -> Nullable<VarChar>, location -> Nullable<VarChar>,
url -> Nullable<VarChar>, url -> Nullable<VarChar>,