Merge pull request #5105 from MikeMcQuaid/brew-info-verbose-analytics-table
info: make verbose analytics use tabular output.
This commit is contained in:
		
						commit
						864be6d82f
					
				| @ -1,11 +1,20 @@ | ||||
| #:  * `info`: | ||||
| #:    Display brief statistics for your Homebrew installation. | ||||
| #: | ||||
| #:  * `info` <formula> [`--verbose`]: | ||||
| #:  * `info` `--analytics` [`--days=`<days>] [`--category=`<category>]: | ||||
| #:    Display Homebrew analytics data (provided neither `HOMEBREW_NO_ANALYTICS` | ||||
| #:    or `HOMEBREW_NO_GITHUB_API` are set) | ||||
| #: | ||||
| #:    The value for `days` must be `30`, `90` or `365`. The default is `30`. | ||||
| #: | ||||
| #:    The value for `category` must be `install`, `install-on-request`, | ||||
| #:    `build-error` or `os-version`. The default is `install`. | ||||
| #: | ||||
| #:  * `info` <formula> [`--analytics`]: | ||||
| #:    Display information about <formula> and analytics data (provided neither | ||||
| #:    `HOMEBREW_NO_ANALYTICS` or `HOMEBREW_NO_GITHUB_API` are set) | ||||
| #: | ||||
| #:    Pass `--verbose` to see more detailed analytics data. | ||||
| #:    Pass `--analytics` to see more detailed analytics data. | ||||
| #: | ||||
| #:  * `info` `--github` <formula>: | ||||
| #:    Open a browser to the GitHub History page for <formula>. | ||||
| @ -47,7 +56,9 @@ module Homebrew | ||||
| 
 | ||||
|   def print_info | ||||
|     if ARGV.named.empty? | ||||
|       if HOMEBREW_CELLAR.exist? | ||||
|       if ARGV.include?("--analytics") | ||||
|         output_analytics | ||||
|       elsif HOMEBREW_CELLAR.exist? | ||||
|         count = Formula.racks.length | ||||
|         puts "#{count} #{"keg".pluralize(count)}, #{HOMEBREW_CELLAR.abv}" | ||||
|       end | ||||
| @ -184,42 +195,156 @@ module Homebrew | ||||
|     caveats = Caveats.new(f) | ||||
|     ohai "Caveats", caveats.to_s unless caveats.empty? | ||||
| 
 | ||||
|     output_analytics(f) | ||||
|     output_formula_analytics(f) | ||||
|   end | ||||
| 
 | ||||
|   def output_analytics(f) | ||||
|     return if ENV["HOMEBREW_NO_ANALYTICS"] | ||||
|     return if ENV["HOMEBREW_NO_GITHUB_API"] | ||||
|   def formulae_api_json(endpoint) | ||||
|     return if ENV["HOMEBREW_NO_ANALYTICS"] || ENV["HOMEBREW_NO_GITHUB_API"] | ||||
| 
 | ||||
|     formulae_json_url = "https://formulae.brew.sh/api/formula/#{f}.json" | ||||
|     output, = curl_output("--max-time", "3", formulae_json_url) | ||||
|     return if output.empty? | ||||
|     output, = curl_output("--max-time", "3", | ||||
|       "https://formulae.brew.sh/api/#{endpoint}") | ||||
|     return if output.blank? | ||||
| 
 | ||||
|     json = begin | ||||
|       JSON.parse(output) | ||||
|     rescue JSON::ParserError | ||||
|       nil | ||||
|     JSON.parse(output) | ||||
|   rescue JSON::ParserError | ||||
|     nil | ||||
|   end | ||||
| 
 | ||||
|   def analytics_table(category, days, results, os_version: false) | ||||
|     oh1 "#{category} (#{days} days)" | ||||
|     total_count = results.values.inject("+") | ||||
|     formatted_total_count = format_count(total_count) | ||||
|     formatted_total_percent = format_percent(100) | ||||
| 
 | ||||
|     index_header = "Index" | ||||
|     count_header = "Count" | ||||
|     percent_header = "Percent" | ||||
|     name_with_options_header = if os_version | ||||
|       "macOS Version" | ||||
|     else | ||||
|       "Name (with options)" | ||||
|     end | ||||
|     return if json.nil? || json.empty? || json["analytics"].empty? | ||||
| 
 | ||||
|     total_index_footer = "Total" | ||||
|     max_index_width = results.length.to_s.length | ||||
|     index_width = [ | ||||
|       index_header.length, | ||||
|       total_index_footer.length, | ||||
|       max_index_width, | ||||
|     ].max | ||||
|     count_width = [ | ||||
|       count_header.length, | ||||
|       formatted_total_count.length, | ||||
|     ].max | ||||
|     percent_width = [ | ||||
|       percent_header.length, | ||||
|       formatted_total_percent.length, | ||||
|     ].max | ||||
|     name_with_options_width = Tty.width - | ||||
|                               index_width - | ||||
|                               count_width - | ||||
|                               percent_width - | ||||
|                               10 # spacing and lines | ||||
| 
 | ||||
|     formatted_index_header = | ||||
|       format "%#{index_width}s", index_header | ||||
|     formatted_name_with_options_header = | ||||
|       format "%-#{name_with_options_width}s", | ||||
|              name_with_options_header[0..name_with_options_width-1] | ||||
|     formatted_count_header = | ||||
|       format "%#{count_width}s", count_header | ||||
|     formatted_percent_header = | ||||
|       format "%#{percent_width}s", percent_header | ||||
|     puts "#{formatted_index_header} | #{formatted_name_with_options_header} | "\ | ||||
|          "#{formatted_count_header} |  #{formatted_percent_header}" | ||||
| 
 | ||||
|     columns_line = "#{"-"*index_width}:|-#{"-"*name_with_options_width}-|-"\ | ||||
|                    "#{"-"*count_width}:|-#{"-"*percent_width}:" | ||||
|     puts columns_line | ||||
| 
 | ||||
|     index = 0 | ||||
|     results.each do |name_with_options, count| | ||||
|       index += 1 | ||||
|       formatted_index = format "%0#{max_index_width}d", index | ||||
|       formatted_index = format "%-#{index_width}s", formatted_index | ||||
|       formatted_name_with_options = | ||||
|         format "%-#{name_with_options_width}s", | ||||
|                name_with_options[0..name_with_options_width-1] | ||||
|       formatted_count = format "%#{count_width}s", format_count(count) | ||||
|       formatted_percent = if total_count.zero? | ||||
|         format "%#{percent_width}s", format_percent(0) | ||||
|       else | ||||
|         format "%#{percent_width}s", | ||||
|                format_percent((count.to_i * 100) / total_count.to_f) | ||||
|       end | ||||
|       puts "#{formatted_index} | #{formatted_name_with_options} | " \ | ||||
|            "#{formatted_count} | #{formatted_percent}%" | ||||
|       next if index > 10 | ||||
|     end | ||||
|     return unless results.length > 1 | ||||
| 
 | ||||
|     formatted_total_footer = | ||||
|       format "%-#{index_width}s", total_index_footer | ||||
|     formatted_blank_footer = | ||||
|       format "%-#{name_with_options_width}s", "" | ||||
|     formatted_total_count_footer = | ||||
|       format "%#{count_width}s", formatted_total_count | ||||
|     formatted_total_percent_footer = | ||||
|       format "%#{percent_width}s", formatted_total_percent | ||||
|     puts "#{formatted_total_footer} | #{formatted_blank_footer} | "\ | ||||
|          "#{formatted_total_count_footer} | #{formatted_total_percent_footer}%" | ||||
|   end | ||||
| 
 | ||||
|   def output_analytics | ||||
|     days = ARGV.value("days") || "30" | ||||
|     valid_days = %w[30 90 365] | ||||
|     unless valid_days.include?(days) | ||||
|       raise ArgumentError("Days must be one of #{valid_days.join(", ")}!") | ||||
|     end | ||||
| 
 | ||||
|     category = ARGV.value("category") || "install" | ||||
|     valid_categories = %w[install install-on-request build-error os-version] | ||||
|     unless valid_categories.include?(category) | ||||
|       raise ArgumentError("Categories must be one of #{valid_categories.join(", ")}") | ||||
|     end | ||||
| 
 | ||||
|     json = formulae_api_json("analytics/#{category}/#{days}d.json") | ||||
|     return if json.blank? || json["items"].blank? | ||||
| 
 | ||||
|     os_version = category == "os-version" | ||||
|     results = {} | ||||
|     json["items"].each do |item| | ||||
|       key = if os_version | ||||
|         item["os_version"] | ||||
|       else | ||||
|         item["formula"] | ||||
|       end | ||||
|       results[key] = item["count"].tr(",", "").to_i | ||||
|     end | ||||
|     analytics_table(category, days, results, os_version: os_version) | ||||
|   end | ||||
| 
 | ||||
|   def output_formula_analytics(f) | ||||
|     json = formulae_api_json("formula/#{f}.json") | ||||
|     return if json.blank? || json["analytics"].blank? | ||||
| 
 | ||||
|     full_analytics = ARGV.include?("--analytics") || ARGV.verbose? | ||||
| 
 | ||||
|     ohai "Analytics" | ||||
|     if ARGV.verbose? | ||||
|       json["analytics"].each do |category, value| | ||||
|         value.each do |range, results| | ||||
|           oh1 "#{category} (#{range})" | ||||
|           results.each do |name_with_options, count| | ||||
|             puts "#{name_with_options}: #{number_readable(count)}" | ||||
|           end | ||||
|     json["analytics"].each do |category, value| | ||||
|       analytics = [] | ||||
| 
 | ||||
|       value.each do |days, results| | ||||
|         days = days.to_i | ||||
|         if full_analytics | ||||
|           analytics_table(category, days, results) | ||||
|         else | ||||
|           total_count = results.values.inject("+") | ||||
|           analytics << "#{number_readable(total_count)} (#{days} days)" | ||||
|         end | ||||
|       end | ||||
|       return | ||||
|     end | ||||
| 
 | ||||
|     json["analytics"].each do |category, value| | ||||
|       analytics = value.map do |range, results| | ||||
|         "#{number_readable(results.values.inject("+"))} (#{range})" | ||||
|       end | ||||
|       puts "#{category}: #{analytics.join(", ")}" | ||||
|       puts "#{category}: #{analytics.join(", ")}" unless full_analytics | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
| @ -247,4 +372,12 @@ module Homebrew | ||||
| 
 | ||||
|     "#{dep.name} #{dep.option_tags.map { |o| "--#{o}" }.join(" ")}" | ||||
|   end | ||||
| 
 | ||||
|   def format_count(count) | ||||
|     count.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse | ||||
|   end | ||||
| 
 | ||||
|   def format_percent(percent) | ||||
|     format "%.2f", percent | ||||
|   end | ||||
| end | ||||
|  | ||||
| @ -23,6 +23,13 @@ end | ||||
| describe Homebrew do | ||||
|   let(:remote) { "https://github.com/Homebrew/homebrew-core" } | ||||
| 
 | ||||
|   specify "::analytics_table" do | ||||
|     results = { ack: 10, wget: 100 } | ||||
|     expect { subject.analytics_table("install", "30", results) } | ||||
|       .to output(/110 |  100.00%/).to_stdout | ||||
|       .and not_to_output.to_stderr | ||||
|   end | ||||
| 
 | ||||
|   specify "::github_remote_path" do | ||||
|     expect(subject.github_remote_path(remote, "Formula/git.rb")) | ||||
|       .to eq("https://github.com/Homebrew/homebrew-core/blob/master/Formula/git.rb") | ||||
|  | ||||
| @ -187,7 +187,7 @@ RSpec::Matchers.define :a_json_string do | ||||
|     begin | ||||
|       JSON.parse(actual) | ||||
|       true | ||||
|     rescue JSON::ParseError | ||||
|     rescue JSON::ParserError | ||||
|       false | ||||
|     end | ||||
|   end | ||||
|  | ||||
| @ -199,11 +199,20 @@ With `--verbose` or `-v`, many commands print extra debugging information. Note | ||||
|   * `info`: | ||||
|     Display brief statistics for your Homebrew installation. | ||||
| 
 | ||||
|   * `info` *`formula`* [`--verbose`]: | ||||
|   * `info` `--analytics` [`--days=`*`days`*] [`--category=`*`category`*]: | ||||
|     Display Homebrew analytics data (provided neither `HOMEBREW_NO_ANALYTICS` | ||||
|     or `HOMEBREW_NO_GITHUB_API` are set) | ||||
| 
 | ||||
|     The value for `days` must be `30`, `90` or `365`. The default is `30`. | ||||
| 
 | ||||
|     The value for `category` must be `install`, `install-on-request`, | ||||
|     `build-error` or `os-version`. The default is `install`. | ||||
| 
 | ||||
|   * `info` *`formula`* [`--analytics`]: | ||||
|     Display information about *`formula`* and analytics data (provided neither | ||||
|     `HOMEBREW_NO_ANALYTICS` or `HOMEBREW_NO_GITHUB_API` are set) | ||||
| 
 | ||||
|     Pass `--verbose` to see more detailed analytics data. | ||||
|     Pass `--analytics` to see more detailed analytics data. | ||||
| 
 | ||||
|   * `info` `--github` *`formula`*: | ||||
|     Open a browser to the GitHub History page for *`formula`*. | ||||
|  | ||||
| @ -208,11 +208,21 @@ Open \fIformula\fR\'s homepage in a browser\. | ||||
| Display brief statistics for your Homebrew installation\. | ||||
| . | ||||
| .TP | ||||
| \fBinfo\fR \fIformula\fR [\fB\-\-verbose\fR] | ||||
| \fBinfo\fR \fB\-\-analytics\fR [\fB\-\-days=\fR\fIdays\fR] [\fB\-\-category=\fR\fIcategory\fR] | ||||
| Display Homebrew analytics data (provided neither \fBHOMEBREW_NO_ANALYTICS\fR or \fBHOMEBREW_NO_GITHUB_API\fR are set) | ||||
| . | ||||
| .IP | ||||
| The value for \fBdays\fR must be \fB30\fR, \fB90\fR or \fB365\fR\. The default is \fB30\fR\. | ||||
| . | ||||
| .IP | ||||
| The value for \fBcategory\fR must be \fBinstall\fR, \fBinstall\-on\-request\fR, \fBbuild\-error\fR or \fBos\-version\fR\. The default is \fBinstall\fR\. | ||||
| . | ||||
| .TP | ||||
| \fBinfo\fR \fIformula\fR [\fB\-\-analytics\fR] | ||||
| Display information about \fIformula\fR and analytics data (provided neither \fBHOMEBREW_NO_ANALYTICS\fR or \fBHOMEBREW_NO_GITHUB_API\fR are set) | ||||
| . | ||||
| .IP | ||||
| Pass \fB\-\-verbose\fR to see more detailed analytics data\. | ||||
| Pass \fB\-\-analytics\fR to see more detailed analytics data\. | ||||
| . | ||||
| .TP | ||||
| \fBinfo\fR \fB\-\-github\fR \fIformula\fR | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Mike McQuaid
						Mike McQuaid