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.
@@ -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