Cover use case of manually inserted recipes; allow recipe names instead of URLs in CSV
This commit is contained in:
parent
fe363c0e26
commit
7495682e41
7 changed files with 96 additions and 103 deletions
|
@ -8,7 +8,7 @@ use {
|
|||
chrono::{Duration, Local},
|
||||
icalendar::Component,
|
||||
regex::Regex,
|
||||
reqwest::{Method, StatusCode},
|
||||
reqwest::{Method, StatusCode, Url},
|
||||
std::collections::HashSet,
|
||||
std::ops::Range,
|
||||
};
|
||||
|
@ -109,43 +109,16 @@ fn prepare_grocery_list(ingredients: &Vec<Ingredient>) -> Result<String> {
|
|||
}
|
||||
|
||||
async fn save_grocery_list(api_client: &ApiClient, filename: &str, contents: &str) -> Result<()> {
|
||||
let dav_base_url = api_client
|
||||
.base_url()
|
||||
.join(&format!("remote.php/dav/files/{}/", api_client.username()))?;
|
||||
|
||||
let filename_components = filename.split('/').collect::<Vec<_>>();
|
||||
filename_components
|
||||
.iter()
|
||||
.take(filename_components.len() - 1)
|
||||
.fold(Ok(dav_base_url.clone()), |url, dir| {
|
||||
.fold(Ok(api_client.webdav_base_url().clone()), |url, dir| {
|
||||
url.map(|u| u.join(&format!("{dir}/")).unwrap())
|
||||
.and_then(|url| {
|
||||
futures::executor::block_on(async {
|
||||
let response = api_client
|
||||
.rest(|client| async {
|
||||
let r = client
|
||||
.request(Method::from_bytes(b"MKCOL").unwrap(), url.clone())
|
||||
.send()
|
||||
.await;
|
||||
Ok(r?)
|
||||
})
|
||||
.await;
|
||||
|
||||
match response.map(|r| r.status()) {
|
||||
Ok(StatusCode::OK)
|
||||
| Ok(StatusCode::METHOD_NOT_ALLOWED /* already exists */) => Ok(url),
|
||||
Ok(status) => Err(anyhow!(
|
||||
"Could not create WebDAV collection {}, server responded with {}",
|
||||
&url,
|
||||
status
|
||||
)),
|
||||
Err(e) => Err(anyhow!(e)),
|
||||
}
|
||||
})
|
||||
})
|
||||
.and_then(|url| ensure_collection_exist(api_client, url))
|
||||
})?;
|
||||
|
||||
let file_url = dav_base_url.join(filename).unwrap();
|
||||
let file_url = api_client.webdav_base_url().join(filename).unwrap();
|
||||
log::info!("Saving grocery list to {}", &file_url);
|
||||
let response = api_client
|
||||
.rest(|client| async {
|
||||
|
@ -168,3 +141,27 @@ async fn save_grocery_list(api_client: &ApiClient, filename: &str, contents: &st
|
|||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_collection_exist(api_client: &ApiClient, url: Url) -> Result<Url> {
|
||||
futures::executor::block_on(async {
|
||||
let response = api_client
|
||||
.rest(|client| async {
|
||||
let r = client
|
||||
.request(Method::from_bytes(b"MKCOL").unwrap(), url.clone())
|
||||
.send()
|
||||
.await;
|
||||
Ok(r?)
|
||||
})
|
||||
.await;
|
||||
|
||||
match response.map(|r| r.status()) {
|
||||
Ok(StatusCode::OK) | Ok(StatusCode::METHOD_NOT_ALLOWED /* already exists */) => Ok(url),
|
||||
Ok(status) => Err(anyhow!(
|
||||
"Could not create WebDAV collection {}, server responded with {}",
|
||||
&url,
|
||||
status
|
||||
)),
|
||||
Err(e) => Err(anyhow!(e)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -10,10 +10,8 @@ pub async fn with(api_client: &ApiClient, calendar_name: &str) -> Result<()> {
|
|||
let response = client
|
||||
.request(
|
||||
report_method.clone(),
|
||||
// TODO extract into helper method
|
||||
api_client.caldav_base_url().join(&format!(
|
||||
"calendars/{}/{}/",
|
||||
api_client.username(),
|
||||
"{}/",
|
||||
calendar_name.to_lowercase().replace(" ", "-")
|
||||
))?,
|
||||
)
|
||||
|
|
|
@ -11,7 +11,7 @@ use {
|
|||
chrono::naive::NaiveDate,
|
||||
futures::future::try_join_all,
|
||||
icalendar::Event,
|
||||
reqwest::StatusCode,
|
||||
reqwest::{StatusCode, Url},
|
||||
std::collections::{HashMap, HashSet},
|
||||
std::fmt::Write,
|
||||
std::iter::Iterator,
|
||||
|
@ -61,17 +61,17 @@ pub async fn with(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn urls_from_csv<'a, RecordsIter>(records: RecordsIter) -> Result<HashSet<String>>
|
||||
fn urls_from_csv<'a, RecordsIter>(records: RecordsIter) -> Result<HashSet<Url>>
|
||||
where
|
||||
RecordsIter: Iterator<Item = &'a CsvRecord>,
|
||||
{
|
||||
Ok(
|
||||
records.fold(std::collections::HashSet::new(), |mut set, r| {
|
||||
if !r.lunch.is_empty() {
|
||||
set.insert(r.lunch.clone());
|
||||
if let Ok(lunch) = Url::try_from(r.lunch.as_str()) {
|
||||
set.insert(lunch);
|
||||
}
|
||||
if !r.dinner.is_empty() {
|
||||
set.insert(r.dinner.clone());
|
||||
if let Ok(dinner) = Url::try_from(r.dinner.as_str()) {
|
||||
set.insert(dinner);
|
||||
}
|
||||
set
|
||||
}),
|
||||
|
@ -93,35 +93,20 @@ async fn get_all_recipes(api_client: &ApiClient) -> Result<HashMap<String, Rc<re
|
|||
.await?;
|
||||
|
||||
let recipes = metadata.iter().map(|rm| async {
|
||||
// TODO: wrap this, heavily refactor this mess
|
||||
let _ = api_client.rest_semaphore.acquire().await.unwrap();
|
||||
let mut retries = 0;
|
||||
let response = loop {
|
||||
let result = api_client
|
||||
.rest(|client| async {
|
||||
let response = client
|
||||
.get(
|
||||
api_client
|
||||
.base_url()
|
||||
.join(&format!("apps/cookbook/api/recipes/{id}", id = rm.id))
|
||||
.unwrap(),
|
||||
)
|
||||
.send()
|
||||
.await;
|
||||
Ok(response?)
|
||||
})
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(response) => break response,
|
||||
Err(_) if retries < 10 => {
|
||||
retries += 1;
|
||||
std::thread::sleep(retries * std::time::Duration::from_millis(500));
|
||||
continue;
|
||||
}
|
||||
_ => bail!("Cannot fetch recipe {} with id {}", rm.name, rm.id),
|
||||
}
|
||||
};
|
||||
let response = api_client
|
||||
.rest(|client| async {
|
||||
let r = client
|
||||
.get(
|
||||
api_client
|
||||
.base_url()
|
||||
.join(&format!("apps/cookbook/api/recipes/{id}", id = rm.id))
|
||||
.unwrap(),
|
||||
)
|
||||
.send()
|
||||
.await;
|
||||
Ok(r?)
|
||||
})
|
||||
.await?;
|
||||
|
||||
response
|
||||
.json::<recipe::Recipe>()
|
||||
|
@ -131,9 +116,17 @@ async fn get_all_recipes(api_client: &ApiClient) -> Result<HashMap<String, Rc<re
|
|||
});
|
||||
|
||||
let recipes = try_join_all(recipes).await?;
|
||||
Ok(HashMap::from_iter(
|
||||
recipes.into_iter().map(|r| (r.url.clone(), r)),
|
||||
))
|
||||
|
||||
Ok(HashMap::from_iter(recipes.into_iter().map(|r| {
|
||||
// Fallback to recipe name as ID if URL is empty
|
||||
let id = match &r.url {
|
||||
Some(u) if !u.is_empty() => u,
|
||||
_ => &r.name,
|
||||
}
|
||||
.clone();
|
||||
|
||||
(id, r)
|
||||
})))
|
||||
}
|
||||
|
||||
async fn publish_events<'a, SchedulingsIter>(
|
||||
|
@ -146,8 +139,7 @@ where
|
|||
SchedulingsIter: Iterator<Item = Scheduling>,
|
||||
{
|
||||
let calendar_url = api_client.caldav_base_url().join(&format!(
|
||||
"calendars/{}/{}/",
|
||||
&api_client.username(),
|
||||
"{}/",
|
||||
calendar.to_lowercase().as_str().replace(" ", "-")
|
||||
))?;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue