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



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/hubba/stats.rb', line 86

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



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/hubba/stats.rb', line 114

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

#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



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
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
217
# File 'lib/hubba/stats.rb', line 172

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']
    },
    '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



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

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

#history_strObject



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/hubba/stats.rb', line 142

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

#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



231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/hubba/stats.rb', line 231

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



37
38
39
40
41
42
43
44
45
# File 'lib/hubba/stats.rb', line 37

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
  @history    = nil
  @size       = nil
  @stars      = nil
end

#sizeObject



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

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



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

def stars
  ## return last stargazers_count entry (as number; 0 if not found)
  @stars ||= history ? history[0].stars : 0
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



221
222
223
224
225
226
227
228
# File 'lib/hubba/stats.rb', line 221

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