hubtime 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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