Can import basic data
This commit is contained in:
		@@ -25,10 +25,10 @@ enum CoupleControllerErr {
 | 
				
			|||||||
    MalformedDateOfDivorce,
 | 
					    MalformedDateOfDivorce,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(serde::Deserialize)]
 | 
					#[derive(serde::Deserialize, Clone)]
 | 
				
			||||||
pub struct CoupleRequest {
 | 
					pub struct CoupleRequest {
 | 
				
			||||||
    wife: Option<MemberID>,
 | 
					    pub wife: Option<MemberID>,
 | 
				
			||||||
    husband: Option<MemberID>,
 | 
					    pub husband: Option<MemberID>,
 | 
				
			||||||
    state: Option<CoupleState>,
 | 
					    state: Option<CoupleState>,
 | 
				
			||||||
    #[serde(flatten, with = "prefix_wedding")]
 | 
					    #[serde(flatten, with = "prefix_wedding")]
 | 
				
			||||||
    wedding: Option<RequestDate>,
 | 
					    wedding: Option<RequestDate>,
 | 
				
			||||||
@@ -37,9 +37,14 @@ pub struct CoupleRequest {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl CoupleRequest {
 | 
					impl CoupleRequest {
 | 
				
			||||||
    pub async fn to_couple(self, couple: &mut Couple) -> anyhow::Result<()> {
 | 
					    pub async fn to_couple(
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        couple: &mut Couple,
 | 
				
			||||||
 | 
					        check_members_existence: bool,
 | 
				
			||||||
 | 
					    ) -> anyhow::Result<()> {
 | 
				
			||||||
        if let Some(wife) = self.wife {
 | 
					        if let Some(wife) = self.wife {
 | 
				
			||||||
            if !members_service::exists(couple.family_id(), wife).await? {
 | 
					            if check_members_existence && !members_service::exists(couple.family_id(), wife).await?
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
                return Err(CoupleControllerErr::WifeNotExisting.into());
 | 
					                return Err(CoupleControllerErr::WifeNotExisting.into());
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -49,7 +54,9 @@ impl CoupleRequest {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if let Some(husband) = self.husband {
 | 
					        if let Some(husband) = self.husband {
 | 
				
			||||||
            if !members_service::exists(couple.family_id(), husband).await? {
 | 
					            if check_members_existence
 | 
				
			||||||
 | 
					                && !members_service::exists(couple.family_id(), husband).await?
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
                return Err(CoupleControllerErr::HusbandNotExisting.into());
 | 
					                return Err(CoupleControllerErr::HusbandNotExisting.into());
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -102,7 +109,7 @@ impl CoupleAPI {
 | 
				
			|||||||
pub async fn create(m: FamilyInPath, req: web::Json<CoupleRequest>) -> HttpResult {
 | 
					pub async fn create(m: FamilyInPath, req: web::Json<CoupleRequest>) -> HttpResult {
 | 
				
			||||||
    let mut couple = couples_service::create(m.family_id()).await?;
 | 
					    let mut couple = couples_service::create(m.family_id()).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if let Err(e) = req.0.to_couple(&mut couple).await {
 | 
					    if let Err(e) = req.0.to_couple(&mut couple, true).await {
 | 
				
			||||||
        log::error!("Failed to apply couple information! {e}");
 | 
					        log::error!("Failed to apply couple information! {e}");
 | 
				
			||||||
        couples_service::delete(&mut couple).await?;
 | 
					        couples_service::delete(&mut couple).await?;
 | 
				
			||||||
        return Ok(HttpResponse::BadRequest().body(e.to_string()));
 | 
					        return Ok(HttpResponse::BadRequest().body(e.to_string()));
 | 
				
			||||||
@@ -137,7 +144,7 @@ pub async fn get_single(m: FamilyAndCoupleInPath) -> HttpResult {
 | 
				
			|||||||
pub async fn update(m: FamilyAndCoupleInPath, req: web::Json<CoupleRequest>) -> HttpResult {
 | 
					pub async fn update(m: FamilyAndCoupleInPath, req: web::Json<CoupleRequest>) -> HttpResult {
 | 
				
			||||||
    let mut couple = m.to_couple();
 | 
					    let mut couple = m.to_couple();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if let Err(e) = req.0.to_couple(&mut couple).await {
 | 
					    if let Err(e) = req.0.to_couple(&mut couple, true).await {
 | 
				
			||||||
        log::error!("Failed to parse couple information {e}!");
 | 
					        log::error!("Failed to parse couple information {e}!");
 | 
				
			||||||
        return Ok(HttpResponse::BadRequest().body(e.to_string()));
 | 
					        return Ok(HttpResponse::BadRequest().body(e.to_string()));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +1,39 @@
 | 
				
			|||||||
use crate::connections::s3_connection;
 | 
					use crate::connections::s3_connection;
 | 
				
			||||||
 | 
					use crate::constants;
 | 
				
			||||||
 | 
					use crate::controllers::couples_controller::CoupleRequest;
 | 
				
			||||||
 | 
					use crate::controllers::members_controller::MemberRequest;
 | 
				
			||||||
use crate::controllers::HttpResult;
 | 
					use crate::controllers::HttpResult;
 | 
				
			||||||
use crate::extractors::family_extractor::FamilyInPath;
 | 
					use crate::extractors::family_extractor::{FamilyInPath, FamilyInPathWithAdminMembership};
 | 
				
			||||||
 | 
					use crate::models::{CoupleID, MemberID, PhotoID};
 | 
				
			||||||
use crate::services::{couples_service, members_service, photos_service};
 | 
					use crate::services::{couples_service, members_service, photos_service};
 | 
				
			||||||
 | 
					use actix_multipart::form::tempfile::TempFile;
 | 
				
			||||||
 | 
					use actix_multipart::form::MultipartForm;
 | 
				
			||||||
use actix_web::HttpResponse;
 | 
					use actix_web::HttpResponse;
 | 
				
			||||||
use std::io::{Cursor, Write};
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					use std::io;
 | 
				
			||||||
 | 
					use std::io::{Cursor, Read, Write};
 | 
				
			||||||
use zip::write::FileOptions;
 | 
					use zip::write::FileOptions;
 | 
				
			||||||
use zip::CompressionMethod;
 | 
					use zip::{CompressionMethod, ZipArchive};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const MEMBERS_FILE: &str = "members.json";
 | 
					const MEMBERS_FILE: &str = "members.json";
 | 
				
			||||||
const COUPLES_FILE: &str = "couples.json";
 | 
					const COUPLES_FILE: &str = "couples.json";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(serde::Deserialize)]
 | 
				
			||||||
 | 
					struct ImportMemberRequest {
 | 
				
			||||||
 | 
					    id: MemberID,
 | 
				
			||||||
 | 
					    photo_id: Option<PhotoID>,
 | 
				
			||||||
 | 
					    #[serde(flatten)]
 | 
				
			||||||
 | 
					    data: MemberRequest,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(serde::Deserialize)]
 | 
				
			||||||
 | 
					struct ImportCoupleRequest {
 | 
				
			||||||
 | 
					    id: CoupleID,
 | 
				
			||||||
 | 
					    photo_id: Option<PhotoID>,
 | 
				
			||||||
 | 
					    #[serde(flatten)]
 | 
				
			||||||
 | 
					    data: CoupleRequest,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Export whole family data
 | 
					/// Export whole family data
 | 
				
			||||||
pub async fn export_family(f: FamilyInPath) -> HttpResult {
 | 
					pub async fn export_family(f: FamilyInPath) -> HttpResult {
 | 
				
			||||||
    let files_opt = FileOptions::default().compression_method(CompressionMethod::Bzip2);
 | 
					    let files_opt = FileOptions::default().compression_method(CompressionMethod::Bzip2);
 | 
				
			||||||
@@ -56,3 +80,120 @@ pub async fn export_family(f: FamilyInPath) -> HttpResult {
 | 
				
			|||||||
        .content_type("application/zip")
 | 
					        .content_type("application/zip")
 | 
				
			||||||
        .body(buff))
 | 
					        .body(buff))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, MultipartForm)]
 | 
				
			||||||
 | 
					pub struct UploadFamilyDataForm {
 | 
				
			||||||
 | 
					    #[multipart(rename = "archive")]
 | 
				
			||||||
 | 
					    archive: TempFile,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Import whole family data
 | 
				
			||||||
 | 
					pub async fn import_family(
 | 
				
			||||||
 | 
					    f: FamilyInPathWithAdminMembership,
 | 
				
			||||||
 | 
					    MultipartForm(form): MultipartForm<UploadFamilyDataForm>,
 | 
				
			||||||
 | 
					) -> HttpResult {
 | 
				
			||||||
 | 
					    let mut zip = ZipArchive::new(form.archive.file)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Parse general information
 | 
				
			||||||
 | 
					    let members_list = serde_json::from_slice::<Vec<ImportMemberRequest>>(&read_zip_file(
 | 
				
			||||||
 | 
					        &mut zip,
 | 
				
			||||||
 | 
					        MEMBERS_FILE,
 | 
				
			||||||
 | 
					    )?)?;
 | 
				
			||||||
 | 
					    let mut couples_list = serde_json::from_slice::<Vec<ImportCoupleRequest>>(&read_zip_file(
 | 
				
			||||||
 | 
					        &mut zip,
 | 
				
			||||||
 | 
					        COUPLES_FILE,
 | 
				
			||||||
 | 
					    )?)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Delete all existing members
 | 
				
			||||||
 | 
					    members_service::delete_all_family(f.family_id()).await?;
 | 
				
			||||||
 | 
					    couples_service::delete_all_family(f.family_id()).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Create empty members set
 | 
				
			||||||
 | 
					    let mut mapped_req_members = HashMap::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut members_id_mapping = HashMap::new();
 | 
				
			||||||
 | 
					    let mut rev_members_id_mapping = HashMap::new();
 | 
				
			||||||
 | 
					    let mut new_members = Vec::with_capacity(members_list.len());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for req_m in members_list {
 | 
				
			||||||
 | 
					        // Create member entry in database
 | 
				
			||||||
 | 
					        let new_m = members_service::create(f.family_id()).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Map new member ID with request id
 | 
				
			||||||
 | 
					        members_id_mapping.insert(req_m.id, new_m.id());
 | 
				
			||||||
 | 
					        rev_members_id_mapping.insert(new_m.id(), req_m.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Save new member structure
 | 
				
			||||||
 | 
					        new_members.push(new_m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Save request member information, mapped with its old id
 | 
				
			||||||
 | 
					        mapped_req_members.insert(req_m.id, req_m);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Set member information, checking for eventual loops
 | 
				
			||||||
 | 
					    for member in &mut new_members {
 | 
				
			||||||
 | 
					        let db_id = member.id();
 | 
				
			||||||
 | 
					        let req_id = *rev_members_id_mapping.get(&db_id).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let req_member = mapped_req_members.get(&req_id).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Map mother and father id and extract member information
 | 
				
			||||||
 | 
					        let mut req_member_data = req_member.data.clone();
 | 
				
			||||||
 | 
					        if let Some(i) = req_member_data.father {
 | 
				
			||||||
 | 
					            req_member_data.father = members_id_mapping.get(&i).copied();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if let Some(i) = req_member_data.mother {
 | 
				
			||||||
 | 
					            req_member_data.mother = members_id_mapping.get(&i).copied();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        req_member_data.to_member(member, true).await?;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Check for loops
 | 
				
			||||||
 | 
					    let members_slice = new_members.iter().collect::<Vec<_>>();
 | 
				
			||||||
 | 
					    if members_service::loop_detection::detect_loop(&members_slice) {
 | 
				
			||||||
 | 
					        return Ok(HttpResponse::BadRequest().body("Loop detected in members relationships!"));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Save member information
 | 
				
			||||||
 | 
					    for member in &mut new_members {
 | 
				
			||||||
 | 
					        members_service::update(member).await?;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Extract and insert couples information
 | 
				
			||||||
 | 
					    let mut couple_mapping = HashMap::new();
 | 
				
			||||||
 | 
					    for c in &mut couples_list {
 | 
				
			||||||
 | 
					        // Map wife and husband
 | 
				
			||||||
 | 
					        if let Some(i) = c.data.wife {
 | 
				
			||||||
 | 
					            c.data.wife = members_id_mapping.get(&i).copied();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Some(i) = c.data.husband {
 | 
				
			||||||
 | 
					            c.data.husband = members_id_mapping.get(&i).copied();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut db_couple = couples_service::create(f.family_id()).await?;
 | 
				
			||||||
 | 
					        couple_mapping.insert(c.id, db_couple.id());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        c.data.clone().to_couple(&mut db_couple, true).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        couples_service::update(&mut db_couple).await?;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Insert photos
 | 
				
			||||||
 | 
					    // TODO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(HttpResponse::Ok().body("go on"))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn read_zip_file<R: Read + io::Seek>(
 | 
				
			||||||
 | 
					    archive: &mut ZipArchive<R>,
 | 
				
			||||||
 | 
					    file: &str,
 | 
				
			||||||
 | 
					) -> anyhow::Result<Vec<u8>> {
 | 
				
			||||||
 | 
					    let mut entry = archive.by_name(file)?;
 | 
				
			||||||
 | 
					    assert!(entry.size() < constants::PHOTOS_MAX_SIZE as u64);
 | 
				
			||||||
 | 
					    let mut buff = Vec::with_capacity(entry.size() as usize);
 | 
				
			||||||
 | 
					    entry.read_to_end(&mut buff)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(buff)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,7 +12,7 @@ use actix_web::{web, HttpResponse};
 | 
				
			|||||||
serde_with::with_prefix!(prefix_birth "birth_");
 | 
					serde_with::with_prefix!(prefix_birth "birth_");
 | 
				
			||||||
serde_with::with_prefix!(prefix_death "death_");
 | 
					serde_with::with_prefix!(prefix_death "death_");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(serde::Deserialize)]
 | 
					#[derive(serde::Deserialize, Clone)]
 | 
				
			||||||
pub struct RequestDate {
 | 
					pub struct RequestDate {
 | 
				
			||||||
    pub year: Option<i16>,
 | 
					    pub year: Option<i16>,
 | 
				
			||||||
    pub month: Option<i16>,
 | 
					    pub month: Option<i16>,
 | 
				
			||||||
@@ -29,7 +29,7 @@ impl RequestDate {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(serde::Deserialize)]
 | 
					#[derive(serde::Deserialize, Clone)]
 | 
				
			||||||
pub struct MemberRequest {
 | 
					pub struct MemberRequest {
 | 
				
			||||||
    first_name: Option<String>,
 | 
					    first_name: Option<String>,
 | 
				
			||||||
    last_name: Option<String>,
 | 
					    last_name: Option<String>,
 | 
				
			||||||
@@ -41,8 +41,8 @@ pub struct MemberRequest {
 | 
				
			|||||||
    postal_code: Option<String>,
 | 
					    postal_code: Option<String>,
 | 
				
			||||||
    country: Option<String>,
 | 
					    country: Option<String>,
 | 
				
			||||||
    sex: Option<Sex>,
 | 
					    sex: Option<Sex>,
 | 
				
			||||||
    mother: Option<MemberID>,
 | 
					    pub mother: Option<MemberID>,
 | 
				
			||||||
    father: Option<MemberID>,
 | 
					    pub father: Option<MemberID>,
 | 
				
			||||||
    #[serde(flatten, with = "prefix_birth")]
 | 
					    #[serde(flatten, with = "prefix_birth")]
 | 
				
			||||||
    birth: Option<RequestDate>,
 | 
					    birth: Option<RequestDate>,
 | 
				
			||||||
    #[serde(default)]
 | 
					    #[serde(default)]
 | 
				
			||||||
@@ -104,7 +104,11 @@ fn check_opt_str_val(
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl MemberRequest {
 | 
					impl MemberRequest {
 | 
				
			||||||
    pub async fn to_member(self, member: &mut Member) -> anyhow::Result<()> {
 | 
					    pub async fn to_member(
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        member: &mut Member,
 | 
				
			||||||
 | 
					        check_members_existence: bool, /* TODO: remove this field */
 | 
				
			||||||
 | 
					    ) -> anyhow::Result<()> {
 | 
				
			||||||
        let c = StaticConstraints::default();
 | 
					        let c = StaticConstraints::default();
 | 
				
			||||||
        check_opt_str_val(
 | 
					        check_opt_str_val(
 | 
				
			||||||
            &self.first_name,
 | 
					            &self.first_name,
 | 
				
			||||||
@@ -191,7 +195,9 @@ impl MemberRequest {
 | 
				
			|||||||
        )?;
 | 
					        )?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if let Some(mother) = self.mother {
 | 
					        if let Some(mother) = self.mother {
 | 
				
			||||||
            if !members_service::exists(member.family_id(), mother).await? {
 | 
					            if check_members_existence
 | 
				
			||||||
 | 
					                && !members_service::exists(member.family_id(), mother).await?
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
                return Err(MemberControllerErr::MotherNotExisting.into());
 | 
					                return Err(MemberControllerErr::MotherNotExisting.into());
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -201,7 +207,9 @@ impl MemberRequest {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if let Some(father) = self.father {
 | 
					        if let Some(father) = self.father {
 | 
				
			||||||
            if !members_service::exists(member.family_id(), father).await? {
 | 
					            if check_members_existence
 | 
				
			||||||
 | 
					                && !members_service::exists(member.family_id(), father).await?
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
                return Err(MemberControllerErr::FatherNotExisting.into());
 | 
					                return Err(MemberControllerErr::FatherNotExisting.into());
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -254,7 +262,7 @@ impl MemberAPI {
 | 
				
			|||||||
pub async fn create(f: FamilyInPath, req: web::Json<MemberRequest>) -> HttpResult {
 | 
					pub async fn create(f: FamilyInPath, req: web::Json<MemberRequest>) -> HttpResult {
 | 
				
			||||||
    let mut member = members_service::create(f.family_id()).await?;
 | 
					    let mut member = members_service::create(f.family_id()).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if let Err(e) = req.0.to_member(&mut member).await {
 | 
					    if let Err(e) = req.0.to_member(&mut member, true).await {
 | 
				
			||||||
        log::error!("Failed to apply member information! {e}");
 | 
					        log::error!("Failed to apply member information! {e}");
 | 
				
			||||||
        members_service::delete(&mut member).await?;
 | 
					        members_service::delete(&mut member).await?;
 | 
				
			||||||
        return Ok(HttpResponse::BadRequest().body(e.to_string()));
 | 
					        return Ok(HttpResponse::BadRequest().body(e.to_string()));
 | 
				
			||||||
@@ -284,7 +292,7 @@ pub async fn get_single(m: FamilyAndMemberInPath) -> HttpResult {
 | 
				
			|||||||
pub async fn update(m: FamilyAndMemberInPath, req: web::Json<MemberRequest>) -> HttpResult {
 | 
					pub async fn update(m: FamilyAndMemberInPath, req: web::Json<MemberRequest>) -> HttpResult {
 | 
				
			||||||
    let mut member = m.to_member();
 | 
					    let mut member = m.to_member();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if let Err(e) = req.0.to_member(&mut member).await {
 | 
					    if let Err(e) = req.0.to_member(&mut member, true).await {
 | 
				
			||||||
        log::error!("Failed to parse member information {e}!");
 | 
					        log::error!("Failed to parse member information {e}!");
 | 
				
			||||||
        return Ok(HttpResponse::BadRequest().body(e.to_string()));
 | 
					        return Ok(HttpResponse::BadRequest().body(e.to_string()));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -196,6 +196,10 @@ async fn main() -> std::io::Result<()> {
 | 
				
			|||||||
                "/family/{id}/data/export",
 | 
					                "/family/{id}/data/export",
 | 
				
			||||||
                web::get().to(data_controller::export_family),
 | 
					                web::get().to(data_controller::export_family),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					            .route(
 | 
				
			||||||
 | 
					                "/family/{id}/data/import",
 | 
				
			||||||
 | 
					                web::put().to(data_controller::import_family),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            // Photos controller
 | 
					            // Photos controller
 | 
				
			||||||
            .route(
 | 
					            .route(
 | 
				
			||||||
                "/photo/{id}",
 | 
					                "/photo/{id}",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -202,7 +202,7 @@ impl NewPhoto {
 | 
				
			|||||||
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash)]
 | 
					#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash)]
 | 
				
			||||||
pub struct MemberID(pub i32);
 | 
					pub struct MemberID(pub i32);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(serde::Serialize, serde::Deserialize)]
 | 
					#[derive(serde::Serialize, serde::Deserialize, Copy, Clone)]
 | 
				
			||||||
pub enum Sex {
 | 
					pub enum Sex {
 | 
				
			||||||
    #[serde(rename = "M")]
 | 
					    #[serde(rename = "M")]
 | 
				
			||||||
    Male,
 | 
					    Male,
 | 
				
			||||||
@@ -310,7 +310,7 @@ pub struct NewMember {
 | 
				
			|||||||
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash)]
 | 
					#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash)]
 | 
				
			||||||
pub struct CoupleID(pub i32);
 | 
					pub struct CoupleID(pub i32);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(serde::Serialize, serde::Deserialize)]
 | 
					#[derive(serde::Serialize, serde::Deserialize, Copy, Clone)]
 | 
				
			||||||
pub enum CoupleState {
 | 
					pub enum CoupleState {
 | 
				
			||||||
    #[serde(rename = "N")]
 | 
					    #[serde(rename = "N")]
 | 
				
			||||||
    None,
 | 
					    None,
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user