11#!/usr/bin/env python
2+ from __future__ import print_function
23
34import argparse
5+ import codecs
6+ import datetime
7+ import json
48import sys
9+ import types
510
611# Python 2 compat
712try :
@@ -13,20 +18,21 @@ except NameError:
1318import fitparse
1419
1520
16- def format_message (message , options ):
17- s = message .name
21+ def format_message (num , message , options ):
22+ s = [ "{}. {}" . format ( num , message .name )]
1823 if options .with_defs :
19- s += ' [%s]' % message .type
20- s += '\n '
24+ s . append ( ' [{}]' . format ( message .type ))
25+ s . append ( '\n ' )
2126
2227 if message .type == 'data' :
2328 for field_data in message :
24- s += ' * %s: %s' % (field_data .name , field_data .value )
29+ s . append ( ' * {}: {}' . format (field_data .name , field_data .value ) )
2530 if field_data .units :
26- s += ' [%s]' % field_data .units
27- s += '\n '
31+ s . append ( ' [{}]' . format ( field_data .units ))
32+ s . append ( '\n ' )
2833
29- return s
34+ s .append ('\n ' )
35+ return "" .join (s )
3036
3137
3238def parse_args (args = None ):
@@ -36,11 +42,12 @@ def parse_args(args=None):
3642 )
3743 parser .add_argument ('-v' , '--verbose' , action = 'count' , default = 0 )
3844 parser .add_argument (
39- '-o' , '--output' , type = argparse .FileType (mode = 'wb' ) ,
40- help = 'File to output to. ' ,
45+ '-o' , '--output' , type = argparse .FileType (mode = 'w' ), default = "-" ,
46+ help = 'File to output data into (defaults to stdout) ' ,
4147 )
4248 parser .add_argument (
43- '-t' , '--type' , choices = ('csv' , 'excel' , 'readable' ), default = 'readable' ,
49+ # TODO: csv
50+ '-t' , '--type' , choices = ('readable' , 'json' ), default = 'readable' ,
4451 help = 'File type to output. (DEFAULT: %(default)s)' ,
4552 )
4653 parser .add_argument (
@@ -56,20 +63,36 @@ def parse_args(args=None):
5663
5764 options = parser .parse_args (args )
5865
59- if (options .type != 'readable' ) and not options .output :
60- parser .error ('Please specify an output file (-o) or set --type readable' )
66+ # Work around argparse.FileType not accepting an `encoding` kwarg in
67+ # Python < 3.4 by closing and reopening the file (unless it's stdout)
68+ if options .output is not sys .stdout :
69+ options .output .close ()
70+ options .output = codecs .open (options .output .name , 'w' , encoding = 'UTF-8' )
6171
62- options .with_defs = (options .verbose >= 1 )
63- options .print_messages = (options .type == 'readable' )
64- options .print_stream = (options .output or sys .stdout )
65-
66- if not options .print_messages and (options .verbose >= 1 ):
67- options .print_messages = True
68- options .print_stream = sys .stdout
72+ options .verbose = options .verbose >= 1
73+ options .with_defs = (options .type == "readable" and options .verbose )
74+ options .as_dict = (options .type != "readable" and options .verbose )
6975
7076 return options
7177
7278
79+ class RecordJSONEncoder (json .JSONEncoder ):
80+ def default (self , obj ):
81+ if isinstance (obj , types .GeneratorType ):
82+ return list (obj )
83+ if isinstance (obj , datetime .datetime ):
84+ return obj .isoformat ()
85+ if isinstance (obj , fitparse .DataMessage ):
86+ return {
87+ "type" : obj .name ,
88+ "data" : {
89+ data .name : data .value for data in obj
90+ }
91+ }
92+ # Fall back to original to raise a TypeError
93+ return super (RecordJSONEncoder , self ).default (obj )
94+
95+
7396def main (args = None ):
7497 options = parse_args (args )
7598
@@ -78,14 +101,18 @@ def main(args=None):
78101 data_processor = fitparse .StandardUnitsDataProcessor (),
79102 check_crc = not (options .ignore_crc ),
80103 )
81- messages = fitfile .get_messages (
104+ records = fitfile .get_messages (
82105 name = options .name ,
83106 with_definitions = options .with_defs ,
107+ as_dict = options .as_dict
84108 )
85109
86- for n , message in enumerate (messages , 1 ):
87- if options .print_messages :
88- print ('{}. {}' .format (n , format_message (message , options ), file = options .print_stream ))
110+ if options .type == "json" :
111+ json .dump (records , fp = options .output , cls = RecordJSONEncoder )
112+ elif options .type == "readable" :
113+ options .output .writelines (format_message (n , record , options )
114+ for n , record in enumerate (records , 1 ))
115+
89116
90117if __name__ == '__main__' :
91118 try :
0 commit comments