763 lines
21 KiB
Ruby
763 lines
21 KiB
Ruby
require "text.rb"
|
|
require "latex.rb"
|
|
|
|
class NilClass
|
|
def lookup_or_nil(x)
|
|
nil;
|
|
end
|
|
end
|
|
|
|
class Array
|
|
def lookup_or_nil(x)
|
|
self[x]
|
|
end
|
|
|
|
def oxford_comma
|
|
if length <= 1 then self.join(", ")
|
|
elsif length == 2 then "#{self[0]} and #{self[1]}"
|
|
else
|
|
"#{self[0...-1].join(", ")}, and #{self[-1]}"
|
|
end
|
|
end
|
|
end
|
|
|
|
class Hash
|
|
def lookup_or_nil(x)
|
|
self[x]
|
|
end
|
|
end
|
|
|
|
class CV < Latex::Builder
|
|
include Text
|
|
|
|
def initialize(who, out, options = {})
|
|
super(out)
|
|
@data = $db["cv"][who]
|
|
@who = who
|
|
@include_rejected_grants = options.fetch(:include_rejected_grants, false)
|
|
@include_pending_papers = options.fetch(:include_pending_papers, false)
|
|
@title = options.fetch(:title, "Curriculum Vitae")
|
|
@since = Hash.new { |h,k| if h.has_key? :rest then h[:rest] else 0 end }
|
|
options[:since].each { |k, v| @since[k] = v } if options.has_key?(:since)
|
|
@skip = options.fetch(:skip, [])
|
|
|
|
gen
|
|
end
|
|
|
|
def CV.make(who, out, options = {})
|
|
CV.new(who, out, options)
|
|
end
|
|
|
|
def make_section(title)
|
|
section!(title)
|
|
print_direct "~"
|
|
endl("-0.4in")
|
|
hrule
|
|
nopagebreak
|
|
print_direct "~"
|
|
endl("-.2in")
|
|
nopagebreak
|
|
puts_direct "\n\n"
|
|
end
|
|
|
|
def gen
|
|
|
|
documentclass("11pt", "article")
|
|
usepackage "fullpage"
|
|
usepackage "bibentry"
|
|
usepackage "enumitem"
|
|
|
|
document do
|
|
pagestyle "empty"
|
|
|
|
render_header
|
|
vspace "0.15in"
|
|
|
|
render_contact_details
|
|
vspace! "-0.1in"
|
|
|
|
render_education unless skip?(:education)
|
|
render_employment_history unless skip?(:employment)
|
|
render_honors unless skip?(:honors)
|
|
render_professional_activities unless skip?(:service)
|
|
render_invited_talks unless skip?(:invited_talks)
|
|
render_courses_taught unless skip?(:courses)
|
|
render_grant_support unless skip?(:grants)
|
|
render_pubs_and_artifacts unless skip?(:pubs)
|
|
end
|
|
end
|
|
|
|
def filter_records(category, records)
|
|
records.select { |record|
|
|
duration_is_since?(record, @since[category])
|
|
}
|
|
end
|
|
|
|
def build_filtered_itemize(category, records, *args)
|
|
args.unshift(filter_records(category, records))
|
|
build_itemize(*args) { |record| yield record }
|
|
end
|
|
|
|
def skip?(category)
|
|
@skip.include? category
|
|
end
|
|
|
|
|
|
############### HEADER ################
|
|
|
|
def render_header
|
|
noindent
|
|
textbf { huge; puts @title }
|
|
hfill
|
|
textbf { huge; puts @data["name"] }
|
|
vspace "0.05in"
|
|
hrule
|
|
end
|
|
|
|
############### CONTACT DETAILS ################
|
|
|
|
def render_contact_details
|
|
noindent
|
|
puts @data["work"]["dept"]
|
|
hfill
|
|
puts address_line_1(@data["home"])
|
|
endl
|
|
|
|
puts @data["work"]["employer"]
|
|
hfill
|
|
puts address_line_2(@data["home"])
|
|
endl
|
|
|
|
puts address_line_1(@data["work"]["address"])
|
|
endl
|
|
puts address_line_2(@data["work"]["address"])
|
|
|
|
puts ""
|
|
puts ""
|
|
smallskip
|
|
noindent
|
|
even_odd = false
|
|
@data["contact"].each do |k,v|
|
|
textit { puts "#{k}:" }
|
|
puts v
|
|
if even_odd then endl
|
|
else hfill end
|
|
even_odd = !even_odd
|
|
end
|
|
end
|
|
|
|
############### EDUCATION ################
|
|
|
|
def render_education
|
|
make_section("Education")
|
|
build_filtered_itemize(:education, @data["education"], "label=,leftmargin=0mm") do |r|
|
|
textit { puts "#{r["degree"]}," }
|
|
puts "#{r["university"]}, #{r["city"]}"
|
|
hfill
|
|
puts r["year"]
|
|
endl
|
|
|
|
puts r["dept"]
|
|
case r["degree"].split(/ +/)[0]
|
|
when "PhD"
|
|
endl
|
|
puts "Thesis: "
|
|
textit { puts "``#{r["Thesis"]}''" }
|
|
endl
|
|
puts "Advisor: #{r["Advisor"]}"
|
|
end
|
|
end
|
|
end
|
|
|
|
############### EMPLOYMENT HISTORY ################
|
|
|
|
def render_employment_history
|
|
make_section("Employment History")
|
|
build_filtered_itemize(:employment, @data["employment"]) do |r|
|
|
puts "#{r["title"]}, #{r["employer"]}"
|
|
hfill
|
|
puts duration(r)
|
|
end
|
|
end
|
|
|
|
############### HONORS ################
|
|
|
|
def render_honors
|
|
make_section("Honors")
|
|
build_filtered_itemize(:honors, @data["honors"], "label=,leftmargin=0mm") do |r|
|
|
puts r["description"]
|
|
end
|
|
end
|
|
|
|
############### PROFESSIONAL ACTIVITIES ################
|
|
|
|
def render_one_activity_class(activity_class, data, role_title)
|
|
role_title =
|
|
case role_title
|
|
when Proc then role_title
|
|
else
|
|
orig_role_title = role_title
|
|
lambda { |r| r[orig_role_title] }
|
|
end
|
|
build_filtered_itemize(activity_class, data, "noitemsep,leftmargin=5mm,label=--") do |r|
|
|
puts(role_title.call(r))
|
|
hfill
|
|
puts duration(r)
|
|
unless r["roles"].nil?
|
|
build_itemize(r["roles"], "noitemsep") do |role|
|
|
puts role["title"]
|
|
hfill
|
|
puts duration(role)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def render_professional_activities(args = {})
|
|
make_section("Professional Activities")
|
|
|
|
unless skip? :reviewer
|
|
subsection!("Reviewer / Program Comittee Member")
|
|
render_one_activity_class(:reviewer, @data["reviewer"],
|
|
lambda { |r|
|
|
venue = LabMetadata.complete_venue(r)["type"]
|
|
raise "Unknown Venue: #{r}" unless venue;
|
|
"#{venue.capitalize}: #{LabMetadata.venue_name(r)}"
|
|
})
|
|
end
|
|
|
|
unless skip? :general_service
|
|
subsection!("Service")
|
|
render_one_activity_class(:general_service, @data["service"]["general"], "description")
|
|
end
|
|
|
|
unless skip? :memberships
|
|
subsection!("Professional Memberships")
|
|
render_one_activity_class(:memberships, @data["memberships"], "org")
|
|
end
|
|
|
|
unless skip? :volunteering
|
|
subsection!("Volunteer Work")
|
|
render_one_activity_class(:volunteering, @data["volunteering"], "org")
|
|
end
|
|
|
|
unless skip? :dept_service
|
|
subsection!("Departmental Service")
|
|
build_itemize(@data["service"]["dept"]) do |dept|
|
|
puts dept["org"]
|
|
render_one_activity_class(:dept_service, dept["service"], "org")
|
|
end
|
|
end
|
|
end
|
|
|
|
############### INVITED TALKS ################
|
|
|
|
def render_invited_talks
|
|
make_section("Invited Talks")
|
|
build_filtered_itemize(:talks, @data["talks"], "noitemsep,leftmargin=5mm,label=--") do |r|
|
|
textit { puts r["venue"] }
|
|
wrap {
|
|
small
|
|
em_dash
|
|
puts " #{r["talk"]}"
|
|
puts "(with #{r["with"].join(', ')})" unless r["with"].nil?
|
|
}
|
|
hfill
|
|
puts r["date"]
|
|
end
|
|
end
|
|
|
|
############### COURSES ################
|
|
|
|
def render_courses_taught
|
|
make_section("Courses")
|
|
|
|
courses = @data["courses"]
|
|
|
|
since = @since[:courses]
|
|
courses = courses.map do |dept|
|
|
dept = dept.clone
|
|
dept["courses"] = dept["courses"].select do |c|
|
|
unless /([0-9]{4})/ =~ c["semester"]
|
|
raise "Invalid Semester: '#{c["semester"]}'"
|
|
end
|
|
$1.to_i >= since
|
|
end
|
|
dept unless dept["courses"].empty?
|
|
end.compact
|
|
|
|
build_itemize(courses, "leftmargin=1mm,label=") do |dept|
|
|
puts dept["venue"]
|
|
build_itemize(dept["courses"], "noitemsep,leftmargin=7mm") do |r|
|
|
texttt { puts r["code"] }
|
|
puts ":"
|
|
wrap {
|
|
small
|
|
puts "#{r["title"]}"
|
|
puts " (as #{r["role"]})" unless r["role"].nil?
|
|
puts "--- #{r["enrollment"]} students" unless r["enrollment"].nil?
|
|
}
|
|
hfill
|
|
puts r["semester"]
|
|
end
|
|
end
|
|
end
|
|
|
|
############### GRANT SUPPORT ################
|
|
|
|
def render_grant_support(args = {})
|
|
make_section("Grant Support")
|
|
|
|
test_for_since = proc { |r| duration_is_since?(r, @since[:grants]) }
|
|
|
|
grant_categories = [
|
|
["Pending Applications", "submitted", ["grant", "gift"]],
|
|
["Awarded Grants", "accepted", ["grant"]],
|
|
["Gifts", "accepted", ["gift"]],
|
|
]
|
|
grant_categories += [
|
|
["Completed", "completed", ["grant", "gift"]]
|
|
] unless skip?(:completed_grants)
|
|
grant_categories += [
|
|
["Declined", "rejected", ["grant", "gift"]]
|
|
] if args.fetch(:include_rejected, @include_rejected_grants)
|
|
|
|
fields_to_display = args.fetch(:fields, [
|
|
"title",
|
|
"agency",
|
|
"role",
|
|
"effective dates",
|
|
"amount",
|
|
"effort",
|
|
"copis",
|
|
"collaborative",
|
|
"feedback",
|
|
"supplements"
|
|
])
|
|
|
|
grant_categories.each do |sec, status, type|
|
|
|
|
## Find the set of grants that apply to the current categories
|
|
grants_in_category = @data["grants"].select { |r|
|
|
(r["status"] == status) and
|
|
(type.include?(r["type"])) and
|
|
test_for_since.call(r)
|
|
}.sort { |a,b| to_date(b["start"]) <=> to_date(a["start"])}
|
|
|
|
|
|
unless grants_in_category.empty?
|
|
|
|
## Header for the category
|
|
subsection!("#{sec} (#{grants_in_category.length})")
|
|
|
|
## For each grant in the category
|
|
grants_in_category.each do |r|
|
|
collaborative_total = 0;
|
|
supplements_total = 0
|
|
our_total = 0;
|
|
total_awarded = 0;
|
|
award_value_is_string = true
|
|
|
|
# Derive grant totals for collaborative grants and
|
|
# grants with supplements, as well as fractional
|
|
# amounts for us.
|
|
if r["amount"].is_a? Numeric then
|
|
collaborative_total =
|
|
if r["collaborative"].nil? then 0 else
|
|
r["collaborative"].map { |x| x["amount"].to_f }.sum
|
|
end
|
|
supplements_total =
|
|
if r["supplements"].nil? then 0 else
|
|
r["supplements"].map { |x| x["amount"].to_f }.sum
|
|
end
|
|
our_total = r["amount"].to_f + supplements_total
|
|
total_awarded = our_total + collaborative_total;
|
|
award_value_is_string = false
|
|
end
|
|
|
|
noindent
|
|
|
|
tabular("rl") do
|
|
|
|
# For each field being displayed
|
|
fields_to_display.each do |k|
|
|
|
|
# Only display fields if they're available
|
|
unless r[k].nil?
|
|
# Field title
|
|
textit { puts(
|
|
case k
|
|
when "copis" then "Co-PI".pluralize(r["copis"].length)
|
|
when "collaborative" then
|
|
"Peer Org.".pluralize(r["collaborative"].length)
|
|
else k.capitalize
|
|
end
|
|
)}
|
|
# Break
|
|
print_direct ":&"
|
|
|
|
# Post-processing on field value
|
|
case k
|
|
when "title", "agency"
|
|
parbox("5in") do
|
|
if k == "title" then textsf { puts r[k] } else puts r[k] end
|
|
end
|
|
when "effective dates"
|
|
puts duration(r)
|
|
when "copis"
|
|
puts r["copis"].join(", ")
|
|
when "amount"
|
|
unless award_value_is_string
|
|
puts format_money our_total
|
|
unless r["collaborative"].nil?
|
|
puts " (Total Across Collaboration: #{format_money total_awarded})"
|
|
end
|
|
else
|
|
puts r["amount"]
|
|
end
|
|
when "collaborative"
|
|
r["collaborative"].each do |i|
|
|
puts i["institution"]
|
|
puts "(#{format_money i["amount"]})"
|
|
end
|
|
when "supplements"
|
|
unless award_value_is_string then
|
|
puts(r["supplements"].map do |supp|
|
|
"#{supp["type"]}"
|
|
end.join(", "))
|
|
puts "(#{"amount".pluralize(r["supplements"].length)} included in total above)"
|
|
else
|
|
puts(r["supplements"].map do |supp|
|
|
"#{supp["type"]} (#{format_money supp["amount"].to_f})"
|
|
end.join(", "))
|
|
end
|
|
else # case k
|
|
puts r[k]
|
|
end # case k
|
|
endl
|
|
end # unless r[k].nil?
|
|
end # fields_to_display.each
|
|
end # block("tabular") (field table)
|
|
endl("0.2in")
|
|
end # grants_in_category.each
|
|
end # unless grants_in_category.empty?
|
|
end # grant_categories.each
|
|
end
|
|
|
|
############### PUBLICATIONS AND ARTEFACTS ################
|
|
|
|
|
|
def render_author_list(record)
|
|
print_direct " (#{
|
|
record["authors"].map do |a|
|
|
case a
|
|
when "Oliver Kennedy" then "\\textbf{O. Kennedy}"
|
|
else safe_text(LabMetadata.person_name(a, :size => :short))
|
|
end
|
|
end.join(", ")
|
|
}#{
|
|
case record["authornote"]
|
|
when nil then ""
|
|
when "alpha" then "; \\textit{Authors listed in alphabetical order}"
|
|
else "; \\textit{#{safe_text(record["authornote"])}}"
|
|
end
|
|
})"
|
|
end
|
|
|
|
def render_editor_list(record)
|
|
v =
|
|
eds = LabMetadata.complete_venue(record).
|
|
lookup_or_nil("editors").
|
|
lookup_or_nil(record["year"])
|
|
if eds
|
|
puts "Ed#{eds.length != 1 ? "s" : ""}. #{eds.join(", ")}"
|
|
end
|
|
end
|
|
|
|
def render_selectivity(record)
|
|
year = record["year"].to_s
|
|
v = LabMetadata.complete_venue(record)
|
|
sel_data = v["selectivity"]
|
|
|
|
rate = "acceptance rate"
|
|
|
|
if sel_data.nil?
|
|
"#{rate} not available"
|
|
elsif sel_data[year].is_a? Numeric
|
|
"#{rate} #{(sel_data[year]*100).round(2)}%"
|
|
elsif not sel_data[year].nil?
|
|
"#{rate} #{sel_data[year]}"
|
|
elsif sel_data["average"].is_a? Numeric
|
|
"#{rate} unknown; average rate #{
|
|
get_avg_selectivity(record)
|
|
}"
|
|
elsif not sel_data["average"].nil?
|
|
"#{rate} #{sel_data["average"]}"
|
|
else
|
|
"#{rate} unknown; average rate #{
|
|
get_avg_selectivity(record)
|
|
}"
|
|
end
|
|
end
|
|
|
|
def get_venue_type(record)
|
|
LabMetadata.complete_venue(record).
|
|
lookup_or_nil("type")
|
|
end
|
|
|
|
def get_publisher(record)
|
|
LabMetadata.complete_venue(record).
|
|
lookup_or_nil("publisher")
|
|
end
|
|
|
|
def get_pub_length_string(record)
|
|
case record["length"]
|
|
when String
|
|
record["length"]
|
|
else
|
|
"#{record["length"]} #{"page".pluralize(record["length"])}";
|
|
end
|
|
end
|
|
|
|
def get_avg_selectivity(record, rounding = 2)
|
|
year = record["year"].to_s
|
|
v = LabMetadata.complete_venue(record)
|
|
sel_data = v["selectivity"]
|
|
if sel_data["average"].is_a? Numeric
|
|
"approximately #{(sel_data["average"]*100).round(rounding)}%"
|
|
elsif not sel_data["average"].nil?
|
|
sel_data["average"]
|
|
else
|
|
"approximately #{
|
|
(sel_data.keys.
|
|
delete_if { |k| /a-zA-Z/ =~ k }.
|
|
map { |k| sel_data[k].to_f }.
|
|
avg * 100).round(rounding)
|
|
}%"
|
|
end
|
|
end
|
|
|
|
def render_paper_title(title)
|
|
print_direct "\\textsc{#{title}}"
|
|
end
|
|
|
|
def render_one_pub_or_artifact(title,list,fmt,filter = proc { true })
|
|
list = list.select { |r| filter.call(r) }
|
|
unless list.empty?
|
|
subsection!("#{title} (#{list.length})")
|
|
build_itemize(list, "leftmargin=0mm,label=") { |r| fmt.call(r) }
|
|
end
|
|
end
|
|
def render_pubs_and_artifacts(args = {})
|
|
|
|
global_filter =
|
|
proc { |r| duration_is_since?(r, @since[:publications])}
|
|
|
|
all_my_pubs = LabMetadata.publications_for(@who)
|
|
|
|
make_section("Publications and Artifacts")
|
|
|
|
# block("center") do
|
|
# puts "(Author lists marked with a * are not correlated with level of contribution)"
|
|
# end
|
|
unless skip?(:journal_pubs)
|
|
render_one_pub_or_artifact(
|
|
"Journal Publications",
|
|
all_my_pubs.where do |r|
|
|
case get_venue_type(r)
|
|
when "journal" then true
|
|
else false
|
|
end
|
|
end,
|
|
proc do |r|
|
|
render_paper_title(r["title"])
|
|
render_author_list(r)
|
|
puts "---"
|
|
extra_fields =
|
|
if r["volume"].nil? then []
|
|
else ["Volume #{r["volume"]}, Number #{r["number"]}"] end +
|
|
if r["issuetitle"].nil? then []
|
|
else [r["issuetitle"]] end
|
|
textit {
|
|
puts "#{LabMetadata.venue_name(r)} #{r["year"]}#{if extra_fields.length > 0 then " (#{extra_fields.join("; ")}), " else ", " end}"
|
|
}
|
|
puts "#{get_pub_length_string(r)}, "
|
|
puts render_selectivity(r)
|
|
end,
|
|
global_filter
|
|
)
|
|
end
|
|
|
|
unless skip?(:conference_pubs)
|
|
render_one_pub_or_artifact(
|
|
"Conference Publications",
|
|
all_my_pubs.where do |r|
|
|
case get_venue_type(r)
|
|
when "conference" then true
|
|
else false
|
|
end
|
|
end,
|
|
proc do |r|
|
|
render_paper_title(r["title"])
|
|
render_author_list(r)
|
|
puts "---"
|
|
textit{ puts LabMetadata.venue_name(r) }
|
|
puts "#{r["year"]}, "
|
|
puts "#{get_pub_length_string(r)}, "
|
|
puts render_selectivity(r)
|
|
end,
|
|
global_filter
|
|
)
|
|
end
|
|
|
|
unless skip?(:workshop_pubs)
|
|
render_one_pub_or_artifact(
|
|
"Workshop Publications",
|
|
all_my_pubs.where do |r|
|
|
case get_venue_type(r)
|
|
when "workshop" then true
|
|
else false
|
|
end
|
|
end,
|
|
proc do |r|
|
|
render_paper_title(r["title"])
|
|
render_author_list(r)
|
|
puts "---"
|
|
textit { puts LabMetadata.venue_name(r) }
|
|
puts "#{r["year"]}, "
|
|
puts "#{get_pub_length_string(r)}, "
|
|
puts render_selectivity(r)
|
|
end,
|
|
global_filter
|
|
)
|
|
end
|
|
|
|
unless skip?(:book_chapters)
|
|
render_one_pub_or_artifact(
|
|
"Book Chapters",
|
|
all_my_pubs.where do |r|
|
|
case get_venue_type(r)
|
|
when "chapter" then true
|
|
else false
|
|
end
|
|
end,
|
|
proc do |r|
|
|
render_paper_title(r["title"])
|
|
render_author_list(r)
|
|
puts "---"
|
|
textit { puts LabMetadata.venue_name(r) }
|
|
render_editor_list(r)
|
|
publisher = get_publisher(r)
|
|
series = r["series"] || $venues[r["venue"]]["series"]
|
|
puts "---" if publisher || series
|
|
puts "#{publisher}#{if series then ";" else "" end}" if publisher
|
|
puts series if series
|
|
textit { puts r["year"] }
|
|
puts "(#{get_pub_length_string(r)}, #{render_selectivity(r)})"
|
|
end,
|
|
global_filter
|
|
)
|
|
end
|
|
|
|
render_one_pub_or_artifact(
|
|
"Submitted Publications",
|
|
@data["publications_submitted"],
|
|
proc do |r|
|
|
render_paper_title(r["title"])
|
|
render_author_list(r)
|
|
puts "---"
|
|
textit { puts "#{LabMetadata.venue_name(r)} #{r["year"]}, " }
|
|
puts "#{get_pub_length_string(r)}; "
|
|
puts "Historical average acceptance rate #{get_avg_selectivity(r, 0)}"
|
|
end,
|
|
global_filter
|
|
) if @include_pending_papers
|
|
|
|
unless skip?(:tech_reports)
|
|
render_one_pub_or_artifact(
|
|
"Technical Reports",
|
|
all_my_pubs.where do |r|
|
|
case get_venue_type(r)
|
|
when "techreport" then true
|
|
else false
|
|
end
|
|
end,
|
|
proc do |r|
|
|
render_paper_title(r["title"])
|
|
render_author_list(r)
|
|
puts "---"
|
|
textit {
|
|
puts "#{LabMetadata.venue_name(r)}#{if r["id"].nil? then "" else " ##{r["id"]}" end}, "
|
|
}
|
|
puts get_pub_length_string(r)
|
|
end,
|
|
global_filter
|
|
)
|
|
end
|
|
|
|
unless skip?(:patents)
|
|
render_one_pub_or_artifact(
|
|
"Patents",
|
|
all_my_pubs.where do |r|
|
|
case get_venue_type(r)
|
|
when "patent" then true
|
|
else false
|
|
end
|
|
end,
|
|
proc do |r|
|
|
render_paper_title(r["title"])
|
|
render_author_list(r)
|
|
puts "--- #{r["country"]} Patent#{if r["status"] == "granted" then "" else " Application" end} ##{r["id"]}"
|
|
end,
|
|
global_filter
|
|
)
|
|
end
|
|
|
|
unless skip?(:artifacts)
|
|
render_one_pub_or_artifact(
|
|
"Artifacts",
|
|
@data["artifacts"],
|
|
proc do |r|
|
|
print_direct "\\textsc{#{safe_text r["name"]}} (#{safe_text r["class"]})"
|
|
unless r["released"].nil?
|
|
d = to_date(r["released"])
|
|
hfill
|
|
puts "First released #{d.strftime("%B %Y")}"
|
|
end
|
|
endl
|
|
print_direct "\\textit{#{safe_text r["description"]}}"
|
|
unless r["url"].nil?
|
|
endl
|
|
url(r["url"])
|
|
end
|
|
unless r["metrics"].nil?
|
|
unless r["metricsasof"].nil?
|
|
endl
|
|
puts "As of #{r["metricsasof"]}, #{r["name"]} had "
|
|
else
|
|
puts "#{r["name"]} has "
|
|
end
|
|
|
|
metrics = r["metrics"].to_a.map do |m,v|
|
|
case m
|
|
when "sitevisits" then "#{safe_text v} unique website visitors"
|
|
when "downloads" then "#{safe_text v} unique downloads"
|
|
else puts "#{safe_text v} #{safe_text m}"
|
|
end
|
|
end
|
|
|
|
case metrics.length
|
|
when 0 then raise "Empty Metrics Field for #{r["name"]}"
|
|
when 1 then puts metrics[0]
|
|
when 2 then puts metrics.join(" and ")
|
|
else
|
|
metrics.push("and #{metrics.pop}")
|
|
puts metrics.join(", ")
|
|
end
|
|
end
|
|
end
|
|
) if args.fetch(:show_artifacts, true)
|
|
end
|
|
end
|
|
|
|
end |