hubba-reports 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +4 -0
- data/Manifest.txt +27 -0
- data/README.md +177 -0
- data/Rakefile +30 -0
- data/lib/hubba/reports.rb +42 -0
- data/lib/hubba/reports/folio.rb +60 -0
- data/lib/hubba/reports/reports/base.rb +36 -0
- data/lib/hubba/reports/reports/catalog.rb +44 -0
- data/lib/hubba/reports/reports/size.rb +38 -0
- data/lib/hubba/reports/reports/stars.rb +37 -0
- data/lib/hubba/reports/reports/summary.rb +39 -0
- data/lib/hubba/reports/reports/timeline.rb +53 -0
- data/lib/hubba/reports/reports/topics.rb +48 -0
- data/lib/hubba/reports/reports/traffic.rb +112 -0
- data/lib/hubba/reports/reports/traffic_pages.rb +133 -0
- data/lib/hubba/reports/reports/traffic_referrers.rb +115 -0
- data/lib/hubba/reports/reports/trending.rb +51 -0
- data/lib/hubba/reports/reports/updates.rb +60 -0
- data/lib/hubba/reports/stats.rb +230 -0
- data/lib/hubba/reports/version.rb +18 -0
- data/test/helper.rb +11 -0
- data/test/stats/j/jekyll~minima.json +25 -0
- data/test/stats/o/openblockchains~awesome-blockchains.json +27 -0
- data/test/stats/o/opendatajson~factbook.json.json +39 -0
- data/test/stats/p/poole~hyde.json +21 -0
- data/test/test_stats.rb +123 -0
- data/test/test_stats_tmp.rb +44 -0
- metadata +125 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
module Hubba
|
2
|
+
|
3
|
+
|
4
|
+
class ReportStars < Report
|
5
|
+
|
6
|
+
def build
|
7
|
+
|
8
|
+
## add stars, last_updates, etc.
|
9
|
+
## org description etc??
|
10
|
+
|
11
|
+
## note: orgs is orgs+users e.g. geraldb, yorobot etc
|
12
|
+
buf = String.new('')
|
13
|
+
buf << "# Stars"
|
14
|
+
buf << " - #{@stats.repos.size} Repos @ #{@stats.orgs.size} Orgs"
|
15
|
+
buf << "\n\n"
|
16
|
+
|
17
|
+
|
18
|
+
repos = @stats.repos.sort do |l,r|
|
19
|
+
## note: use reverse sort (right,left) - e.g. most stars first
|
20
|
+
r.stats.stars <=> l.stats.stars
|
21
|
+
end
|
22
|
+
|
23
|
+
## pp repos
|
24
|
+
|
25
|
+
repos.each_with_index do |repo,i|
|
26
|
+
buf << "#{i+1}. ★#{repo.stats.stars} **#{repo.full_name}** (#{repo.stats.size} kb)\n"
|
27
|
+
end
|
28
|
+
buf << "<!-- break -->\n" ## markdown hack: add a list end marker
|
29
|
+
buf << "\n\n"
|
30
|
+
|
31
|
+
|
32
|
+
buf
|
33
|
+
end # method build
|
34
|
+
end # class ReportStars
|
35
|
+
|
36
|
+
|
37
|
+
end # module Hubba
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Hubba
|
2
|
+
|
3
|
+
|
4
|
+
class ReportSummary < Report
|
5
|
+
|
6
|
+
def build
|
7
|
+
## create a (summary report)
|
8
|
+
##
|
9
|
+
## add stars, last_updates, etc.
|
10
|
+
## org description etc??
|
11
|
+
|
12
|
+
## note: orgs is orgs+users e.g. geraldb, yorobot etc
|
13
|
+
buf = String.new('')
|
14
|
+
buf << "# Summary"
|
15
|
+
buf << " - #{@stats.repos.size} Repos @ #{@stats.orgs.size} Orgs"
|
16
|
+
buf << "\n\n"
|
17
|
+
|
18
|
+
|
19
|
+
@stats.orgs.each do |org|
|
20
|
+
name = org[0]
|
21
|
+
repos = org[1]
|
22
|
+
buf << "### #{name} _(#{repos.size})_\n"
|
23
|
+
buf << "\n"
|
24
|
+
|
25
|
+
### add stats for repos
|
26
|
+
entries = []
|
27
|
+
repos.each do |repo|
|
28
|
+
entries << "**#{repo.name}** ★#{repo.stats.stars} (#{repo.stats.size} kb)"
|
29
|
+
end
|
30
|
+
|
31
|
+
buf << entries.join( ' · ' ) ## use interpunct? - was: • (bullet)
|
32
|
+
buf << "\n\n"
|
33
|
+
end
|
34
|
+
|
35
|
+
buf
|
36
|
+
end # method build
|
37
|
+
end # class ReportSummary
|
38
|
+
|
39
|
+
end # module Hubba
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Hubba
|
2
|
+
|
3
|
+
|
4
|
+
class ReportTimeline < Report
|
5
|
+
|
6
|
+
def build
|
7
|
+
## create a (timeline report)
|
8
|
+
|
9
|
+
## note: orgs is orgs+users e.g. geraldb, yorobot etc
|
10
|
+
buf = String.new('')
|
11
|
+
buf << "# Timeline"
|
12
|
+
buf << " - #{@stats.repos.size} Repos @ #{@stats.orgs.size} Orgs"
|
13
|
+
buf << "\n\n"
|
14
|
+
|
15
|
+
|
16
|
+
repos = @stats.repos.sort do |l,r|
|
17
|
+
## note: use reverse sort (right,left) - e.g. most stars first
|
18
|
+
## r[:stars] <=> l[:stars]
|
19
|
+
|
20
|
+
## sort by created_at (use julian days)
|
21
|
+
r.stats.created.jd <=> l.stats.created.jd
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
## pp repos
|
26
|
+
|
27
|
+
|
28
|
+
last_year = -1
|
29
|
+
last_month = -1
|
30
|
+
|
31
|
+
repos.each_with_index do |repo,i|
|
32
|
+
year = repo.stats.created.year
|
33
|
+
month = repo.stats.created.month
|
34
|
+
|
35
|
+
if last_year != year
|
36
|
+
buf << "\n## #{year}\n\n"
|
37
|
+
end
|
38
|
+
|
39
|
+
if last_month != month
|
40
|
+
buf << "\n### #{month}\n\n"
|
41
|
+
end
|
42
|
+
|
43
|
+
last_year = year
|
44
|
+
last_month = month
|
45
|
+
|
46
|
+
buf << "- #{repo.stats.created_at.strftime('%Y-%m-%d')} ★#{repo.stats.stars} **#{repo.full_name}** (#{repo.stats.size} kb)\n"
|
47
|
+
end
|
48
|
+
|
49
|
+
buf
|
50
|
+
end # method build
|
51
|
+
end # class ReportTimeline
|
52
|
+
|
53
|
+
end # module Hubba
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Hubba
|
2
|
+
|
3
|
+
|
4
|
+
class ReportTopics < Report
|
5
|
+
|
6
|
+
def build
|
7
|
+
|
8
|
+
## note: orgs is orgs+users e.g. geraldb, yorobot etc
|
9
|
+
buf = String.new('')
|
10
|
+
buf << "# Topics"
|
11
|
+
buf << " - #{@stats.repos.size} Repos @ #{@stats.orgs.size} Orgs"
|
12
|
+
buf << "\n\n"
|
13
|
+
|
14
|
+
|
15
|
+
topics = {} ## collect all topics with refs to repos
|
16
|
+
|
17
|
+
@stats.repos.each do |repo|
|
18
|
+
repo.stats.topics.each do |topic|
|
19
|
+
repos = topics[ topic ] ||= []
|
20
|
+
repos << repo
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
topics = topics.sort {|(ltopic,_),(rtopic,_)|
|
25
|
+
ltopic <=> rtopic ## sort topic by a-z
|
26
|
+
}
|
27
|
+
.to_h # convert back to hash (from array)
|
28
|
+
|
29
|
+
|
30
|
+
topics.each do |topic,repos|
|
31
|
+
buf << "`#{topic}` _(#{repos.size})_\n"
|
32
|
+
end
|
33
|
+
buf << "\n"
|
34
|
+
|
35
|
+
|
36
|
+
topics.each do |topic,repos|
|
37
|
+
buf << "## `#{topic}` _(#{repos.size})_\n"
|
38
|
+
|
39
|
+
buf << repos.map {|repo| repo.full_name }.join( ' · ' ) ## use interpunct? - was: • (bullet)
|
40
|
+
buf << "\n\n"
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
buf
|
45
|
+
end # method build
|
46
|
+
end # class ReportTopics
|
47
|
+
|
48
|
+
end # module Hubba
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module Hubba
|
2
|
+
|
3
|
+
|
4
|
+
class ReportTraffic < Report
|
5
|
+
|
6
|
+
def build
|
7
|
+
|
8
|
+
|
9
|
+
## note: orgs is orgs+users e.g. geraldb, yorobot etc
|
10
|
+
buf = String.new('')
|
11
|
+
buf << "# Traffic"
|
12
|
+
buf << " - #{@stats.repos.size} Repos @ #{@stats.orgs.size} Orgs"
|
13
|
+
buf << "\n\n"
|
14
|
+
|
15
|
+
buf << "traffic over the last 14 days - page views / unique, clones / unique\n"
|
16
|
+
buf << "\n"
|
17
|
+
|
18
|
+
|
19
|
+
### step 1: filter all repos w/o traffic summary
|
20
|
+
repos = @stats.repos.select do |repo|
|
21
|
+
traffic = repo.stats.traffic || {}
|
22
|
+
summary = traffic['summary'] || {}
|
23
|
+
|
24
|
+
res = summary['views'] && summary['clones'] ## return true if present
|
25
|
+
puts " no traffic/summary/{views,clones} - skipping >#{repo.full_name}<..." unless res
|
26
|
+
res
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
repos = repos.sort do |l,r|
|
31
|
+
lsummary = l.stats.traffic['summary']
|
32
|
+
rsummary = r.stats.traffic['summary']
|
33
|
+
|
34
|
+
## note: use reverse sort (right,left) - e.g. most page views first
|
35
|
+
res = rsummary['views']['count'] <=> lsummary['views']['count']
|
36
|
+
res = rsummary['views']['uniques'] <=> lsummary['views']['uniques'] if res == 0
|
37
|
+
res = rsummary['clones']['count'] <=> lsummary['clones']['count'] if res == 0
|
38
|
+
res = rsummary['clones']['uniques'] <=> lsummary['clones']['uniques'] if res == 0
|
39
|
+
res = l.full_name <=> r.full_name if res == 0
|
40
|
+
res
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
repos_by_org = repos.group_by { |repo|
|
45
|
+
# csvreader/csvreader" =>
|
46
|
+
# csvreader
|
47
|
+
repo.owner # user username / org | login
|
48
|
+
}
|
49
|
+
.sort { |(lowner,lrepos), (rowner,rrepos)|
|
50
|
+
lviews = lrepos.reduce(0) {|sum,repo| sum+repo.stats.traffic['summary']['views']['count'] }
|
51
|
+
rviews = rrepos.reduce(0) {|sum,repo| sum+repo.stats.traffic['summary']['views']['count'] }
|
52
|
+
lclones = lrepos.reduce(0) {|sum,repo| sum+repo.stats.traffic['summary']['clones']['count'] }
|
53
|
+
rclones = rrepos.reduce(0) {|sum,repo| sum+repo.stats.traffic['summary']['clones']['count'] }
|
54
|
+
res = rviews <=> lviews
|
55
|
+
res = rclones <=> lclones if res == 0
|
56
|
+
res = lrepos.size <=> rrepos.size if res == 0
|
57
|
+
res = lowner <=> rowner if res == 0
|
58
|
+
res
|
59
|
+
}
|
60
|
+
.to_h ## convert back to hash
|
61
|
+
|
62
|
+
|
63
|
+
repos_by_org.each_with_index do |(owner, repos),i|
|
64
|
+
views = repos.reduce(0) {|sum,repo| sum+repo.stats.traffic['summary']['views']['count'] }
|
65
|
+
clones = repos.reduce(0) {|sum,repo| sum+repo.stats.traffic['summary']['clones']['count'] }
|
66
|
+
|
67
|
+
buf << "#{i+1}. **#{owner}** views: #{views}, clones: #{clones} _(#{repos.size})_"
|
68
|
+
buf << "\n"
|
69
|
+
|
70
|
+
### todo - sort by count / uniques !!
|
71
|
+
repos.each do |repo|
|
72
|
+
|
73
|
+
summary = repo.stats.traffic['summary']
|
74
|
+
|
75
|
+
## note: sublist indent four (4) spaces
|
76
|
+
buf << " - #{repo.name} -- "
|
77
|
+
buf << " views: #{summary['views']['count']} / #{summary['views']['uniques']} - "
|
78
|
+
buf << " clones: #{summary['clones']['count']} / #{summary['clones']['uniques']}"
|
79
|
+
buf << "\n"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
buf << "<!-- break -->\n" ## markdown hack: add a list end marker
|
84
|
+
buf << "\n\n"
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
### all page paths
|
89
|
+
buf << "All repos:"
|
90
|
+
buf << "\n\n"
|
91
|
+
|
92
|
+
|
93
|
+
## pp repos
|
94
|
+
|
95
|
+
repos.each_with_index do |repo,i|
|
96
|
+
summary = repo.stats.traffic['summary']
|
97
|
+
|
98
|
+
buf << "#{i+1}. **#{repo.full_name}** -- "
|
99
|
+
buf << " views: #{summary['views']['count']} / #{summary['views']['uniques']} - "
|
100
|
+
buf << " clones: #{summary['clones']['count']} / #{summary['clones']['uniques']}"
|
101
|
+
buf << "\n"
|
102
|
+
end
|
103
|
+
buf << "<!-- break -->\n" ## markdown hack: add a list end marker
|
104
|
+
buf << "\n\n"
|
105
|
+
|
106
|
+
|
107
|
+
buf
|
108
|
+
end # method build
|
109
|
+
end # class ReportTraffic
|
110
|
+
|
111
|
+
|
112
|
+
end # module Hubba
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module Hubba
|
2
|
+
|
3
|
+
|
4
|
+
class ReportTrafficPages < Report ## todo/check: rename to TrafficPaths - why? why not?
|
5
|
+
|
6
|
+
def build
|
7
|
+
|
8
|
+
## note: orgs is orgs+users e.g. geraldb, yorobot etc
|
9
|
+
buf = String.new('')
|
10
|
+
buf << "# Traffic Pages"
|
11
|
+
buf << " - #{@stats.repos.size} Repos @ #{@stats.orgs.size} Orgs"
|
12
|
+
buf << "\n\n"
|
13
|
+
|
14
|
+
buf << "popular pages over the last 14 days - page views / unique\n"
|
15
|
+
buf << "\n"
|
16
|
+
|
17
|
+
|
18
|
+
### step 1: filter all repos w/o traffic summary
|
19
|
+
repos = @stats.repos.select do |repo|
|
20
|
+
traffic = repo.stats.traffic || {}
|
21
|
+
summary = traffic['summary'] || {}
|
22
|
+
|
23
|
+
paths = summary['paths']
|
24
|
+
res = paths && paths.size > 0 ## return true if present and non-empty array too
|
25
|
+
puts " no traffic/summary/paths - skipping >#{repo.full_name}<..." unless res
|
26
|
+
res
|
27
|
+
end
|
28
|
+
|
29
|
+
## collect all paths entries
|
30
|
+
lines = []
|
31
|
+
repos.each do |repo|
|
32
|
+
summary = repo.stats.traffic['summary']
|
33
|
+
# e.g.
|
34
|
+
# "paths": [
|
35
|
+
# {
|
36
|
+
# "path": "/csvreader/csvreader",
|
37
|
+
# "title": "GitHub - csvreader/csvreader: csvreader library / gem - read tabular data in ...",
|
38
|
+
# "count": 33,
|
39
|
+
# "uniques": 25
|
40
|
+
# },
|
41
|
+
|
42
|
+
paths = summary['paths']
|
43
|
+
if paths
|
44
|
+
### clean (normalize) paths
|
45
|
+
paths.each do |line|
|
46
|
+
# "/csvreader/csvreader" =>
|
47
|
+
# csvreader/csvreader
|
48
|
+
path = line['path'][1..-1] ## cut of leading slash (/)
|
49
|
+
|
50
|
+
## /blob/master, /tree/master, /master
|
51
|
+
path = path.sub( %r{/blob/(master|gh-pages)(?=/)}, '' )
|
52
|
+
path = path.sub( %r{/tree/(master|gh-pages)(?=/)}, '' )
|
53
|
+
path = path.sub( %r{/(master|gh-pages)(?=/|$)}, '' ) ## ending in master (e.g. /search/master)
|
54
|
+
|
55
|
+
line['path'] = path
|
56
|
+
end
|
57
|
+
lines += paths
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
## sort by 1) count
|
63
|
+
## 2) uniques
|
64
|
+
## 3) a-z path
|
65
|
+
lines = lines.sort do |l,r|
|
66
|
+
res = r['count'] <=> l['count']
|
67
|
+
res = r['uniques'] <=> l['uniques'] if res == 0
|
68
|
+
res = l['path'] <=> r['path'] if res == 0
|
69
|
+
res
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
lines_by_path = lines.group_by { |line|
|
74
|
+
parts = line['path'].split( '/' )
|
75
|
+
parts[0]
|
76
|
+
}
|
77
|
+
.sort { |(lpath,llines), (rpath,rlines)|
|
78
|
+
lcount = llines.reduce(0) {|sum,line| sum+line['count'] }
|
79
|
+
rcount = rlines.reduce(0) {|sum,line| sum+line['count'] }
|
80
|
+
res = rcount <=> lcount
|
81
|
+
res = llines.size <=> rlines.size if res == 0
|
82
|
+
res = lpath <=> rpath if res == 0
|
83
|
+
res
|
84
|
+
}
|
85
|
+
.to_h ## convert back to hash
|
86
|
+
|
87
|
+
|
88
|
+
lines_by_path.each_with_index do |(path, lines),i|
|
89
|
+
count = lines.reduce(0) {|sum,line| sum+line['count']}
|
90
|
+
uniques = lines.reduce(0) {|sum,line| sum+line['uniques']}
|
91
|
+
buf << "#{i+1}. **#{path}** #{count} / #{uniques} _(#{lines.size})_"
|
92
|
+
buf << "\n"
|
93
|
+
|
94
|
+
### todo - sort by count / uniques !!
|
95
|
+
lines.each do |line|
|
96
|
+
## e.g. convert
|
97
|
+
## openfootball/football.json/tree/master/2020 =>
|
98
|
+
## football.json/tree/master/2020
|
99
|
+
parts = line['path'].split( '/' )
|
100
|
+
path = parts[1..-1].join( '/' )
|
101
|
+
|
102
|
+
## note: sublist indent four (4) spaces
|
103
|
+
buf << " - #{line['count']} / #{line['uniques']} -- #{path}"
|
104
|
+
buf << "\n"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
buf << "<!-- break -->\n" ## markdown hack: add a list end marker
|
109
|
+
buf << "\n\n"
|
110
|
+
|
111
|
+
|
112
|
+
### all page paths
|
113
|
+
buf << "All pages:"
|
114
|
+
buf << "\n\n"
|
115
|
+
|
116
|
+
lines.each_with_index do |line,i|
|
117
|
+
buf << "#{i+1}. #{line['count']} / #{line['uniques']} -- #{line['path']}"
|
118
|
+
buf << "\n"
|
119
|
+
end
|
120
|
+
buf << "<!-- break -->\n" ## markdown hack: add a list end marker
|
121
|
+
buf << "\n\n"
|
122
|
+
|
123
|
+
|
124
|
+
buf
|
125
|
+
end # method build
|
126
|
+
|
127
|
+
|
128
|
+
|
129
|
+
|
130
|
+
end # class ReportTrafficPages
|
131
|
+
|
132
|
+
|
133
|
+
end # module Hubba
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module Hubba
|
2
|
+
|
3
|
+
|
4
|
+
class ReportTrafficReferrers < Report
|
5
|
+
|
6
|
+
def build
|
7
|
+
|
8
|
+
## note: orgs is orgs+users e.g. geraldb, yorobot etc
|
9
|
+
buf = String.new('')
|
10
|
+
buf << "# Traffic Referrers"
|
11
|
+
buf << " - #{@stats.repos.size} Repos @ #{@stats.orgs.size} Orgs"
|
12
|
+
buf << "\n\n"
|
13
|
+
|
14
|
+
|
15
|
+
buf << "popular referrer sources over the last 14 days - page views / unique\n"
|
16
|
+
buf << "\n"
|
17
|
+
|
18
|
+
|
19
|
+
### step 1: filter all repos w/o traffic summary
|
20
|
+
repos = @stats.repos.select do |repo|
|
21
|
+
traffic = repo.stats.traffic || {}
|
22
|
+
summary = traffic['summary'] || {}
|
23
|
+
|
24
|
+
referrers = summary['referrers']
|
25
|
+
res = referrers && referrers.size > 0 ## return true if present and non-empty array too
|
26
|
+
puts " no traffic/summary/referrers - skipping >#{repo.full_name}<..." unless res
|
27
|
+
res
|
28
|
+
end
|
29
|
+
|
30
|
+
## collect all referrers entries
|
31
|
+
lines = []
|
32
|
+
repos.each do |repo|
|
33
|
+
summary = repo.stats.traffic['summary']
|
34
|
+
# e.g.
|
35
|
+
# "referrers" =>
|
36
|
+
# [{"referrer"=>"github.com", "count"=>327, "uniques"=>198},
|
37
|
+
# {"referrer"=>"openfootball.github.io", "count"=>71, "uniques"=>54},
|
38
|
+
# {"referrer"=>"Google", "count"=>5, "uniques"=>5},
|
39
|
+
# {"referrer"=>"reddit.com", "count"=>4, "uniques"=>4}]
|
40
|
+
|
41
|
+
|
42
|
+
referrers = summary['referrers']
|
43
|
+
if referrers
|
44
|
+
lines += referrers.map do |referrer|
|
45
|
+
# note: return a new copy with (repo) path added
|
46
|
+
referrer.merge( 'path' => repo.full_name )
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
## sort by 1) count
|
54
|
+
## 2) uniques
|
55
|
+
## 3) a-z referrer
|
56
|
+
## 4) a-z path
|
57
|
+
lines = lines.sort do |l,r|
|
58
|
+
res = r['count'] <=> l['count']
|
59
|
+
res = r['uniques'] <=> l['uniques'] if res == 0
|
60
|
+
res = l['referrer'] <=> r['referrer'] if res == 0
|
61
|
+
res = l['path'] <=> r['path'] if res == 0
|
62
|
+
res
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
lines_by_referrer = lines.group_by { |line| line['referrer'] }
|
67
|
+
.sort { |(lreferrer,llines),
|
68
|
+
(rreferrer,rlines)|
|
69
|
+
lcount = llines.reduce(0) {|sum,line| sum+line['count'] }
|
70
|
+
rcount = rlines.reduce(0) {|sum,line| sum+line['count'] }
|
71
|
+
res = rcount <=> lcount
|
72
|
+
res = llines.size <=> rlines.size if res == 0
|
73
|
+
res = lreferrer <=> rreferrer if res == 0
|
74
|
+
res
|
75
|
+
}
|
76
|
+
.to_h ## convert back to hash
|
77
|
+
|
78
|
+
|
79
|
+
lines_by_referrer.each_with_index do |(referrer, lines),i|
|
80
|
+
count = lines.reduce(0) {|sum,line| sum+line['count']}
|
81
|
+
uniques = lines.reduce(0) {|sum,line| sum+line['uniques']}
|
82
|
+
buf << "#{i+1}. **#{referrer}** #{count} / #{uniques} _(#{lines.size})_"
|
83
|
+
buf << "\n"
|
84
|
+
|
85
|
+
### todo - sort by count / uniques !!
|
86
|
+
lines.each do |line|
|
87
|
+
## note: sublist indent four (4) spaces
|
88
|
+
buf << " - #{line['count']} / #{line['uniques']} -- #{line['path']}"
|
89
|
+
buf << "\n"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
buf << "<!-- break -->\n" ## markdown hack: add a list end marker
|
94
|
+
buf << "\n\n"
|
95
|
+
|
96
|
+
|
97
|
+
### all referrer sources / records by page views
|
98
|
+
buf << "All referrers:"
|
99
|
+
buf << "\n\n"
|
100
|
+
|
101
|
+
lines.each_with_index do |line,i|
|
102
|
+
buf << "- #{line['referrer']} -- #{line['count']} / #{line['uniques']} -- #{line['path']}"
|
103
|
+
buf << "\n"
|
104
|
+
end
|
105
|
+
|
106
|
+
buf << "<!-- break -->\n" ## markdown hack: add a list end marker
|
107
|
+
buf << "\n\n"
|
108
|
+
|
109
|
+
|
110
|
+
|
111
|
+
buf
|
112
|
+
end # method build
|
113
|
+
end # class ReportTrafficReferrers
|
114
|
+
|
115
|
+
end # module Hubba
|