Can create couple
This commit is contained in:
parent
75438f4ae0
commit
29c503a6b0
@ -68,17 +68,20 @@ CREATE TABLE members (
|
||||
);
|
||||
|
||||
CREATE TABLE couples (
|
||||
wife integer NOT NULL REFERENCES members,
|
||||
husband integer NOT NULL REFERENCES members,
|
||||
id SERIAL PRIMARY KEY,
|
||||
family_id integer NOT NULL REFERENCES families,
|
||||
wife integer NULL REFERENCES members,
|
||||
husband integer NULL REFERENCES members,
|
||||
state varchar(1) NULL,
|
||||
photo_id INTEGER NULL REFERENCES photos ON DELETE SET NULL,
|
||||
time_create BIGINT NOT NULL,
|
||||
time_update BIGINT NOT NULL,
|
||||
wedding_year smallint NULL,
|
||||
wedding_month smallint NULL,
|
||||
wedding_day smallint NULL,
|
||||
divorce_year smallint NULL,
|
||||
divorce_month smallint NULL,
|
||||
divorce_day smallint NULL,
|
||||
|
||||
PRIMARY KEY(wife, husband)
|
||||
divorce_day smallint NULL
|
||||
);
|
||||
|
||||
-- Create views
|
||||
|
99
geneit_backend/src/controllers/couples_controller.rs
Normal file
99
geneit_backend/src/controllers/couples_controller.rs
Normal file
@ -0,0 +1,99 @@
|
||||
use crate::controllers::members_controller::RequestDate;
|
||||
use crate::controllers::HttpResult;
|
||||
use crate::extractors::family_extractor::FamilyInPath;
|
||||
use crate::models::{Couple, CoupleState, MemberID};
|
||||
use crate::services::{couples_service, members_service};
|
||||
use actix_web::{web, HttpResponse};
|
||||
|
||||
serde_with::with_prefix!(prefix_wedding "wedding_");
|
||||
serde_with::with_prefix!(prefix_divorce "divorce_");
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
enum CoupleControllerErr {
|
||||
#[error("Wife and husband are identical!")]
|
||||
IdenticalWifeHusband,
|
||||
#[error("Wife does not exist!")]
|
||||
WifeNotExisting,
|
||||
#[error("Husband does not exist!")]
|
||||
HusbandNotExisting,
|
||||
#[error("Invalid date of wedding")]
|
||||
MalformedDateOfWedding,
|
||||
#[error("Invalid date of divorce")]
|
||||
MalformedDateOfDivorce,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct CoupleRequest {
|
||||
wife: Option<MemberID>,
|
||||
husband: Option<MemberID>,
|
||||
state: Option<CoupleState>,
|
||||
#[serde(flatten, with = "prefix_wedding")]
|
||||
wedding: Option<RequestDate>,
|
||||
#[serde(flatten, with = "prefix_divorce")]
|
||||
divorce: Option<RequestDate>,
|
||||
}
|
||||
|
||||
impl CoupleRequest {
|
||||
pub async fn to_couple(self, couple: &mut Couple) -> anyhow::Result<()> {
|
||||
if let Some(wife) = self.wife {
|
||||
if !members_service::exists(couple.family_id(), wife).await? {
|
||||
return Err(CoupleControllerErr::WifeNotExisting.into());
|
||||
}
|
||||
|
||||
if self.wife == self.husband {
|
||||
return Err(CoupleControllerErr::IdenticalWifeHusband.into());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(husband) = self.husband {
|
||||
if !members_service::exists(couple.family_id(), husband).await? {
|
||||
return Err(CoupleControllerErr::HusbandNotExisting.into());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(d) = &self.wedding {
|
||||
if !d.check() {
|
||||
return Err(CoupleControllerErr::MalformedDateOfWedding.into());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(d) = &self.divorce {
|
||||
if !d.check() {
|
||||
return Err(CoupleControllerErr::MalformedDateOfDivorce.into());
|
||||
}
|
||||
}
|
||||
|
||||
couple.set_wife(self.wife);
|
||||
couple.set_husband(self.husband);
|
||||
couple.set_state(self.state);
|
||||
|
||||
couple.wedding_year = self.wedding.as_ref().map(|m| m.year).unwrap_or_default();
|
||||
couple.wedding_month = self.wedding.as_ref().map(|m| m.month).unwrap_or_default();
|
||||
couple.wedding_day = self.wedding.as_ref().map(|m| m.day).unwrap_or_default();
|
||||
|
||||
couple.divorce_year = self.divorce.as_ref().map(|m| m.year).unwrap_or_default();
|
||||
couple.divorce_month = self.divorce.as_ref().map(|m| m.month).unwrap_or_default();
|
||||
couple.divorce_day = self.divorce.as_ref().map(|m| m.day).unwrap_or_default();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new couple
|
||||
pub async fn create(m: FamilyInPath, req: web::Json<CoupleRequest>) -> HttpResult {
|
||||
let mut couple = couples_service::create(m.family_id()).await?;
|
||||
|
||||
if let Err(e) = req.0.to_couple(&mut couple).await {
|
||||
log::error!("Failed to apply couple information! {e}");
|
||||
couples_service::delete(&mut couple).await?;
|
||||
return Ok(HttpResponse::BadRequest().body(e.to_string()));
|
||||
}
|
||||
|
||||
if let Err(e) = couples_service::update(&mut couple).await {
|
||||
log::error!("Failed to update couple information! {e}");
|
||||
couples_service::delete(&mut couple).await?;
|
||||
return Ok(HttpResponse::InternalServerError().finish());
|
||||
}
|
||||
|
||||
Ok(HttpResponse::Ok().json(couple))
|
||||
}
|
@ -5,6 +5,7 @@ use actix_web::HttpResponse;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
|
||||
pub mod auth_controller;
|
||||
pub mod couples_controller;
|
||||
pub mod families_controller;
|
||||
pub mod members_controller;
|
||||
pub mod photos_controller;
|
||||
|
@ -6,8 +6,8 @@ use actix_web::{web, App, HttpServer};
|
||||
use geneit_backend::app_config::AppConfig;
|
||||
use geneit_backend::connections::s3_connection;
|
||||
use geneit_backend::controllers::{
|
||||
auth_controller, families_controller, members_controller, photos_controller, server_controller,
|
||||
users_controller,
|
||||
auth_controller, couples_controller, families_controller, members_controller,
|
||||
photos_controller, server_controller, users_controller,
|
||||
};
|
||||
|
||||
#[actix_web::main]
|
||||
@ -162,6 +162,12 @@ async fn main() -> std::io::Result<()> {
|
||||
"/family/{id}/member/{member_id}/photo",
|
||||
web::delete().to(members_controller::remove_photo),
|
||||
)
|
||||
// Couples controller
|
||||
.route(
|
||||
"/family/{id}/couple/create",
|
||||
web::post().to(couples_controller::create),
|
||||
)
|
||||
// Photos controller
|
||||
.route(
|
||||
"/photo/{id}",
|
||||
web::get().to(photos_controller::get_full_size),
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::app_config::AppConfig;
|
||||
use crate::schema::{families, members, memberships, photos, users};
|
||||
use crate::schema::{couples, families, members, memberships, photos, users};
|
||||
use crate::utils::crypt_utils::sha256;
|
||||
use diesel::prelude::*;
|
||||
|
||||
@ -297,3 +297,105 @@ pub struct NewMember {
|
||||
pub time_create: i64,
|
||||
pub time_update: i64,
|
||||
}
|
||||
|
||||
/// Member ID holder
|
||||
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash)]
|
||||
pub struct CoupleID(pub i32);
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub enum CoupleState {
|
||||
#[serde(rename = "N")]
|
||||
None,
|
||||
#[serde(rename = "E")]
|
||||
Engaged,
|
||||
#[serde(rename = "M")]
|
||||
Married,
|
||||
#[serde(rename = "D")]
|
||||
Divorced,
|
||||
}
|
||||
|
||||
impl CoupleState {
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self {
|
||||
CoupleState::None => "N",
|
||||
CoupleState::Engaged => "E",
|
||||
CoupleState::Married => "M",
|
||||
CoupleState::Divorced => "D",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_str(s: &str) -> Option<Self> {
|
||||
serde_json::from_str(s).ok()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Queryable, Debug, serde::Serialize)]
|
||||
pub struct Couple {
|
||||
id: i32,
|
||||
family_id: i32,
|
||||
wife: Option<i32>,
|
||||
husband: Option<i32>,
|
||||
state: Option<String>,
|
||||
photo_id: Option<i32>,
|
||||
time_create: i64,
|
||||
pub time_update: i64,
|
||||
pub wedding_year: Option<i16>,
|
||||
pub wedding_month: Option<i16>,
|
||||
pub wedding_day: Option<i16>,
|
||||
pub divorce_year: Option<i16>,
|
||||
pub divorce_month: Option<i16>,
|
||||
pub divorce_day: Option<i16>,
|
||||
}
|
||||
|
||||
impl Couple {
|
||||
pub fn id(&self) -> CoupleID {
|
||||
CoupleID(self.id)
|
||||
}
|
||||
|
||||
pub fn family_id(&self) -> FamilyID {
|
||||
FamilyID(self.family_id)
|
||||
}
|
||||
|
||||
pub fn state(&self) -> Option<CoupleState> {
|
||||
self.state
|
||||
.as_deref()
|
||||
.map(CoupleState::parse_str)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn set_state(&mut self, s: Option<CoupleState>) {
|
||||
self.state = s.map(|s| s.as_str().to_string())
|
||||
}
|
||||
|
||||
pub fn set_wife(&mut self, s: Option<MemberID>) {
|
||||
self.wife = s.map(|s| s.0)
|
||||
}
|
||||
|
||||
pub fn wife(&self) -> Option<MemberID> {
|
||||
self.wife.map(MemberID)
|
||||
}
|
||||
|
||||
pub fn set_husband(&mut self, s: Option<MemberID>) {
|
||||
self.husband = s.map(|s| s.0)
|
||||
}
|
||||
|
||||
pub fn husband(&self) -> Option<MemberID> {
|
||||
self.husband.map(MemberID)
|
||||
}
|
||||
|
||||
pub fn set_photo_id(&mut self, p: Option<PhotoID>) {
|
||||
self.photo_id = p.map(|p| p.0);
|
||||
}
|
||||
|
||||
pub fn photo_id(&self) -> Option<PhotoID> {
|
||||
self.photo_id.map(PhotoID)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Insertable)]
|
||||
#[diesel(table_name = couples)]
|
||||
pub struct NewCouple {
|
||||
pub family_id: i32,
|
||||
pub time_create: i64,
|
||||
pub time_update: i64,
|
||||
}
|
||||
|
@ -1,10 +1,15 @@
|
||||
// @generated automatically by Diesel CLI.
|
||||
|
||||
diesel::table! {
|
||||
couples (wife, husband) {
|
||||
wife -> Int4,
|
||||
husband -> Int4,
|
||||
couples (id) {
|
||||
id -> Int4,
|
||||
family_id -> Int4,
|
||||
wife -> Nullable<Int4>,
|
||||
husband -> Nullable<Int4>,
|
||||
state -> Nullable<Varchar>,
|
||||
photo_id -> Nullable<Int4>,
|
||||
time_create -> Int8,
|
||||
time_update -> Int8,
|
||||
wedding_year -> Nullable<Int2>,
|
||||
wedding_month -> Nullable<Int2>,
|
||||
wedding_day -> Nullable<Int2>,
|
||||
@ -90,6 +95,7 @@ diesel::table! {
|
||||
}
|
||||
}
|
||||
|
||||
diesel::joinable!(couples -> families (family_id));
|
||||
diesel::joinable!(couples -> photos (photo_id));
|
||||
diesel::joinable!(members -> families (family_id));
|
||||
diesel::joinable!(members -> photos (photo_id));
|
||||
|
124
geneit_backend/src/services/couples_service.rs
Normal file
124
geneit_backend/src/services/couples_service.rs
Normal file
@ -0,0 +1,124 @@
|
||||
use crate::connections::db_connection;
|
||||
use crate::models::{Couple, CoupleID, FamilyID, MemberID, NewCouple};
|
||||
use crate::schema::couples;
|
||||
use crate::services::photos_service;
|
||||
use crate::utils::time_utils::time;
|
||||
use diesel::prelude::*;
|
||||
|
||||
/// Create a new couple
|
||||
pub async fn create(family_id: FamilyID) -> anyhow::Result<Couple> {
|
||||
db_connection::execute(|conn| {
|
||||
let res: Couple = diesel::insert_into(couples::table)
|
||||
.values(&NewCouple {
|
||||
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 couple
|
||||
pub async fn get_by_id(id: CoupleID) -> anyhow::Result<Couple> {
|
||||
db_connection::execute(|conn| couples::table.filter(couples::dsl::id.eq(id.0)).first(conn))
|
||||
}
|
||||
|
||||
/// Get all the couples of a family
|
||||
pub async fn get_all_of_family(id: FamilyID) -> anyhow::Result<Vec<Couple>> {
|
||||
db_connection::execute(|conn| {
|
||||
couples::table
|
||||
.filter(couples::dsl::family_id.eq(id.0))
|
||||
.get_results(conn)
|
||||
})
|
||||
}
|
||||
|
||||
/// Get all the couples associated to a member
|
||||
pub async fn get_all_of_member(id: MemberID) -> anyhow::Result<Vec<Couple>> {
|
||||
db_connection::execute(|conn| {
|
||||
couples::table
|
||||
.filter(
|
||||
couples::dsl::wife
|
||||
.eq(id.0)
|
||||
.or(couples::dsl::husband.eq(id.0)),
|
||||
)
|
||||
.get_results(conn)
|
||||
})
|
||||
}
|
||||
|
||||
/// Check whether a couple with a given id exists or not
|
||||
pub async fn exists(family_id: FamilyID, couple_id: CoupleID) -> anyhow::Result<bool> {
|
||||
db_connection::execute(|conn| {
|
||||
let count: i64 = couples::table
|
||||
.filter(
|
||||
couples::id
|
||||
.eq(couple_id.0)
|
||||
.and(couples::family_id.eq(family_id.0)),
|
||||
)
|
||||
.count()
|
||||
.get_result(conn)?;
|
||||
|
||||
Ok(count != 0)
|
||||
})
|
||||
}
|
||||
|
||||
/// Update the information of a couple
|
||||
pub async fn update(couple: &mut Couple) -> anyhow::Result<()> {
|
||||
couple.time_update = time() as i64;
|
||||
|
||||
db_connection::execute(|conn| {
|
||||
diesel::update(couples::dsl::couples.filter(couples::dsl::id.eq(couple.id().0)))
|
||||
.set((
|
||||
couples::dsl::state.eq(couple.state().map(|c| c.as_str().to_string())),
|
||||
couples::dsl::wife.eq(couple.wife().map(|m| m.0)),
|
||||
couples::dsl::husband.eq(couple.husband().map(|m| m.0)),
|
||||
couples::dsl::photo_id.eq(couple.photo_id().map(|p| p.0)),
|
||||
couples::dsl::time_update.eq(couple.time_update),
|
||||
couples::dsl::wedding_year.eq(couple.wedding_year),
|
||||
couples::dsl::wedding_month.eq(couple.wedding_month),
|
||||
couples::dsl::wedding_day.eq(couple.wedding_day),
|
||||
couples::dsl::divorce_year.eq(couple.divorce_year),
|
||||
couples::dsl::divorce_month.eq(couple.divorce_month),
|
||||
couples::dsl::divorce_day.eq(couple.divorce_day),
|
||||
))
|
||||
.execute(conn)
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete a couple photo
|
||||
pub async fn remove_photo(couple: &mut Couple) -> anyhow::Result<()> {
|
||||
match couple.photo_id() {
|
||||
None => {}
|
||||
Some(photo) => {
|
||||
photos_service::delete(photo).await?;
|
||||
couple.set_photo_id(None);
|
||||
update(couple).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete a couple
|
||||
pub async fn delete(couple: &mut Couple) -> anyhow::Result<()> {
|
||||
remove_photo(couple).await?;
|
||||
|
||||
// Remove the couple
|
||||
db_connection::execute(|conn| {
|
||||
diesel::delete(couples::dsl::couples.filter(couples::dsl::id.eq(couple.id().0)))
|
||||
.execute(conn)
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete all the couples of a family
|
||||
pub async fn delete_all_family(family_id: FamilyID) -> anyhow::Result<()> {
|
||||
for mut m in get_all_of_family(family_id).await? {
|
||||
delete(&mut m).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
@ -5,7 +5,7 @@ use crate::models::{
|
||||
Family, FamilyID, FamilyMembership, Membership, NewFamily, NewMembership, UserID,
|
||||
};
|
||||
use crate::schema::{families, memberships};
|
||||
use crate::services::{members_service, users_service};
|
||||
use crate::services::{couples_service, members_service, users_service};
|
||||
use crate::utils::string_utils::rand_str;
|
||||
use crate::utils::time_utils::time;
|
||||
use diesel::prelude::*;
|
||||
@ -183,7 +183,8 @@ pub async fn update_family(family: &Family) -> anyhow::Result<()> {
|
||||
|
||||
/// Delete a family
|
||||
pub async fn delete_family(family_id: FamilyID) -> anyhow::Result<()> {
|
||||
// TODO : delete couples
|
||||
// Delete all family couples
|
||||
couples_service::delete_all_family(family_id).await?;
|
||||
|
||||
// Remove all family members
|
||||
members_service::delete_all_family(family_id).await?;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::connections::db_connection;
|
||||
use crate::models::{FamilyID, Member, MemberID, NewMember};
|
||||
use crate::schema::members;
|
||||
use crate::services::photos_service;
|
||||
use crate::services::{couples_service, photos_service};
|
||||
use crate::utils::time_utils::time;
|
||||
use diesel::prelude::*;
|
||||
use diesel::RunQueryDsl;
|
||||
@ -102,7 +102,16 @@ pub async fn remove_photo(member: &mut Member) -> anyhow::Result<()> {
|
||||
|
||||
/// Delete a member
|
||||
pub async fn delete(member: &mut Member) -> anyhow::Result<()> {
|
||||
// TODO : remove associated couple
|
||||
// Remove associated couple
|
||||
for mut c in couples_service::get_all_of_member(member.id()).await? {
|
||||
// Check if only one person is attached to the couple
|
||||
// i.e. if the current member is the last person attached to the couple
|
||||
if c.wife().is_some() && c.husband().is_some() {
|
||||
continue;
|
||||
}
|
||||
|
||||
couples_service::delete(&mut c).await?;
|
||||
}
|
||||
|
||||
remove_photo(member).await?;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
//! # Backend services
|
||||
|
||||
pub mod couples_service;
|
||||
pub mod families_service;
|
||||
pub mod login_token_service;
|
||||
pub mod mail_service;
|
||||
|
Loading…
Reference in New Issue
Block a user