fit_kit 0.1.0 → 0.3.0

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: 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: