From bcda74ff35a8acf74ed361f55d84dd7ba41adb1d Mon Sep 17 00:00:00 2001 From: Djkato Date: Sat, 25 Mar 2023 00:04:12 +0100 Subject: [PATCH] auto parse and match csv to fields --- src/backend/backend_manager.rs | 81 ++++++++++++++++++++++++++++++--- src/backend/database_handler.rs | 4 +- src/backend/parser.rs | 48 +++++++++++-------- src/ui/table_window.rs | 72 +++++++++++++++-------------- 4 files changed, 143 insertions(+), 62 deletions(-) diff --git a/src/backend/backend_manager.rs b/src/backend/backend_manager.rs index bed0618..fdcc62f 100644 --- a/src/backend/backend_manager.rs +++ b/src/backend/backend_manager.rs @@ -1,5 +1,5 @@ -use super::csv_handler::ImportedData; -use super::database_handler::{DBLoginData, Tables}; +use super::csv_handler::{DataEntry, ImportedData}; +use super::database_handler::{DBLoginData, TableField, Tables}; use super::parser::parse; use sqlx::MySqlConnection; use std::sync::Arc; @@ -70,22 +70,69 @@ impl BackendManger { } } } - Communication::GetTableDescription(table_index) => { + Communication::GetTableDescription(table_index, sender) => { let mut db_table_data = self.db_table_data.lock().await; + let mut csv_data = self.csv_data.lock().await; db_table_data .tables .get_mut(table_index) .unwrap() .describe_table(&mut self.db_connection.as_mut().unwrap()) .await; + if csv_data.are_headers { + match try_match_headers_to_fields( + &mut db_table_data + .tables + .get_mut(table_index) + .unwrap() + .fields + .as_mut() + .unwrap() + .as_mut(), + &csv_data.data.iter_row(0), + ) { + Ok(_) => { + for field in db_table_data + .tables + .get_mut(table_index) + .unwrap() + .fields + .as_mut() + .unwrap() + { + println!( + " > automapping field \"{}\" to col \"{:?}\"", + &field.description.field, field.mapped_to_col + ); + if let Some(col_index) = field.mapped_to_col { + for cell in csv_data.data.iter_col_mut(col_index) { + cell.curr_field_description = + Some(field.description.clone()); + parse(cell); + } + } + } + sender.send(true).unwrap_or_else(|e| { + println!( + "Server - failed to respond to getTableDescription, {}", + e + ) + }); + } + Err(_) => sender.send(false).unwrap_or_else(|e| { + println!("Server - failed to respond to getTableDescription, {}", e) + }), + } + } else { + sender.send(false).unwrap_or_else(|e| { + println!("Server - failed to respond to getTableDescription, {}", e) + }); + } } Communication::TryParseCol(col_index) => { let mut csv_data = self.csv_data.lock().await; for cell in csv_data.data.iter_col_mut(col_index) { if cell.curr_field_description.is_some() { - if let Some(desc) = &cell.curr_field_description { - println!("{} should be of type {}", cell.data, desc.field_type) - } parse(cell); } } @@ -94,6 +141,26 @@ impl BackendManger { } } } + +pub fn try_match_headers_to_fields( + db_fields: &mut Vec, + csv_headers: &std::slice::Iter<'_, DataEntry>, +) -> Result<(), ()> { + let mut has_matched_some = false; + for field in db_fields { + for (i, header) in csv_headers.clone().enumerate() { + if &field.description.field == &header.data { + field.mapped_to_col = Some(i); + has_matched_some = true; + } + } + } + match has_matched_some { + true => return Ok(()), + false => return Err(()), + } +} + pub enum Communication { ValidateCreditentials( DBLoginData, @@ -101,6 +168,6 @@ pub enum Communication { ), AreCreditentialsValidated(oneshot::Sender), LoadImportFilePath(String, oneshot::Sender), - GetTableDescription(usize), + GetTableDescription(usize, oneshot::Sender), TryParseCol(usize), } diff --git a/src/backend/database_handler.rs b/src/backend/database_handler.rs index 14681fa..7177727 100644 --- a/src/backend/database_handler.rs +++ b/src/backend/database_handler.rs @@ -8,12 +8,12 @@ pub struct Tables { pub tables: Vec, } -#[derive(Default)] +#[derive(Default, Clone)] pub struct Table { pub name: String, pub fields: Option>, } -#[derive(Default)] +#[derive(Default, Clone)] pub struct TableField { pub description: FieldDescription, pub mapped_to_col: Option, diff --git a/src/backend/parser.rs b/src/backend/parser.rs index f1b8524..52498dd 100644 --- a/src/backend/parser.rs +++ b/src/backend/parser.rs @@ -202,30 +202,38 @@ fn parse_tinyint(cell: &mut DataEntry, args: Option) { } } fn parse_date(cell: &mut DataEntry) { - //YYYY-MM-DD from ‘1000-01-01’ to ‘9999-12-31’ - match NaiveDate::parse_from_str(&cell.data, "%Y-%m-%d") { - Ok(_) => cell.is_parsed = Some(Ok(())), - Err(e) => { - cell.is_parsed = Some(Err(Arc::from(, - >>::into(format!( - "Invalid date: {}", - e - ))))) + //YYYY-MM-DD from ‘1000-01-01’ to ‘9999-12-31’,also ’0000-00-00’ + if cell.data == "0000-00-00" { + cell.is_parsed = Some(Ok(())) + } else { + match NaiveDate::parse_from_str(&cell.data, "%Y-%m-%d") { + Ok(_) => cell.is_parsed = Some(Ok(())), + Err(e) => { + cell.is_parsed = Some(Err(Arc::from(, + >>::into(format!( + "Invalid date: {}", + e + ))))) + } } } } fn parse_datetime(cell: &mut DataEntry) { - //YYYY-MM-DD HH:MM:SS - match NaiveDateTime::parse_from_str(&cell.data, "%Y-%m-%d %H:%M:%S") { - Ok(_) => cell.is_parsed = Some(Ok(())), - Err(e) => { - cell.is_parsed = Some(Err(Arc::from(, - >>::into(format!( - "Invalid date: {}", - e - ))))) + //YYYY-MM-DD HH:MM:SS, also ’0000-00-00’ + if cell.data == "0000-00-00 00:00:00" { + cell.is_parsed = Some(Ok(())) + } else { + match NaiveDateTime::parse_from_str(&cell.data, "%Y-%m-%d %H:%M:%S") { + Ok(_) => cell.is_parsed = Some(Ok(())), + Err(e) => { + cell.is_parsed = Some(Err(Arc::from(, + >>::into(format!( + "Invalid date: {}", + e + ))))) + } } } } diff --git a/src/ui/table_window.rs b/src/ui/table_window.rs index 0c3096e..f0f0fd8 100644 --- a/src/ui/table_window.rs +++ b/src/ui/table_window.rs @@ -1,7 +1,6 @@ use crate::backend::backend_manager::Communication; use crate::backend::csv_handler::ImportedData; -use crate::backend::database_handler::Tables; -use eframe::glow::CONSTANT_COLOR; +use crate::backend::database_handler::{Tables,TableField}; use egui::{ComboBox, Context, Ui}; use egui_extras::{Column, Size, StripBuilder, TableBuilder}; use std::error::Error; @@ -15,7 +14,10 @@ pub struct SpreadSheetWindow { is_file_loaded_receiver: Option>, is_file_loaded: bool, current_table: Option, - is_current_table_described: bool, + is_table_described_receiver: Option>, + is_table_described: bool, + is_table_matched_with_headers: bool, + } impl SpreadSheetWindow { pub fn default( @@ -27,10 +29,12 @@ impl SpreadSheetWindow { sender, is_file_loaded_receiver: None, is_file_loaded: false, + is_table_described_receiver: None, + is_table_described: false, + is_table_matched_with_headers: false, csv_data_handle, db_table_data_handle, current_table: None, - is_current_table_described: false, } } } @@ -122,23 +126,36 @@ impl SpreadSheetWindow { }); /* If a table is selected, try if it's fields are discovered */ - if let Some(table_i) = self.current_table { - if db_table_data.tables.get(table_i).unwrap().fields.is_some() { - self.is_current_table_described = true; - } else { + if !self.is_table_described{ + + if let Some(table_i) = self.current_table { + if db_table_data.tables.get(table_i).unwrap().fields.is_some() { + self.is_table_described = true; + } else if self.is_table_described_receiver.is_some(){ + match self.is_table_described_receiver.as_mut().unwrap().try_recv(){ + Ok(res) => {self.is_table_matched_with_headers = res}, + Err(e) => println!("Failed receiving is_table_described, {}",e), + } + } else{ + let (sender, receiver ) = oneshot::channel(); + self.is_table_described_receiver = Some(receiver); self.sender - .try_send(Communication::GetTableDescription(table_i)) - .unwrap_or_else(|e| println!("Failed asking to describe table, {}", e)); - } + .try_send(Communication::GetTableDescription(table_i, sender)) + .unwrap_or_else(|e| println!("Failed asking to describe table, {}", e)); } } - if self.is_current_table_described { + } + } + + if self.is_table_described { self.table_builder(ui); } } fn table_builder(&mut self, ui: &mut Ui) { if let Ok(csv_data) = &mut self.csv_data_handle.try_lock() { if let Ok(db_table_data) = &mut self.db_table_data_handle.try_lock() { + //ref to all fields in curr db table + let mut curr_db_table_fields: &mut Vec = db_table_data.tables.get_mut(self.current_table.unwrap()).unwrap().fields.as_mut().unwrap(); let mut table = TableBuilder::new(ui) .striped(true) .cell_layout(egui::Layout::left_to_right(egui::Align::Center)); @@ -146,7 +163,8 @@ impl SpreadSheetWindow { for _i in 0..csv_data.data.cols() { table = table.column(Column::auto().resizable(true).clip(false)); } - + /* If cols got prematched by name */ + table .column(Column::remainder()) .min_scrolled_height(0.0) @@ -162,33 +180,21 @@ impl SpreadSheetWindow { } else { combo_box = ComboBox::new(i, ""); } + //if any field is assinged to this combobox, show it's text, else "----" - if let Some(selected_field) = db_table_data - .tables - .get(self.current_table.unwrap()) - .unwrap() - .fields - .as_ref() - .unwrap() + if let Some(selected_field) = curr_db_table_fields .iter() .find(|field| field.mapped_to_col == Some(i)) { combo_box = combo_box .selected_text(selected_field.description.field.clone()); - } else { + } else{ combo_box = combo_box.selected_text("-----"); } - + /* When a Field gets attached to Col, */ combo_box.show_ui(ui, |ui| { - for field in db_table_data - .tables - .get_mut(self.current_table.unwrap()) - .unwrap() - .fields - .as_mut() - .unwrap() - .iter_mut() + for field in curr_db_table_fields.iter_mut() { if ui .selectable_value( @@ -198,7 +204,7 @@ impl SpreadSheetWindow { ) .clicked() { - self.is_current_table_described = false; + self.is_table_described = false; match self.sender.try_send(Communication::TryParseCol(i)){ Ok(_) => { for cel in csv_data.data.iter_col_mut(i) { @@ -215,9 +221,9 @@ impl SpreadSheetWindow { } }) .body(|body| { - body.rows(15., csv_data.data.rows(), |row_index, mut row| { + body.rows(15., csv_data.data.rows() -1, |row_index, mut row| { for curr_cell in - csv_data.data.iter_row_mut(row_index) + csv_data.data.iter_row_mut(row_index+1) { /* If cell is bound to a field, color it's bg according to is_parsed */ row.col(|ui| {