patchwork_csv_utils 0.1.17-x86_64-darwin → 0.1.18-x86_64-darwin

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 439ab3bf8ec06c55a28602b5b64ece16308e0c9d568e067f7c1a541537e310c2
4
- data.tar.gz: 953bc26c08024b45fbf3ca37f4b4d3b8c583497c58f7218933fe57a9238252d5
3
+ metadata.gz: 2093c7f1cfa6dd2f47a396db761689368dc2b3db8808c701865ccdeb03bbb4e9
4
+ data.tar.gz: f569e92156d8de5bbdd2046faa8c7ebc5df13c664064b1242f0d9cc73c7b4c2a
5
5
  SHA512:
6
- metadata.gz: d1fa09fa1933d6071fd402c3e34000d8fdd3697a13385e65a5b67659845fddaf93a0fdf629b21fe6ac3023412279fdb5f94d3b34d80ff84bf4af46607ae596cf
7
- data.tar.gz: ce7a9d68c134d74e7296e38122976e5d0e3b10263d0e5cc538486e903ae2df479bf5e2d24a4dc765fa0b6ed0b4bb803f583d91423f4eb94e4550201cade08b0d
6
+ metadata.gz: 6ba6857a9248f637a833e971e6b69765ea856aa7ea62d13ffeb7d8ef161694a8b6f1b6f87d1d1b48924ffbad93c66ffa2ae234e3fd45d92c70a8f30271da4e09
7
+ data.tar.gz: 477e67ef185683b93eb3a06f06e288536bb0e5bfc4ac5688fdbca720792378a2d02d20338484ed06289feafcaf96b162a9ad96feddaf913d0d56966727ab730b
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- patchwork_csv_utils (0.1.17)
4
+ patchwork_csv_utils (0.1.18)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -9,7 +9,7 @@ pub mod utils;
9
9
  fn init() -> Result<(), magnus::Error> {
10
10
  let module = define_module("CsvUtils")?;
11
11
  module.define_singleton_method("dedup", function!(dedup, 4))?;
12
- module.define_singleton_method("to_csv", function!(to_csv, 6))?;
13
- module.define_singleton_method("transform_csv", function!(transform_csv, 6))?;
12
+ module.define_singleton_method("to_csv", function!(to_csv, 5))?;
13
+ module.define_singleton_method("transform_csv", function!(transform_csv, 5))?;
14
14
  Ok(())
15
15
  }
@@ -9,8 +9,7 @@ use crate::utils::{FileExtension, magnus_err, missing_header, to_datetime_error,
9
9
  pub fn transform_csv(ruby: &Ruby, csv_path: String,
10
10
  target_path: String, exclusions: RArray,
11
11
  mandatory_headers: RArray,
12
- status_exclusions: RArray,
13
- expected_trust_name: String,) -> magnus::error::Result<()> {
12
+ status_exclusions: RArray) -> magnus::error::Result<()> {
14
13
  if !csv_path.has_extension(&["csv"]) {
15
14
  return Err(Error::new(ruby.exception_standard_error(), "csv_path must be a csv file".to_string()));
16
15
  }
@@ -41,7 +40,6 @@ pub fn transform_csv(ruby: &Ruby, csv_path: String,
41
40
  let actual_start = header_map.get("Actual Start").ok_or(missing_header(ruby, "Actual Start"))?;
42
41
  let actual_end = header_map.get("Actual End").ok_or(missing_header(ruby, "Actual End"))?;
43
42
  let status = header_map.get("Status");
44
- let trust_name = header_map.get("Trust").ok_or(missing_header(ruby, "Trust"))?;
45
43
 
46
44
  let mandatory_records = get_mandatory_records(&ruby, &mut csv, &headers_list, &mandatory_headers)?;
47
45
 
@@ -60,8 +58,6 @@ pub fn transform_csv(ruby: &Ruby, csv_path: String,
60
58
  let column_value = record.get(*column_index).ok_or(missing_value(ruby, column))?;
61
59
  let column_value = column_value.trim_end();
62
60
 
63
- validate_trust_name(ruby, &expected_trust_name, trust_name, ri, i, &column_value.to_string())?;
64
-
65
61
  if i == *date {
66
62
  let current = string_to_datetime(column_value).ok_or(to_datetime_error(ruby, column_value, ri, "Date"))?;
67
63
  date_value = current;
@@ -65,6 +65,7 @@ fn create_header_map(headers: &Vec<String>) -> HashMap<String, usize> {
65
65
 
66
66
  pub trait FileExtension {
67
67
  fn has_extension<S: AsRef<str>>(&self, extensions: &[S]) -> bool;
68
+ fn extension(&self) -> Option<&str>;
68
69
  }
69
70
 
70
71
  impl<P: AsRef<Path>> FileExtension for P {
@@ -77,5 +78,8 @@ impl<P: AsRef<Path>> FileExtension for P {
77
78
 
78
79
  false
79
80
  }
81
+ fn extension(&self) -> Option<&str> {
82
+ self.as_ref().extension().and_then(OsStr::to_str)
83
+ }
80
84
  }
81
85
 
@@ -2,8 +2,8 @@ use std::collections::HashMap;
2
2
  use std::fs::File;
3
3
  use std::io::{BufWriter, Write};
4
4
 
5
- use calamine::{Data, open_workbook, Range, Reader, Xls};
6
- use chrono::{NaiveDateTime, Utc};
5
+ use calamine::{Data, open_workbook, Range, Reader, Xls, open_workbook_auto};
6
+ use chrono::{NaiveDateTime, Timelike, Utc};
7
7
  use magnus::{RArray, Ruby};
8
8
 
9
9
  use crate::utils::{FileExtension, magnus_err, missing_header, to_datetime_error, check_mandatory_headers, missing_value, index_of_header_in_mandatory_list};
@@ -13,17 +13,20 @@ pub fn to_csv(ruby: &Ruby, xls_path: String,
13
13
  exclusions: RArray,
14
14
  mandatory_headers: RArray,
15
15
  status_exclusions: RArray,
16
- expected_trust_name: String,
17
16
  ) -> magnus::error::Result<()> {
18
- if !xls_path.has_extension(&["xls"]) {
19
- return Err(magnus::Error::new(ruby.exception_standard_error(), "xls_path must be an xls file".to_string()));
17
+ if !xls_path.has_extension(&["xls","xlsx"]) {
18
+ return Err(magnus::Error::new(ruby.exception_standard_error(), "xls_path must be an xls or xlsx file".to_string()));
20
19
  }
21
20
 
22
21
  let exclusions = RArray::to_vec(exclusions)?;
23
22
  let mandatory_headers: Vec<String> = RArray::to_vec(mandatory_headers)?;
24
23
  let status_exclusions = RArray::to_vec(status_exclusions)?;
25
24
 
26
- let mut workbook: Xls<_> = open_workbook(xls_path.clone()).map_err(|e| magnus_err(ruby, e, format!("could not open xls: {}", xls_path).as_str()))?;
25
+
26
+ let mut workbook = open_workbook_auto(&xls_path)
27
+ .map_err(|e| magnus_err(ruby, e, format!("could not open workbook: {}", xls_path).as_str()))?;
28
+
29
+
27
30
  let range = workbook.worksheet_range_at(0)
28
31
  .ok_or(magnus::Error::new(ruby.exception_standard_error(), "no worksheet found in xls".to_string()))
29
32
  .and_then(|r| r.map_err(|e| magnus_err(ruby, e, "could not read worksheet range")))?;
@@ -38,15 +41,14 @@ pub fn to_csv(ruby: &Ruby, xls_path: String,
38
41
  let csv_out_file = File::create(target_path.clone()).map_err(|e| magnus_err(ruby, e, format!("could not create csv file: {}", target_path).as_str()))?;
39
42
  let mut dest = BufWriter::new(csv_out_file);
40
43
 
41
- write_csv(ruby, &mut dest, &range, header_map, exclusions, mandatory_headers, headers_list, status_exclusions, expected_trust_name)
44
+ write_csv(ruby, &mut dest, &range, header_map, exclusions, mandatory_headers, headers_list, status_exclusions)
42
45
  }
43
46
 
44
47
  fn write_csv<W: Write>(ruby: &Ruby, dest: &mut W, range: &Range<Data>,
45
48
  header_map: HashMap<String, usize>, exclusions: Vec<String>,
46
49
  mandatory_headers: Vec<String>,
47
50
  headers_list: Vec<String>,
48
- status_exclusions: Vec<String>,
49
- expected_trust_name: String) -> magnus::error::Result<()> {
51
+ status_exclusions: Vec<String>) -> magnus::error::Result<()> {
50
52
  let n = mandatory_headers.len() - 1;
51
53
  let request_id = header_map.get("Request Id").ok_or(missing_header(ruby, "Request Id"))?;
52
54
  let date = header_map.get("Date").ok_or(missing_header(ruby, "Date"))?;
@@ -55,7 +57,6 @@ fn write_csv<W: Write>(ruby: &Ruby, dest: &mut W, range: &Range<Data>,
55
57
  let actual_start = header_map.get("Actual Start").ok_or(missing_header(ruby, "Actual Start"))?;
56
58
  let actual_end = header_map.get("Actual End").ok_or(missing_header(ruby, "Actual End"))?;
57
59
  let status = header_map.get("Status");
58
- let trust_name = header_map.get("Trust").ok_or(missing_header(ruby, "Trust"))?;
59
60
 
60
61
  let mandatory_rows = get_mandatory_records(ruby, range, &headers_list, &mandatory_headers)?;
61
62
 
@@ -69,7 +70,7 @@ fn write_csv<W: Write>(ruby: &Ruby, dest: &mut W, range: &Range<Data>,
69
70
  if date_value_is_not_present(&date, &r) {
70
71
  return Err(magnus::Error::new(ruby.exception_standard_error(), format!("Date value is not present in row: {}", ri)));
71
72
  }
72
- validate_trust_name(ruby, &expected_trust_name, trust_name, ri, &r)?;
73
+ // validate_trust_name(ruby, &expected_trust_name, trust_name, ri, &r)?;
73
74
 
74
75
  for (i, c) in mandatory_headers.iter().enumerate() {
75
76
  let column_index = header_map.get(c).ok_or(missing_header(ruby, c))?;
@@ -77,10 +78,49 @@ fn write_csv<W: Write>(ruby: &Ruby, dest: &mut W, range: &Range<Data>,
77
78
 
78
79
  match *c {
79
80
  Data::Empty => Ok(()),
80
- Data::String(ref s) | Data::DateTimeIso(ref s) | Data::DurationIso(ref s) => {
81
+ Data::String(ref s) | Data::DurationIso(ref s) => {
81
82
  handle_commas(dest, s)
82
83
  }
83
84
  Data::Float(ref f) => write!(dest, "{}", f),
85
+ Data::DateTimeIso(ref s) => {
86
+ // Normalize the string to ensure manageable precision
87
+ let normalized_s = if s.contains('.') {
88
+ let parts: Vec<&str> = s.split('.').collect();
89
+ format!("{}.{}", parts[0], &parts[1][..std::cmp::min(parts[1].len(), 6)]) // Keep up to 6 fractional seconds
90
+ } else {
91
+ s.to_string()
92
+ };
93
+
94
+ // Attempt to parse the normalized string as a full datetime
95
+ let mut current = NaiveDateTime::parse_from_str(&normalized_s, "%Y-%m-%dT%H:%M:%S%.f")
96
+ .or_else(|_| {
97
+ // If parsing as datetime fails, try parsing as date-only
98
+ NaiveDateTime::parse_from_str(&format!("{}T00:00:00", normalized_s), "%Y-%m-%dT%H:%M:%S%.f")
99
+ })
100
+ .or_else(|_| {
101
+ // If parsing as time-only fails, try parsing as time-only
102
+ NaiveDateTime::parse_from_str(&format!("1970-01-01T{}", normalized_s), "%Y-%m-%dT%H:%M:%S%.f")
103
+ })
104
+ .map_err(|_| to_datetime_error(ruby, s, ri, "Date or Time"))?;
105
+
106
+ // Apply the same logic as for Data::DateTime
107
+ if i == *date {
108
+ date_value = current;
109
+ } else if i == *start || i == *end || i == *actual_start || i == *actual_end {
110
+ current = transform_time_to_datetime(date_value, current);
111
+ }
112
+
113
+ // Round up to the next second if we have any fractional seconds
114
+ let adjusted_time = if current.nanosecond() > 0 {
115
+ current + chrono::Duration::seconds(1) - chrono::Duration::nanoseconds(current.nanosecond() as i64)
116
+ } else {
117
+ current
118
+ };
119
+
120
+ // Format the output to ensure consistent precision
121
+ let formatted_output = adjusted_time.format("%Y-%m-%d %H:%M:%S").to_string();
122
+ write!(dest, "{}", formatted_output)
123
+ }
84
124
  Data::DateTime(ref d) => {
85
125
  let mut current = d.as_datetime().ok_or(to_datetime_error(ruby, &d.to_string(), ri, "Date"))?;
86
126
  if i == *date {
@@ -171,7 +211,7 @@ fn handle_commas<W: Write>(dest: &mut W, s: &str) -> std::io::Result<()> {
171
211
 
172
212
  fn clean_strings(s: &str) -> String {
173
213
  s.replace("\n", " ")
174
- .replace("\r", " ")
214
+ .replace("\r", "")
175
215
  .replace("\"", "")
176
216
  }
177
217
 
Binary file
Binary file
Binary file
Binary file
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CsvUtils
4
- VERSION = '0.1.17'
4
+ VERSION = '0.1.18'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: patchwork_csv_utils
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.17
4
+ version: 0.1.18
5
5
  platform: x86_64-darwin
6
6
  authors:
7
7
  - kingsley.hendrickse
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-11-26 00:00:00.000000000 Z
11
+ date: 2024-11-28 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Deduplication of CSV files and XLS to CSV conversion.
14
14
  email: