Publish cal events to server
This commit is contained in:
parent
dce761b0f9
commit
8e1547750e
|
@ -157,6 +157,7 @@ dependencies = [
|
||||||
"strum_macros",
|
"strum_macros",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
|
"uuid",
|
||||||
"webbrowser",
|
"webbrowser",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -889,6 +890,12 @@ version = "0.3.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
|
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-crate"
|
name = "proc-macro-crate"
|
||||||
version = "1.1.3"
|
version = "1.1.3"
|
||||||
|
@ -917,6 +924,36 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.2.13"
|
version = "0.2.13"
|
||||||
|
@ -1371,6 +1408,16 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"rand",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcpkg"
|
name = "vcpkg"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
|
|
@ -66,5 +66,9 @@ version = "0.5"
|
||||||
version = "1"
|
version = "1"
|
||||||
features = ["rt-multi-thread", "net", "macros"]
|
features = ["rt-multi-thread", "net", "macros"]
|
||||||
|
|
||||||
|
[dependencies.uuid]
|
||||||
|
version = "1.1"
|
||||||
|
features = ["v4", "fast-rng"]
|
||||||
|
|
||||||
[dependencies.webbrowser]
|
[dependencies.webbrowser]
|
||||||
version = "0.7"
|
version = "0.7"
|
||||||
|
|
|
@ -6,13 +6,15 @@ use {
|
||||||
crate::commands::import,
|
crate::commands::import,
|
||||||
crate::constants,
|
crate::constants,
|
||||||
crate::recipe,
|
crate::recipe,
|
||||||
anyhow::Result,
|
anyhow::{bail, Result},
|
||||||
chrono::naive::{NaiveDate, NaiveDateTime, NaiveTime},
|
chrono::naive::{NaiveDate, NaiveDateTime, NaiveTime},
|
||||||
chrono::{DateTime, Local, TimeZone},
|
chrono::{DateTime, Local, TimeZone},
|
||||||
futures::future::try_join_all,
|
futures::future::try_join_all,
|
||||||
ics::properties as calprop,
|
ics::properties as calprop,
|
||||||
ics::Event,
|
ics::Event,
|
||||||
|
reqwest::StatusCode,
|
||||||
std::collections::{HashMap, HashSet},
|
std::collections::{HashMap, HashSet},
|
||||||
|
std::fmt::Write,
|
||||||
std::iter::Iterator,
|
std::iter::Iterator,
|
||||||
std::path::Path,
|
std::path::Path,
|
||||||
};
|
};
|
||||||
|
@ -53,11 +55,11 @@ pub async fn with(api_client: &ApiClient, calendar: &str, csv_file: &Path) -> Re
|
||||||
})
|
})
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
publish_events(&api_client, calendar, events)?;
|
publish_events(&api_client, calendar, events).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_event(date: NaiveDate, meal: Meal, recipe: &recipe::Recipe) -> Event {
|
fn to_event(date: NaiveDate, meal: Meal, recipe: &recipe::Recipe) -> (String, Event) {
|
||||||
// TODO: this is momentarily hardcoded, should be an option
|
// TODO: this is momentarily hardcoded, should be an option
|
||||||
let meal_time = match meal {
|
let meal_time = match meal {
|
||||||
Meal::Lunch => NaiveTime::from_hms(12, 00, 00),
|
Meal::Lunch => NaiveTime::from_hms(12, 00, 00),
|
||||||
|
@ -74,7 +76,7 @@ fn to_event(date: NaiveDate, meal: Meal, recipe: &recipe::Recipe) -> Event {
|
||||||
let end_time = NaiveDateTime::new(date, meal_time);
|
let end_time = NaiveDateTime::new(date, meal_time);
|
||||||
let start_time = end_time - recipe.total_time();
|
let start_time = end_time - recipe.total_time();
|
||||||
|
|
||||||
let mut event = Event::new(uid, dt_fmt(&Local::now()));
|
let mut event = Event::new(uid.clone(), dt_fmt(&Local::now()));
|
||||||
event.push(calprop::Summary::new(&recipe.name));
|
event.push(calprop::Summary::new(&recipe.name));
|
||||||
event.push(calprop::Description::new(format!("cookbook@{}", recipe.id)));
|
event.push(calprop::Description::new(format!("cookbook@{}", recipe.id)));
|
||||||
event.push(calprop::Location::new(&recipe.url));
|
event.push(calprop::Location::new(&recipe.url));
|
||||||
|
@ -91,7 +93,7 @@ fn to_event(date: NaiveDate, meal: Meal, recipe: &recipe::Recipe) -> Event {
|
||||||
);
|
);
|
||||||
event.add_alarm(alarm);
|
event.add_alarm(alarm);
|
||||||
|
|
||||||
event
|
(uid, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn urls_from_csv<'a, RecordsIter>(records: RecordsIter) -> Result<HashSet<String>>
|
fn urls_from_csv<'a, RecordsIter>(records: RecordsIter) -> Result<HashSet<String>>
|
||||||
|
@ -112,7 +114,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dt_fmt(datetime: &DateTime<Local>) -> String {
|
fn dt_fmt(datetime: &DateTime<Local>) -> String {
|
||||||
datetime.format("%Y%m%dT%H%M%SZ").to_string()
|
datetime.format("%Y%m%dT%H%M%S").to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_all_recipes(api_client: &ApiClient) -> Result<HashMap<String, recipe::Recipe>> {
|
async fn get_all_recipes(api_client: &ApiClient) -> Result<HashMap<String, recipe::Recipe>> {
|
||||||
|
@ -148,21 +150,15 @@ async fn get_all_recipes(api_client: &ApiClient) -> Result<HashMap<String, recip
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn publish_events<'a, EventsIter>(
|
async fn publish_events<'a, EventsIter>(
|
||||||
api_client: &ApiClient,
|
api_client: &ApiClient,
|
||||||
calendar: &str,
|
calendar: &str,
|
||||||
events: EventsIter,
|
events: EventsIter,
|
||||||
) -> Result<()>
|
) -> Result<()>
|
||||||
where
|
where
|
||||||
EventsIter: Iterator<Item = Event<'a>>,
|
EventsIter: Iterator<Item = (String, Event<'a>)>,
|
||||||
{
|
{
|
||||||
let calendar_url = api_client.base_url.join(&format!(
|
let calendar_prototype: ics::ICalendar = ics::ICalendar::new(
|
||||||
"remote.php/dav/calendars/{}/{}/",
|
|
||||||
&api_client.username,
|
|
||||||
calendar.to_lowercase().as_str().replace(" ", "-")
|
|
||||||
));
|
|
||||||
|
|
||||||
let cal = ics::ICalendar::new(
|
|
||||||
"2.0",
|
"2.0",
|
||||||
format!(
|
format!(
|
||||||
"-//{}//NONSGML {}//EN",
|
"-//{}//NONSGML {}//EN",
|
||||||
|
@ -170,12 +166,48 @@ where
|
||||||
env!("CARGO_PKG_NAME")
|
env!("CARGO_PKG_NAME")
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
let cal = events.fold(cal, |mut cal, e| {
|
|
||||||
cal.add_event(e);
|
let dav_base = api_client
|
||||||
cal
|
.rest
|
||||||
|
.head(api_client.base_url.join("/.well-known/caldav")?)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let calendar_url = dav_base.url().join(&format!(
|
||||||
|
"calendars/{}/{}/",
|
||||||
|
&api_client.username,
|
||||||
|
calendar.to_lowercase().as_str().replace(" ", "-")
|
||||||
|
))?;
|
||||||
|
|
||||||
|
let calendar_prototype = &calendar_prototype;
|
||||||
|
let calendar_url = &calendar_url;
|
||||||
|
let update_requests = events.map(|(event_id, event)| async move {
|
||||||
|
let mut cal = calendar_prototype.clone();
|
||||||
|
cal.add_event(event);
|
||||||
|
|
||||||
|
let url = calendar_url.join(&format!("{}.ics", event_id)).unwrap();
|
||||||
|
api_client
|
||||||
|
.rest
|
||||||
|
.put(url)
|
||||||
|
.header("Content-Type", "text/calendar; charset=utf-8")
|
||||||
|
.body(cal.to_string())
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
});
|
});
|
||||||
|
|
||||||
println!("CALENDAR: \n {:?}", cal);
|
let responses = try_join_all(update_requests).await?;
|
||||||
|
let failed_responses = responses.into_iter().filter(|response| {
|
||||||
|
![StatusCode::NO_CONTENT, StatusCode::CREATED].contains(&response.status())
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut errors = String::new();
|
||||||
|
for r in failed_responses {
|
||||||
|
write!(errors, "\n{}", r.text().await.unwrap())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !errors.is_empty() {
|
||||||
|
bail!("Error while updating calendar events: {}", errors);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue