postrunner 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/postrunner/ActivitiesDB.rb +86 -31
- data/lib/postrunner/Activity.rb +222 -30
- data/lib/postrunner/ActivityLink.rb +59 -0
- data/lib/postrunner/ActivityListView.rb +37 -90
- data/lib/postrunner/ActivitySummary.rb +12 -12
- data/lib/postrunner/ActivityView.rb +49 -72
- data/lib/postrunner/BackedUpFile.rb +56 -0
- data/lib/postrunner/ChartView.rb +14 -16
- data/lib/postrunner/DeviceList.rb +3 -7
- data/lib/postrunner/FlexiTable.rb +28 -1
- data/lib/postrunner/HTMLBuilder.rb +64 -16
- data/lib/postrunner/Main.rb +63 -19
- data/lib/postrunner/NavButtonRow.rb +103 -0
- data/lib/postrunner/PagingButtons.rb +77 -0
- data/lib/postrunner/PersonalRecords.rb +338 -79
- data/lib/postrunner/RecordListPageView.rb +69 -0
- data/lib/postrunner/RuntimeConfig.rb +5 -3
- data/lib/postrunner/TrackView.rb +14 -16
- data/lib/postrunner/UserProfileView.rb +3 -7
- data/lib/postrunner/View.rb +97 -0
- data/lib/postrunner/ViewBottom.rb +54 -0
- data/lib/postrunner/ViewButtons.rb +68 -0
- data/lib/postrunner/ViewFrame.rb +93 -0
- data/lib/postrunner/ViewTop.rb +80 -0
- data/lib/postrunner/version.rb +1 -1
- data/misc/icons/activities.png +0 -0
- data/misc/icons/activities.svg +1582 -0
- data/misc/icons/record-small.png +0 -0
- data/misc/icons/record.png +0 -0
- data/misc/icons/record.svg +15712 -0
- data/spec/ActivitySummary_spec.rb +3 -1
- data/spec/PostRunner_spec.rb +45 -0
- data/spec/View_spec.rb +61 -0
- data/spec/spec_helper.rb +21 -7
- metadata +19 -3
- data/lib/postrunner/ViewWidgets.rb +0 -153
@@ -0,0 +1,69 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
# encoding: UTF-8
|
3
|
+
#
|
4
|
+
# = RecordListPageView.rb -- PostRunner - Manage the data from your Garmin sport devices.
|
5
|
+
#
|
6
|
+
# Copyright (c) 2015 by Chris Schlaeger <cs@taskjuggler.org>
|
7
|
+
#
|
8
|
+
# This program is free software; you can redistribute it and/or modify
|
9
|
+
# it under the terms of version 2 of the GNU General Public License as
|
10
|
+
# published by the Free Software Foundation.
|
11
|
+
#
|
12
|
+
|
13
|
+
require 'fit4ruby'
|
14
|
+
|
15
|
+
require 'postrunner/FlexiTable'
|
16
|
+
require 'postrunner/View'
|
17
|
+
require 'postrunner/ViewFrame'
|
18
|
+
require 'postrunner/ViewButtons'
|
19
|
+
require 'postrunner/PagingButtons'
|
20
|
+
|
21
|
+
module PostRunner
|
22
|
+
|
23
|
+
# Generates an HTML page with all personal records for a particular sport
|
24
|
+
# type.
|
25
|
+
class RecordListPageView < View
|
26
|
+
|
27
|
+
include Fit4Ruby::Converters
|
28
|
+
|
29
|
+
# Create a RecordListPageView object.
|
30
|
+
# @param db [ActivityDB] Activity database
|
31
|
+
# @param records [PersonalRecords] Database with personal records
|
32
|
+
# @param page_count [Fixnum] Number of total pages
|
33
|
+
# @param page_index [Fixnum] Index of the page
|
34
|
+
def initialize(db, records, page_count, page_index)
|
35
|
+
@db = db
|
36
|
+
@unit_system = @db.cfg[:unit_system]
|
37
|
+
@records = records
|
38
|
+
|
39
|
+
views = @db.views
|
40
|
+
views.current_page = "records-0.html"
|
41
|
+
|
42
|
+
pages = PagingButtons.new((0..(page_count - 1)).map do |i|
|
43
|
+
"records-#{i}.html"
|
44
|
+
end)
|
45
|
+
pages.current_page =
|
46
|
+
"records-#{page_index}.html"
|
47
|
+
|
48
|
+
@sport_name = Activity::ActivityTypes[@records.sport]
|
49
|
+
super("#{@sport_name} Records", views, pages)
|
50
|
+
|
51
|
+
body {
|
52
|
+
frame_width = 800
|
53
|
+
|
54
|
+
@doc.div({ :class => 'main' }) {
|
55
|
+
ViewFrame.new("All-time #{@sport_name} Records",
|
56
|
+
frame_width, @records.all_time).to_html(@doc)
|
57
|
+
|
58
|
+
@records.yearly.each do |year, record|
|
59
|
+
next if record.empty?
|
60
|
+
ViewFrame.new("#{year} #{@sport_name} Records",
|
61
|
+
frame_width, record).to_html(@doc)
|
62
|
+
end
|
63
|
+
}
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
#
|
4
4
|
# = ActivitiesDB.rb -- PostRunner - Manage the data from your Garmin sport devices.
|
5
5
|
#
|
6
|
-
# Copyright (c) 2014 by Chris Schlaeger <cs@taskjuggler.org>
|
6
|
+
# Copyright (c) 2014, 2015 by Chris Schlaeger <cs@taskjuggler.org>
|
7
7
|
#
|
8
8
|
# This program is free software; you can redistribute it and/or modify
|
9
9
|
# it under the terms of version 2 of the GNU General Public License as
|
@@ -12,6 +12,7 @@
|
|
12
12
|
|
13
13
|
require 'yaml'
|
14
14
|
require 'fit4ruby'
|
15
|
+
require 'postrunner/BackedUpFile'
|
15
16
|
|
16
17
|
module PostRunner
|
17
18
|
|
@@ -25,7 +26,8 @@ module PostRunner
|
|
25
26
|
@options = {
|
26
27
|
:version => '0.0.0',
|
27
28
|
:unit_system => :metric,
|
28
|
-
:import_dir => nil
|
29
|
+
:import_dir => nil,
|
30
|
+
:html_dir => File.join(dir, 'html')
|
29
31
|
}
|
30
32
|
@config_file = File.join(dir, 'config.yml')
|
31
33
|
|
@@ -68,7 +70,7 @@ module PostRunner
|
|
68
70
|
|
69
71
|
def save_options
|
70
72
|
begin
|
71
|
-
|
73
|
+
BackedUpFile.write(@config_file, @options.to_yaml)
|
72
74
|
Log.info "Runtime config file '#{@config_file}' written"
|
73
75
|
rescue
|
74
76
|
Log.error "Cannot write config file '#{@config_file}': #{$!}"
|
data/lib/postrunner/TrackView.rb
CHANGED
@@ -12,37 +12,35 @@
|
|
12
12
|
|
13
13
|
require 'fit4ruby'
|
14
14
|
|
15
|
-
require 'postrunner/
|
15
|
+
require 'postrunner/ViewFrame'
|
16
16
|
|
17
17
|
module PostRunner
|
18
18
|
|
19
19
|
class TrackView
|
20
20
|
|
21
|
-
include ViewWidgets
|
22
|
-
|
23
21
|
def initialize(activity)
|
24
22
|
@activity = activity
|
25
23
|
@session = @activity.fit_activity.sessions[0]
|
26
24
|
@has_geo_data = @session.has_geo_data?
|
27
25
|
end
|
28
26
|
|
29
|
-
def
|
27
|
+
def to_html(doc)
|
30
28
|
return unless @has_geo_data
|
31
29
|
|
32
|
-
doc.
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
30
|
+
doc.head {
|
31
|
+
doc.unique(:trackview_style) {
|
32
|
+
doc.style(style)
|
33
|
+
doc.link({ 'rel' => 'stylesheet',
|
34
|
+
'href' => 'openlayers/theme/default/style.css',
|
35
|
+
'type' => 'text/css' })
|
36
|
+
doc.script({ 'src' => 'openlayers/OpenLayers.js' })
|
37
|
+
}
|
38
|
+
doc.script(java_script)
|
39
|
+
}
|
42
40
|
|
43
|
-
|
41
|
+
ViewFrame.new('Map', 600) {
|
44
42
|
doc.div({ 'id' => 'map', 'class' => 'trackmap' })
|
45
|
-
}
|
43
|
+
}.to_html(doc)
|
46
44
|
end
|
47
45
|
|
48
46
|
private
|
@@ -12,14 +12,12 @@
|
|
12
12
|
|
13
13
|
require 'fit4ruby'
|
14
14
|
|
15
|
-
require 'postrunner/
|
15
|
+
require 'postrunner/ViewFrame'
|
16
16
|
|
17
17
|
module PostRunner
|
18
18
|
|
19
19
|
class UserProfileView
|
20
20
|
|
21
|
-
include ViewWidgets
|
22
|
-
|
23
21
|
def initialize(fit_activity, unit_system)
|
24
22
|
@fit_activity = fit_activity
|
25
23
|
@unit_system = unit_system
|
@@ -28,9 +26,7 @@ module PostRunner
|
|
28
26
|
def to_html(doc)
|
29
27
|
return nil if @fit_activity.user_profiles.empty?
|
30
28
|
|
31
|
-
|
32
|
-
profile.to_html(doc)
|
33
|
-
}
|
29
|
+
ViewFrame.new('User Profile', 600, profile).to_html(doc)
|
34
30
|
end
|
35
31
|
|
36
32
|
def to_s
|
@@ -47,7 +43,7 @@ module PostRunner
|
|
47
43
|
unit = { :metric => 'm', :statute => 'ft' }[@unit_system]
|
48
44
|
height = profile.get_as('height', unit)
|
49
45
|
t.cell('Height:', { :width => '40%' })
|
50
|
-
t.cell("#{'%.
|
46
|
+
t.cell("#{'%.2f' % height} #{unit}", { :width => '60%' })
|
51
47
|
t.new_row
|
52
48
|
end
|
53
49
|
if profile.weight
|
@@ -0,0 +1,97 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
# encoding: UTF-8
|
3
|
+
#
|
4
|
+
# = View.rb -- PostRunner - Manage the data from your Garmin sport devices.
|
5
|
+
#
|
6
|
+
# Copyright (c) 2015 by Chris Schlaeger <cs@taskjuggler.org>
|
7
|
+
#
|
8
|
+
# This program is free software; you can redistribute it and/or modify
|
9
|
+
# it under the terms of version 2 of the GNU General Public License as
|
10
|
+
# published by the Free Software Foundation.
|
11
|
+
#
|
12
|
+
|
13
|
+
require 'postrunner/HTMLBuilder'
|
14
|
+
require 'postrunner/ViewTop'
|
15
|
+
require 'postrunner/ViewBottom'
|
16
|
+
|
17
|
+
module PostRunner
|
18
|
+
|
19
|
+
# Base class for all generated HTML pages.
|
20
|
+
class View
|
21
|
+
|
22
|
+
attr_reader :doc
|
23
|
+
|
24
|
+
# Create a new View object.
|
25
|
+
# @param title [String] The title of the HTML page
|
26
|
+
# @param views [ViewButtons] List of all cross referenced View objects
|
27
|
+
# @param pages [PagingButtons] List of all pages of this View
|
28
|
+
def initialize(title, views, pages)
|
29
|
+
@doc = HTMLBuilder.new(title)
|
30
|
+
@views = views
|
31
|
+
@pages = pages
|
32
|
+
|
33
|
+
@doc.unique(:view_style) {
|
34
|
+
style
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
# Create the body section of the HTML document.
|
39
|
+
def body
|
40
|
+
ViewTop.new(@views, @pages).to_html(@doc)
|
41
|
+
yield if block_given?
|
42
|
+
ViewBottom.new.to_html(@doc)
|
43
|
+
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
# Convert the View into an HTML document.
|
48
|
+
def to_html
|
49
|
+
@doc.to_html
|
50
|
+
end
|
51
|
+
|
52
|
+
# Write the HTML document to a file
|
53
|
+
# @param file_name [String] Name of the file to write
|
54
|
+
def write(file_name)
|
55
|
+
begin
|
56
|
+
File.write(file_name, to_html)
|
57
|
+
rescue IOError
|
58
|
+
Log.fatal "Cannot write file '#{file_name}: #{$!}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def style
|
65
|
+
@doc.head {
|
66
|
+
@doc.style(<<"EOT"
|
67
|
+
body {
|
68
|
+
font-family: verdana,arial,sans-serif;
|
69
|
+
margin: 0px;
|
70
|
+
}
|
71
|
+
.flexitable {
|
72
|
+
width: 100%;
|
73
|
+
border: 2px solid #545454;
|
74
|
+
border-collapse: collapse;
|
75
|
+
font-size:11pt;
|
76
|
+
}
|
77
|
+
.ft_head_row {
|
78
|
+
background-color: #DEDEDE
|
79
|
+
}
|
80
|
+
.ft_even_row {
|
81
|
+
background-color: #FCFCFC
|
82
|
+
}
|
83
|
+
.ft_odd_row {
|
84
|
+
background-color: #F1F1F1
|
85
|
+
}
|
86
|
+
.ft_cell {
|
87
|
+
border: 1px solid #CCCCCC;
|
88
|
+
padding: 1px 3px;
|
89
|
+
}
|
90
|
+
EOT
|
91
|
+
)
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
# encoding: UTF-8
|
3
|
+
#
|
4
|
+
# = ViewBottom.rb -- PostRunner - Manage the data from your Garmin sport devices.
|
5
|
+
#
|
6
|
+
# Copyright (c) 2015 by Chris Schlaeger <cs@taskjuggler.org>
|
7
|
+
#
|
8
|
+
# This program is free software; you can redistribute it and/or modify
|
9
|
+
# it under the terms of version 2 of the GNU General Public License as
|
10
|
+
# published by the Free Software Foundation.
|
11
|
+
#
|
12
|
+
|
13
|
+
require 'postrunner/HTMLBuilder'
|
14
|
+
require 'postrunner/version'
|
15
|
+
|
16
|
+
module PostRunner
|
17
|
+
|
18
|
+
# This class generates the footer of a HTML page.
|
19
|
+
class ViewBottom
|
20
|
+
|
21
|
+
# Generate the HTML code to that describes the foot section.
|
22
|
+
# @param doc [HTMLBuilder] Reference to the HTML document to add to.
|
23
|
+
def to_html(doc)
|
24
|
+
doc.unique(:viewbottom_style) {
|
25
|
+
doc.head { doc.style(style) }
|
26
|
+
}
|
27
|
+
doc.div({ :class => 'footer' }){
|
28
|
+
doc.hr
|
29
|
+
doc.div({ :class => 'copyright' }) {
|
30
|
+
doc.text("Generated by ")
|
31
|
+
doc.a('PostRunner',
|
32
|
+
{ :href => 'https://github.com/scrapper/postrunner' })
|
33
|
+
doc.text(" #{VERSION} on #{Time.now}")
|
34
|
+
}
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def style
|
41
|
+
<<EOT
|
42
|
+
.footer {
|
43
|
+
clear: both;
|
44
|
+
width: 100%;
|
45
|
+
height: 30px;
|
46
|
+
padding: 15px 0px;
|
47
|
+
text-align: center;
|
48
|
+
font-size: 9pt;
|
49
|
+
}
|
50
|
+
EOT
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
# encoding: UTF-8
|
3
|
+
#
|
4
|
+
# = ViewButtons.rb -- PostRunner - Manage the data from your Garmin sport devices.
|
5
|
+
#
|
6
|
+
# Copyright (c) 2015 by Chris Schlaeger <cs@taskjuggler.org>
|
7
|
+
#
|
8
|
+
# This program is free software; you can redistribute it and/or modify
|
9
|
+
# it under the terms of version 2 of the GNU General Public License as
|
10
|
+
# published by the Free Software Foundation.
|
11
|
+
#
|
12
|
+
|
13
|
+
module PostRunner
|
14
|
+
|
15
|
+
# This class generates a simple icon menue to select from a set of HTML
|
16
|
+
# pages (called views). The current page is represented as an inactive icon.
|
17
|
+
# All other icons are buttons that contain links to other pages.
|
18
|
+
class ViewButtons
|
19
|
+
|
20
|
+
# Create a ViewButtons object.
|
21
|
+
# @params views [Array of NavButtonDef] icons and URLs for all pages.
|
22
|
+
def initialize(views)
|
23
|
+
if views.empty?
|
24
|
+
raise ArgumentError.new("'views' must not be empty")
|
25
|
+
end
|
26
|
+
@views = views
|
27
|
+
self.current_page = views[0].url
|
28
|
+
end
|
29
|
+
|
30
|
+
# Get the URL of the current page
|
31
|
+
# @return [String]
|
32
|
+
def current_page
|
33
|
+
@current_view_url
|
34
|
+
end
|
35
|
+
|
36
|
+
# Set the the current page.
|
37
|
+
# @param page_url [String] URL of the current page. This must either be
|
38
|
+
# nil or a URL in the predefined set.
|
39
|
+
def current_page=(page_url)
|
40
|
+
unless page_url
|
41
|
+
@current_view_url = nil
|
42
|
+
return
|
43
|
+
end
|
44
|
+
|
45
|
+
if (current = @views.find { |v| v.url == page_url })
|
46
|
+
@current_view_url = current.url
|
47
|
+
else
|
48
|
+
raise ArgumentError.new("#{page_url} is not a URL of a known view")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Iterate over all buttons. A NavButtonDef object is passed to the block
|
53
|
+
# that contains the icon and URL for the button. If no URL is set, the
|
54
|
+
# button is inactive.
|
55
|
+
def each
|
56
|
+
@views.each do |view|
|
57
|
+
view = view.clone
|
58
|
+
if @current_view_url == view.url
|
59
|
+
view.url = nil
|
60
|
+
end
|
61
|
+
yield(view)
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
# encoding: UTF-8
|
3
|
+
#
|
4
|
+
# = ViewFrame.rb -- PostRunner - Manage the data from your Garmin sport devices.
|
5
|
+
#
|
6
|
+
# Copyright (c) 2015 by Chris Schlaeger <cs@taskjuggler.org>
|
7
|
+
#
|
8
|
+
# This program is free software; you can redistribute it and/or modify
|
9
|
+
# it under the terms of version 2 of the GNU General Public License as
|
10
|
+
# published by the Free Software Foundation.
|
11
|
+
#
|
12
|
+
|
13
|
+
module PostRunner
|
14
|
+
|
15
|
+
# Creates an HTML frame around the passed object or HTML block.
|
16
|
+
class ViewFrame
|
17
|
+
|
18
|
+
# Create a ViewFrame object.
|
19
|
+
# @param title [String] Title/heading of the framed box
|
20
|
+
# @param width [Fixnum or nil] Width of the frame. Use nil to set no
|
21
|
+
# width.
|
22
|
+
# @param content [Any object that respons to to_html] Object to frame
|
23
|
+
# @param &block [HTMLBuilder actions]
|
24
|
+
def initialize(title, width = 600, content = nil, &block)
|
25
|
+
@title = title
|
26
|
+
@content = content
|
27
|
+
@block = block
|
28
|
+
@width = width
|
29
|
+
end
|
30
|
+
|
31
|
+
# Generate the HTML code for the frame and the enclosing content.
|
32
|
+
# @param doc [HTMLBuilder] HTML document
|
33
|
+
def to_html(doc)
|
34
|
+
doc.unique(:viewframe_style) {
|
35
|
+
# Add the necessary style sheet snippets to the document head.
|
36
|
+
doc.head { doc.style(style) }
|
37
|
+
}
|
38
|
+
|
39
|
+
attr = { 'class' => 'widget_frame' }
|
40
|
+
attr['style'] = "width: #{@width}px" if @width
|
41
|
+
doc.div(attr) {
|
42
|
+
doc.div({ 'class' => 'widget_frame_title' }) {
|
43
|
+
doc.b(@title)
|
44
|
+
}
|
45
|
+
doc.div {
|
46
|
+
# The @content holds an object that must respond to to_html to
|
47
|
+
# generate the HTML code.
|
48
|
+
if @content
|
49
|
+
if @content.is_a?(Array)
|
50
|
+
@content.each { |c| c.to_html(doc) }
|
51
|
+
else
|
52
|
+
@content.to_html(doc)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
# The block generates HTML code directly
|
56
|
+
@block.yield if @block
|
57
|
+
}
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def style
|
64
|
+
<<EOT
|
65
|
+
.widget_frame {
|
66
|
+
box-sizing: border-box;
|
67
|
+
padding: 10px 15px 15px 15px;
|
68
|
+
margin: 15px auto 15px auto;
|
69
|
+
border: 1px solid #ddd;
|
70
|
+
background: #fff;
|
71
|
+
background: linear-gradient(#f6f6f6 0, #fff 50px);
|
72
|
+
background: -o-linear-gradient(#f6f6f6 0, #fff 50px);
|
73
|
+
background: -ms-linear-gradient(#f6f6f6 0, #fff 50px);
|
74
|
+
background: -moz-linear-gradient(#f6f6f6 0, #fff 50px);
|
75
|
+
background: -webkit-linear-gradient(#f6f6f6 0, #fff 50px);
|
76
|
+
box-shadow: 0 3px 10px rgba(0,0,0,0.15);
|
77
|
+
-o-box-shadow: 0 3px 10px rgba(0,0,0,0.1);
|
78
|
+
-ms-box-shadow: 0 3px 10px rgba(0,0,0,0.1);
|
79
|
+
-moz-box-shadow: 0 3px 10px rgba(0,0,0,0.1);
|
80
|
+
-webkit-box-shadow: 0 3px 10px rgba(0,0,0,0.1);
|
81
|
+
}
|
82
|
+
.widget_frame_title {
|
83
|
+
font-size:13pt;
|
84
|
+
padding-bottom: 5px;
|
85
|
+
text-align: left;
|
86
|
+
}
|
87
|
+
EOT
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|