2023-08-04 17:03:46 +00:00
|
|
|
use crate::connections::db_connection;
|
|
|
|
use crate::models::{FamilyID, Member, MemberID, NewMember};
|
|
|
|
use crate::schema::members;
|
|
|
|
use crate::utils::time_utils::time;
|
|
|
|
use diesel::prelude::*;
|
|
|
|
use diesel::RunQueryDsl;
|
|
|
|
|
|
|
|
/// Create a new family member
|
|
|
|
pub async fn create(family_id: FamilyID) -> anyhow::Result<Member> {
|
|
|
|
db_connection::execute(|conn| {
|
|
|
|
let res: Member = diesel::insert_into(members::table)
|
|
|
|
.values(&NewMember {
|
|
|
|
family_id: family_id.0,
|
|
|
|
time_create: time() as i64,
|
|
|
|
time_update: time() as i64,
|
|
|
|
})
|
|
|
|
.get_result(conn)?;
|
|
|
|
|
|
|
|
Ok(res)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the information of a member
|
|
|
|
pub async fn get_by_id(id: MemberID) -> anyhow::Result<Member> {
|
|
|
|
db_connection::execute(|conn| members::table.filter(members::dsl::id.eq(id.0)).first(conn))
|
|
|
|
}
|
|
|
|
|
2023-08-04 17:17:51 +00:00
|
|
|
/// Get all the members of a family
|
|
|
|
pub async fn get_all_of_family(id: FamilyID) -> anyhow::Result<Vec<Member>> {
|
|
|
|
db_connection::execute(|conn| {
|
|
|
|
members::table
|
|
|
|
.filter(members::dsl::family_id.eq(id.0))
|
|
|
|
.get_results(conn)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-08-04 17:03:46 +00:00
|
|
|
/// Check whether a member with a given id exists or not
|
|
|
|
pub async fn exists(family_id: FamilyID, member_id: MemberID) -> anyhow::Result<bool> {
|
|
|
|
db_connection::execute(|conn| {
|
|
|
|
let count: i64 = members::table
|
|
|
|
.filter(
|
|
|
|
members::id
|
|
|
|
.eq(member_id.0)
|
|
|
|
.and(members::family_id.eq(family_id.0)),
|
|
|
|
)
|
|
|
|
.count()
|
|
|
|
.get_result(conn)?;
|
|
|
|
|
|
|
|
Ok(count != 0)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Update the information of a member
|
|
|
|
pub async fn update(member: &mut Member) -> anyhow::Result<()> {
|
|
|
|
member.time_update = time() as i64;
|
|
|
|
|
|
|
|
db_connection::execute(|conn| {
|
|
|
|
diesel::update(members::dsl::members.filter(members::dsl::id.eq(member.id().0)))
|
|
|
|
.set((
|
|
|
|
members::dsl::first_name.eq(member.first_name.clone()),
|
|
|
|
members::dsl::last_name.eq(member.last_name.clone()),
|
|
|
|
members::dsl::birth_last_name.eq(member.birth_last_name.clone()),
|
2023-08-05 17:15:52 +00:00
|
|
|
members::dsl::photo_id.eq(member.photo_id().map(|p| p.0)),
|
2023-08-04 17:03:46 +00:00
|
|
|
members::dsl::email.eq(member.email.clone()),
|
|
|
|
members::dsl::phone.eq(member.phone.clone()),
|
|
|
|
members::dsl::address.eq(member.address.clone()),
|
|
|
|
members::dsl::city.eq(member.city.clone()),
|
|
|
|
members::dsl::postal_code.eq(member.postal_code.clone()),
|
|
|
|
members::dsl::country.eq(member.country.clone()),
|
|
|
|
members::dsl::sex.eq(member.sex().map(|s| s.to_str().to_string())),
|
|
|
|
members::dsl::time_update.eq(member.time_update),
|
|
|
|
members::dsl::mother.eq(member.mother().map(|m| m.0)),
|
|
|
|
members::dsl::father.eq(member.father().map(|m| m.0)),
|
|
|
|
members::dsl::birth_year.eq(member.birth_year),
|
|
|
|
members::dsl::birth_month.eq(member.birth_month),
|
|
|
|
members::dsl::birth_day.eq(member.birth_day),
|
|
|
|
members::dsl::death_year.eq(member.death_year),
|
|
|
|
members::dsl::death_month.eq(member.death_month),
|
|
|
|
members::dsl::death_day.eq(member.death_day),
|
|
|
|
members::dsl::note.eq(member.note.clone()),
|
|
|
|
))
|
|
|
|
.execute(conn)
|
|
|
|
})?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Delete a member
|
|
|
|
pub async fn delete(member: &Member) -> anyhow::Result<()> {
|
|
|
|
// TODO : remove associated couple
|
|
|
|
// TODO : remove user photo
|
|
|
|
|
|
|
|
// Remove the member
|
|
|
|
db_connection::execute(|conn| {
|
|
|
|
diesel::delete(members::dsl::members.filter(members::dsl::id.eq(member.id().0)))
|
|
|
|
.execute(conn)
|
|
|
|
})?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2023-08-04 17:17:51 +00:00
|
|
|
|
|
|
|
/// Delete all the members of a family
|
|
|
|
pub async fn delete_all_family(family_id: FamilyID) -> anyhow::Result<()> {
|
|
|
|
for m in get_all_of_family(family_id).await? {
|
|
|
|
delete(&m).await?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2023-08-05 10:10:23 +00:00
|
|
|
|
|
|
|
pub mod loop_detection {
|
|
|
|
use crate::models::{Member, MemberID};
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct LoopStack<'a> {
|
|
|
|
curr: MemberID,
|
|
|
|
prev: Option<&'a LoopStack<'a>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> LoopStack<'a> {
|
|
|
|
pub fn contains(&self, id: MemberID) -> bool {
|
|
|
|
if let Some(ls) = &self.prev {
|
|
|
|
if ls.contains(id) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.curr == id
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn detect_loop_control(
|
|
|
|
members: &HashMap<MemberID, &&Member>,
|
|
|
|
curr_stack: &LoopStack,
|
|
|
|
next: Option<MemberID>,
|
|
|
|
) -> bool {
|
|
|
|
match next {
|
|
|
|
None => false,
|
|
|
|
Some(id) => {
|
|
|
|
if curr_stack.contains(id) {
|
|
|
|
log::debug!("Loop detected! {:?}", curr_stack);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
detect_loop_recurse(
|
|
|
|
members,
|
|
|
|
&LoopStack {
|
|
|
|
curr: id,
|
|
|
|
prev: Some(curr_stack),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Recurse loop detection
|
|
|
|
fn detect_loop_recurse(members: &HashMap<MemberID, &&Member>, curr_stack: &LoopStack) -> bool {
|
|
|
|
let member = match members.get(&curr_stack.curr) {
|
|
|
|
Some(m) => m,
|
|
|
|
None => {
|
|
|
|
log::warn!("Member {:?} not found in the tree for loop detection, this should never happen!", curr_stack.curr);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
detect_loop_control(members, curr_stack, member.mother())
|
|
|
|
|| detect_loop_control(members, curr_stack, member.father())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Detect loops in members hierarchy
|
|
|
|
pub fn detect_loop(members: &[&Member]) -> bool {
|
|
|
|
let mut map = HashMap::new();
|
|
|
|
for m in members {
|
|
|
|
map.insert(m.id(), m);
|
|
|
|
}
|
|
|
|
|
|
|
|
for m in members {
|
|
|
|
if detect_loop_recurse(
|
|
|
|
&map,
|
|
|
|
&LoopStack {
|
|
|
|
curr: m.id(),
|
|
|
|
prev: None,
|
|
|
|
},
|
|
|
|
) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|