postrunner 0.0.6 → 0.0.7
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/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
|
+
|