store events in db
parent
bb547dc222
commit
11dab8a049
13
schema.sql
13
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
|
||||
);
|
||||
|
|
|
@ -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<R>;
|
||||
}
|
||||
|
||||
pub type Props = Vec<(String, String)>;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
@ -12,9 +17,26 @@ pub struct Object {
|
|||
pub content: HashMap<String, (Props, String)>,
|
||||
}
|
||||
|
||||
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<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()
|
||||
}
|
||||
}
|
||||
|
|
51
src/main.rs
51
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<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 {
|
||||
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(())
|
||||
})?;
|
||||
|
|
18
src/model.rs
18
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<String>,
|
||||
}
|
||||
|
||||
#[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<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>,
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@ table! {
|
|||
calendar -> VarChar,
|
||||
id -> VarChar,
|
||||
|
||||
start -> Timestamp,
|
||||
end -> Nullable<Timestamp>,
|
||||
dtstart -> Timestamp,
|
||||
dtend -> Nullable<Timestamp>,
|
||||
summary -> VarChar,
|
||||
location -> Nullable<VarChar>,
|
||||
url -> Nullable<VarChar>,
|
||||
|
|
Loading…
Reference in New Issue