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 +4 -4
- data/CHANGELOG.md +7 -1
- data/Cargo.lock +11 -1
- data/README.md +45 -1
- data/ext/fit_kit/Cargo.toml +1 -0
- data/ext/fit_kit/src/lib.rs +32 -55
- data/lib/fit_kit/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3fb71b8237876ebc05273a7cb9c78306424ad04ba4c1505f8bc70781cf2c35c2
|
4
|
+
data.tar.gz: 5ac086bb3a7b95729c74ad69c6818495ef3111b0cbf062b3f0beca072bcd53ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f56f45e3911e4ba9cfab9bf1acb57b53f58e06cf3abae892dc3ad5f58d48c10ba8ec54a895056e0612295b32815db838ccb7f01428d9558aeac190fd6ca3ba6a
|
7
|
+
data.tar.gz: 4544ae74e7bc2510f4bcff6826ecc08850e9c01f6bdf0c6e7f95873c2e80753989bbc471855cfd72f6293946016d682223d33e64816310fe517b38ee1b4c7d4c
|
data/CHANGELOG.md
CHANGED
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
|
-
# [
|
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
|
data/ext/fit_kit/Cargo.toml
CHANGED
data/ext/fit_kit/src/lib.rs
CHANGED
@@ -1,12 +1,8 @@
|
|
1
|
-
use fitparser::{self,
|
2
|
-
use
|
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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
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
|
-
|
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
|
-
|
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
|
-
//
|
97
|
-
let
|
98
|
-
for
|
99
|
-
|
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(
|
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
|
|
data/lib/fit_kit/version.rb
CHANGED
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.
|
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-
|
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:
|