lib/githuh/cli/commands/issue/export.rb in githuh-0.2.1 vs lib/githuh/cli/commands/issue/export.rb in githuh-0.3.0

- old
+ new

@@ -1,58 +1,69 @@ #!/usr/bin/env ruby # frozen_string_literal: true # vim: ft=ruby -require 'bundler/setup' -require 'dry/cli' -require 'json' -require 'tty/progressbar' -require 'csv' -require 'active_support/inflector' +require "bundler/setup" +require "dry/cli" +require "json" +require "tty/progressbar" +require "csv" +require "active_support/inflector" +require "yaml" -require_relative '../base' +require_relative "../base" module Githuh module CLI module Commands module Issue class Export < Base FORMATS = { - json: 'json', - csv: 'csv' + json: "json", + csv: "csv", }.freeze - DEFAULT_FORMAT = :csv + DEFAULT_FORMAT = :csv DEFAULT_OUTPUT_FORMAT = "<username>.<repo>.issues.<format>" - attr_accessor :filename, :file, :output, :repo, :issues, :format, :record_count + attr_accessor :filename, :file, :output, :repo, :issues, :format, :record_count, :mapping desc "Export Repo issues into a CSV or JSON format\n" \ " Default output file is " + DEFAULT_OUTPUT_FORMAT.bold.yellow argument :repo, type: :string, required: true, desc: 'Name of the repo, eg "rails/rails"' - option :file, required: false, desc: 'Output file, overrides ' + DEFAULT_OUTPUT_FORMAT - option :format, values: FORMATS.keys.map(&:to_s), default: DEFAULT_FORMAT.to_s, required: false, desc: 'Output format' + option :file, required: false, desc: "Output file, overrides " + DEFAULT_OUTPUT_FORMAT + option :format, values: FORMATS.keys.map(&:to_s), default: DEFAULT_FORMAT.to_s, required: false, desc: "Output format" + option :mapping, type: :string, require: false, desc: "YAML file with label to estimates mapping" - def call(repo: nil, file: nil, format: nil, **opts) + def call(repo: nil, file: nil, format: nil, mapping: nil, **opts) super(**opts) self.record_count = 0 - self.repo = repo + self.repo = repo raise ArgumentError, "argument <repo> is required" unless repo raise ArgumentError, "argument <repo> is not a repository, expected eg 'rails/rails'" unless repo =~ %r{/} + self.mapping = {} + if mapping && ::File.exist?(mapping) + self.mapping = ::YAML.safe_load(::File.read(mapping))['label-to-estimates'] || {} + end + + Export.const_set(:LabelEstimates, self.mapping) + self.issues = [] self.output = StringIO.new self.format = (format || DEFAULT_FORMAT).to_sym self.filename = file || file_name(repo) - self.file = File.open(filename, 'w') + self.file = File.open(filename, "w") print_summary + raise ArgumentError, "Format is not provided" unless FORMATS.key?(format&.to_sym) + # —————————— actually get all issues ——————————————— self.file.write send("render_as_#{format}", fetch_issues) # ———————————————————————————————————————————————————————— print_conclusion @@ -60,13 +71,13 @@ file.close if file.respond_to?(:close) && !file.closed? end def fetch_issues client.auto_paginate = true - self.issues = filter_issues(client.issues(repo, query: default_options)).tap do |issue_list| + self.issues = filter_issues(client.issues(repo, query: default_options)).tap do |issue_list| self.record_count = issue_list.size - bar('Issues')&.advance + bar("Issues")&.advance end end def filter_issues(issues_list) issues_list.reject do |issue| @@ -77,54 +88,45 @@ def bar_size record_count + 1 end def default_options - { state: 'open' } + { state: "open" } end def self.issue_labels(issue) issue.labels.map(&:name) end def self.find_user(client, username) - @user_cache ||= {} + @user_cache ||= {} @user_cache[username] ||= client.user(username).name end CSV_MAP = { - 'Labels' => ->(_client, issue) { issue_labels(issue).reject { |l| LABEL_ESTIMATES.key?(l) }.join(',').downcase }, - 'Type' => ->(*) { 'feature' }, - 'Estimate' => ->(_client, issue) do - el = issue_labels(issue).find { |l| LABEL_ESTIMATES.key?(l) } - el ? LABEL_ESTIMATES[el] : nil + "Labels" => ->(_client, issue) { issue_labels(issue).reject { |l| LabelEstimates.key?(l) }.join(",").downcase }, + "Type" => ->(*) { "feature" }, + "Estimate" => ->(_client, issue) do + el = issue_labels(issue).find { |l| LabelEstimates.key?(l) } + el ? LabelEstimates[el] : nil end, - 'Current State' => ->(*) { 'unstarted' }, - 'Requested By' => ->(client, issue) do + "Current State" => ->(*) { "unstarted" }, + "Requested By" => ->(client, issue) do find_user(client, issue.user.login) end, - 'Owned By' => ->(client, issue) do + "Owned By" => ->(client, issue) do find_user(client, issue.user.login) end, - 'Description' => ->(_client, issue) { + "Description" => ->(_client, issue) { issue.body }, - 'Created at' => ->(_client, issue) { issue.created_at }, + "Created at" => ->(_client, issue) { issue.created_at }, }.freeze - LABEL_ESTIMATES = { - 'XXL(13 eng day)' => 15, - 'XL(8 eng day)' => 15, - 'xs(<=1 eng day)' => 3, - 'L(5 eng day)' => 15, - 'm(3 eng day)' => 9, - 's(2 eng day)' => 6 - }.freeze - CSV_HEADER = %w(Id Title Labels Type Estimate) + - ['Current State', 'Created at', 'Accepted at', 'Deadline', 'Requested By', - 'Owned By', 'Description', 'Comment', 'Comment', 'Comment', 'Comment'].freeze + ["Current State", "Created at", "Accepted at", "Deadline", "Requested By", + "Owned By", "Description", "Comment", "Comment", "Comment", "Comment"].freeze # Id,Title,Labels,Type,Estimate,Current State,Created at,Accepted at,Deadline,Requested By,Owned By,Description,Comment,Comment # 100, existing started story,"label one,label two",feature,1,started,"Nov 22, 2007",,,user1,user2,this will update story 100,, # ,new story,label one,feature,-1,unscheduled,,,,user1,,this will create a new story in the icebox,comment1,comment2 def render_as_csv(issue_list) @@ -134,20 +136,20 @@ csv << CSV_HEADER issue_list.each do |issue| row = [] CSV_HEADER.each do |column| method = column.downcase.underscore.to_sym - value = if CSV_MAP[column] - CSV_MAP[column][client, issue] - else - begin - issue.to_h[method] - rescue StandardError - nil + value = if CSV_MAP[column] + CSV_MAP[column][client, issue] + else + begin + issue.to_h[method] + rescue StandardError + nil + end end - end - value = value.strip if value.is_a?(String) + value = value.strip if value.is_a?(String) row << value end csv << row bar&.advance end @@ -162,31 +164,30 @@ private def print_conclusion puts puts TTY::Box.info("Success: written a total of #{record_count} records to #{filename}", - width: ui_width, padding: 1) + **{ width: ui_width, padding: 1 }) puts end def print_summary puts puts TTY::Box.info("Format : #{self.format}\n" \ "File : #{filename}\n" \ "Repo : #{repo}\n", - width: ui_width, - padding: 1) + **{ width: ui_width, padding: 1 }) puts end def file_name(repo) "#{repo.gsub(%r{/}, '.')}.issues.#{FORMATS[self.format.to_sym]}" end end end - register 'issue', aliases: ['r'] do |prefix| - prefix.register 'export', Issue::Export + register "issue", aliases: ["r"] do |prefix| + prefix.register "export", Issue::Export end end end end