gh-issues-stats 0.4.0
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.
- checksums.yaml +7 -0
- data/bin/gh-issues-stats +6 -0
- data/lib/gh-issues-stats.rb +16 -0
- data/lib/github/issues/app/base.rb +128 -0
- data/lib/github/issues/app/labels.rb +38 -0
- data/lib/github/issues/app/mixins/chart.rb +116 -0
- data/lib/github/issues/app/mixins/exec.rb +87 -0
- data/lib/github/issues/app/mixins/legend.rb +40 -0
- data/lib/github/issues/app/mixins/options.rb +75 -0
- data/lib/github/issues/app/mixins/statistics.rb +42 -0
- data/lib/github/issues/app/mixins/table.rb +109 -0
- data/lib/github/issues/app/month.rb +31 -0
- data/lib/github/issues/app/year.rb +32 -0
- data/lib/github/issues/app.rb +21 -0
- data/lib/github/issues/database.rb +133 -0
- data/lib/github/issues/mixins/database/marshal.rb +37 -0
- data/lib/github/issues/mixins/database/schema.rb +38 -0
- data/lib/github/issues/mixins/database/store.rb +100 -0
- data/lib/github/issues/mixins/fetch.rb +126 -0
- data/lib/github/issues/mixins/group.rb +105 -0
- data/lib/github/issues/mixins/statistics.rb +77 -0
- data/lib/github/issues/version.rb +7 -0
- data/lib/github/issues.rb +200 -0
- metadata +222 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Github
|
|
4
|
+
class Issues
|
|
5
|
+
##
|
|
6
|
+
# Mixin for calculating issues statistics
|
|
7
|
+
module Statistics
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
# Collects statistics about issues
|
|
12
|
+
#
|
|
13
|
+
# @param object [Hash] Hash containing :created, :closed, and :finished issue arrays
|
|
14
|
+
#
|
|
15
|
+
# @return [Hash] Hash containing calculated statistics
|
|
16
|
+
def stats_combined(object)
|
|
17
|
+
{
|
|
18
|
+
ratio: {
|
|
19
|
+
all: stats_closed_created_ratio(object[:closed], object[:created]),
|
|
20
|
+
finished: stats_closed_created_ratio(object[:finished], object[:created])
|
|
21
|
+
},
|
|
22
|
+
close_time: {
|
|
23
|
+
all: {
|
|
24
|
+
average: stats_average_closing_time(object[:closed]),
|
|
25
|
+
median: stats_median_closing_time(object[:closed])
|
|
26
|
+
},
|
|
27
|
+
finished: {
|
|
28
|
+
average: stats_average_closing_time(object[:finished]),
|
|
29
|
+
median: stats_median_closing_time(object[:finished])
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
##
|
|
36
|
+
# Calculates the ratio of closed issues to created issues
|
|
37
|
+
#
|
|
38
|
+
# @param closed [Array<Hashie::Mash>] Array of closed issues
|
|
39
|
+
# @param created [Array<Hashie::Mash>] Array of created issues
|
|
40
|
+
#
|
|
41
|
+
# @return [Float] Ratio of closed to created issues
|
|
42
|
+
def stats_closed_created_ratio(closed, created)
|
|
43
|
+
return 0 if created.nil? || created.empty? || closed.nil? || closed.empty?
|
|
44
|
+
|
|
45
|
+
closed.size.to_f / created.size
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
##
|
|
49
|
+
# Calculates the average closing time for a set of issues
|
|
50
|
+
#
|
|
51
|
+
# @param closed [Array<Hashie::Mash>] Array of closed issues
|
|
52
|
+
#
|
|
53
|
+
# @return [Float] Average closing time in seconds
|
|
54
|
+
def stats_average_closing_time(closed)
|
|
55
|
+
return 0 if closed.nil? || closed.empty?
|
|
56
|
+
|
|
57
|
+
total = closed.sum { |issue| issue.closed_at - issue.created_at }
|
|
58
|
+
total.to_f / closed.size
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
##
|
|
62
|
+
# Calculates the median closing time for a set of issues
|
|
63
|
+
#
|
|
64
|
+
# @param closed [Array<Hashie::Mash>] Array of closed issues
|
|
65
|
+
#
|
|
66
|
+
# @return [Float] Median closing time in seconds
|
|
67
|
+
def stats_median_closing_time(closed)
|
|
68
|
+
return 0 if closed.nil? || closed.empty?
|
|
69
|
+
|
|
70
|
+
times = closed.map { |issue| issue.closed_at - issue.created_at }
|
|
71
|
+
times.sort!
|
|
72
|
+
size = times.size
|
|
73
|
+
(times[(size - 1) / 2] + times[size / 2]) / 2.0
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'hashie'
|
|
5
|
+
|
|
6
|
+
module Github
|
|
7
|
+
##
|
|
8
|
+
# Main class for analyzing GitHub issues
|
|
9
|
+
class Issues
|
|
10
|
+
include Statistics
|
|
11
|
+
include Fetch
|
|
12
|
+
include Group
|
|
13
|
+
|
|
14
|
+
CACHE_DIR = File.join(Dir.home, '.cache', 'gh-issues-stats').freeze
|
|
15
|
+
DATABASE_NAME = 'issues.db'
|
|
16
|
+
|
|
17
|
+
##
|
|
18
|
+
# Repository to analyze (e.g., 'owner/repo')
|
|
19
|
+
attr_reader :repository
|
|
20
|
+
|
|
21
|
+
## Cache path for storing database file
|
|
22
|
+
attr_reader :cache
|
|
23
|
+
|
|
24
|
+
##
|
|
25
|
+
# Credentials for GitHub API access
|
|
26
|
+
attr_reader :credentials
|
|
27
|
+
|
|
28
|
+
##
|
|
29
|
+
# Database instance for storing and retrieving issues
|
|
30
|
+
attr_reader :database
|
|
31
|
+
|
|
32
|
+
##
|
|
33
|
+
# Refresh interval in seconds for updating the stored issues
|
|
34
|
+
attr_reader :refresh
|
|
35
|
+
|
|
36
|
+
##
|
|
37
|
+
# Initialize the Issues analyzer
|
|
38
|
+
#
|
|
39
|
+
# @param repository [String] GitHub repository (e.g., 'owner/repo')
|
|
40
|
+
# @param cache [String] Path to cache directory
|
|
41
|
+
# @param credentials [Hash] Optional GitHub API credentials
|
|
42
|
+
# @param refresh [Integer] Refresh interval in seconds (default: 86400)
|
|
43
|
+
#
|
|
44
|
+
# @return [Github::Issues] The initialized issues analyzer
|
|
45
|
+
def initialize(repository, cache: CACHE_DIR, credentials: {}, refresh: 86_400)
|
|
46
|
+
@repository = repository
|
|
47
|
+
@credentials = credentials
|
|
48
|
+
@refresh = refresh
|
|
49
|
+
|
|
50
|
+
dir = File.join(cache, repository)
|
|
51
|
+
FileUtils.mkdir_p(dir)
|
|
52
|
+
@cache = dir
|
|
53
|
+
|
|
54
|
+
database_path = File.join(dir, DATABASE_NAME)
|
|
55
|
+
github_repository_exist!(credentials, repository) unless File.exist?(database_path)
|
|
56
|
+
@database = Database.new(database_path)
|
|
57
|
+
|
|
58
|
+
at_exit do
|
|
59
|
+
database.close
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
##
|
|
64
|
+
# Get all unique labels from the issues
|
|
65
|
+
#
|
|
66
|
+
# @return [Array<String>] List of unique labels
|
|
67
|
+
def labels
|
|
68
|
+
ensure_issues_loaded(database, refresh, credentials, repository)
|
|
69
|
+
database.all_labels.sort.reverse
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
##
|
|
73
|
+
# List all issues
|
|
74
|
+
#
|
|
75
|
+
# @return [Array<Hashie::Mash>] List of issues
|
|
76
|
+
def all
|
|
77
|
+
ensure_issues_loaded(database, refresh, credentials, repository)
|
|
78
|
+
fetch_issues_from_database(database)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
##
|
|
82
|
+
# List issues filtered by labels
|
|
83
|
+
#
|
|
84
|
+
# @param labels [Array<String>] List of labels to filter issues
|
|
85
|
+
#
|
|
86
|
+
# @return [Array<Hashie::Mash>] List of filtered issues
|
|
87
|
+
def filtered_by_labels(labels)
|
|
88
|
+
ensure_issues_loaded(database, refresh, credentials, repository)
|
|
89
|
+
fetch_issues_from_database(database, labels:)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
##
|
|
93
|
+
# Total average closing time of all issues
|
|
94
|
+
#
|
|
95
|
+
# @return [Float] Average closing time in seconds
|
|
96
|
+
def all_average_closing_time
|
|
97
|
+
average_closing_time_filtered_by_labels([])
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
##
|
|
101
|
+
# Total average closing time of all issues filtered by labels
|
|
102
|
+
#
|
|
103
|
+
# @param labels [Array<String>] List of labels to filter issues
|
|
104
|
+
#
|
|
105
|
+
# @return [Float] average closing time in seconds
|
|
106
|
+
def average_closing_time_filtered_by_labels(labels)
|
|
107
|
+
ensure_issues_loaded(database, refresh, credentials, repository)
|
|
108
|
+
list = fetch_issues_from_database(database, labels:)
|
|
109
|
+
return 0 if list.empty?
|
|
110
|
+
|
|
111
|
+
closed = list.select { |issue| issue.state == 'closed' }
|
|
112
|
+
stats_average_closing_time(closed)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
##
|
|
116
|
+
# Total median closing time of all issues
|
|
117
|
+
#
|
|
118
|
+
# @return [Float] Median closing time in seconds
|
|
119
|
+
def all_median_closing_time
|
|
120
|
+
median_closing_time_filtered_by_labels([])
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
##
|
|
124
|
+
# Total median closing time of all issues filtered by labels
|
|
125
|
+
#
|
|
126
|
+
# @param labels [Array<String>] List of labels to filter issues
|
|
127
|
+
#
|
|
128
|
+
# @return [Float] Median closing time in seconds
|
|
129
|
+
def median_closing_time_filtered_by_labels(labels)
|
|
130
|
+
ensure_issues_loaded(database, refresh, credentials, repository)
|
|
131
|
+
list = fetch_issues_from_database(database, labels:)
|
|
132
|
+
return 0 if list.empty?
|
|
133
|
+
|
|
134
|
+
closed = list.select { |issue| issue.state == 'closed' }
|
|
135
|
+
stats_median_closing_time(closed)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
##
|
|
139
|
+
# Issues grouped per year with optional statistics
|
|
140
|
+
#
|
|
141
|
+
# @param stats [Boolean] Whether to include statistics for each year
|
|
142
|
+
#
|
|
143
|
+
# @return [Hashie::Mash] Issues grouped by year with optional statistics
|
|
144
|
+
def per_year(stats: true)
|
|
145
|
+
per_year_filtered_by_labels([], stats:)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
##
|
|
149
|
+
# Issues grouped per year filtered by labels with optional statistics
|
|
150
|
+
#
|
|
151
|
+
# @param labels [Array<String>] List of labels to filter issues
|
|
152
|
+
# @param stats [Boolean] Whether to include statistics for each year
|
|
153
|
+
#
|
|
154
|
+
# @return [Hashie::Mash] Issues grouped by year with optional statistics
|
|
155
|
+
def per_year_filtered_by_labels(labels, stats: true)
|
|
156
|
+
ensure_issues_loaded(database, refresh, credentials, repository)
|
|
157
|
+
list = fetch_issues_from_database(database, labels:)
|
|
158
|
+
return if list.empty?
|
|
159
|
+
|
|
160
|
+
hash = group_by_period(list)
|
|
161
|
+
hash.each_key { |year| hash[year][:stats] = stats_combined(hash[year]) } if stats
|
|
162
|
+
|
|
163
|
+
Hashie::Mash.new(hash)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
##
|
|
167
|
+
# Issues grouped per month for a specific year with optional statistics
|
|
168
|
+
#
|
|
169
|
+
# @param year [Integer] Year to filter issues
|
|
170
|
+
# @param stats [Boolean] Whether to include statistics for each month
|
|
171
|
+
#
|
|
172
|
+
# @return [Hashie::Mash] Issues grouped by month with optional statistics
|
|
173
|
+
def per_month(year, stats: true)
|
|
174
|
+
per_month_filtered_by_labels(year, [], stats:)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
##
|
|
178
|
+
# Issues grouped per month for a specific year filtered by labels with
|
|
179
|
+
# optional statistics
|
|
180
|
+
#
|
|
181
|
+
# @param year [Integer] Year to filter issues
|
|
182
|
+
# @param labels [Array<String>] List of labels to filter issues
|
|
183
|
+
# @param stats [Boolean] Whether to include statistics for each month
|
|
184
|
+
#
|
|
185
|
+
# @return [Hashie::Mash] Issues grouped by month with optional statistics
|
|
186
|
+
def per_month_filtered_by_labels(year, labels, stats: true)
|
|
187
|
+
ensure_issues_loaded(database, refresh, credentials, repository)
|
|
188
|
+
list = fetch_issues_from_database(database, labels:)
|
|
189
|
+
return if list.empty?
|
|
190
|
+
|
|
191
|
+
list = list.select { |issue| issue.created_at.year == year }
|
|
192
|
+
return if list.empty?
|
|
193
|
+
|
|
194
|
+
hash = group_by_period(list, period: :month)
|
|
195
|
+
hash.each_key { |month| hash[month][:stats] = stats_combined(hash[month]) } if stats
|
|
196
|
+
|
|
197
|
+
Hashie::Mash.new(hash)
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: gh-issues-stats
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.4.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Tobias Schäfer
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: clamp
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 1.3.2
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 1.3.2
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: faraday-retry
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 2.4.0
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 2.4.0
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: hashie
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 5.1.0
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: 5.1.0
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: octokit
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: 10.0.0
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: 10.0.0
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: pastel
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: 0.8.0
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: 0.8.0
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: sqlite3
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - "~>"
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: 2.9.0
|
|
89
|
+
type: :runtime
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - "~>"
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: 2.9.0
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: tty-pager
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - "~>"
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: 0.14.0
|
|
103
|
+
type: :runtime
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - "~>"
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: 0.14.0
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: tty-spinner
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - "~>"
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: 0.9.3
|
|
117
|
+
type: :runtime
|
|
118
|
+
prerelease: false
|
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - "~>"
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: 0.9.3
|
|
124
|
+
- !ruby/object:Gem::Dependency
|
|
125
|
+
name: tty-table
|
|
126
|
+
requirement: !ruby/object:Gem::Requirement
|
|
127
|
+
requirements:
|
|
128
|
+
- - "~>"
|
|
129
|
+
- !ruby/object:Gem::Version
|
|
130
|
+
version: 0.12.0
|
|
131
|
+
type: :runtime
|
|
132
|
+
prerelease: false
|
|
133
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - "~>"
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: 0.12.0
|
|
138
|
+
- !ruby/object:Gem::Dependency
|
|
139
|
+
name: unicode_plot
|
|
140
|
+
requirement: !ruby/object:Gem::Requirement
|
|
141
|
+
requirements:
|
|
142
|
+
- - "~>"
|
|
143
|
+
- !ruby/object:Gem::Version
|
|
144
|
+
version: 0.0.5
|
|
145
|
+
type: :runtime
|
|
146
|
+
prerelease: false
|
|
147
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
148
|
+
requirements:
|
|
149
|
+
- - "~>"
|
|
150
|
+
- !ruby/object:Gem::Version
|
|
151
|
+
version: 0.0.5
|
|
152
|
+
- !ruby/object:Gem::Dependency
|
|
153
|
+
name: zeitwerk
|
|
154
|
+
requirement: !ruby/object:Gem::Requirement
|
|
155
|
+
requirements:
|
|
156
|
+
- - "~>"
|
|
157
|
+
- !ruby/object:Gem::Version
|
|
158
|
+
version: 2.7.1
|
|
159
|
+
type: :runtime
|
|
160
|
+
prerelease: false
|
|
161
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
162
|
+
requirements:
|
|
163
|
+
- - "~>"
|
|
164
|
+
- !ruby/object:Gem::Version
|
|
165
|
+
version: 2.7.1
|
|
166
|
+
description: 'Analyse Github repository issues lifecycle.
|
|
167
|
+
|
|
168
|
+
'
|
|
169
|
+
email:
|
|
170
|
+
- github@blackox.org
|
|
171
|
+
executables:
|
|
172
|
+
- gh-issues-stats
|
|
173
|
+
extensions: []
|
|
174
|
+
extra_rdoc_files: []
|
|
175
|
+
files:
|
|
176
|
+
- bin/gh-issues-stats
|
|
177
|
+
- lib/gh-issues-stats.rb
|
|
178
|
+
- lib/github/issues.rb
|
|
179
|
+
- lib/github/issues/app.rb
|
|
180
|
+
- lib/github/issues/app/base.rb
|
|
181
|
+
- lib/github/issues/app/labels.rb
|
|
182
|
+
- lib/github/issues/app/mixins/chart.rb
|
|
183
|
+
- lib/github/issues/app/mixins/exec.rb
|
|
184
|
+
- lib/github/issues/app/mixins/legend.rb
|
|
185
|
+
- lib/github/issues/app/mixins/options.rb
|
|
186
|
+
- lib/github/issues/app/mixins/statistics.rb
|
|
187
|
+
- lib/github/issues/app/mixins/table.rb
|
|
188
|
+
- lib/github/issues/app/month.rb
|
|
189
|
+
- lib/github/issues/app/year.rb
|
|
190
|
+
- lib/github/issues/database.rb
|
|
191
|
+
- lib/github/issues/mixins/database/marshal.rb
|
|
192
|
+
- lib/github/issues/mixins/database/schema.rb
|
|
193
|
+
- lib/github/issues/mixins/database/store.rb
|
|
194
|
+
- lib/github/issues/mixins/fetch.rb
|
|
195
|
+
- lib/github/issues/mixins/group.rb
|
|
196
|
+
- lib/github/issues/mixins/statistics.rb
|
|
197
|
+
- lib/github/issues/version.rb
|
|
198
|
+
homepage: https://github.com/tschaefer/gh-issues-stats
|
|
199
|
+
licenses:
|
|
200
|
+
- MIT
|
|
201
|
+
metadata:
|
|
202
|
+
rubygems_mfa_required: 'true'
|
|
203
|
+
source_code_uri: https://github.com/tschaefer/gh-issues-stats
|
|
204
|
+
bug_tracker_uri: https://github.com/tschaefer/gh-issues-stats/issues
|
|
205
|
+
rdoc_options: []
|
|
206
|
+
require_paths:
|
|
207
|
+
- lib
|
|
208
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
209
|
+
requirements:
|
|
210
|
+
- - ">="
|
|
211
|
+
- !ruby/object:Gem::Version
|
|
212
|
+
version: 3.3.0
|
|
213
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
214
|
+
requirements:
|
|
215
|
+
- - ">="
|
|
216
|
+
- !ruby/object:Gem::Version
|
|
217
|
+
version: '0'
|
|
218
|
+
requirements: []
|
|
219
|
+
rubygems_version: 4.0.4
|
|
220
|
+
specification_version: 4
|
|
221
|
+
summary: Analyse Github repository issues lifecycle.
|
|
222
|
+
test_files: []
|