hubba-reports 0.1.0
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 +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
|