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,51 @@
1
+ module Hubba
2
+
3
+ class ReportTrending < Report
4
+
5
+ def build
6
+
7
+ ## note: orgs is orgs+users e.g. geraldb, yorobot etc
8
+ buf = String.new('')
9
+ buf << "# Trending"
10
+ buf << " - #{@stats.repos.size} Repos @ #{@stats.orgs.size} Orgs"
11
+ buf << "\n\n"
12
+
13
+ ###
14
+ ## todo:
15
+ ## use calc per month (days: 30)
16
+ ## per week is too optimistic (e.g. less than one star/week e.g. 0.6 or something)
17
+
18
+ repos = @stats.repos.sort do |l,r|
19
+ ## note: use reverse sort (right,left) - e.g. most stars first
20
+ ## r[:stars] <=> l[:stars]
21
+
22
+ ## sort by created_at (use julian days)
23
+ ## r[:created_at].to_date.jd <=> l[:created_at].to_date.jd
24
+
25
+ res = r.diff <=> l.diff
26
+ res = r.stats.stars <=> l.stats.stars if res == 0
27
+ res = r.stats.created.jd <=> l.stats.created.jd if res == 0
28
+ res
29
+ end
30
+
31
+
32
+ ## pp repos
33
+
34
+
35
+ repos.each_with_index do |repo,i|
36
+ if repo.diff == 0
37
+ buf << "- -/- "
38
+ else
39
+ buf << "- #{repo.diff}/month "
40
+ end
41
+
42
+ buf << " ★#{repo.stats.stars} **#{repo.full_name}** (#{repo.stats.size} kb) - "
43
+ buf << "#{repo.stats.history_str}\n"
44
+ end
45
+
46
+
47
+ buf
48
+ end # method build
49
+ end # class ReportTrending
50
+
51
+ end # module Hubba
@@ -0,0 +1,60 @@
1
+ module Hubba
2
+
3
+
4
+ class ReportUpdates < Report
5
+
6
+ def build
7
+
8
+ ## note: orgs is orgs+users e.g. geraldb, yorobot etc
9
+ buf = String.new('')
10
+ buf << "# Updates"
11
+ buf << " - #{@stats.repos.size} Repos @ #{@stats.orgs.size} Orgs\n"
12
+ buf << "\n\n"
13
+
14
+ repos = @stats.repos.sort do |l,r|
15
+ r.stats.committed.jd <=> l.stats.committed.jd
16
+ end
17
+
18
+ ## pp repos
19
+
20
+
21
+ buf << "committed / pushed / updated / created\n\n"
22
+
23
+ today = Date.today
24
+
25
+ repos.each_with_index do |repo,i|
26
+
27
+ days_ago = today.jd - repo.stats.committed.jd
28
+
29
+ diff1 = repo.stats.committed.jd - repo.stats.pushed.jd
30
+ diff2 = repo.stats.committed.jd - repo.stats.updated.jd
31
+ diff3 = repo.stats.pushed.jd - repo.stats.updated.jd
32
+
33
+ buf << "- (#{days_ago}d) **#{repo.full_name}** ★#{repo.stats.stars} - "
34
+ buf << "#{repo.stats.committed} "
35
+ buf << "("
36
+ buf << (diff1==0 ? '=' : "#{diff1}d")
37
+ buf << "/"
38
+ buf << (diff2==0 ? '=' : "#{diff2}d")
39
+ buf << ")"
40
+ buf << " / "
41
+ buf << "#{repo.stats.pushed} "
42
+ buf << "("
43
+ buf << (diff3==0 ? '=' : "#{diff3}d")
44
+ buf << ")"
45
+ buf << " / "
46
+ buf << "#{repo.stats.updated} / "
47
+ buf << "#{repo.stats.created} - "
48
+ buf << "‹#{repo.stats.last_commit_message}›"
49
+ buf << " (#{repo.stats.size} kb)"
50
+ buf << "\n"
51
+ end
52
+ buf << "<!-- break -->\n" ## markdown hack: add a list end marker
53
+ buf << "\n\n"
54
+
55
+
56
+ buf
57
+ end # method build
58
+ end # class ReportUpdates
59
+
60
+ end # module Hubba
@@ -0,0 +1,230 @@
1
+ module Hubba
2
+
3
+ ####
4
+ # keep track of repo stats over time (with history hash)
5
+
6
+ class Stats ## todo/check: rename to GithubRepoStats or RepoStats - why? why not?
7
+
8
+ ## attr_reader :data - needed - why? why not?
9
+
10
+ def full_name() @data['full_name']; end
11
+ def description() @data['description'] || ''; end ## todo/check: return nil if not found - why? why not?
12
+ alias_method :descr, :description
13
+ alias_method :desc, :description
14
+
15
+ def topics() @data['topics'] || []; end ## todo/check: return nil if not found - why? why not?
16
+
17
+
18
+ ## note: return datetime objects (NOT strings); if not present/available return nil/null
19
+ def created_at() @cache['created_at'] ||= parse_datetime( @data['created_at'] ); end
20
+ def updated_at() @cache['updated_at'] ||= parse_datetime( @data['updated_at'] ); end
21
+ def pushed_at() @cache['pushed_at'] ||= parse_datetime( @data['pushed_at'] ); end
22
+
23
+ ## date (only) versions
24
+ def created() @cache['created'] ||= parse_date( @data['created_at'] ); end
25
+ def updated() @cache['updated'] ||= parse_date( @data['updated_at'] ); end
26
+ def pushed() @cache['pushed'] ||= parse_date( @data['pushed_at'] ); end
27
+
28
+ def size
29
+ # size of repo in kb (as reported by github api)
30
+ @data['size'] || 0 ## return 0 if not found - why? why not? (return nil - why? why not??)
31
+ end
32
+
33
+
34
+ def history
35
+ @cache['history'] ||= begin
36
+ if @data['history']
37
+ build_history( @data['history'] )
38
+ else
39
+ nil
40
+ end
41
+ end
42
+ end
43
+
44
+
45
+ def stars
46
+ ## return last stargazers_count entry (as number; 0 if not found)
47
+ @cache['stars'] ||= history ? history[0].stars : 0
48
+ end
49
+
50
+ ###################
51
+ # traffic
52
+ def traffic() @data['traffic']; end
53
+
54
+
55
+ ###########
56
+ # commits
57
+ def commits() @data['commits']; end
58
+
59
+ def last_commit ## convenience shortcut; get first/last commit (use [0]) or nil
60
+ if @data['commits'] && @data['commits'][0]
61
+ @data['commits'][0]
62
+ else
63
+ nil
64
+ end
65
+ end
66
+
67
+
68
+ def committed ## last commit date (from author NOT committer)
69
+ @cache['committed'] ||= parse_date( last_commit_author_date )
70
+ end
71
+
72
+ def committed_at() ## last commit date (from author NOT committer)
73
+ @cache['committed_at'] ||= parse_datetime( last_commit_author_date )
74
+ end
75
+
76
+ def last_commit_author_date
77
+ h = last_commit
78
+ h ? h['author']['date'] : nil
79
+ end
80
+
81
+
82
+ def last_commit_message ## convenience shortcut; last commit message
83
+ h = last_commit
84
+
85
+ committer_name = h['committer']['name']
86
+ author_name = h['author']['name']
87
+ message = h['message']
88
+
89
+ buf = ""
90
+ buf << message
91
+ buf << " by #{author_name}"
92
+
93
+ if committer_name != author_name
94
+ buf << " w/ #{committer_name}"
95
+ end
96
+ end # method commit_message
97
+
98
+
99
+
100
+ ###
101
+ # helpers
102
+ def parse_datetime( str ) str ? DateTime.strptime( str, '%Y-%m-%dT%H:%M:%S') : nil; end
103
+ def parse_date( str ) str ? Date.strptime( str, '%Y-%m-%d') : nil; end
104
+
105
+ ########
106
+ ## build history items (structs)
107
+
108
+ class HistoryItem
109
+
110
+ attr_reader :date, :stars, :forks ## read-only attributes
111
+ attr_accessor :prev, :next ## read/write attributes (for double linked list/nodes/items)
112
+
113
+ def initialize( date:, stars:, forks: )
114
+ @date = date
115
+ @stars = stars
116
+ @forks = forks
117
+ @next = nil
118
+ end
119
+
120
+ ## link items (append item at the end/tail)
121
+ def append( item )
122
+ @next = item
123
+ item.prev = self
124
+ end
125
+
126
+ def diff_days
127
+ if @next
128
+ ## note: use jd=julian days for calculation
129
+ @date.jd - @next.date.jd
130
+ else
131
+ nil ## last item (tail)
132
+ end
133
+ end
134
+
135
+ def diff_stars
136
+ if @next
137
+ @stars - @next.stars
138
+ else
139
+ nil ## last item (tail)
140
+ end
141
+ end
142
+ end ## class HistoryItem
143
+
144
+
145
+ def build_history( timeseries )
146
+ items = []
147
+
148
+ keys = timeseries.keys.sort.reverse ## newest (latest) items first
149
+ keys.each do |key|
150
+ h = timeseries[ key ]
151
+
152
+ item = HistoryItem.new(
153
+ date: Date.strptime( key, '%Y-%m-%d' ),
154
+ stars: h['stargazers_count'] || 0,
155
+ forks: h['forks_count'] || 0 )
156
+
157
+ ## link items
158
+ last_item = items[-1]
159
+ last_item.append( item ) if last_item ## if not nil? append (note first item has no prev item)
160
+
161
+ items << item
162
+ end
163
+
164
+ ## todo/check: return [] for empty items array (items.empty?) - why?? why not??
165
+ if items.empty?
166
+ nil
167
+ else
168
+ items
169
+ end
170
+ end ## method build_history
171
+
172
+
173
+
174
+ def calc_diff_stars( samples: 3, days: 30 )
175
+ ## samples: use n history item samples e.g. 3 samples
176
+ ## days e.g. 7 days (per week), 30 days (per month)
177
+
178
+ if history.nil?
179
+ nil ## todo/check: return 0.0 too - why? why not?
180
+ elsif history.size == 1
181
+ ## just one item; CANNOT calc diff; return zero
182
+ 0.0
183
+ else
184
+ idx = [history.size, samples].min ## calc last index
185
+ last = history[idx-1]
186
+ first = history[0]
187
+
188
+ diff_days = first.date.jd - last.date.jd
189
+ diff_stars = first.stars - last.stars
190
+
191
+ ## note: use factor 1000 for fixed integer division
192
+ ## converts to float at the end
193
+
194
+ ## todo: check for better way (convert to float upfront - why? why not?)
195
+
196
+ diff = (diff_stars * days * 1000) / diff_days
197
+ ## puts "diff=#{diff}:#{diff.class.name}" ## check if it's a float
198
+ (diff.to_f/1000.0)
199
+ end
200
+ end
201
+
202
+
203
+ def history_str ## todo/check: rename/change to format_history or fmt_history - why? why not?
204
+ ## returns "pretty printed" history as string buffer
205
+ buf = ''
206
+ buf << "[#{history.size}]: "
207
+
208
+ history.each do |item|
209
+ buf << "#{item.stars}"
210
+
211
+ diff_stars = item.diff_stars
212
+ diff_days = item.diff_days
213
+ if diff_stars && diff_days ## note: last item has no diffs
214
+ if diff_stars > 0 || diff_stars < 0
215
+ if diff_stars > 0
216
+ buf << " (+#{diff_stars}"
217
+ else
218
+ buf << " (#{diff_stars}"
219
+ end
220
+ buf << " in #{diff_days}d) "
221
+ else ## diff_stars == 0
222
+ buf << " (#{diff_days}d) "
223
+ end
224
+ end
225
+ end
226
+ buf
227
+ end # method history_str
228
+ end # class Stats
229
+
230
+ end # module Hubba
@@ -0,0 +1,18 @@
1
+ module HubbaReports
2
+ MAJOR = 0 ## todo: namespace inside version or something - why? why not??
3
+ MINOR = 1
4
+ PATCH = 0
5
+ VERSION = [MAJOR,MINOR,PATCH].join('.')
6
+
7
+ def self.version
8
+ VERSION
9
+ end
10
+
11
+ def self.banner
12
+ "hubba-reports/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
13
+ end
14
+
15
+ def self.root
16
+ "#{File.expand_path( File.dirname(File.dirname(File.dirname(File.dirname(__FILE__)))) )}"
17
+ end
18
+ end # module HubbaReports
@@ -0,0 +1,11 @@
1
+ # minitest setup
2
+ require 'minitest/autorun'
3
+
4
+
5
+ ## note: also use local version of hubba!!!
6
+ $LOAD_PATH.unshift( "../hubba/lib" )
7
+
8
+
9
+ ## our own code
10
+ require 'hubba/reports'
11
+
@@ -0,0 +1,25 @@
1
+ {
2
+ "full_name": "jekyll/minima",
3
+ "created_at": "2016-05-20T23:07:56Z",
4
+ "updated_at": "2018-02-11T16:13:33Z",
5
+ "pushed_at": "2018-02-07T22:14:11Z",
6
+ "size": 321,
7
+ "history": {
8
+ "2018-02-12": {
9
+ "stargazers_count": 717
10
+ }
11
+ },
12
+ "commits": [
13
+ {
14
+ "author": {
15
+ "name": "ashmaroli",
16
+ "date": "2018-02-21T19:35:59Z"
17
+ },
18
+ "committer": {
19
+ "name": "Frank Taillandier",
20
+ "date": "2018-02-21T19:35:59Z"
21
+ },
22
+ "message": "social icons should resolve baseurl properly (#201)"
23
+ }
24
+ ]
25
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "full_name": "openblockchains/awesome-blockchains",
3
+ "created_at": "2017-09-13T22:33:56Z",
4
+ "size": 1620,
5
+ "history": {
6
+ "2017-12-10": {
7
+ "stargazers_count": 1084
8
+ },
9
+ "2018-01-28": {
10
+ "stargazers_count": 1411
11
+ },
12
+ "2018-02-08": {
13
+ "stargazers_count": 1526
14
+ }
15
+ },
16
+ "commits": [
17
+ {
18
+ "committer": {
19
+ "date": "2018-02-08T09:33:28Z",
20
+ "name": "Gerald Bauer"
21
+ },
22
+ "message": "Update README.md\n\nJust a little typo cryto -> crypto."
23
+ }
24
+ ],
25
+ "updated_at": "2018-02-08T19:26:35Z",
26
+ "pushed_at": "2018-02-08T09:33:29Z"
27
+ }
@@ -0,0 +1,39 @@
1
+ {
2
+ "full_name": "opendatajson/factbook.json",
3
+ "created_at": "2014-07-12T12:43:52Z",
4
+ "history": {
5
+ "2017-02-11": {
6
+ "stargazers_count": 457
7
+ },
8
+ "2017-02-12": {
9
+ "stargazers_count": 457
10
+ },
11
+ "2017-06-18": {
12
+ "stargazers_count": 505
13
+ },
14
+ "2017-07-28": {
15
+ "stargazers_count": 512
16
+ },
17
+ "2017-12-10": {
18
+ "stargazers_count": 533
19
+ },
20
+ "2018-01-28": {
21
+ "stargazers_count": 536
22
+ },
23
+ "2018-02-08": {
24
+ "stargazers_count": 539
25
+ }
26
+ },
27
+ "commits": [
28
+ {
29
+ "committer": {
30
+ "date": "2017-03-29T17:23:29Z",
31
+ "name": "GitHub"
32
+ },
33
+ "message": "Update MONGO.md"
34
+ }
35
+ ],
36
+ "size": 7355,
37
+ "updated_at": "2018-02-01T12:35:19Z",
38
+ "pushed_at": "2017-03-29T17:23:30Z"
39
+ }