patchwork_csv_utils 0.1.17-x86_64-darwin → 0.1.19-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: c566f86e6b5f7dda3afb47413541a2ffa1d61c7f6223b4f1dfd7be6918b41e7c
4
+ data.tar.gz: 20efd8d7d1f58ae1331ad627efd6a6b236ed417c29d0248fb752e754cb2ea05b
5
5
  SHA512:
6
- metadata.gz: d1fa09fa1933d6071fd402c3e34000d8fdd3697a13385e65a5b67659845fddaf93a0fdf629b21fe6ac3023412279fdb5f94d3b34d80ff84bf4af46607ae596cf
7
- data.tar.gz: ce7a9d68c134d74e7296e38122976e5d0e3b10263d0e5cc538486e903ae2df479bf5e2d24a4dc765fa0b6ed0b4bb803f583d91423f4eb94e4550201cade08b0d
6
+ metadata.gz: d4363b4bcb7aa2fed0d3397484daf5e7ade8c2025890929d2ea5e932529c3ffefa135f3eeff57f47995072f3b88b4b931a21bb4a8420d66535e738f1c6b1dc4c
7
+ data.tar.gz: ac32b811b6f111fd4a295a46c57874fb4a41bcdbd796f14eed56a97c30ca2987516cb0e7452b97490768cc5eb099141ff6a3ea8a0c23950b6be331e05f25425b
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.19)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -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};
@@ -15,15 +15,17 @@ pub fn to_csv(ruby: &Ruby, xls_path: String,
15
15
  status_exclusions: RArray,
16
16
  expected_trust_name: String,
17
17
  ) -> 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()));
18
+ if !xls_path.has_extension(&["xls","xlsx"]) {
19
+ return Err(magnus::Error::new(ruby.exception_standard_error(), "xls_path must be an xls or xlsx file".to_string()));
20
20
  }
21
21
 
22
22
  let exclusions = RArray::to_vec(exclusions)?;
23
23
  let mandatory_headers: Vec<String> = RArray::to_vec(mandatory_headers)?;
24
24
  let status_exclusions = RArray::to_vec(status_exclusions)?;
25
25
 
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()))?;
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
+
27
29
  let range = workbook.worksheet_range_at(0)
28
30
  .ok_or(magnus::Error::new(ruby.exception_standard_error(), "no worksheet found in xls".to_string()))
29
31
  .and_then(|r| r.map_err(|e| magnus_err(ruby, e, "could not read worksheet range")))?;
@@ -77,10 +79,49 @@ fn write_csv<W: Write>(ruby: &Ruby, dest: &mut W, range: &Range<Data>,
77
79
 
78
80
  match *c {
79
81
  Data::Empty => Ok(()),
80
- Data::String(ref s) | Data::DateTimeIso(ref s) | Data::DurationIso(ref s) => {
82
+ Data::String(ref s) | Data::DurationIso(ref s) => {
81
83
  handle_commas(dest, s)
82
84
  }
83
85
  Data::Float(ref f) => write!(dest, "{}", f),
86
+ Data::DateTimeIso(ref s) => {
87
+ // Normalize the string to ensure manageable precision
88
+ let normalized_s = if s.contains('.') {
89
+ let parts: Vec<&str> = s.split('.').collect();
90
+ format!("{}.{}", parts[0], &parts[1][..std::cmp::min(parts[1].len(), 6)]) // Keep up to 6 fractional seconds
91
+ } else {
92
+ s.to_string()
93
+ };
94
+
95
+ // Attempt to parse the normalized string as a full datetime
96
+ let mut current = NaiveDateTime::parse_from_str(&normalized_s, "%Y-%m-%dT%H:%M:%S%.f")
97
+ .or_else(|_| {
98
+ // If parsing as datetime fails, try parsing as date-only
99
+ NaiveDateTime::parse_from_str(&format!("{}T00:00:00", normalized_s), "%Y-%m-%dT%H:%M:%S%.f")
100
+ })
101
+ .or_else(|_| {
102
+ // If parsing as time-only fails, try parsing as time-only
103
+ NaiveDateTime::parse_from_str(&format!("1970-01-01T{}", normalized_s), "%Y-%m-%dT%H:%M:%S%.f")
104
+ })
105
+ .map_err(|_| to_datetime_error(ruby, s, ri, "Date or Time"))?;
106
+
107
+ // Apply the same logic as for Data::DateTime
108
+ if i == *date {
109
+ date_value = current;
110
+ } else if i == *start || i == *end || i == *actual_start || i == *actual_end {
111
+ current = transform_time_to_datetime(date_value, current);
112
+ }
113
+
114
+ // Round up to the next second if we have any fractional seconds
115
+ let adjusted_time = if current.nanosecond() > 0 {
116
+ current + chrono::Duration::seconds(1) - chrono::Duration::nanoseconds(current.nanosecond() as i64)
117
+ } else {
118
+ current
119
+ };
120
+
121
+ // Format the output to ensure consistent precision
122
+ let formatted_output = adjusted_time.format("%Y-%m-%d %H:%M:%S").to_string();
123
+ write!(dest, "{}", formatted_output)
124
+ }
84
125
  Data::DateTime(ref d) => {
85
126
  let mut current = d.as_datetime().ok_or(to_datetime_error(ruby, &d.to_string(), ri, "Date"))?;
86
127
  if i == *date {
@@ -171,7 +212,7 @@ fn handle_commas<W: Write>(dest: &mut W, s: &str) -> std::io::Result<()> {
171
212
 
172
213
  fn clean_strings(s: &str) -> String {
173
214
  s.replace("\n", " ")
174
- .replace("\r", " ")
215
+ .replace("\r", "")
175
216
  .replace("\"", "")
176
217
  }
177
218
 
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.19'
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.19
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-12-04 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Deduplication of CSV files and XLS to CSV conversion.
14
14
  email: