From 6689e6c7f910d29f6127acba1a40e302110266ea Mon Sep 17 00:00:00 2001 From: Priec Date: Fri, 4 Sep 2026 18:09:16 +0200 Subject: [PATCH] implemented FROM next to the link for other table columns --- client-gui2 | 2 +- common/src/proto/descriptor.bin | Bin 247049 -> 247550 bytes server | 2 +- web/src/pages/import_export/import/logic.rs | 88 ++++++++++++++++++-- 4 files changed, 83 insertions(+), 9 deletions(-) diff --git a/client-gui2 b/client-gui2 index 0a536189..0461f0f6 160000 --- a/client-gui2 +++ b/client-gui2 @@ -1 +1 @@ -Subproject commit 0a536189bbc017f9174cbc5d276828a84c16f2e0 +Subproject commit 0461f0f6021109bd0aa97426686b41f835a83a9d diff --git a/common/src/proto/descriptor.bin b/common/src/proto/descriptor.bin index adaa6b437bd40e291f2c71e5320633d88cf83fbd..bd7a59ab734ecaf7dcf4cc97c59876496bb0c954 100644 GIT binary patch delta 2123 zcmZ`)&u`pB7+wF^D4FI*yR=E`q~#+LrKp=wA)qAUKud{~Dos$@puh!Bc4s#x_RMN* zQ!N*Bs{p|XX>V|cD^d;>;ve7-;RFXHE=Y(s)N{_V!56a4Daz8Gkc z3Yn$E>HfpLT~bG%icYr97~PSSOQpL)i!Pmr;o4_B&IKiW(56UcLUtqBr%cg6=pMd_ zht%PNEZ2gvV?n7%xXhwXN~%YkS@fcUr$@(8N<#9ZSfn)IIyG2a4vFidDAQaIOF9ES zj1}(^Hv|)rro1m0^&$~>L4rQeDpQtsPw9kP-WIy2;#fUSX@BcIdi(V^H>jUST~Wl5 zL_O@a9cTsWhJvlNNm6B44M88|vf%)gI$Yu@=}ISxx$Xwr1@Z=bcXUO6%{y|jBq+7 z9;cWM4sB(O3zbuc#o&}^7&x?rH%52v~C1D$CcJee+NeS7lUQl|ha%g)b2+rsU zZ4N(@X(n(8ILO={R4ovwFy{FUr{&$Ao@X1(_Zx1b4@_ge9{qFCdHMc^ zdy&nurrC+_HZCn!D-Ww{FS4IpnAB~I3!&HP0ra=02d%At+H}T1N;3-AYD2Ya z9~p0O%-9*n*dwwYUM~lW$Eg}ymre3+QJc5IJu^cStO*y_-V})$yl0-QELn==G)?Q3 zWaa>N&Ml2aav;)9unge~qrBVl_8`(E<PRgm@~2t__Es9f>E1!zjt|fSwDXE>2h|T@&R{}2Tx&k8c{btA!`hoo zKpxiKYy$ES-u%vEZWBWw8pkJ2@Wh({T00(wg!ex*fgcjWkR#3!=2FD%aRfj~0V~tv zC_5%4kz-1ZL(Ak)9hhu1cg0yMC>(>CSjD{(DGBm)XB_3o2K(Y{S&MA01Uv1daH(iy zBMOJNEUAu?DMB3y#j5egR4Gi=vuhz0IzlcKpCgRHHI7LnQHFjjosvB#+xI87PKZu3 zR^B&uC`wJxP@n5AZYz7AnG4Q@ZXcBFX9!e@@KI$At#Yhj;sLY(7_A!0b|bn*Vm3|k ziRL^DnBTOC*&ls<#aU_h-C1KV^9=LRUpUXMBMjY^f6|c7b4D253Y^9Hlj#d=6K-)$ vlqI7_f%DGI#4X*0XD~)`?!2`uR;Zd>DCZF?7$bS|G;r{9PoDajhs7uLJ0 z+1Zp{Fyev$!PP?C5#m${q@1|%Um*Sl_zNn&GaIktARtl>Gk*Jd-#733-pg-RzWi$C zvtLMKk-pqG*R)5Y)B*+Sr5cMeYu|DwHx$+rRd`svRuqn;CZcbvCK_=C$kEv<^4 z?rWPZC!#P}#03^ngco)=k`Dvc!F=SZ3e9UaS=5FFEFlfR!T zED;_fBn@z0Ew&p-$j#O+LLs-hFe6zRH%EZ|)GWb!BaOa&fwT zgZ)O)UlMqCoexNOLMQH3@~24pQBP=To`6fGQuV*u9-D@D%p+Q9w*UCZC?m*ww|0?j zKpCmQO84l4$&FRAF}_z@Vy~H7qj%d|4@3UpSyv@R9G6xC$`paX1rvF3dIEZT`P%e^ zlD60C?50KHryZ{z*5{G^wCKjNgpMmJ@lEhFhY`GUVfB!j{>J0e zs!OR9&_)HNfHo>91+*~+^_JC&)2??dT%Xt4P19JS1M^rH*j=x7s?G&=ckz|!$$+k| zvNtT%3*08O@_FiPRLAGY9^STD35F@$W+m7Ixe2``84~u;u5)B;1PRyld8c$k^}Ig7 zdsWkXfcH$(hPIhNgbJ#h|6c+5KwumI1N@l1&3f{e Sm-ZJg>IDDv bool { + self.columns + .iter() + .any(|column| column.name == name && column.verification) + } + + fn writable_types(&self) -> HashMap { + self.types + .iter() + .filter(|(name, _)| !self.is_verification(name)) + .map(|(name, data_type)| (name.clone(), data_type.clone())) + .collect() + } } /// GET /admin/import @@ -625,7 +639,8 @@ async fn prepared( read_mapping(locale, &chosen, &source).map_err(|message| reject(headers, message))?; let prepared_names = destination.prepared_names(&assignments); let mut prepared = prepare(&assignments, &source, &prepared_names); - normalize_dates(locale, &mut prepared, &destination.types, form.date_format) + let writable_types = destination.writable_types(); + normalize_dates(locale, &mut prepared, &writable_types, form.date_format) .map_err(|message| reject(headers, message))?; Ok((destination, source, prepared)) } @@ -657,7 +672,8 @@ fn source_options(locale: Locale, source: &Source) -> Vec { } /// One prepared row as the staged import takes it: values under their destination -/// names, converted to the column's type. +/// names. Writable values are converted to their column type; projection +/// assertions retain the CSV cell's exact text. fn import_row( locale: Locale, destination: &Destination, @@ -666,6 +682,21 @@ fn import_row( ) -> Result { let mut data = HashMap::new(); for (index, column) in columns.iter().enumerate() { + let raw = row.get(index).map(String::as_str).unwrap_or_default(); + if destination.is_verification(column) { + let kind = if raw.is_empty() { + prost_types::value::Kind::NullValue(prost_types::NullValue::NullValue as i32) + } else { + prost_types::value::Kind::StringValue(raw.to_string()) + }; + data.insert( + column.clone(), + prost_types::Value { + kind: Some(kind), + }, + ); + continue; + } let data_type = destination.types.get(column).ok_or_else(|| { tr!( locale, @@ -673,11 +704,7 @@ fn import_row( "column" => column.clone(), ) })?; - let value = csv_value( - locale, - row.get(index).map(String::as_str).unwrap_or_default(), - data_type, - )?; + let value = csv_value(locale, raw, data_type)?; data.insert(column.clone(), value); } Ok(TableDataImportRow { data, link_display_columns: Vec::new() }) @@ -773,6 +800,9 @@ fn unavailable(headers: &HeaderMap, message: String) -> Response { #[cfg(test)] mod tests { use super::*; + use prost_types::value::Kind; + + use crate::pages::import_export::import::destination::DestinationKey; fn source() -> Source { read_source( @@ -801,4 +831,48 @@ mod tests { assert!(options[3].label.contains('4'), "{}", options[3].label); } + + #[test] + fn projection_assertions_keep_exact_csv_text() { + let destination = Destination { + table_name: "orders".to_string(), + table_revision: 1, + columns: vec![ + DestinationColumn { + key: DestinationKey::Column(1), + name: "projected_amount".to_string(), + required: false, + verification: true, + }, + DestinationColumn { + key: DestinationKey::Column(2), + name: "ordinary_number".to_string(), + required: false, + verification: false, + }, + ], + types: HashMap::from([ + ("projected_amount".to_string(), "NUMERIC".to_string()), + ("ordinary_number".to_string(), "BIGINT".to_string()), + ]), + }; + + let row = import_row( + Locale::default(), + &destination, + &["projected_amount".to_string(), "ordinary_number".to_string()], + &["10.00".to_string(), "001".to_string()], + ) + .unwrap(); + + assert_eq!( + row.data["projected_amount"].kind, + Some(Kind::StringValue("10.00".to_string())) + ); + assert_eq!( + row.data["ordinary_number"].kind, + Some(Kind::NumberValue(1.0)) + ); + assert!(!destination.writable_types().contains_key("projected_amount")); + } }