feat(helloasso): add dry run flag
This commit is contained in:
parent
700ba180b6
commit
f8a5aac75f
5 changed files with 96 additions and 24 deletions
19
src/main.rs
19
src/main.rs
|
@ -97,8 +97,7 @@ fn get_proxy_from_url(proxy_url: &Option<String>) -> Result<Option<reqwest::Prox
|
|||
})
|
||||
}
|
||||
|
||||
async fn launch_adapter(source: SourceType, config: &Config) -> Result<()> {
|
||||
|
||||
async fn launch_adapter(source: SourceType, config: &Config, dry_run: bool) -> Result<()> {
|
||||
let mut user_cache = load_user_cache().context("Failed to load user cache")?;
|
||||
|
||||
if !&config.paheko_base_url.ends_with('/') {
|
||||
|
@ -114,11 +113,11 @@ async fn launch_adapter(source: SourceType, config: &Config) -> Result<()> {
|
|||
client_id: config.paheko_client_id.clone(),
|
||||
client_secret: config.paheko_client_secret.clone()
|
||||
};
|
||||
let paheko_client: paheko::AuthentifiedClient = paheko_client.login(paheko_credentials).await?;
|
||||
let paheko_client = paheko_client.login(paheko_credentials).await.context("Paheko login")?;
|
||||
|
||||
match source {
|
||||
SourceType::Csv => sync_csv::sync_csv(&paheko_client, config, &mut user_cache).await?,
|
||||
SourceType::Helloasso => sync_helloasso::sync_helloasso(&paheko_client, config, &mut user_cache).await?
|
||||
SourceType::Csv => sync_csv::sync_csv(&paheko_client, config, &mut user_cache, dry_run).await?,
|
||||
SourceType::Helloasso => sync_helloasso::sync_helloasso(&paheko_client, config, &mut user_cache, dry_run).await?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -134,7 +133,11 @@ struct App {
|
|||
|
||||
/// output debug info
|
||||
#[argh(switch, short = 'i')]
|
||||
info: bool
|
||||
env_info: bool,
|
||||
|
||||
/// dry run
|
||||
#[argh(switch, short = 'd')]
|
||||
dry_run: bool
|
||||
}
|
||||
|
||||
enum SourceType {
|
||||
|
@ -148,7 +151,7 @@ async fn main() {
|
|||
dotenvy::dotenv().expect("Could not load dot env file");
|
||||
let config: Config = envy::from_env().expect("Failed to load env vars");
|
||||
|
||||
if app.info {
|
||||
if app.env_info {
|
||||
dbg!(config);
|
||||
return;
|
||||
}
|
||||
|
@ -161,7 +164,7 @@ async fn main() {
|
|||
return;
|
||||
}
|
||||
};
|
||||
let res = launch_adapter(source, &config).await;
|
||||
let res = launch_adapter(source, &config, app.dry_run).await;
|
||||
match res {
|
||||
Err(err) => {
|
||||
eprintln!("Program failed, details bellow");
|
||||
|
|
|
@ -318,7 +318,8 @@ impl AuthentifiedClient {
|
|||
{
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Row {
|
||||
_id: u64,
|
||||
#[allow(dead_code)]
|
||||
id: u64,
|
||||
label: String,
|
||||
reference: String,
|
||||
#[serde(deserialize_with = "deserialize_json_list")]
|
||||
|
|
|
@ -33,7 +33,12 @@ fn process_price(value: String) -> f64 {
|
|||
|
||||
|
||||
// read csv from stdin
|
||||
pub async fn sync_csv(paheko_client: &paheko::AuthentifiedClient, config: &Config, _user_cache: &mut UserCache) -> Result<()> {
|
||||
pub async fn sync_csv(
|
||||
paheko_client: &paheko::AuthentifiedClient,
|
||||
config: &Config,
|
||||
_user_cache: &mut UserCache,
|
||||
_dry_run: bool
|
||||
) -> Result<()> {
|
||||
// raw row record directly from CSV
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
struct AnswerRecord {
|
||||
|
|
|
@ -2,15 +2,54 @@ use crate::helloasso;
|
|||
use crate::paheko;
|
||||
use crate::{
|
||||
Config, UserCache,
|
||||
get_proxy_from_url, get_auth_client_from_cache,
|
||||
get_proxy_from_url, write_user_cache
|
||||
};
|
||||
use crate::utils::{parse_and_get_birthday_year, parse_normalize_phone, normalize_str};
|
||||
use crate::sync_paheko::{GeneralizedAnswer, sync_paheko};
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{Context, Result};
|
||||
use url::Url;
|
||||
use email_address::EmailAddress;
|
||||
|
||||
/// Get authentified HelloAsso client
|
||||
async fn get_auth_client_from_cache(
|
||||
user_cache: &mut UserCache,
|
||||
ha_client: &mut helloasso::Client,
|
||||
login_payload: helloasso::LoginPayload
|
||||
) -> Result<helloasso::AuthentifiedClient> {
|
||||
// TODO: find a better way to have the logic implemented
|
||||
async fn login(
|
||||
user_cache: &mut UserCache,
|
||||
ha_client: &mut helloasso::Client,
|
||||
login_payload: helloasso::LoginPayload
|
||||
) -> Result<helloasso::AuthentifiedClient> {
|
||||
let auth_client = ha_client.login(login_payload).await.context("Failed to login to HelloAsso")?;
|
||||
user_cache.helloasso_session = Some(auth_client.session.clone());
|
||||
write_user_cache(user_cache).expect("Unable to write user cache");
|
||||
eprintln!("HelloAsso: Logged in and wrote token to cache");
|
||||
Ok(auth_client)
|
||||
}
|
||||
|
||||
eprintln!("Init HA client");
|
||||
|
||||
match &user_cache.helloasso_session {
|
||||
Some(cached_session) => {
|
||||
let auth_client = ha_client.authentified_client(cached_session.clone());
|
||||
|
||||
if !auth_client.verify_auth().await? {
|
||||
println!("HelloAsso: Need to relog, token invalid");
|
||||
return login(user_cache, ha_client, login_payload).await
|
||||
}
|
||||
eprintln!("HelloAsso: Used anterior token");
|
||||
Ok(auth_client)
|
||||
},
|
||||
None => {
|
||||
eprintln!("HelloAsso: First time login");
|
||||
login(user_cache, ha_client, login_payload).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// rust how to access inner enum value
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
enum HelloassoCustomFieldType {
|
||||
|
@ -52,7 +91,14 @@ fn read_custom_field(form_answer: &helloasso::FormAnswer, custom_field: Helloass
|
|||
}
|
||||
|
||||
|
||||
pub async fn sync_helloasso(paheko_client: &paheko::AuthentifiedClient, config: &Config, user_cache: &mut UserCache) -> Result<()> {
|
||||
pub async fn sync_helloasso(
|
||||
paheko_client: &paheko::AuthentifiedClient,
|
||||
config: &Config,
|
||||
user_cache: &mut UserCache,
|
||||
dry_run: bool
|
||||
) -> Result<()> {
|
||||
|
||||
eprintln!("wow");
|
||||
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"),
|
||||
|
@ -84,6 +130,7 @@ pub async fn sync_helloasso(paheko_client: &paheko::AuthentifiedClient, config:
|
|||
.or(Some(answer.payer_user.email.clone()))
|
||||
}
|
||||
|
||||
let mut kept_original_answers: Vec<helloasso::FormAnswer> = vec![];
|
||||
let mut generalized_answers: Vec<GeneralizedAnswer> = vec![];
|
||||
for answer in answers {
|
||||
let email = choose_email(&answer);
|
||||
|
@ -132,10 +179,26 @@ pub async fn sync_helloasso(paheko_client: &paheko::AuthentifiedClient, config:
|
|||
generalized_answer.email = None;
|
||||
generalized_answer.phone = None;
|
||||
}
|
||||
if answer.id == 64756581 {
|
||||
if let Some(wrong_first_name) = generalized_answer.first_name {
|
||||
let compos: Vec<&str> = wrong_first_name.split(" et ").collect();
|
||||
generalized_answer.first_name = Some(compos.get(0).unwrap().to_string());
|
||||
generalized_answer.linked_user_first_name = Some(compos.get(1).unwrap().to_string());
|
||||
}
|
||||
}
|
||||
|
||||
generalized_answers.push(generalized_answer);
|
||||
kept_original_answers.push(answer);
|
||||
}
|
||||
println!("Generated GeneralizedAnswers");
|
||||
|
||||
|
||||
if dry_run {
|
||||
eprintln!("Will stop because dry run mode is enabled.");
|
||||
list_answers(kept_original_answers, generalized_answers);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
sync_paheko(
|
||||
paheko_client,
|
||||
config,
|
||||
|
@ -145,4 +208,12 @@ pub async fn sync_helloasso(paheko_client: &paheko::AuthentifiedClient, config:
|
|||
).await
|
||||
}
|
||||
|
||||
|
||||
fn list_answers(original_answers: Vec<helloasso::FormAnswer>, generalized_answers: Vec<GeneralizedAnswer>) {
|
||||
for (ha_answer, ga) in std::iter::zip(original_answers, generalized_answers) {
|
||||
println!(
|
||||
"{} {:?} {:?} {:?} {:?}",
|
||||
ha_answer.id, ga.first_name, ga.last_name,
|
||||
ga.email, ga.linked_user_first_name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,8 +81,7 @@ pub async fn sync_paheko(
|
|||
|
||||
// 1. get summary of existing paheko users
|
||||
let mut existing_users = paheko_client.get_users().await.context("Get users")?;
|
||||
// 2. get summary of transactions for that year
|
||||
// TODO: also get for the year n-1
|
||||
// 2. get summary of transactions for the years we will use
|
||||
let existing_transactions = paheko_client.get_transactions(&config.paheko_accounting_years_ids)
|
||||
.await.context("Get transactions")?;
|
||||
// 3. get summary of services_users for that year
|
||||
|
@ -116,7 +115,6 @@ pub async fn sync_paheko(
|
|||
.filter(|t| t.reference == answer.reference)
|
||||
.collect();
|
||||
|
||||
// dbg!(&existing_subscriptions);
|
||||
// check for existing user in paheko by email
|
||||
// TODO: check user with fuzzing first name and last name
|
||||
let existing_user_opt = existing_users
|
||||
|
@ -166,7 +164,6 @@ pub async fn sync_paheko(
|
|||
eprintln!(" User is already subscribed to this activity");
|
||||
} else {
|
||||
// add activity for first member
|
||||
// TODO: check if activity already exists
|
||||
let user_registration = paheko_client.register_user_to_service(
|
||||
pk_users_summaries.get(0).unwrap(),
|
||||
&pk_membership,
|
||||
|
@ -191,7 +188,7 @@ pub async fn sync_paheko(
|
|||
// add first_name
|
||||
match answer.linked_user_first_name {
|
||||
Some(name) => {
|
||||
second_answer.first_name = Some(name);
|
||||
second_answer.first_name = Some(normalize_first_name(name));
|
||||
},
|
||||
None => {
|
||||
second_answer.first_name = None;
|
||||
|
@ -237,13 +234,11 @@ pub async fn sync_paheko(
|
|||
label: format!("{} {:?} via {}", pk_membership.service_name, pk_membership.mode_name, via_name),
|
||||
amount: pk_membership.payed_amount,
|
||||
reference: answer.reference.clone(),
|
||||
// TODO: make these field configurable
|
||||
credit_account_code: "756".to_string(), // cotisations account
|
||||
debit_account_code: debit_account_code.to_string(), // helloasso account
|
||||
inception_time: answer.inception_time,
|
||||
kind: paheko::TransactionKind::Revenue,
|
||||
linked_users: pk_users_summaries.iter().map(|x| x.id.clone()).collect(),
|
||||
// the linked_services, depend on a patch to paheko API code to work (see https://forge.lefuturiste.fr/mbess/paheko-fork/commit/a4fdd816112f51db23a2b02ac160b0513a5b09c5)
|
||||
linked_subscriptions: pk_user_service_registrations.iter().map(|x| x.id.clone()).collect()
|
||||
};
|
||||
paheko_client.register_transaction(transaction)
|
||||
|
@ -286,9 +281,6 @@ pub async fn sync_paheko(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: handle donation amount
|
||||
|
||||
pk_memberships.push(pk_membership);
|
||||
}
|
||||
eprintln!("{via_name} sync done.");
|
||||
|
|
Loading…
Reference in a new issue