feat: basic import of user activity

This commit is contained in:
Matthieu Bessat 2023-12-27 01:03:33 +01:00
parent 853a3be680
commit 1fcf35eaa7
4 changed files with 122 additions and 20 deletions

View file

@ -262,12 +262,13 @@ async fn launch_adapter() -> Result<()> {
}
// get summary of users
let existing_users = paheko_client.get_users().await.context("Get users")?;
let mut existing_users = paheko_client.get_users().await.context("Get users")?;
// get summary of transactions for that year
let existing_transactions = paheko_client.get_transactions(1).await.context("Get transactions")?;
// TODO: before creating any users, get the current maximum id of the users table to predict
// the next auto-incrementing id.
let mut pk_next_id = paheko_client.get_user_next_id().await.context("Get paheko users next id")?;
for answer in answers {
eprintln!("Processing answer:");
@ -275,8 +276,8 @@ async fn launch_adapter() -> Result<()> {
eprintln!(" email: {:?}", email);
let paheko_user = paheko::User {
id: generate_id(),
let mut pk_user = paheko::User {
id: utils::Id(0),
first_name: Some(normalize_str(answer.user.first_name.clone())),
last_name: normalize_str(answer.user.last_name.clone()),
email,
@ -295,6 +296,13 @@ async fn launch_adapter() -> Result<()> {
register_time: answer.order.inception_time,
};
// apply custom user override
// this particular answer had duplicate phone and email from another answer
if answer.id == 64756582 {
pk_user.email = None;
pk_user.phone = None;
}
// check for existing transactions
if let Some(_) = existing_transactions.iter().find(
|summary| summary.reference == format!("HA/{}", answer.id)
@ -303,18 +311,19 @@ async fn launch_adapter() -> Result<()> {
continue;
}
let existing_user_opt = existing_users.iter().find(|user| user.email == pk_user.email).cloned();
// check for existing paheko user, or create paheko user
let paheko_user_summary = match existing_users.iter().find(|user| user.email == paheko_user.email) {
Some(user) => user.clone(),
let pk_user_summary = match existing_user_opt.clone() {
Some(user) => user,
None => {
let c = paheko_client.create_user(&paheko_user).await.context("Expected to create paheko user")?;
let c = paheko_client.create_user(
&pk_user, pk_next_id.clone()
).await.context("Expected to create paheko user")?;
eprintln!(" Created paheko user");
UserSummary {
id: utils::Id(0),
first_name: paheko_user.first_name.clone(),
last_name: paheko_user.last_name.clone(),
email: paheko_user.email.clone()
}
pk_next_id += 1;
existing_users.push(c.clone());
c
}
};
@ -323,7 +332,7 @@ async fn launch_adapter() -> Result<()> {
campaign: "".to_string(),
inception_time: Utc::now(),
mode: helloasso_to_paheko_membership(&answer.mode),
users: vec![paheko_user.id.clone()],
users: vec![pk_user.id.clone()],
external_references: paheko::ExternalReferences {
helloasso_ref: paheko::HelloassoReferences {
answer_id: answer.id,
@ -331,12 +340,11 @@ async fn launch_adapter() -> Result<()> {
}
}
};
dbg!(&pk_membership.users);
// then create optional linked user
if answer.mode == helloasso::MembershipMode::Couple {
let mut second_pk_user = paheko_user.clone();
second_pk_user.id = generate_id();
let mut second_pk_user = pk_user.clone();
second_pk_user.id = utils::Id(0);
second_pk_user.email = None;
second_pk_user.phone = None;
second_pk_user.skills = None;
@ -354,10 +362,22 @@ async fn launch_adapter() -> Result<()> {
}
}
if existing_user_opt.is_none() {
let second_pk_user_summary = paheko_client.create_user(&second_pk_user, pk_next_id)
.await.context("Expected to create second paheko user")?;
eprintln!(" Created conjoint paheko user");
pk_next_id += 1;
}
// TODO: get existing linked user from previous year
pk_membership.users.push(second_pk_user.id.clone());
pk_users.push(second_pk_user);
}
pk_users.push(paheko_user);
// add activity
paheko_client.register_user_to_service(&pk_user_summary).await.context("Registering user to paheko server")?;
pk_users.push(pk_user);
pk_memberships.push(pk_membership);
}
dbg!(&pk_users);

View file

@ -237,6 +237,23 @@ impl AuthentifiedClient {
Ok(serde_json::from_value(users_val.results)?)
}
pub async fn get_user_next_id(&self) -> Result<u64> {
let query: String = r#"
SELECT id FROM users ORDER BY id DESC LIMIT 1
"#.to_string();
let users_id_val = self.sql_query(query).await.context("Fetching users")?;
#[derive(Deserialize)]
struct UserIdEntry {
id: u64
}
let users_ids: Vec<UserIdEntry> = serde_json::from_value(users_id_val.results)?;
Ok(users_ids.iter().nth(0).map(|x| x.id).unwrap_or(1)+1)
}
pub async fn get_transactions(&self, id_year: u32)
-> Result<Vec<TransactionSummary>>
{
@ -249,7 +266,7 @@ impl AuthentifiedClient {
Ok(serde_json::from_value(val.results)?)
}
pub async fn create_user(&self, user: &User)
pub async fn create_user(&self, user: &User, next_id: u64)
-> Result<UserSummary>
{
// single-user import
@ -294,11 +311,52 @@ impl AuthentifiedClient {
}
Ok(
UserSummary {
id: Id(0),
id: Id(next_id),
first_name: u.first_name,
last_name: u.last_name,
email: u.email
}
)
}
pub async fn register_user_to_service(&self, user: &UserSummary)
-> Result<()>
{
// single-user import
// create virtual file
let u = user.clone();
let mut csv_content: String = String::new();
csv_content.push_str(
r#""Numéro de membre","Activité","Tarif","Date d'inscription","Date d'expiration","Montant à régler","Payé ?""#);
csv_content.push_str("\n");
csv_content.push_str(
format!("{},{:?},{:?},{:?},{:?},{:?},{:?}\n",
u.id,
"Cotisation 2023-2024",
"Physique Individuelle",
"10/10/2023",
"10/10/2025",
"10",
"Oui"
).as_str());
use reqwest::multipart::Form;
use reqwest::multipart::Part;
let part = Part::text(csv_content).file_name("file");
let form = Form::new()
.part("file", part);
let res = self.client
.post(self.base_url.join("services/subscriptions/import")?)
.multipart(form)
.send().await?;
if res.status() != 200 {
return Err(APIClientError::InvalidStatusCode.into());
}
Ok(())
}
}

View file

@ -1,4 +1,5 @@
use serde::{Serialize, Deserialize, Deserializer};
use std::fmt;
use rand::{thread_rng, Rng};
use chrono::prelude::{DateTime, Utc};
@ -15,6 +16,11 @@ impl Into<String> for Id {
format!("{:x}", self.0)
}
}
impl fmt::Display for Id {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
pub fn generate_id() -> Id {
Id(thread_rng().gen())