139 lines
5.6 KiB
Rust
139 lines
5.6 KiB
Rust
|
use crate::helloasso;
|
||
|
use crate::paheko;
|
||
|
use crate::{
|
||
|
Config, UserCache,
|
||
|
get_proxy_from_url, get_auth_client_from_cache, parse_and_get_birthday_year, parse_normalize_phone, normalize_str
|
||
|
};
|
||
|
use crate::sync_paheko::{GeneralizedAnswer, sync_paheko};
|
||
|
|
||
|
use anyhow::Result;
|
||
|
use url::Url;
|
||
|
|
||
|
/// rust how to access inner enum value
|
||
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||
|
enum HelloassoCustomFieldType {
|
||
|
Email,
|
||
|
Address,
|
||
|
PostalCode,
|
||
|
City,
|
||
|
Phone,
|
||
|
Job,
|
||
|
Skills,
|
||
|
Birthday,
|
||
|
LinkedUserFirstName
|
||
|
}
|
||
|
|
||
|
impl TryFrom<&str> for HelloassoCustomFieldType {
|
||
|
type Error = ();
|
||
|
|
||
|
fn try_from(subject: &str) -> Result<Self, Self::Error> {
|
||
|
match subject {
|
||
|
"Prénom conjoint" => Ok(HelloassoCustomFieldType::LinkedUserFirstName),
|
||
|
"ADRESSE" => Ok(HelloassoCustomFieldType::Address),
|
||
|
"CODE POSTAL" => Ok(HelloassoCustomFieldType::PostalCode),
|
||
|
"VILLE" => Ok(HelloassoCustomFieldType::City),
|
||
|
"EMAIL" => Ok(HelloassoCustomFieldType::Email),
|
||
|
"PROFESSION" => Ok(HelloassoCustomFieldType::Job),
|
||
|
"TÉLÉPHONE" => Ok(HelloassoCustomFieldType::Phone),
|
||
|
"DATE DE NAISSANCE" => Ok(HelloassoCustomFieldType::Birthday),
|
||
|
"CENTRE D'INTÉRÊTS / COMPÉTENCES" => Ok(HelloassoCustomFieldType::Skills),
|
||
|
_ => Err(())
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn read_custom_field(form_answer: &helloasso::FormAnswer, custom_field: HelloassoCustomFieldType) -> Option<String> {
|
||
|
// FIXME: compute the type directly at deserialization with serde
|
||
|
form_answer.custom_fields.iter()
|
||
|
.find(|f| HelloassoCustomFieldType::try_from(f.name.as_str()) == Ok(custom_field))
|
||
|
.map(|cf| cf.answer.clone())
|
||
|
}
|
||
|
|
||
|
|
||
|
pub async fn sync_helloasso(paheko_client: &paheko::AuthentifiedClient, config: &Config, user_cache: &mut UserCache) -> Result<()> {
|
||
|
let mut ha_client: helloasso::Client = helloasso::Client::new(helloasso::ClientConfig {
|
||
|
base_url: Url::parse("https://api.helloasso.com/v5/")
|
||
|
.expect("Expected valid helloasso API base URL"),
|
||
|
proxy: get_proxy_from_url(&config.helloasso_proxy)?,
|
||
|
user_agent: "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/112.0".to_string()
|
||
|
});
|
||
|
|
||
|
let login_payload = helloasso::LoginPayload {
|
||
|
email: config.helloasso_email.clone(),
|
||
|
password: config.helloasso_password.clone()
|
||
|
};
|
||
|
let auth_client: helloasso::AuthentifiedClient =
|
||
|
get_auth_client_from_cache(user_cache, &mut ha_client, login_payload).await?;
|
||
|
|
||
|
let org = auth_client.organization(&config.helloasso_organization_slug);
|
||
|
let answers = org.get_form_answers(&config.helloasso_form_name).await?;
|
||
|
|
||
|
println!("Got {} answers to the membership form. Processing...", &answers.len());
|
||
|
|
||
|
use email_address::*;
|
||
|
fn choose_email(answer: &helloasso::FormAnswer) -> Option<String> {
|
||
|
read_custom_field(answer, HelloassoCustomFieldType::Email)
|
||
|
.and_then(|x| {
|
||
|
if !EmailAddress::is_valid(&x) {
|
||
|
None
|
||
|
} else {
|
||
|
Some(x)
|
||
|
}
|
||
|
})
|
||
|
.or(Some(answer.payer_user.email.clone()))
|
||
|
}
|
||
|
|
||
|
let mut generalized_answers: Vec<GeneralizedAnswer> = vec![];
|
||
|
for answer in answers {
|
||
|
// eprintln!("Processing answer:");
|
||
|
let email = choose_email(&answer);
|
||
|
// eprintln!(" email: {:?}", email);
|
||
|
|
||
|
let mut generalized_answer = GeneralizedAnswer {
|
||
|
first_name: Some(normalize_str(answer.user.first_name.clone())),
|
||
|
last_name: normalize_str(answer.user.last_name.clone()),
|
||
|
email,
|
||
|
phone: parse_normalize_phone(read_custom_field(&answer, HelloassoCustomFieldType::Phone)),
|
||
|
skills: read_custom_field(&answer, HelloassoCustomFieldType::Skills).map(normalize_str),
|
||
|
address: read_custom_field(&answer, HelloassoCustomFieldType::Address)
|
||
|
.map(normalize_str)
|
||
|
.expect("Expected ha answer to have address"),
|
||
|
postal_code: read_custom_field(&answer, HelloassoCustomFieldType::PostalCode)
|
||
|
.expect("Expected ha answer to have postalcode"),
|
||
|
city: read_custom_field(&answer, HelloassoCustomFieldType::City)
|
||
|
.map(normalize_str)
|
||
|
.expect("Expected ha answer to have city"),
|
||
|
country: answer.payer_user.country.clone().trim()[..=1].to_string(), // we expect country code ISO 3166-1 alpha-2
|
||
|
job: read_custom_field(&answer, HelloassoCustomFieldType::Job).map(normalize_str),
|
||
|
birth_year: read_custom_field(&answer, HelloassoCustomFieldType::Birthday).and_then(parse_and_get_birthday_year),
|
||
|
inception_time: answer.order.inception_time,
|
||
|
reference: format!("HA/{}", answer.id),
|
||
|
donation_amount: 0,
|
||
|
subscription_amount: answer.amount,
|
||
|
membership_mode: serde_json::from_value(serde_json::Value::String(answer.mode.clone()))
|
||
|
.expect("Expected a membership mode to be valid"),
|
||
|
linked_user_first_name: read_custom_field(&answer, HelloassoCustomFieldType::LinkedUserFirstName)
|
||
|
};
|
||
|
|
||
|
// apply custom user override
|
||
|
// this particular answer had duplicate phone and email from another answer
|
||
|
if answer.id == 64756582 {
|
||
|
generalized_answer.email = None;
|
||
|
generalized_answer.phone = None;
|
||
|
}
|
||
|
|
||
|
generalized_answers.push(generalized_answer);
|
||
|
}
|
||
|
println!("Generated GeneralizedAnswers");
|
||
|
sync_paheko(
|
||
|
paheko_client,
|
||
|
config,
|
||
|
user_cache,
|
||
|
generalized_answers,
|
||
|
"512",
|
||
|
"HelloAsso"
|
||
|
).await
|
||
|
}
|
||
|
|
||
|
|