hubtime 0.0.1

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,48 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ module Hubtime
4
+ class Cacher
5
+ def self.clear(type)
6
+ dir = nil
7
+ case type
8
+ when "all"
9
+ dir = File.join(File.expand_path("."), "data")
10
+ when "cache", "charts"
11
+ dir = File.join(File.expand_path("."), "data", type)
12
+ else
13
+ dir = File.join(File.expand_path("."), "data", "cache", type)
14
+ end
15
+
16
+ raise "Unknown clear type" unless dir
17
+ FileUtils.rm_rf(dir, :secure => true) if File.exist?(dir)
18
+ end
19
+ def initialize(directory)
20
+ root = File.join(File.expand_path("."), "data", "cache")
21
+ @directory = File.join(root, directory)
22
+ FileUtils.mkdir_p(@directory)
23
+ end
24
+
25
+ def read(key)
26
+ file_name = File.join(@directory, sanitized_file_name_from(key))
27
+ return nil unless File.exist?(file_name)
28
+ YAML.load(File.read(file_name))
29
+ end
30
+
31
+ def write(key, value)
32
+ file_name = File.join(@directory, sanitized_file_name_from(key))
33
+ directory = File.dirname(file_name)
34
+ FileUtils.mkdir_p(directory) unless File.exist?(directory)
35
+ File.open(file_name, 'w') {|f| f.write(YAML.dump(value)) }
36
+ value
37
+ end
38
+
39
+ private
40
+
41
+ def sanitized_file_name_from(file_name)
42
+ parts = file_name.to_s.split('.')
43
+ file_extension = '.' + parts.pop if parts.size > 1
44
+ base = parts.join('.').gsub(/[^\w\-\/]+/, '_') + file_extension.to_s
45
+ "#{base}.yml"
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,92 @@
1
+ <%
2
+ data_type = "commits" if data_type == "count"
3
+ other_type = "commits" if other_type == "count"
4
+ title = "#{data_type.titleize} by Week: #{username}"
5
+ %>
6
+
7
+ <html>
8
+ <head>
9
+ <title><%= title %></title>
10
+ <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
11
+ <script src="http://code.highcharts.com/highcharts.js"></script>
12
+ <script src="http://code.highcharts.com/modules/exporting.js"></script>
13
+ </head>
14
+ <body>
15
+ <div id="container" style="min-width: 400px; height: 400px; margin: 0 auto"></div>
16
+
17
+ <script type="text/javascript">
18
+ //<![CDATA[
19
+ var chart = new Highcharts.Chart({
20
+ chart: {
21
+ renderTo: 'container',
22
+ },
23
+ plotOptions: {
24
+ series: {
25
+ shadow: false,
26
+ lineWidth: 1,
27
+ marker: {
28
+ enabled: false
29
+ }
30
+ },
31
+ area: {
32
+ stacking: 'normal'
33
+ }
34
+ },
35
+ title: {
36
+ text: '<%= title %>'
37
+ },
38
+ xAxis: {
39
+ categories: <%= labels.to_json %>
40
+ },
41
+ yAxis: [
42
+ { // data
43
+ title: {
44
+ text: ''
45
+ },
46
+ min: 0
47
+ },
48
+ { // other
49
+ title: {
50
+ text: ''
51
+ },
52
+ labels: {
53
+ style: {
54
+ color: '#5EAAE4'
55
+ }
56
+ },
57
+ opposite: true,
58
+ min: 0
59
+ }
60
+ ],
61
+ tooltip: {
62
+ formatter: function() {
63
+ return '' + this.series.name + ': ' + this.y.toString();
64
+ }
65
+ },
66
+ credits: {
67
+ enabled: false
68
+ },
69
+ series: [
70
+ <% data.each do |key, array| %>
71
+ {
72
+ name: '<%= key.include?('/') ? key : key.titleize %>',
73
+ <%= "color: '#F17F49'," if data.size == 1 %>
74
+
75
+ type: 'area',
76
+ data: <%= array.to_json %>
77
+ },
78
+ <% end %>
79
+ {
80
+ name: '<%= other_type.titleize %>',
81
+ color: '#5EAAE4',
82
+ type: 'line',
83
+ dashStyle: 'LongDash',
84
+ yAxis: 1,
85
+ data: <%= other.to_json %>
86
+ },
87
+ ]
88
+ });
89
+ //]]>
90
+ </script>
91
+ </body>
92
+ </html>
@@ -0,0 +1,90 @@
1
+ <%
2
+ title = "Impact by Week: #{username}"
3
+ %>
4
+
5
+ <html>
6
+ <head>
7
+ <title><%= title %></title>
8
+ <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
9
+ <script src="http://code.highcharts.com/highcharts.js"></script>
10
+ <script src="http://code.highcharts.com/modules/exporting.js"></script>
11
+ </head>
12
+ <body>
13
+ <div id="container" style="min-width: 400px; height: 400px; margin: 0 auto"></div>
14
+
15
+ <script type="text/javascript">
16
+ //<![CDATA[
17
+ var chart = new Highcharts.Chart({
18
+ chart: {
19
+ renderTo: 'container',
20
+ },
21
+ plotOptions: {
22
+ series: {
23
+ shadow: false,
24
+ lineWidth: 1,
25
+ marker: {
26
+ enabled: false
27
+ }
28
+ }
29
+ },
30
+ title: {
31
+ text: '<%= title %>'
32
+ },
33
+ xAxis: {
34
+ categories: <%= labels.to_json %>
35
+ },
36
+ yAxis: [
37
+ { // additions and deletions
38
+ title: {
39
+ text: ''
40
+ }
41
+ },
42
+ { // total impact
43
+ title: {
44
+ text: ''
45
+ },
46
+ labels: {
47
+ style: {
48
+ color: '#047DDA'
49
+ }
50
+ },
51
+ opposite: true,
52
+ min: 0
53
+ }
54
+ ],
55
+ tooltip: {
56
+ formatter: function() {
57
+ if(this.series.name == 'Deletions')
58
+ return (-1 * this.y).toString();
59
+ else
60
+ return this.y.toString();
61
+ }
62
+ },
63
+ credits: {
64
+ enabled: false
65
+ },
66
+ series: [{
67
+ name: 'Additions',
68
+ color: '#1DB34F',
69
+ type: 'area',
70
+ data: <%= additions.to_json %>
71
+ }, {
72
+ name: 'Deletions',
73
+ color: '#AD1017',
74
+ type: 'area',
75
+ data: <%= deletions.collect{|d| -1*d }.to_json %>
76
+ },
77
+ {
78
+ name: 'Total Impact',
79
+ color: '#047DDA',
80
+ type: 'line',
81
+ //dashStyle: 'LongDash',
82
+ yAxis: 1,
83
+ data: <%= impacts.to_json %>
84
+ },
85
+ ]
86
+ });
87
+ //]]>
88
+ </script>
89
+ </body>
90
+ </html>
@@ -0,0 +1,51 @@
1
+ <%
2
+ data_type = "commits" if data_type == "count"
3
+ title = "#{data_type.titleize} by Repository: #{username}"
4
+ %>
5
+
6
+ <html>
7
+ <head>
8
+ <title><%= title %></title>
9
+ <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
10
+ <script src="http://code.highcharts.com/highcharts.js"></script>
11
+ <script src="http://code.highcharts.com/modules/exporting.js"></script>
12
+ </head>
13
+ <body>
14
+ <div id="container" style="min-width: 400px; height: 400px; margin: 0 auto"></div>
15
+
16
+ <script type="text/javascript">
17
+ //<![CDATA[
18
+ var chart = new Highcharts.Chart({
19
+ chart: {
20
+ renderTo: 'container',
21
+ plotBackgroundColor: null,
22
+ plotBorderWidth: null,
23
+ plotShadow: false
24
+ },
25
+ title: {
26
+ text: '<%= title %>'
27
+ },
28
+ tooltip: {
29
+ pointFormat: '{series.name}: <b>{point.percentage}%</b>',
30
+ percentageDecimals: 1
31
+ },
32
+ plotOptions: {
33
+ pie: {
34
+ allowPointSelect: true,
35
+ cursor: 'pointer',
36
+ dataLabels: {
37
+ enabled: false
38
+ },
39
+ showInLegend: true
40
+ }
41
+ },
42
+ series: [{
43
+ type: 'pie',
44
+ name: 'Repositories',
45
+ data: <%= data.to_json %>
46
+ }]
47
+ });
48
+ //]]>
49
+ </script>
50
+ </body>
51
+ </html>
@@ -0,0 +1,42 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ module Hubtime
4
+ class Commit
5
+ attr_reader :repo_name, :sha, :additions, :deletions, :committer, :time
6
+ def initialize(hash, repo_name = nil, committer = nil)
7
+ @repo_name = repo_name # or figure it out
8
+ @sha = hash["sha"]
9
+ @additions = hash["stats"]["additions"].to_i
10
+ @deletions = hash["stats"]["deletions"].to_i
11
+ @committer = committer # or figure it out
12
+ @time = parse_time(hash)
13
+ end
14
+
15
+ def to_s
16
+ "#{time.strftime('%F')} commit: #{repo_name} : #{sha} by #{committer} with #{additions} additions and #{deletions} deletions}"
17
+ end
18
+
19
+ def count
20
+ return 0 if impact <= 0
21
+ 1
22
+ end
23
+
24
+ def impact
25
+ additions + deletions
26
+ end
27
+
28
+
29
+ protected
30
+
31
+ def parse_time(hash)
32
+ return nil unless hash["commit"]
33
+ if hash["commit"]["author"]
34
+ return Time.parse(hash["commit"]["author"]["date"]) if hash["commit"]["author"]["date"]
35
+ end
36
+ if hash["commit"]["committer"]
37
+ return Time.parse(hash["commit"]["committer"]["date"]) if hash["commit"]["committer"]["date"]
38
+ end
39
+ return nil
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,67 @@
1
+ # -*- encoding : utf-8 -*-
2
+ Octokit.user_agent = "Hubtime : Octokit Ruby Gem #{Octokit::VERSION}"
3
+
4
+ module Hubtime
5
+ class GithubService
6
+ def self.owner
7
+ @owner ||= self.new
8
+ end
9
+
10
+ attr_reader :client
11
+ def initialize
12
+ @client = Octokit::Client.new(:login => HubConfig.user, :password => HubConfig.password, :auto_traversal => true)
13
+ # puts @client.ratelimit_remaining
14
+ end
15
+
16
+ def commits(username, start_time, end_time, &block)
17
+ self.repositories(username).each do |repo_name|
18
+ puts "#{repo_name}"
19
+ repo = Repo.new(repo_name, username, start_time, end_time)
20
+ repo.commits do |commit|
21
+ block.call commit
22
+ end
23
+ end
24
+ end
25
+
26
+ def repositories(username)
27
+ repos = []
28
+
29
+ client.repositories.each do |hash|
30
+ repos << hash.full_name
31
+ end
32
+
33
+ unless username == client.login
34
+ client.repositories(username).each do |hash|
35
+ repos << hash.full_name
36
+ end
37
+ end
38
+
39
+ self.organizations(username).each do |org_name|
40
+ client.organization_repositories(org_name).each do |hash|
41
+ repos << hash.full_name
42
+ end
43
+ end
44
+
45
+ # return these, ignoring requested ones
46
+ repos.compact.uniq - HubConfig.ignore
47
+ end
48
+
49
+ protected
50
+
51
+ def organizations(username)
52
+ names = []
53
+
54
+ client.organizations.each do |hash|
55
+ names << hash.login
56
+ end
57
+
58
+ unless username == client.login
59
+ client.organizations(username).each do |hash|
60
+ names << hash.login
61
+ end
62
+ end
63
+
64
+ names.compact.uniq
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,122 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'openssl'
4
+ require 'digest/sha2'
5
+
6
+ module Hubtime
7
+ class HubConfig
8
+ def self.instance
9
+ @config ||= HubConfig.new
10
+ end
11
+
12
+ def self.auth(user, password)
13
+ store(user, password)
14
+ end
15
+
16
+ def self.store(user, password)
17
+ instance.store(user, password)
18
+ end
19
+
20
+ def self.user
21
+ instance.user
22
+ end
23
+
24
+ def self.password
25
+ instance.password
26
+ end
27
+
28
+ def self.display_password
29
+ '*' * password.size
30
+ end
31
+
32
+ def self.ignore
33
+ instance.ignore
34
+ end
35
+
36
+ def self.add_ignore(repo_name)
37
+ instance.add_ignore(repo_name)
38
+ end
39
+
40
+ def self.threads
41
+ 8
42
+ end
43
+
44
+ attr_reader :user, :password, :ignore
45
+ def initialize
46
+ @file_name = "config"
47
+ hash = read_file
48
+ @user = hash["user"]
49
+ @password = Stuff.decrypt(hash["password"])
50
+ @ignore = hash["ignore"] || []
51
+ end
52
+
53
+ def read_file
54
+ YAML.load_file(file)
55
+ end
56
+
57
+ def write_file!
58
+ hash = {}
59
+ ["user", "password", "ignore"].each do |key|
60
+ hash[key] = instance_variable_get("@#{key}")
61
+ end
62
+
63
+ hash["password"] = Stuff.encrypt(hash["password"])
64
+
65
+ File.open(file, 'w' ) do |out|
66
+ YAML.dump(hash, out)
67
+ end
68
+ end
69
+
70
+ def file
71
+ file = File.join(File.expand_path("."), "hubtime_config.yml")
72
+
73
+ unless File.exists?(file)
74
+ File.open(file, 'w' ) do |out|
75
+ YAML.dump({}, out )
76
+ end
77
+ end
78
+ file
79
+ end
80
+
81
+ def add_ignore(repo_name)
82
+ @ignore << repo_name
83
+ @ignore.uniq!
84
+ write_file!
85
+ end
86
+
87
+ def store(user, password)
88
+ @user = user
89
+ @password = password
90
+ write_file!
91
+ end
92
+
93
+ class Stuff
94
+ def self.key
95
+ sha256 = Digest::SHA2.new(256)
96
+ sha256.digest("better than plain text")
97
+ end
98
+ def self.iv
99
+ "kjfdhkkkjkjhfdskljghfkdjhags"
100
+ end
101
+ def self.encrypt(payload)
102
+ return nil if payload == nil
103
+ aes = OpenSSL::Cipher.new("AES-256-CFB")
104
+ aes.encrypt
105
+ aes.key = key
106
+ aes.iv = iv
107
+
108
+ aes.update(payload) + aes.final
109
+ end
110
+
111
+ def self.decrypt(encrypted)
112
+ return nil if encrypted == nil
113
+
114
+ aes = OpenSSL::Cipher.new("AES-256-CFB")
115
+ aes.decrypt
116
+ aes.key = key
117
+ aes.iv = iv
118
+ aes.update(encrypted) + aes.final
119
+ end
120
+ end
121
+ end
122
+ end