hubba 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 990a81f63411ae5b97cd3ae1e6892cb8e8f788d3
4
- data.tar.gz: 1061e2b8963f97072cbdcedb5c6ba8c23ac3b691
3
+ metadata.gz: e3e04406b046b751a5eb400e8955417ae99498e7
4
+ data.tar.gz: 57752bfd47aea766d3db5c467659876216a644a0
5
5
  SHA512:
6
- metadata.gz: 05efa27d809de02a2e5582abe3c0d9b8f70629246d6d973d640b3fbb648365d7e0c6ecf32359837b06a83b2607d1ed4d9b5799af9c0f27d4244cce4dfe235a23
7
- data.tar.gz: 9ad616f2209a6f1c566b895d0e908b5a1cc5706d85626eb0519bb74117d4a6b4dc2f71c40eac2adb3e7caa1cfe4af33cfd1f7de57d96dd2d2390126a99e58899
6
+ metadata.gz: f96d7fa167b0e7cf8b685674aa80d19eca790ad237a9ea43cd40650d1c799448ce5827b009bb84b9d14c774b991693e1b0570735bf6573f64ed2c0a9e46c1c1f
7
+ data.tar.gz: 5cbdcd4a949763f19ba6275cec64cc1f3cfda6b05abe02950721befac8df17b1a5a1d64d1e41ae8048d23c06b93f41436d69bde91338e81313aa6e19af89236a
@@ -11,6 +11,11 @@ lib/hubba/version.rb
11
11
  test/cache/users~geraldb~orgs.json
12
12
  test/cache/users~geraldb~repos.json
13
13
  test/helper.rb
14
+ test/stats/jekyll~minima.json
15
+ test/stats/openblockchains~awesome-blockchains.json
16
+ test/stats/opendatajson~factbook.json.json
17
+ test/stats/poole~hyde.json
14
18
  test/test_cache.rb
15
19
  test/test_config.rb
16
20
  test/test_stats.rb
21
+ test/test_stats_tmp.rb
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ Hoe.spec 'hubba' do
5
5
 
6
6
  self.version = Hubba::VERSION
7
7
 
8
- self.summary = 'hubba - (yet) another (lite) GitHub HTTP API client / library'
8
+ self.summary = 'hubba - (yet) another (lite) GitHub HTTP API client / library'
9
9
  self.description = summary
10
10
 
11
11
  self.urls = ['https://github.com/gittiscripts/hubba']
@@ -14,37 +14,156 @@ module Hubba
14
14
  @data['full_name'] = full_name # e.g. poole/hyde etc.
15
15
  end
16
16
 
17
- def full_name() @data['full_name']; end
18
17
 
19
- def created_at() @data['created_at']; end
20
- def updated_at() @data['updated_at']; end
21
- def pushed_at() @data['pushed_at']; end
18
+ def full_name() @full_name ||= @data['full_name']; end
22
19
 
23
- def history() @data['history']; end
20
+ ## note: return datetime objects (NOT strings); if not present/available return nil/null
21
+ def created_at() @created_at ||= @data['created_at'] ? DateTime.strptime( @data['created_at'], '%Y-%m-%dT%H:%M:%S') : nil; end
22
+ def updated_at() @updated_at ||= @data['updated_at'] ? DateTime.strptime( @data['updated_at'], '%Y-%m-%dT%H:%M:%S') : nil; end
23
+ def pushed_at() @pushed_at ||= @data['pushed_at'] ? DateTime.strptime( @data['pushed_at'], '%Y-%m-%dT%H:%M:%S') : nil; end
24
24
 
25
+ def history() @history ||= @data['history'] ? build_history( @data['history'] ) : nil; end
25
26
 
26
27
  def size
27
28
  # size of repo in kb (as reported by github api)
28
- @data['size'] || 0 ## return 0 if not found - why? why not? (return nil - why? why not??)
29
+ @size ||= @data['size'] || 0 ## return 0 if not found - why? why not? (return nil - why? why not??)
29
30
  end
30
31
 
31
32
  def stars
32
33
  ## return last stargazers_count entry (as number; 0 if not found)
33
- t1 = 0
34
+ @stars ||= history ? history[0].stars : 0
35
+ end
36
+
37
+ def reset_cache
38
+ ## reset (invalidate) cached values from data hash
39
+ ## use after reading or fetching
40
+ @full_name = nil
41
+ @created_at = @updated_at = @pushed_at = nil
42
+ @history = nil
43
+ @size = nil
44
+ @stars = nil
45
+ end
46
+
47
+
48
+ ########
49
+ ## build history items (structs)
50
+
51
+ class HistoryItem
52
+
53
+ attr_reader :date, :stars ## read-only attributes
54
+ attr_accessor :prev, :next ## read/write attributes (for double linked list/nodes/items)
55
+
56
+ def initialize( date:, stars: )
57
+ @date = date
58
+ @stars = stars
59
+ end
60
+
61
+ ## link items (append item at the end/tail)
62
+ def append( item )
63
+ @next = item
64
+ item.prev = self
65
+ end
66
+
67
+ def diff_days
68
+ if @next
69
+ ## note: use jd=julian days for calculation
70
+ @date.jd - @next.date.jd
71
+ else
72
+ nil ## last item (tail)
73
+ end
74
+ end
75
+
76
+ def diff_stars
77
+ if @next
78
+ @stars - @next.stars
79
+ else
80
+ nil ## last item (tail)
81
+ end
82
+ end
83
+ end ## class HistoryItem
84
+
85
+
86
+ def build_history( timeseries )
87
+ items = []
88
+
89
+ keys = timeseries.keys.sort.reverse ## newest (latest) items first
90
+ keys.each do |key|
91
+ h = timeseries[ key ]
34
92
 
35
- if history
36
- history_keys = history.keys.sort.reverse
37
- ## todo/fix: for now assumes one entry per week
38
- ## simple case [0] and [1] for a week later
39
- ## check actual date - why? why not?
40
- stats_t1 = history_keys[0] ? history[ history_keys[0] ] : nil
41
- if stats_t1
42
- t1 = stats_t1['stargazers_count'] || 0
43
- end
44
- end
45
- t1
46
- end # method stars
93
+ item = HistoryItem.new(
94
+ date: Date.strptime( key, '%Y-%m-%d' ),
95
+ stars: h['stargazers_count'] || 0 )
47
96
 
97
+ ## link items
98
+ last_item = items[-1]
99
+ last_item.append( item ) if last_item ## if not nil? append (note first item has no prev item)
100
+
101
+ items << item
102
+ end
103
+
104
+ ## todo/check: return [] for empty items array (items.empty?) - why?? why not??
105
+ if items.empty?
106
+ nil
107
+ else
108
+ items
109
+ end
110
+ end ## method build_history
111
+
112
+
113
+
114
+ def calc_diff_stars( samples: 3, days: 30 )
115
+ ## samples: use n history item samples e.g. 3 samples
116
+ ## days e.g. 7 days (per week), 30 days (per month)
117
+
118
+ if history.nil?
119
+ nil ## todo/check: return 0.0 too - why? why not?
120
+ elsif history.size == 1
121
+ ## just one item; CANNOT calc diff; return zero
122
+ 0.0
123
+ else
124
+ idx = [history.size, samples].min ## calc last index
125
+ last = history[idx-1]
126
+ first = history[0]
127
+
128
+ diff_days = first.date.jd - last.date.jd
129
+ diff_stars = first.stars - last.stars
130
+
131
+ ## note: use factor 1000 for fixed integer division
132
+ ## converts to float at the end
133
+
134
+ ## todo: check for better way (convert to float upfront - why? why not?)
135
+
136
+ diff = (diff_stars * days * 1000) / diff_days
137
+ puts "diff=#{diff}:#{diff.class.name}" ## check if it's a float
138
+ (diff.to_f/1000.0)
139
+ end
140
+ end
141
+
142
+ def history_str
143
+ ## returns "pretty printed" history as string buffer
144
+ buf = ''
145
+ buf << "[#{history.size}]: "
146
+
147
+ history.each do |item|
148
+ buf << "#{item.stars}"
149
+
150
+ diff_stars = item.diff_stars
151
+ diff_days = item.diff_days
152
+ if diff_stars && diff_days ## note: last item has no diffs
153
+ if diff_stars > 0 || diff_stars < 0
154
+ if diff_stars > 0
155
+ buf << " (+#{diff_stars}"
156
+ else
157
+ buf << " (#{diff_stars}"
158
+ end
159
+ buf << " in #{diff_days}d) "
160
+ else ## diff_stars == 0
161
+ buf << " (#{diff_days}d) "
162
+ end
163
+ end
164
+ end
165
+ buf
166
+ end # method history_str
48
167
 
49
168
 
50
169
  ###############################
@@ -89,32 +208,39 @@ module Hubba
89
208
  }
90
209
 
91
210
  ## for now store only the latest commit (e.g. a single commit in an array)
92
- @data[ 'commits'] = [commit]
211
+ @data[ 'commits' ] = [commit]
93
212
 
94
213
  pp @data
214
+
215
+ reset_cache
216
+ self ## return self for (easy chaining)
95
217
  end
96
218
 
97
219
 
98
220
 
99
221
  def write( data_dir: './data' )
100
222
  basename = full_name.gsub( '/', '~' ) ## e.g. poole/hyde become poole~hyde
101
- puts "writing (saving) to #{basename}..."
223
+ puts "writing stats to #{basename}..."
102
224
  File.open( "#{data_dir}/#{basename}.json", 'w:utf-8' ) do |f|
103
225
  f.write JSON.pretty_generate( data )
104
226
  end
227
+ self ## return self for (easy chaining)
105
228
  end
106
229
 
107
- def read( data_dir: './data' ) ## note: use read instead of load (load is kind of keyword for loading code)
230
+
231
+ def read( data_dir: './data' )
108
232
  ## note: skip reading if file not present
109
233
  basename = full_name.gsub( '/', '~' ) ## e.g. poole/hyde become poole~hyde
110
234
  filename = "#{data_dir}/#{basename}.json"
111
235
  if File.exist?( filename )
112
- puts "reading (loading) from #{basename}..."
236
+ puts "reading stats from #{basename}..."
113
237
  json = File.open( filename, 'r:utf-8' ) { |file| file.read } ## todo/fix: use read_utf8
114
238
  @data = JSON.parse( json )
239
+ reset_cache
115
240
  else
116
- puts "skipping reading (loading) from #{basename} -- file not found"
241
+ puts "skipping reading stats from #{basename} -- file not found"
117
242
  end
243
+ self ## return self for (easy chaining)
118
244
  end
119
245
 
120
246
  end # class Stats
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Hubba
4
4
  MAJOR = 0 ## todo: namespace inside version or something - why? why not??
5
- MINOR = 3
5
+ MINOR = 4
6
6
  PATCH = 0
7
7
  VERSION = [MAJOR,MINOR,PATCH].join('.')
8
8
 
@@ -0,0 +1,21 @@
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
+ "committer": {
15
+ "date": "2018-01-29T19:02:39Z",
16
+ "name": "Frank Taillandier"
17
+ },
18
+ "message": "Release 💎 v2.3.0 (#195)"
19
+ }
20
+ ]
21
+ }
@@ -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
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "full_name": "poole/hyde",
3
+ "created_at": "2013-02-07T07:01:38Z",
4
+ "updated_at": "2018-02-12T05:44:07Z",
5
+ "pushed_at": "2018-01-14T02:41:16Z",
6
+ "size": 28428,
7
+ "history": {
8
+ "2018-02-12": {
9
+ "stargazers_count": 2125
10
+ }
11
+ },
12
+ "commits": [
13
+ {
14
+ "committer": {
15
+ "date": "2015-05-11T20:21:43Z",
16
+ "name": "Mark Otto"
17
+ },
18
+ "message": "Merge pull request #91 from pborreli/patch-1\n\nFixed typo"
19
+ }
20
+ ]
21
+ }
@@ -32,15 +32,14 @@ class TestCache < MiniTest::Test
32
32
  def test_cache
33
33
  orgs = @cache.get( '/users/geraldb/orgs' )
34
34
  assert_equal 4, orgs.size
35
-
35
+
36
36
  repos = @cache.get( '/users/geraldb/repos' )
37
37
  assert_equal 3, repos.size
38
38
  end # method test_cache
39
39
 
40
40
  def test_cache_miss
41
- assert_equal nil, @cache.get( '/test/hello' )
42
- assert_equal nil, @cache.get( '/test/hola' )
41
+ assert_nil @cache.get( '/test/hello' )
42
+ assert_nil @cache.get( '/test/hola' )
43
43
  end # method test_cache_miss
44
44
 
45
45
  end # class TestCache
46
-
@@ -10,31 +10,77 @@ require 'helper'
10
10
 
11
11
  class TestStats < MiniTest::Test
12
12
 
13
- def setup
14
- @gh = Hubba::Github.new( cache_dir: "#{Hubba.root}/test/cache" )
13
+ def test_awesome_blockchains
14
+
15
+ stats = Hubba::Stats.new( 'openblockchains/awesome-blockchains' )
16
+
17
+ assert_equal 0, stats.size
18
+ assert_equal 0, stats.stars
19
+ assert_nil stats.history
20
+
21
+ stats.read( data_dir: "#{Hubba.root}/test/stats" )
22
+
23
+ assert_equal 1620, stats.size
24
+ assert_equal 1526, stats.stars
25
+ assert_equal 1526, stats.history[0].stars
26
+ assert_equal 1411, stats.history[1].stars
27
+ assert_equal 1084, stats.history[2].stars
28
+ assert_equal 1084, stats.history[-1].stars
29
+ assert_equal 3, stats.history.size
30
+
31
+ assert_equal Date.new(2018, 2, 8 ), stats.history[0].date
32
+ assert_equal Date.new(2018, 1, 28 ), stats.history[1].date
33
+ assert_equal Date.new(2017, 12, 10 ), stats.history[2].date
34
+
35
+ assert_equal 11, stats.history[0].diff_days
36
+ assert_equal 49, stats.history[1].diff_days
37
+ assert_nil stats.history[2].diff_days
38
+
39
+ assert_equal 115, stats.history[0].diff_stars
40
+ assert_equal 327, stats.history[1].diff_stars
41
+ assert_nil stats.history[2].diff_stars
42
+
43
+ assert_equal 221.0, stats.calc_diff_stars ## defaults to samples: 3, days: 30
44
+ assert_equal 51.566, stats.calc_diff_stars( samples: 5, days: 7 )
45
+
46
+ pp stats.history_str ## pp history pretty printed to string (buffer)
15
47
  end
16
48
 
17
- def test_stats
18
- repos = [
19
- 'henrythemes/jekyll-starter-theme',
20
- 'poole/hyde',
21
- 'jekyll/minima'
22
- ]
23
49
 
24
- repos.each do |repo|
25
- stats = Hubba::Stats.new( repo )
26
- stats.read( data_dir: './tmp' )
50
+ def test_factbook_json
51
+
52
+ stats = Hubba::Stats.new( 'opendatajson/factbook.json' )
53
+
54
+ assert_equal 0, stats.size
55
+ assert_equal 0, stats.stars
56
+ assert_nil stats.history
57
+
58
+ stats.read( data_dir: "#{Hubba.root}/test/stats" )
59
+
60
+ assert_equal 7355, stats.size
61
+ assert_equal 539, stats.stars
62
+ assert_equal 539, stats.history[0].stars
63
+ assert_equal 536, stats.history[1].stars
64
+ assert_equal 533, stats.history[2].stars
65
+ assert_equal 457, stats.history[-1].stars
66
+ assert_equal 7, stats.history.size
67
+
68
+ assert_equal Date.new(2018, 2, 8 ), stats.history[0].date
69
+ assert_equal Date.new(2018, 1, 28 ), stats.history[1].date
70
+ assert_equal Date.new(2017, 12, 10 ), stats.history[2].date
71
+
72
+ assert_equal 11, stats.history[0].diff_days
73
+ assert_equal 49, stats.history[1].diff_days
74
+ assert_nil stats.history[-1].diff_days
27
75
 
28
- puts "stars before fetch: #{stats.stars}"
29
- puts "size before fetch: #{stats.size} kb"
30
- stats.fetch( @gh )
31
- puts "stars after fetch: #{stats.stars}"
32
- puts "size after fetch: #{stats.size} kb"
76
+ assert_equal 3, stats.history[0].diff_stars
77
+ assert_equal 3, stats.history[1].diff_stars
78
+ assert_nil stats.history[-1].diff_stars
33
79
 
34
- stats.write( data_dir: './tmp' )
35
- end
80
+ assert_equal 3.0, stats.calc_diff_stars ## defaults to samples: 3, days: 30
81
+ assert_equal 1.012, stats.calc_diff_stars( samples: 5, days: 7 )
36
82
 
37
- assert true # for now everything ok if we get here
83
+ pp stats.history_str ## pp history pretty printed to string (buffer)
38
84
  end
39
85
 
40
86
  end # class TestStats
@@ -0,0 +1,43 @@
1
+ # encoding: utf-8
2
+
3
+ ###
4
+ # to run use
5
+ # ruby -I ./lib -I ./test test/test_stats_tmp.rb
6
+
7
+
8
+ require 'helper'
9
+
10
+
11
+ class TestStatsTmp < MiniTest::Test
12
+
13
+ def setup
14
+ @gh = Hubba::Github.new( cache_dir: "#{Hubba.root}/test/cache" )
15
+ end
16
+
17
+ def test_stats
18
+ repos = [
19
+ 'henrythemes/jekyll-starter-theme',
20
+ 'poole/hyde',
21
+ 'jekyll/minima'
22
+ ]
23
+
24
+ repos.each do |repo|
25
+ stats = Hubba::Stats.new( repo )
26
+ stats.read( data_dir: './tmp' )
27
+
28
+ puts "stars before fetch: #{stats.stars}"
29
+ puts "size before fetch: #{stats.size} kb"
30
+
31
+ ## note/todo: enable for "live" online testing
32
+ ## stats.fetch( @gh )
33
+
34
+ puts "stars after fetch: #{stats.stars}"
35
+ puts "size after fetch: #{stats.size} kb"
36
+
37
+ stats.write( data_dir: './tmp' )
38
+ end
39
+
40
+ assert true # for now everything ok if we get here
41
+ end
42
+
43
+ end # class TestStatsTmp
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hubba
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-08 00:00:00.000000000 Z
11
+ date: 2018-02-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: logutils
@@ -52,7 +52,7 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.16'
55
- description: hubba - (yet) another (lite) GitHub HTTP API client / library
55
+ description: hubba - (yet) another (lite) GitHub HTTP API client / library
56
56
  email: ruby-talk@ruby-lang.org
57
57
  executables: []
58
58
  extensions: []
@@ -74,9 +74,14 @@ files:
74
74
  - test/cache/users~geraldb~orgs.json
75
75
  - test/cache/users~geraldb~repos.json
76
76
  - test/helper.rb
77
+ - test/stats/jekyll~minima.json
78
+ - test/stats/openblockchains~awesome-blockchains.json
79
+ - test/stats/opendatajson~factbook.json.json
80
+ - test/stats/poole~hyde.json
77
81
  - test/test_cache.rb
78
82
  - test/test_config.rb
79
83
  - test/test_stats.rb
84
+ - test/test_stats_tmp.rb
80
85
  homepage: https://github.com/gittiscripts/hubba
81
86
  licenses:
82
87
  - Public Domain
@@ -102,5 +107,5 @@ rubyforge_project:
102
107
  rubygems_version: 2.5.2
103
108
  signing_key:
104
109
  specification_version: 4
105
- summary: hubba - (yet) another (lite) GitHub HTTP API client / library
110
+ summary: hubba - (yet) another (lite) GitHub HTTP API client / library
106
111
  test_files: []