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