Class: Hubba::Stats

Inherits:
Object
  • Object
show all
Defined in:
lib/hubba/stats.rb

Overview

keep track of repo stats over time (with history hash)

Defined Under Namespace

Classes: HistoryItem

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(full_name) ⇒ Stats

Returns a new instance of Stats.



12
13
14
15
# File 'lib/hubba/stats.rb', line 12

def initialize( full_name )
  @data = {}
  @data['full_name'] = full_name  # e.g. poole/hyde etc.

end

Instance Attribute Details

#dataObject (readonly)

todo/check: rename to GithubRepoStats or RepoStats - why? why not?



10
11
12
# File 'lib/hubba/stats.rb', line 10

def data
  @data
end

Instance Method Details

#build_history(timeseries) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/hubba/stats.rb', line 136

def build_history( timeseries )
  items = []

  keys  = timeseries.keys.sort.reverse   ## newest (latest) items first

  keys.each do |key|
    h = timeseries[ key ]

    item = HistoryItem.new(
             date:  Date.strptime( key, '%Y-%m-%d' ),
             stars: h['stargazers_count'] || 0 )

    ## link items

    last_item = items[-1]
    last_item.append( item )   if last_item     ## if not nil? append (note first item has no prev item)


    items << item
  end

  ## todo/check: return [] for empty items array (items.empty?) - why?? why not??

  if items.empty?
    nil
  else
    items
  end
end

#calc_diff_stars(samples: 3, days: 30) ⇒ Object



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/hubba/stats.rb', line 164

def calc_diff_stars( samples: 3, days: 30 )
  ## samples: use n history item samples e.g. 3 samples

  ## days e.g. 7 days (per week), 30 days (per month)


  if history.nil?
    nil   ## todo/check: return 0.0 too - why? why not?

  elsif history.size == 1
    ## just one item; CANNOT calc diff; return zero

    0.0
  else
    idx   = [history.size, samples].min   ## calc last index

    last  = history[idx-1]
    first = history[0]

    diff_days  = first.date.jd - last.date.jd
    diff_stars = first.stars   - last.stars

    ## note: use factor 1000 for fixed integer division

    ##  converts to float at the end


    ##  todo: check for better way (convert to float upfront - why? why not?)


    diff = (diff_stars * days * 1000) / diff_days
    puts "diff=#{diff}:#{diff.class.name}"    ## check if it's a float

    (diff.to_f/1000.0)
  end
end

#commitsObject



45
# File 'lib/hubba/stats.rb', line 45

def commits() @data['commits']; end

#committedObject

last commit date (from author NOT committer)



55
56
57
# File 'lib/hubba/stats.rb', line 55

def committed()   ## last commit date (from author NOT committer)

  @committed ||= last_commit ? Date.strptime( last_commit['author']['date'], '%Y-%m-%d') : nil
end

#committed_atObject

last commit date (from author NOT committer)



59
60
61
# File 'lib/hubba/stats.rb', line 59

def committed_at()   ## last commit date (from author NOT committer)

  @committed_at ||= last_commit ? DateTime.strptime( last_commit['author']['date'], '%Y-%m-%dT%H:%M:%S') : nil
end

#createdObject

date (only) versions



26
# File 'lib/hubba/stats.rb', line 26

def created() @created ||= @data['created_at'] ? Date.strptime( @data['created_at'], '%Y-%m-%d') : nil; end

#created_atObject

note: return datetime objects (NOT strings); if not present/available return nil/null



21
# File 'lib/hubba/stats.rb', line 21

def created_at() @created_at ||= @data['created_at'] ? DateTime.strptime( @data['created_at'], '%Y-%m-%dT%H:%M:%S') : nil; end

#fetch(gh) ⇒ Object

fetch / read / write methods



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/hubba/stats.rb', line 222

def fetch( gh )   ## update stats / fetch data from github via api

  puts "fetching #{full_name}..."
  repo = gh.repo( full_name )

  ## e.g. 2015-05-11T20:21:43Z

  ## puts Time.iso8601( repo.data['created_at'] )

  @data['created_at'] = repo.data['created_at']
  @data['updated_at'] = repo.data['updated_at']
  @data['pushed_at']  = repo.data['pushed_at']

  @data['size']       = repo.data['size']  # size in kb (kilobyte)


  rec = {}

  puts "stargazers_count"
  puts repo.data['stargazers_count']
  rec['stargazers_count'] = repo.data['stargazers_count']

  today = Date.today.strftime( '%Y-%m-%d' )   ## e.g. 2016-09-27

  puts "add record #{today} to history..."
  pp rec      # check if stargazers_count is a number (NOT a string)


  @data[ 'history' ] ||= {}
  @data[ 'history' ][ today ] = rec

  ##########################

  ## also check / keep track of (latest) commit

  commits = gh.repo_commits( full_name )
  puts "last commit/update:"
  ## pp commits

  commit = {
    'committer' => {
      'date' => commits.data[0]['commit']['committer']['date'],
      'name' => commits.data[0]['commit']['committer']['name']
    },
    'author' => {
      'date' => commits.data[0]['commit']['author']['date'],
      'name' => commits.data[0]['commit']['author']['name']
    },
    'message' => commits.data[0]['commit']['message']
  }

  ## for now store only the latest commit (e.g. a single commit in an array)

  @data[ 'commits' ] = [commit]

  pp @data

  reset_cache
  self   ## return self for (easy chaining)

end

#full_nameObject



18
# File 'lib/hubba/stats.rb', line 18

def full_name() @full_name ||= @data['full_name']; end

#historyObject



32
# File 'lib/hubba/stats.rb', line 32

def history()    @history ||= @data['history'] ? build_history( @data['history'] ) : nil; end

#history_strObject



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/hubba/stats.rb', line 192

def history_str
  ## returns "pretty printed" history as string buffer

  buf = ''
  buf << "[#{history.size}]: "

  history.each do |item|
    buf << "#{item.stars}"

    diff_stars = item.diff_stars
    diff_days  = item.diff_days
    if diff_stars && diff_days  ## note: last item has no diffs

      if diff_stars > 0 || diff_stars < 0
        if diff_stars > 0
          buf << " (+#{diff_stars}"
        else
          buf << " (#{diff_stars}"
        end
        buf << " in #{diff_days}d) "
      else  ## diff_stars == 0

        buf << " (#{diff_days}d) "
      end
    end
  end
  buf
end

#last_commitObject

convenience shortcut; get first/last commit (use [0]) or nil



47
48
49
50
51
52
53
# File 'lib/hubba/stats.rb', line 47

def last_commit()   ## convenience shortcut; get first/last commit (use [0]) or nil

  if @data['commits'] && @data['commits'][0]
     @data['commits'][0]
  else
    nil
  end
end

#last_commit_messageObject

convenience shortcut; last commit message



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/hubba/stats.rb', line 64

def last_commit_message() ## convenience shortcut; last commit message

  h = last_commit

  committer_name = h['committer']['name']
  author_name    = h['author']['name']
  message        = h['message']

  buf = ""
  buf << message
  buf << " by #{author_name}"

  if committer_name != author_name
    buf << " w/ #{committer_name}"
  end
end

#pushedObject



28
# File 'lib/hubba/stats.rb', line 28

def pushed()  @pushed  ||= @data['pushed_at']  ? Date.strptime( @data['pushed_at'],  '%Y-%m-%d') : nil; end

#pushed_atObject



23
# File 'lib/hubba/stats.rb', line 23

def pushed_at()  @pushed_at  ||= @data['pushed_at']  ? DateTime.strptime( @data['pushed_at'],  '%Y-%m-%dT%H:%M:%S') : nil; end

#read(data_dir: './data') ⇒ Object



285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/hubba/stats.rb', line 285

def read( data_dir: './data' )
  ## note: skip reading if file not present

  basename = full_name.gsub( '/', '~' )   ## e.g. poole/hyde become poole~hyde

  filename = "#{data_dir}/#{basename}.json"
  if File.exist?( filename )
    puts "reading stats from #{basename}..."
    json = File.open( filename, 'r:utf-8' ) { |file| file.read }   ## todo/fix: use read_utf8

    @data = JSON.parse( json )
    reset_cache
  else
    puts "skipping reading stats from #{basename} -- file not found"
  end
  self   ## return self for (easy chaining)

end

#reset_cacheObject



82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/hubba/stats.rb', line 82

def reset_cache
  ## reset (invalidate) cached values from data hash

  ##   use after reading or fetching

  @full_name    = nil
  @created_at   = @updated_at = @pushed_at = nil
  @created      = @updated    = @pused     = nil
  @history      = nil
  @size         = nil
  @stars        = nil

  @committed_at = nil
  @committed    = nil
end

#sizeObject



34
35
36
37
# File 'lib/hubba/stats.rb', line 34

def size
  # size of repo in kb (as reported by github api)

  @size ||= @data['size'] || 0   ## return 0 if not found - why? why not? (return nil - why? why not??)

end

#starsObject



39
40
41
42
# File 'lib/hubba/stats.rb', line 39

def stars
  ## return last stargazers_count entry (as number; 0 if not found)

  @stars ||= history ? history[0].stars : 0
end

#updatedObject



27
# File 'lib/hubba/stats.rb', line 27

def updated() @updated ||= @data['updated_at'] ? Date.strptime( @data['updated_at'], '%Y-%m-%d') : nil; end

#updated_atObject



22
# File 'lib/hubba/stats.rb', line 22

def updated_at() @updated_at ||= @data['updated_at'] ? DateTime.strptime( @data['updated_at'], '%Y-%m-%dT%H:%M:%S') : nil; end

#write(data_dir: './data') ⇒ Object



275
276
277
278
279
280
281
282
# File 'lib/hubba/stats.rb', line 275

def write( data_dir: './data' )
  basename = full_name.gsub( '/', '~' )   ## e.g. poole/hyde become poole~hyde

  puts "writing stats to #{basename}..."
  File.open( "#{data_dir}/#{basename}.json", 'w:utf-8' ) do |f|
      f.write JSON.pretty_generate( data )
  end
  self   ## return self for (easy chaining)

end