Can import data from FinancesManager
This commit is contained in:
		
							
								
								
									
										40
									
								
								moneymgr_backend/src/controllers/backup_controller.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								moneymgr_backend/src/controllers/backup_controller.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					use crate::controllers::HttpResult;
 | 
				
			||||||
 | 
					use crate::converters::finances_manager_converter::FinancesManagerFile;
 | 
				
			||||||
 | 
					use crate::extractors::auth_extractor::AuthExtractor;
 | 
				
			||||||
 | 
					use crate::extractors::file_extractor::FileExtractor;
 | 
				
			||||||
 | 
					use crate::models::accounts::AccountType;
 | 
				
			||||||
 | 
					use crate::services::accounts_service::UpdateAccountQuery;
 | 
				
			||||||
 | 
					use crate::services::movements_service::UpdateMovementQuery;
 | 
				
			||||||
 | 
					use crate::services::{accounts_service, movements_service};
 | 
				
			||||||
 | 
					use actix_web::HttpResponse;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Import data from a [FinancesManager](https://gitlab.com/pierre42100/cpp-financesmanager) file
 | 
				
			||||||
 | 
					pub async fn import_financesmanager(auth: AuthExtractor, file: FileExtractor) -> HttpResult {
 | 
				
			||||||
 | 
					    let file = FinancesManagerFile::parse(&String::from_utf8_lossy(&file.buff))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Create each account & push the movements independently
 | 
				
			||||||
 | 
					    for file_account in file.accounts {
 | 
				
			||||||
 | 
					        let account = accounts_service::create(
 | 
				
			||||||
 | 
					            auth.user_id(),
 | 
				
			||||||
 | 
					            &UpdateAccountQuery {
 | 
				
			||||||
 | 
					                name: file_account.name,
 | 
				
			||||||
 | 
					                r#type: AccountType::Cash,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for file_movement in file_account.movements {
 | 
				
			||||||
 | 
					            movements_service::create(&UpdateMovementQuery {
 | 
				
			||||||
 | 
					                account_id: account.id(),
 | 
				
			||||||
 | 
					                time: file_movement.time,
 | 
				
			||||||
 | 
					                label: file_movement.label,
 | 
				
			||||||
 | 
					                file_id: None,
 | 
				
			||||||
 | 
					                amount: file_movement.amount,
 | 
				
			||||||
 | 
					                checked: false,
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .await?;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(HttpResponse::Accepted().finish())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -4,6 +4,7 @@ use std::error::Error;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
pub mod accounts_controller;
 | 
					pub mod accounts_controller;
 | 
				
			||||||
pub mod auth_controller;
 | 
					pub mod auth_controller;
 | 
				
			||||||
 | 
					pub mod backup_controller;
 | 
				
			||||||
pub mod files_controller;
 | 
					pub mod files_controller;
 | 
				
			||||||
pub mod movement_controller;
 | 
					pub mod movement_controller;
 | 
				
			||||||
pub mod server_controller;
 | 
					pub mod server_controller;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								moneymgr_backend/src/converters.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								moneymgr_backend/src/converters.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					pub mod finances_manager_converter;
 | 
				
			||||||
							
								
								
									
										107
									
								
								moneymgr_backend/src/converters/finances_manager_converter.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								moneymgr_backend/src/converters/finances_manager_converter.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,107 @@
 | 
				
			|||||||
 | 
					use std::num::{ParseFloatError, ParseIntError};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(thiserror::Error, Debug)]
 | 
				
			||||||
 | 
					enum FinancesManagerDecodeError {
 | 
				
			||||||
 | 
					    #[error("Movement entry is not a three-part component! got {0} parts instead of 3!")]
 | 
				
			||||||
 | 
					    MovementParts(usize),
 | 
				
			||||||
 | 
					    #[error("Could not decode time!")]
 | 
				
			||||||
 | 
					    Time(ParseIntError),
 | 
				
			||||||
 | 
					    #[error("Could not decode amount!")]
 | 
				
			||||||
 | 
					    Amount(ParseFloatError),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(serde::Deserialize, serde::Serialize)]
 | 
				
			||||||
 | 
					pub struct FinancesManagerMovement {
 | 
				
			||||||
 | 
					    pub label: String,
 | 
				
			||||||
 | 
					    pub time: u64,
 | 
				
			||||||
 | 
					    pub amount: f32,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(serde::Deserialize, serde::Serialize)]
 | 
				
			||||||
 | 
					pub struct FinancesManagerAccount {
 | 
				
			||||||
 | 
					    pub name: String,
 | 
				
			||||||
 | 
					    pub movements: Vec<FinancesManagerMovement>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(serde::Deserialize, serde::Serialize)]
 | 
				
			||||||
 | 
					pub struct FinancesManagerFile {
 | 
				
			||||||
 | 
					    pub accounts: Vec<FinancesManagerAccount>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl FinancesManagerFile {
 | 
				
			||||||
 | 
					    /// Parse a finance manager file
 | 
				
			||||||
 | 
					    pub fn parse(file: &str) -> anyhow::Result<Self> {
 | 
				
			||||||
 | 
					        let mut res = Self { accounts: vec![] };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut curr_account = None;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for l in file.lines() {
 | 
				
			||||||
 | 
					            // Check if we reached the end of an account
 | 
				
			||||||
 | 
					            if l.trim().is_empty() {
 | 
				
			||||||
 | 
					                if let Some(a) = curr_account {
 | 
				
			||||||
 | 
					                    res.accounts.push(a);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                curr_account = None;
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if l == "==============" {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            match &mut curr_account {
 | 
				
			||||||
 | 
					                // Header
 | 
				
			||||||
 | 
					                None => {
 | 
				
			||||||
 | 
					                    curr_account = Some(FinancesManagerAccount {
 | 
				
			||||||
 | 
					                        name: l.to_string(),
 | 
				
			||||||
 | 
					                        movements: vec![],
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Account content
 | 
				
			||||||
 | 
					                Some(account) => {
 | 
				
			||||||
 | 
					                    let split = l.split(';').collect::<Vec<_>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if split.len() != 3 {
 | 
				
			||||||
 | 
					                        return Err(FinancesManagerDecodeError::MovementParts(split.len()).into());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    account.movements.push(FinancesManagerMovement {
 | 
				
			||||||
 | 
					                        label: split[1].to_string(),
 | 
				
			||||||
 | 
					                        time: split[0]
 | 
				
			||||||
 | 
					                            .parse::<u64>()
 | 
				
			||||||
 | 
					                            .map_err(FinancesManagerDecodeError::Time)?,
 | 
				
			||||||
 | 
					                        amount: split[2]
 | 
				
			||||||
 | 
					                            .parse::<f32>()
 | 
				
			||||||
 | 
					                            .map_err(FinancesManagerDecodeError::Amount)?,
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Push last account
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(res)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Encode FinancesManager file
 | 
				
			||||||
 | 
					    pub fn encode(&self) -> String {
 | 
				
			||||||
 | 
					        let mut out = String::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for account in &self.accounts {
 | 
				
			||||||
 | 
					            out.push_str(&account.name);
 | 
				
			||||||
 | 
					            out.push_str("\n==============\n");
 | 
				
			||||||
 | 
					            for movement in &account.movements {
 | 
				
			||||||
 | 
					                out.push_str(&format!(
 | 
				
			||||||
 | 
					                    "{};{};{}\n",
 | 
				
			||||||
 | 
					                    movement.time,
 | 
				
			||||||
 | 
					                    movement.label.replace(';', ","),
 | 
				
			||||||
 | 
					                    movement.amount
 | 
				
			||||||
 | 
					                ));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            out.push_str("\n\n\n");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        out
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,6 +2,7 @@ pub mod app_config;
 | 
				
			|||||||
pub mod connections;
 | 
					pub mod connections;
 | 
				
			||||||
pub mod constants;
 | 
					pub mod constants;
 | 
				
			||||||
pub mod controllers;
 | 
					pub mod controllers;
 | 
				
			||||||
 | 
					pub mod converters;
 | 
				
			||||||
pub mod extractors;
 | 
					pub mod extractors;
 | 
				
			||||||
pub mod models;
 | 
					pub mod models;
 | 
				
			||||||
pub mod routines;
 | 
					pub mod routines;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -161,6 +161,11 @@ async fn main() -> std::io::Result<()> {
 | 
				
			|||||||
                "/api/stats/balance_variation",
 | 
					                "/api/stats/balance_variation",
 | 
				
			||||||
                web::get().to(stats_controller::balance_variation),
 | 
					                web::get().to(stats_controller::balance_variation),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					            // Backup controller
 | 
				
			||||||
 | 
					            .route(
 | 
				
			||||||
 | 
					                "/api/backup/financesmanager/import",
 | 
				
			||||||
 | 
					                web::post().to(backup_controller::import_financesmanager),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            // Static assets
 | 
					            // Static assets
 | 
				
			||||||
            .route("/", web::get().to(static_controller::root_index))
 | 
					            .route("/", web::get().to(static_controller::root_index))
 | 
				
			||||||
            .route(
 | 
					            .route(
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user