feat: basic import of user activity
This commit is contained in:
parent
853a3be680
commit
1fcf35eaa7
4 changed files with 122 additions and 20 deletions
18
TODO.md
Normal file
18
TODO.md
Normal file
|
@ -0,0 +1,18 @@
|
|||
Following the dev
|
||||
|
||||
like rossman said, you need to split up things
|
||||
|
||||
# schedule
|
||||
|
||||
## 2023-12-23
|
||||
|
||||
- have a rust client to fetch list of users from sql
|
||||
|
||||
## 2023-12-27
|
||||
|
||||
- Normalize address, cities
|
||||
|
||||
all of the normalization work should not have been done here,
|
||||
|
||||
we should have created our own platform so people can register and pay later (either in cash, or using 3rd party payment like stripe)
|
||||
|
56
src/main.rs
56
src/main.rs
|
@ -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);
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
Loading…
Reference in a new issue