inq 26.0.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/.cirrus.yml +84 -0
- data/.codeclimate.yml +23 -0
- data/.github_changelog_generator +2 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/.rubocop.yml +260 -0
- data/.travis.yml +24 -0
- data/CHANGELOG.md +499 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/CONTRIBUTING.md +34 -0
- data/Gemfile +15 -0
- data/ISSUES.md +62 -0
- data/LICENSE.txt +21 -0
- data/README.md +91 -0
- data/Rakefile +76 -0
- data/bin/console +14 -0
- data/bin/prerelease-generate-changelog +28 -0
- data/bin/setup +8 -0
- data/bors.toml +17 -0
- data/build-debug.rb +20 -0
- data/exe/inq +7 -0
- data/fixtures/vcr_cassettes/how-is-example-empty-repository.yml +597 -0
- data/fixtures/vcr_cassettes/how-is-example-repository.yml +768 -0
- data/fixtures/vcr_cassettes/how-is-from-config-frontmatter.yml +23940 -0
- data/fixtures/vcr_cassettes/how-is-how-is-travis-api-repos-builds.yml +66 -0
- data/fixtures/vcr_cassettes/how-is-with-config-file.yml +23940 -0
- data/fixtures/vcr_cassettes/how_is_contributions_additions_count.yml +247 -0
- data/fixtures/vcr_cassettes/how_is_contributions_all_contributors.yml +247 -0
- data/fixtures/vcr_cassettes/how_is_contributions_changed_files.yml +247 -0
- data/fixtures/vcr_cassettes/how_is_contributions_changes.yml +247 -0
- data/fixtures/vcr_cassettes/how_is_contributions_commits.yml +247 -0
- data/fixtures/vcr_cassettes/how_is_contributions_compare_url.yml +81 -0
- data/fixtures/vcr_cassettes/how_is_contributions_default_branch.yml +81 -0
- data/fixtures/vcr_cassettes/how_is_contributions_deletions_count.yml +247 -0
- data/fixtures/vcr_cassettes/how_is_contributions_new_contributors.yml +402 -0
- data/fixtures/vcr_cassettes/how_is_contributions_summary.yml +325 -0
- data/fixtures/vcr_cassettes/how_is_contributions_summary_2.yml +325 -0
- data/inq.gemspec +45 -0
- data/lib/inq.rb +63 -0
- data/lib/inq/cacheable.rb +71 -0
- data/lib/inq/cli.rb +135 -0
- data/lib/inq/config.rb +123 -0
- data/lib/inq/constants.rb +9 -0
- data/lib/inq/date_time_helpers.rb +48 -0
- data/lib/inq/exe.rb +66 -0
- data/lib/inq/frontmatter.rb +51 -0
- data/lib/inq/report.rb +140 -0
- data/lib/inq/report_collection.rb +113 -0
- data/lib/inq/sources.rb +11 -0
- data/lib/inq/sources/ci/appveyor.rb +87 -0
- data/lib/inq/sources/ci/travis.rb +159 -0
- data/lib/inq/sources/github.rb +57 -0
- data/lib/inq/sources/github/contributions.rb +204 -0
- data/lib/inq/sources/github/issue_fetcher.rb +148 -0
- data/lib/inq/sources/github/issues.rb +126 -0
- data/lib/inq/sources/github/pulls.rb +29 -0
- data/lib/inq/sources/github_helpers.rb +106 -0
- data/lib/inq/template.rb +9 -0
- data/lib/inq/templates/contributions_partial.html +1 -0
- data/lib/inq/templates/issues_or_pulls_partial.html +7 -0
- data/lib/inq/templates/new_contributors_partial.html +5 -0
- data/lib/inq/templates/report.html +19 -0
- data/lib/inq/templates/report_partial.html +12 -0
- data/lib/inq/text.rb +26 -0
- data/lib/inq/version.rb +6 -0
- metadata +263 -0
data/lib/inq/cli.rb
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "inq"
|
4
|
+
require "inq/constants"
|
5
|
+
require "okay/simple_opts"
|
6
|
+
|
7
|
+
module Inq
|
8
|
+
##
|
9
|
+
# Parses command-line arguments for inq.
|
10
|
+
class CLI
|
11
|
+
MissingArgument = Class.new(OptionParser::MissingArgument)
|
12
|
+
|
13
|
+
REPO_REGEXP = /.+\/.+/
|
14
|
+
DATE_REGEXP = /\d\d\d\d-\d\d-\d\d/
|
15
|
+
|
16
|
+
attr_reader :options, :help_text
|
17
|
+
|
18
|
+
def self.parse(*args)
|
19
|
+
new.parse(*args)
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@options = nil
|
24
|
+
@help_text = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
# Parses an Array of command-line arguments into an equivalent Hash.
|
28
|
+
#
|
29
|
+
# The results of this can be used to control the behavior of the rest
|
30
|
+
# of the library.
|
31
|
+
#
|
32
|
+
# @params argv [Array] An array of command-line arguments, e.g. +ARGV+.
|
33
|
+
# @return [Hash] A Hash containing data used to control Inq's behavior.
|
34
|
+
def parse(argv)
|
35
|
+
parser, options = parse_main(argv)
|
36
|
+
|
37
|
+
# Options that are mutually-exclusive with everything else.
|
38
|
+
options = {:help => true} if options[:help]
|
39
|
+
options = {:version => true} if options[:version]
|
40
|
+
|
41
|
+
validate_options!(options)
|
42
|
+
|
43
|
+
@options = options
|
44
|
+
@help_text = parser.to_s
|
45
|
+
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# parse_main() is as short as can be managed. It's fine as-is.
|
52
|
+
# rubocop:disable Metrics/MethodLength
|
53
|
+
|
54
|
+
# This does a significant chunk of the work for parse().
|
55
|
+
#
|
56
|
+
# @return [Array] An array containing the +OptionParser+ and the result
|
57
|
+
# of running it.
|
58
|
+
def parse_main(argv)
|
59
|
+
defaults = {
|
60
|
+
report: Inq::DEFAULT_REPORT_FILE,
|
61
|
+
}
|
62
|
+
|
63
|
+
opts = Okay::SimpleOpts.new(defaults: defaults)
|
64
|
+
|
65
|
+
opts.banner = <<~EOF
|
66
|
+
Usage: inq --repository REPOSITORY --date REPORT_DATE [--output REPORT_FILE]
|
67
|
+
inq --config CONFIG_FILE --date REPORT_DATE
|
68
|
+
EOF
|
69
|
+
|
70
|
+
opts.separator "\nOptions:"
|
71
|
+
|
72
|
+
opts.simple("--config CONFIG_FILE",
|
73
|
+
"YAML config file for automated reports.",
|
74
|
+
:config)
|
75
|
+
|
76
|
+
opts.simple("--no-user-config",
|
77
|
+
"Don't load user configuration file.",
|
78
|
+
:no_user_config)
|
79
|
+
|
80
|
+
opts.simple("--env-config",
|
81
|
+
"Use environment variables for configuration.",
|
82
|
+
"Read this first: https://inqrb.com/config",
|
83
|
+
:env_login)
|
84
|
+
|
85
|
+
opts.simple("--repository USER/REPO", REPO_REGEXP,
|
86
|
+
"Repository to generate a report for.",
|
87
|
+
:repository)
|
88
|
+
|
89
|
+
opts.simple("--date YYYY-MM-DD", DATE_REGEXP, "Last date of the report.",
|
90
|
+
:date)
|
91
|
+
|
92
|
+
opts.simple("--output REPORT_FILE", format_regexp,
|
93
|
+
"Output file for the report.",
|
94
|
+
"Supported file formats: #{formats}.",
|
95
|
+
:report)
|
96
|
+
|
97
|
+
opts.simple("--verbose", "Print debug information.", :verbose)
|
98
|
+
opts.simple("-v", "--version", "Prints version information.", :version)
|
99
|
+
opts.simple("-h", "--help", "Print help text.", :help)
|
100
|
+
|
101
|
+
[opts, opts.parse(argv)]
|
102
|
+
end
|
103
|
+
|
104
|
+
# rubocop:enable Metrics/MethodLength
|
105
|
+
|
106
|
+
# Given an +options+ Hash, determine if we got a valid combination of
|
107
|
+
# options.
|
108
|
+
#
|
109
|
+
# 1. Anything with `--help` and `--version` is always valid.
|
110
|
+
# 2. Otherwise, `--repository` or `--config` is required.
|
111
|
+
# 3. If `--repository` or `--config` is required, so is `--date`.
|
112
|
+
#
|
113
|
+
# @param options [Hash] The result of CLI#parse().
|
114
|
+
# @raise [MissingArgument] if we did not get a valid options Hash.
|
115
|
+
def validate_options!(options)
|
116
|
+
return if options[:help] || options[:version]
|
117
|
+
raise MissingArgument, "--date" unless options[:date]
|
118
|
+
raise MissingArgument, "--repository or --config" unless
|
119
|
+
options[:repository] || options[:config]
|
120
|
+
end
|
121
|
+
|
122
|
+
# @return [String] A comma-separated list of supported formats.
|
123
|
+
def formats
|
124
|
+
Inq.supported_formats.join(", ")
|
125
|
+
end
|
126
|
+
|
127
|
+
# @return [Regexp] a +Regexp+ object which matches any path ending
|
128
|
+
# with an extension corresponding to a supported format.
|
129
|
+
def format_regexp
|
130
|
+
regexp_parts = Inq.supported_formats.map { |x| Regexp.escape(x) }
|
131
|
+
|
132
|
+
/.+\.(#{regexp_parts.join("|")})/
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
data/lib/inq/config.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
require "inq/text"
|
5
|
+
|
6
|
+
module Inq
|
7
|
+
HOME_CONFIG = File.join(Dir.home, ".config", "inq", "config.yml")
|
8
|
+
|
9
|
+
# Usage:
|
10
|
+
# Inq::Config
|
11
|
+
# .load_site_configs("/path/to/config1.yml", "/path/to/config2.yml")
|
12
|
+
# .load_file("./repo-config.yml")
|
13
|
+
# Or:
|
14
|
+
# Inq::Config
|
15
|
+
# .load_defaults
|
16
|
+
# .load_file("./repo-config.yml")
|
17
|
+
# Or:
|
18
|
+
# Inq::Config
|
19
|
+
# .load_defaults
|
20
|
+
# .load({"repository" => "how-is/example-repository"})
|
21
|
+
class Config < Hash
|
22
|
+
attr_reader :site_configs
|
23
|
+
|
24
|
+
# If the INQ_USE_ENV+ environment variable is set, load config from
|
25
|
+
# the environment.
|
26
|
+
#
|
27
|
+
# Otherwise, load the the default config file.
|
28
|
+
#
|
29
|
+
# @return [Hash] A Hash representation of the config.
|
30
|
+
def load_defaults
|
31
|
+
if ENV["INQ_USE_ENV"] == "true"
|
32
|
+
load_env
|
33
|
+
else
|
34
|
+
load_site_configs(HOME_CONFIG)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize
|
39
|
+
super()
|
40
|
+
@site_configs = []
|
41
|
+
end
|
42
|
+
|
43
|
+
# Load the config files as specified via +files+.
|
44
|
+
#
|
45
|
+
# @param files [Array<String>] The path(s) for config files.
|
46
|
+
# @return [Config] The config hash. (+self+)
|
47
|
+
def load_site_configs(*files)
|
48
|
+
# Allows both:
|
49
|
+
# load_site_configs('foo', 'bar')
|
50
|
+
# load_site_configs(['foo', bar'])
|
51
|
+
# but not:
|
52
|
+
# load_site_configs(['foo'], 'bar')
|
53
|
+
files = files[0] if files.length == 1 && files[0].is_a?(Array)
|
54
|
+
|
55
|
+
load_files(*files)
|
56
|
+
end
|
57
|
+
|
58
|
+
# TODO: See if this can be consolidated with load_site_configs.
|
59
|
+
def load_files(*file_paths)
|
60
|
+
files = (site_configs + file_paths).map { |f| Pathname.new(f) }
|
61
|
+
|
62
|
+
# Keep only files that exist.
|
63
|
+
files.select!(&:file?)
|
64
|
+
|
65
|
+
# Load the YAML files into Hashes.
|
66
|
+
configs = files.map { |file| YAML.safe_load(file.read) }
|
67
|
+
|
68
|
+
# Apply configs.
|
69
|
+
load(*configs)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Take a collection of config hashes and cascade them, meaning values
|
73
|
+
# in later ones override values in earlier ones.
|
74
|
+
#
|
75
|
+
# E.g., this results in +{'a'=>'x', 'c'=>'d'}+:
|
76
|
+
# load({'a'=>'b'}, {'c'=>'d'}, {'a'=>'x'})
|
77
|
+
#
|
78
|
+
# And this results in +{'a'=>['b', 'c']}+:
|
79
|
+
# load({'a'=>['b']}, {'a'=>['c']})
|
80
|
+
#
|
81
|
+
# @param [Array<Hash>] The configuration hashes.
|
82
|
+
# @return [Config] The final configuration value.
|
83
|
+
def load(*configs)
|
84
|
+
configs.each do |config|
|
85
|
+
config.each do |k, v|
|
86
|
+
if self[k] && self[k].is_a?(Array)
|
87
|
+
self[k] += v
|
88
|
+
else
|
89
|
+
self[k] = v
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
self
|
95
|
+
end
|
96
|
+
|
97
|
+
# Load config info from environment variables.
|
98
|
+
#
|
99
|
+
# Supported environment variables:
|
100
|
+
# - INQ_GITHUB_TOKEN: a GitHub authentication token.
|
101
|
+
# - INQ_GITHUB_USERNAME: the GitHub username corresponding to the token.
|
102
|
+
#
|
103
|
+
# @return [Config] The resulting configuration.
|
104
|
+
def load_env
|
105
|
+
Inq::Text.puts "Using configuration from environment variables."
|
106
|
+
|
107
|
+
gh_token = ENV["INQ_GITHUB_TOKEN"] || ENV["HOWIS_GITHUB_TOKEN"]
|
108
|
+
gh_username = ENV["INQ_GITHUB_USERNAME"] || ENV["HOWIS_GITHUB_USERNAME"]
|
109
|
+
|
110
|
+
raise "INQ_GITHUB_TOKEN environment variable is not set" \
|
111
|
+
unless gh_token
|
112
|
+
raise "INQ_GITHUB_USERNAME environment variable is not set" \
|
113
|
+
unless gh_username
|
114
|
+
|
115
|
+
load({
|
116
|
+
"sources/github" => {
|
117
|
+
"username" => gh_username,
|
118
|
+
"token" => gh_token,
|
119
|
+
},
|
120
|
+
})
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Inq
|
4
|
+
# The file name used for a report if one isn't specified.
|
5
|
+
DEFAULT_REPORT_FILE = "report.html"
|
6
|
+
|
7
|
+
# Used by things making HTTP requests.
|
8
|
+
USER_AGENT = "inq/#{Inq::VERSION} (https://github.com/duckinator/inq)"
|
9
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "inq/version"
|
4
|
+
require "date"
|
5
|
+
|
6
|
+
module Inq
|
7
|
+
##
|
8
|
+
# Various helper functions for working with DateTime objects.
|
9
|
+
module DateTimeHelpers
|
10
|
+
# Check if +left+ is less than or equal to +right+, where both are string
|
11
|
+
# representations of a date.
|
12
|
+
#
|
13
|
+
# @param left [String] A string representation of a date.
|
14
|
+
# @param right [String] A string representation of a date.
|
15
|
+
# @return [Boolean] True if +left+ is less-than-or-equal to +right+,
|
16
|
+
# otherwise false.
|
17
|
+
def date_le(left, right)
|
18
|
+
left = str_to_dt(left)
|
19
|
+
right = str_to_dt(right)
|
20
|
+
|
21
|
+
left <= right
|
22
|
+
end
|
23
|
+
|
24
|
+
# Check if +left+ is greater than or equal to +right+, where both are string
|
25
|
+
# representations of a date.
|
26
|
+
#
|
27
|
+
# @param left [String] A string representation of a date.
|
28
|
+
# @param right [String] A string representation of a date.
|
29
|
+
# @return [Boolean] True if +left+ is greater-than-or-equal to +right+,
|
30
|
+
# otherwise false.
|
31
|
+
def date_ge(left, right)
|
32
|
+
left = str_to_dt(left)
|
33
|
+
right = str_to_dt(right)
|
34
|
+
|
35
|
+
left >= right
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# Converts a +String+ representation of a date to a +DateTime+.
|
41
|
+
#
|
42
|
+
# @param str [String] A date.
|
43
|
+
# @return [DateTime] A DateTime representation of +str+.
|
44
|
+
def str_to_dt(str)
|
45
|
+
DateTime.parse(str)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/inq/exe.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "inq"
|
4
|
+
require "inq/cli"
|
5
|
+
require "inq/config"
|
6
|
+
require "inq/text"
|
7
|
+
|
8
|
+
module Inq
|
9
|
+
##
|
10
|
+
# A module which implements the entire command-line interface for Inq.
|
11
|
+
module Exe
|
12
|
+
def self.run(argv)
|
13
|
+
cli = parse_args(argv)
|
14
|
+
options = cli.options
|
15
|
+
|
16
|
+
abort cli.help_text if options[:help]
|
17
|
+
abort Inq::VERSION_STRING if options[:version]
|
18
|
+
|
19
|
+
execute(options)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.parse_args(argv)
|
23
|
+
Inq::CLI.parse(argv)
|
24
|
+
rescue OptionParser::ParseError => e
|
25
|
+
abort "inq: error: #{e.message}"
|
26
|
+
end
|
27
|
+
private_class_method :parse_args
|
28
|
+
|
29
|
+
def self.load_config(options)
|
30
|
+
config = Inq::Config.new
|
31
|
+
|
32
|
+
config.load_defaults unless options[:no_user_config]
|
33
|
+
config.load_env if options[:env_config]
|
34
|
+
|
35
|
+
if options[:config]
|
36
|
+
config.load_files(options[:config])
|
37
|
+
else
|
38
|
+
config.load(Inq.default_config(options[:repository]))
|
39
|
+
end
|
40
|
+
|
41
|
+
config
|
42
|
+
end
|
43
|
+
private_class_method :load_config
|
44
|
+
|
45
|
+
def self.save_reports(reports)
|
46
|
+
files = reports.save_all
|
47
|
+
Inq::Text.puts "Saved reports to:"
|
48
|
+
files.each { |file| Inq::Text.puts "- #{file}" }
|
49
|
+
end
|
50
|
+
private_class_method :save_reports
|
51
|
+
|
52
|
+
def self.execute(options)
|
53
|
+
date = options[:date]
|
54
|
+
config = load_config(options)
|
55
|
+
reports = Inq.from_config(config, date)
|
56
|
+
save_reports(reports)
|
57
|
+
rescue => e
|
58
|
+
raise if options[:verbose]
|
59
|
+
|
60
|
+
warn "inq: error: #{e.message} (Pass --verbose for more details.)"
|
61
|
+
warn " at: #{e.backtrace_locations.first}"
|
62
|
+
exit 1
|
63
|
+
end
|
64
|
+
private_class_method :execute
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "inq/version"
|
4
|
+
require "okay/warning_helpers"
|
5
|
+
|
6
|
+
module Inq
|
7
|
+
##
|
8
|
+
# Module for generating YAML frontmatter, as used by Jekyll and other
|
9
|
+
# blog engines.
|
10
|
+
module Frontmatter
|
11
|
+
extend Okay::WarningHelpers
|
12
|
+
|
13
|
+
# Generates YAML frontmatter, as is used in Jekyll and other blog engines.
|
14
|
+
#
|
15
|
+
# E.g.,
|
16
|
+
# generate_frontmatter({'foo' => "bar %{baz}"}, {'baz' => "asdf"})
|
17
|
+
# => "---\nfoo: bar asdf\n"
|
18
|
+
#
|
19
|
+
# @param frontmatter [Hash] Frontmatter for the report.
|
20
|
+
# @param report_data [Hash] The report data itself.
|
21
|
+
# @return [String] A YAML dump of the generated frontmatter.
|
22
|
+
def self.generate(frontmatter, report_data)
|
23
|
+
return "" if frontmatter.nil?
|
24
|
+
|
25
|
+
frontmatter = convert_keys(frontmatter, :to_s)
|
26
|
+
report_data = convert_keys(report_data, :to_sym)
|
27
|
+
|
28
|
+
frontmatter = frontmatter.map { |k, v|
|
29
|
+
# Sometimes report_data has unused keys, which generates a warning, but
|
30
|
+
# we're okay with it.
|
31
|
+
v = silence_warnings { v % report_data }
|
32
|
+
|
33
|
+
[k, v]
|
34
|
+
}.to_h
|
35
|
+
|
36
|
+
YAML.dump(frontmatter) + "---\n\n"
|
37
|
+
end
|
38
|
+
|
39
|
+
# @example
|
40
|
+
# convert_keys({'foo' => 'bar'}, :to_sym)
|
41
|
+
# # => {:foo => 'bar'}
|
42
|
+
# @param data [Hash] The input hash.
|
43
|
+
# @param method_name [Symbol] The method name used to convert keys.
|
44
|
+
# (E.g. :to_s, :to_sym, etc.)
|
45
|
+
# @return [Hash] The converted result.
|
46
|
+
def self.convert_keys(data, method_name)
|
47
|
+
data.map { |k, v| [k.send(method_name), v] }.to_h
|
48
|
+
end
|
49
|
+
private_class_method :convert_keys
|
50
|
+
end
|
51
|
+
end
|
data/lib/inq/report.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "inq/frontmatter"
|
4
|
+
require "inq/cacheable"
|
5
|
+
require "inq/sources/github/contributions"
|
6
|
+
require "inq/sources/github/issues"
|
7
|
+
require "inq/sources/github/pulls"
|
8
|
+
require "inq/sources/ci/travis"
|
9
|
+
require "inq/sources/ci/appveyor"
|
10
|
+
require "inq/template"
|
11
|
+
require "json"
|
12
|
+
|
13
|
+
module Inq
|
14
|
+
##
|
15
|
+
# Class for generating a report.
|
16
|
+
class Report
|
17
|
+
def initialize(config, end_date)
|
18
|
+
@config = config
|
19
|
+
@repository = config["repository"]
|
20
|
+
|
21
|
+
# NOTE: Use DateTime because it defaults to UTC and that's less gross
|
22
|
+
# than trying to get Date to use UTC.
|
23
|
+
#
|
24
|
+
# Not using UTC for this results in #compare_url giving different
|
25
|
+
# results for different time zones, which makes it harder to test.
|
26
|
+
#
|
27
|
+
# (I'm also guessing/hoping that GitHub's URLs use UTC.)
|
28
|
+
end_dt = DateTime.strptime(end_date, "%Y-%m-%d")
|
29
|
+
start_dt = start_dt_from_end_dt(end_dt)
|
30
|
+
|
31
|
+
@end_date = end_dt.strftime("%Y-%m-%d")
|
32
|
+
@start_date = start_dt.strftime("%Y-%m-%d")
|
33
|
+
end
|
34
|
+
|
35
|
+
def cache
|
36
|
+
@cache ||= Cacheable.new(@config, @start_date, @end_date)
|
37
|
+
end
|
38
|
+
|
39
|
+
def contributions
|
40
|
+
@gh_contributions ||= Sources::Github::Contributions.new(@config, @start_date, @end_date, cache)
|
41
|
+
end
|
42
|
+
|
43
|
+
def issues
|
44
|
+
@gh_issues ||= Sources::Github::Issues.new(@config, @start_date, @end_date, cache)
|
45
|
+
end
|
46
|
+
|
47
|
+
def pulls
|
48
|
+
@gh_pulls ||= Sources::Github::Pulls.new(@config, @start_date, @end_date, cache)
|
49
|
+
end
|
50
|
+
|
51
|
+
def travis
|
52
|
+
@travis ||= Sources::CI::Travis.new(@config, @start_date, @end_date, cache)
|
53
|
+
end
|
54
|
+
|
55
|
+
def appveyor
|
56
|
+
@appveyor ||= Sources::CI::Appveyor.new(@config, @start_date, @end_date, cache)
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_h(frontmatter_data = nil)
|
60
|
+
@report_hash ||= report_hash
|
61
|
+
frontmatter = Frontmatter.generate(frontmatter_data, @report_hash)
|
62
|
+
|
63
|
+
@report_hash.merge(frontmatter: frontmatter)
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_html_partial(frontmatter = nil)
|
67
|
+
Inq::Template.apply("report_partial.html", to_h(frontmatter))
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_html(frontmatter = nil)
|
71
|
+
template_data = to_h(frontmatter).merge({report: to_html_partial})
|
72
|
+
Inq::Template.apply("report.html", template_data)
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_json(frontmatter = nil)
|
76
|
+
frontmatter.to_s + JSON.pretty_generate(to_h)
|
77
|
+
end
|
78
|
+
|
79
|
+
def save_as(filename)
|
80
|
+
File.write(filename, to_format_for(filename))
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_format_for(filename)
|
84
|
+
format = File.extname(filename)[1..-1]
|
85
|
+
send("to_#{format}")
|
86
|
+
end
|
87
|
+
private :to_format_for
|
88
|
+
|
89
|
+
def start_dt_from_end_dt(end_dt)
|
90
|
+
d = end_dt.day
|
91
|
+
m = end_dt.month
|
92
|
+
y = end_dt.year
|
93
|
+
start_year = y
|
94
|
+
start_month = m - 1
|
95
|
+
if start_month <= 0
|
96
|
+
start_month = 12 - start_month
|
97
|
+
start_year -= 1
|
98
|
+
end
|
99
|
+
|
100
|
+
DateTime.new(start_year, start_month, d)
|
101
|
+
end
|
102
|
+
|
103
|
+
# rubocop:disable Metrics/AbcSize
|
104
|
+
def report_hash
|
105
|
+
{
|
106
|
+
title: "How is #{@repository}?",
|
107
|
+
repository: @repository,
|
108
|
+
|
109
|
+
contributions_summary: contributions.to_html,
|
110
|
+
new_contributors: contributions.new_contributors_html,
|
111
|
+
issues_summary: issues.to_html,
|
112
|
+
pulls_summary: pulls.to_html,
|
113
|
+
|
114
|
+
issues: issues.to_a,
|
115
|
+
pulls: issues.to_a,
|
116
|
+
|
117
|
+
average_issue_age: issues.average_age,
|
118
|
+
average_pull_age: pulls.average_age,
|
119
|
+
|
120
|
+
oldest_issue_link: issues.oldest["url"],
|
121
|
+
oldest_issue_date: issues.oldest["createdAt"],
|
122
|
+
|
123
|
+
newest_issue_link: issues.newest["url"],
|
124
|
+
newest_issue_date: issues.newest["createdAt"],
|
125
|
+
|
126
|
+
newest_pull_link: pulls.newest["url"],
|
127
|
+
newest_pull_date: pulls.newest["createdAt"],
|
128
|
+
|
129
|
+
oldest_pull_link: pulls.oldest["url"],
|
130
|
+
oldest_pull_date: pulls.oldest["createdAt"],
|
131
|
+
|
132
|
+
travis_builds: travis.builds,
|
133
|
+
appveyor_builds: appveyor.builds,
|
134
|
+
|
135
|
+
date: @end_date,
|
136
|
+
}
|
137
|
+
end
|
138
|
+
# rubocop:enable Metrics/AbcSize
|
139
|
+
end
|
140
|
+
end
|