fit_kit 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cfc4c2435b6e655fe41d4e467e373b5b282eaab6f738922d1d5a93721c8adaaa
4
- data.tar.gz: a1ab87078789a8e83fc13d9f2ee39bf4e060d35f2b290adbadd95e4def05afcf
3
+ metadata.gz: 3fb71b8237876ebc05273a7cb9c78306424ad04ba4c1505f8bc70781cf2c35c2
4
+ data.tar.gz: 5ac086bb3a7b95729c74ad69c6818495ef3111b0cbf062b3f0beca072bcd53ff
5
5
  SHA512:
6
- metadata.gz: 2c25a7c3cb74581d959c406c89fa8a1df686c7632e52c52cab58c3b3a9bd1771e90ef7b0d2c67ae386e21a12d45eed52759dd951bc14b8b2a8622877d88d095c
7
- data.tar.gz: bc93a12200a50dd2d7cdaf4e9eaf165a7308ca59e83640ac26cd4b701305cda9a9b0e7908b885a7d55d4ec1281c5dc62db0b5ffbd57705ff1f8153c4ddb1ce4e
6
+ metadata.gz: f56f45e3911e4ba9cfab9bf1acb57b53f58e06cf3abae892dc3ad5f58d48c10ba8ec54a895056e0612295b32815db838ccb7f01428d9558aeac190fd6ca3ba6a
7
+ data.tar.gz: 4544ae74e7bc2510f4bcff6826ecc08850e9c01f6bdf0c6e7f95873c2e80753989bbc471855cfd72f6293946016d682223d33e64816310fe517b38ee1b4c7d4c
data/CHANGELOG.md CHANGED
@@ -1,4 +1,10 @@
1
- ## [Unreleased]
1
+ ## [0.3.0] - 2024-10-16
2
+
3
+ - Simplify to return hash for the parsing
4
+
5
+ ## [0.2.0] - 2024-10-15
6
+
7
+ - Simplify to return hash fileds for records
2
8
 
3
9
  ## [0.1.0] - 2024-10-14
4
10
 
data/Cargo.lock CHANGED
@@ -41,7 +41,7 @@ dependencies = [
41
41
  "bitflags",
42
42
  "cexpr",
43
43
  "clang-sys",
44
- "itertools",
44
+ "itertools 0.12.1",
45
45
  "lazy_static",
46
46
  "lazycell",
47
47
  "proc-macro2",
@@ -131,6 +131,7 @@ name = "fit_kit"
131
131
  version = "0.1.0"
132
132
  dependencies = [
133
133
  "fitparser",
134
+ "itertools 0.13.0",
134
135
  "magnus",
135
136
  ]
136
137
 
@@ -183,6 +184,15 @@ dependencies = [
183
184
  "either",
184
185
  ]
185
186
 
187
+ [[package]]
188
+ name = "itertools"
189
+ version = "0.13.0"
190
+ source = "registry+https://github.com/rust-lang/crates.io-index"
191
+ checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
192
+ dependencies = [
193
+ "either",
194
+ ]
195
+
186
196
  [[package]]
187
197
  name = "js-sys"
188
198
  version = "0.3.72"
data/README.md CHANGED
@@ -17,7 +17,51 @@ If bundler is not being used to manage dependencies, install the gem by executin
17
17
  ```ruby
18
18
  test_fit_file = File.join(Dir.pwd, "example.fit")
19
19
  fit_data_records = ::FitKit.parse_fit_file(test_fit_file)
20
- # [RFitDataRecord, RFitDataRecord, RFitDataRecord ...]
20
+ # { record: [{...}, {...}], session: [{...}], lap: [..], activity: [...] }
21
+ ```
22
+
23
+ ## Performance
24
+ Here is the performance parsing __4090__ fit files on my M1 Mac Mini (16G, 8 Cores) took 6 seconds (in parallel):
25
+
26
+ ```txt
27
+ ❯ ruby app.rb
28
+ Parsing 4090 fit files...
29
+ user system total real
30
+ 0.129862 0.102642 45.192900 ( 6.121117)
31
+ ```
32
+
33
+ Code to parse a given folder (contains __4090__ fit files) in parallel:
34
+
35
+ ```ruby
36
+ require 'fit_kit'
37
+ require 'benchmark'
38
+ require 'parallel'
39
+
40
+ def parse_concurrently
41
+ puts "Parsing all fit files here"
42
+ fit_files = Dir.glob("/Users/mikeli/docs/HealthFit/*.{fit,FIT}")
43
+ puts "Parsing #{fit_files.size} fit files..."
44
+
45
+ # Determine the number of processors
46
+ num_processors = Parallel.processor_count
47
+
48
+ # Parse files concurrently
49
+ Parallel.each(fit_files, in_processes: num_processors) do |file|
50
+ begin
51
+ FitKit.parse_fit_file(file)
52
+ rescue => e
53
+ puts "Error parsing #{file}: #{e.message}"
54
+ end
55
+ end
56
+ end
57
+
58
+
59
+ Benchmark.bm do |x|
60
+ x.report do
61
+ parse_concurrently
62
+ end
63
+ end
64
+
21
65
  ```
22
66
 
23
67
  ## Contributing
@@ -11,4 +11,5 @@ crate-type = ["cdylib"]
11
11
 
12
12
  [dependencies]
13
13
  fitparser = "0.7.0"
14
+ itertools = "0.13.0"
14
15
  magnus = { version = "0.6.2" }
@@ -1,12 +1,8 @@
1
- use fitparser::{self, FitDataField, FitDataRecord, Value};
2
- use magnus::{function, method, prelude::*, Error, IntoValue, RArray, Ruby};
1
+ use fitparser::{self, FitDataRecord, Value};
2
+ use itertools::Itertools;
3
+ use magnus::{function, prelude::*, Error, IntoValue, RArray, RHash, Ruby, Symbol};
3
4
  use std::fs::File;
4
5
 
5
- ///////////////////////// RFitDataField ///////////////////////////
6
- // define a wrapper ruby class for FitDataField
7
- #[magnus::wrap(class = "RFitDataField")]
8
- struct RFitDataField(FitDataField);
9
-
10
6
  // recursive method to turn Fit value into magnus::Value
11
7
  fn value_to_rb_value(value: &Value) -> magnus::Value {
12
8
  match value {
@@ -38,52 +34,22 @@ fn value_to_rb_value(value: &Value) -> magnus::Value {
38
34
  }
39
35
  }
40
36
 
41
- impl RFitDataField {
42
- fn name(&self) -> String {
43
- self.0.name().to_string()
44
- }
45
-
46
- fn value(&self) -> magnus::Value {
47
- value_to_rb_value(self.0.value())
48
- }
49
- }
50
-
51
- ///////////////////////// RFitDataRecord ///////////////////////////
52
- #[magnus::wrap(class = "RFitDataRecord")]
53
- struct RFitDataRecord(FitDataRecord);
54
-
55
- impl RFitDataRecord {
56
- fn kind(&self) -> String {
57
- self.0.kind().to_string()
37
+ fn get_fields_hash(record: &FitDataRecord) -> RHash {
38
+ let hash = RHash::new();
39
+ for field in record.fields() {
40
+ let value = value_to_rb_value(field.value());
41
+ let pair = RHash::new();
42
+ pair.aset(Symbol::new("units"), field.units()).unwrap();
43
+ pair.aset(Symbol::new("value"), value).unwrap();
44
+ // here we add the stuff to the hash
45
+ let field_name_symbol = Symbol::new(field.name());
46
+ hash.aset(field_name_symbol, pair).unwrap();
58
47
  }
59
48
 
60
- fn fields(&self) -> RArray {
61
- let array = RArray::new();
62
- for field in self.0.fields() {
63
- array.push(RFitDataField(field.clone())).unwrap();
64
- }
65
- array
66
- }
49
+ hash
67
50
  }
68
51
 
69
- // Here we define two ruby classes
70
- // RFitDataRecord and RFitDataField
71
- fn define_ruby_classes(ruby: &Ruby) -> Result<(), magnus::Error> {
72
- let class = ruby.define_class("RFitDataField", ruby.class_object())?;
73
-
74
- // define bunch of methods
75
- class.define_method("name", method!(RFitDataField::name, 0))?;
76
- class.define_method("value", method!(RFitDataField::value, 0))?;
77
-
78
- // definie the the other one here
79
- let data_record_class = ruby.define_class("RFitDataRecord", ruby.class_object())?;
80
- data_record_class.define_method("kind", method!(RFitDataRecord::kind, 0))?;
81
- data_record_class.define_method("fields", method!(RFitDataRecord::fields, 0))?;
82
-
83
- Ok(())
84
- }
85
-
86
- fn parse_fit_file(file_path: String) -> Result<RArray, magnus::Error> {
52
+ fn parse_fit_file(file_path: String) -> Result<RHash, magnus::Error> {
87
53
  let mut fp = File::open(file_path)
88
54
  .map_err(|e| Error::new(Ruby::get().unwrap().exception_io_error(), e.to_string()))?;
89
55
  let data = fitparser::from_reader(&mut fp).map_err(|e| {
@@ -93,19 +59,30 @@ fn parse_fit_file(file_path: String) -> Result<RArray, magnus::Error> {
93
59
  )
94
60
  })?;
95
61
 
96
- // finally we have the result array of record
97
- let array = RArray::new();
98
- for record in data {
99
- array.push(RFitDataRecord(record)).unwrap();
62
+ // now let's group by the record by kind
63
+ let result_hash = RHash::new();
64
+ for (kind, kind_records) in data
65
+ .iter()
66
+ .chunk_by(|record| record.kind().to_string())
67
+ .into_iter()
68
+ {
69
+ // turn records into rarray
70
+ let array = RArray::new();
71
+ for record in kind_records {
72
+ // TODO here do not pass RFitDataRecord
73
+ // turn it into fields_hash directly
74
+ array.push(get_fields_hash(record)).unwrap();
75
+ }
76
+
77
+ result_hash.aset(Symbol::new(kind), array).unwrap();
100
78
  }
101
79
 
102
- Ok(array)
80
+ Ok(result_hash)
103
81
  }
104
82
 
105
83
  #[magnus::init]
106
84
  fn init(ruby: &Ruby) -> Result<(), Error> {
107
85
  let module = ruby.define_module("FitKit")?;
108
- let _ = define_ruby_classes(&ruby);
109
86
 
110
87
  module.define_singleton_method("parse_fit_file", function!(parse_fit_file, 1))?;
111
88
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FitKit
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fit_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - 29decibel
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-10-15 00:00:00.000000000 Z
11
+ date: 2024-10-16 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Garmin fit file parser wrapping Rust crate fitparse_rs.
14
14
  email: