Update image with new functionality for OEPL days and all day events

This commit is contained in:
Jan-Henrik 2024-01-07 00:33:01 +01:00
parent 0de6b8391f
commit 2a2821c5b2
4 changed files with 78 additions and 24 deletions

30
Cargo.lock generated
View file

@ -96,6 +96,7 @@ dependencies = [
"iana-time-zone",
"js-sys",
"num-traits",
"time",
"wasm-bindgen",
"winapi",
]
@ -296,7 +297,7 @@ checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [
"cfg-if",
"libc",
"wasi",
"wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
@ -476,10 +477,12 @@ name = "ics-adapter"
version = "0.1.0"
dependencies = [
"anyhow",
"chrono",
"icalendar",
"reqwest",
"serde",
"tokio",
"urlencoding",
"warp",
]
@ -622,7 +625,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
dependencies = [
"libc",
"wasi",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.48.0",
]
@ -1198,6 +1201,17 @@ dependencies = [
"syn",
]
[[package]]
name = "time"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
"winapi",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
@ -1389,6 +1403,12 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "urlencoding"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
[[package]]
name = "utf-8"
version = "0.7.6"
@ -1457,6 +1477,12 @@ dependencies = [
"tracing",
]
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"

View file

@ -12,3 +12,5 @@ reqwest = { version = "0.11", features = ["native-tls-vendored"] }
anyhow = "1"
icalendar = { version = "0.15", features = ["chrono-tz"] }
serde = { version = "1.0", features = ["derive"] }
urlencoding = "2"
chrono = "0"

View file

@ -16,6 +16,20 @@ services:
As you can see, you only have to forward port 3000 to wherever you like, in this case port 3000.
The URL you have to enter into the OEPL Google Calendar config then looks like this:
```
http://<docker-host>:3000/calendar/<ics-url>/entries
```
It is important that you urlencode the `<ics-url>` with a tool like this: https://www.urlencoder.org/
With an actual .ics url, it looks like this:
```
http://192.168.178.42:3000/calendar/https%3A%2F%2Fnextcloud.net%2Fremote.php%2Fdav%2Fpublic-calendars%2Fasdlkijf/entries
```
Previous versions also supported these URL formats, but these are not compatible with modern OEPL features:
```
http://<docker-host>:3000/get?url=<ics-url>
```

View file

@ -19,11 +19,15 @@ fn now_timestamp_secs() -> i64 {
async fn handler(params: HashMap<String, String>) -> Result<impl Reply, Rejection> {
match params.get("url") {
Some(url) => Ok(warp::reply::json(&convert(&url).await.map_err(|_| warp::reject::reject())?.entries)),
Some(url) => Ok(warp::reply::json(&convert(&url, params.get("days")).await.map_err(|_| warp::reject::reject())?.entries)),
None => Err(warp::reject::reject())
}
}
async fn new_handler(url: String, params: HashMap<String, String>) -> Result<impl Reply, Rejection> {
Ok(warp::reply::json(&convert(&url, params.get("days")).await.map_err(|_| warp::reject::reject())?.entries))
}
#[derive(Serialize)]
struct CustomCalendar {
entries: Vec<CustomCalendarEntry>,
@ -36,9 +40,11 @@ struct CustomCalendarEntry {
end: i64,
location: String,
description: String,
isallday: bool,
}
async fn convert(url: &str) -> Result<CustomCalendar> {
async fn convert(url: &str, days: Option<&String>) -> Result<CustomCalendar> {
let url = urlencoding::decode(url)?.into_owned();
let ics_text = reqwest::get(url)
.await?
.text()
@ -49,7 +55,7 @@ async fn convert(url: &str) -> Result<CustomCalendar> {
let mut entries = Vec::new();
let filter_start = now_timestamp_secs();
let filter_end = now_timestamp_secs() + (24 * 60 * 60);
let filter_end = now_timestamp_secs() + (24 * 60 * 60) * days.unwrap_or(&String::from("1")).parse().unwrap_or(1) as i64;
for event in calendar.components {
if let Some(event) = event.as_event() {
@ -57,10 +63,6 @@ async fn convert(url: &str) -> Result<CustomCalendar> {
println!("No start!");
continue;
};
let Some(end) = event.get_end() else {
println!("No end!");
continue;
};
let start = match convert_time(start) {
Ok(t) => { t },
@ -70,23 +72,30 @@ async fn convert(url: &str) -> Result<CustomCalendar> {
}
};
let end = match convert_time(end) {
Ok(t) => { t },
Err(e) => {
println!("Invalid end timestamp: {:?}", e);
continue;
}
let end = match event.get_end() {
Some(end) => {
match convert_time(end) {
Ok(t) => { t },
Err(e) => {
println!("Invalid end timestamp: {:?}", e);
continue;
}
}
},
None => start + chrono::Duration::days(1),
};
if start < filter_start || start > filter_end {
if start.timestamp() < filter_start || start.timestamp() > filter_end {
continue;
}
entries.push(CustomCalendarEntry {
title: event.get_summary().unwrap_or("").to_string(),
description: event.get_description().unwrap_or("").to_string(),
location: event.get_location().unwrap_or("").to_string(),
start,
end
start: start.timestamp(),
end: end.timestamp(),
isallday: start.time() == chrono::NaiveTime::from_hms_opt(0, 0, 0).unwrap() && end - start == chrono::Duration::days(1) // the event has a length of 24 hours and
// starts at 00:00
});
}
}
@ -94,7 +103,7 @@ async fn convert(url: &str) -> Result<CustomCalendar> {
Ok(CustomCalendar{entries})
}
fn convert_time(dt: icalendar::DatePerhapsTime) -> Result<i64> {
fn convert_time(dt: icalendar::DatePerhapsTime) -> Result<chrono::DateTime<chrono::Utc>> {
Ok(match dt {
icalendar::DatePerhapsTime::DateTime(cdt) => {
let cdt = match cdt {
@ -106,20 +115,23 @@ fn convert_time(dt: icalendar::DatePerhapsTime) -> Result<i64> {
},
_ => cdt,
};
cdt.try_into_utc().ok_or(anyhow::Error::msg("failed to convert to utc"))?.timestamp()
cdt.try_into_utc().ok_or(anyhow::Error::msg("failed to convert to utc"))?
},
icalendar::DatePerhapsTime::Date(nd) => nd.and_hms_opt(0, 0, 0).unwrap().timestamp(),
icalendar::DatePerhapsTime::Date(nd) => nd.and_hms_opt(0, 0, 0).unwrap().and_utc(),
})
}
#[tokio::main]
async fn main() {
let converter = warp::get()
.and(warp::path("get"))
let converter = warp::path("get")
.and(warp::query::<HashMap<String, String>>())
.and_then(handler);
warp::serve(converter)
let new_converter = warp::path!("calendar" / String / "entries")
.and(warp::query::<HashMap<String, String>>())
.and_then(new_handler);
warp::serve(warp::get().and(converter.or(new_converter)))
.run(([0, 0, 0, 0], 3000))
.await
}