918 lines
27 KiB
Ruby
918 lines
27 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)
|
|
@include_pending_grants = options.fetch(:include_pending_grants, false)
|
|
@include_blurb = options.fetch(:include_blurb, 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, [:blurb])
|
|
@abbreviate = options.fetch(:abbreviate, [])
|
|
|
|
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"
|
|
|
|
unless skip?(:contact)
|
|
render_contact_details
|
|
vspace! "-0.1in"
|
|
end
|
|
|
|
render_blurb if @include_blurb
|
|
render_education unless skip?(:education)
|
|
render_employment_history unless skip?(:employment)
|
|
render_honors unless skip?(:honors)
|
|
render_students unless skip?(:students)
|
|
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_short_grant_support if @abbreviate.include? :grants
|
|
render_pubs_and_artifacts unless skip?(:publications)
|
|
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 or @abbreviate.include? category
|
|
end
|
|
|
|
def since_title_subscript(category)
|
|
if @since.has_key? category
|
|
" (since #{@since[category]})"
|
|
else "" end
|
|
end
|
|
|
|
|
|
############### HEADER ################
|
|
|
|
def render_header
|
|
noindent
|
|
textbf { huge; puts @title }
|
|
hfill
|
|
textbf { huge; puts @data["name"] }
|
|
endl "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
|
|
|
|
############### BLURB ################
|
|
|
|
def render_blurb
|
|
make_section("Overview")
|
|
puts @data["blurb"]
|
|
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(elems = @data["employment"])
|
|
make_section("Employment History")
|
|
build_filtered_itemize(:employment, elems) 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!("Chair Positions")
|
|
render_one_activity_class(:reviewer, @data["chairs"],
|
|
lambda { |r|
|
|
position = r["position"]
|
|
raise "Position missing from #{r}" unless position
|
|
venue = LabMetadata.complete_venue(r)["type"]
|
|
raise "Unknown Venue: #{r}" unless venue;
|
|
"#{position.capitalize}: #{LabMetadata.venue_name(r, size: :full)}"
|
|
})
|
|
|
|
subsection!("Program Comittee Member")
|
|
render_one_activity_class(:reviewer, @data["reviewer"].select { |r| r.fetch("pc", false) },
|
|
lambda { |r|
|
|
venue = LabMetadata.complete_venue(r)["type"]
|
|
raise "Unknown Venue: #{r}" unless venue;
|
|
"#{venue.capitalize}: #{LabMetadata.venue_name(r)}"
|
|
})
|
|
|
|
subsection!("Reviewer")
|
|
render_one_activity_class(:reviewer, @data["reviewer"].select { |r| not r.fetch("pc", false) },
|
|
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"+since_title_subscript(:talks))
|
|
build_filtered_itemize(:talks, @data["talks"], "noitemsep,leftmargin=5mm", enumerate: true) 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 Taught"+since_title_subscript(: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"+since_title_subscript(:grants))
|
|
|
|
test_for_since = proc { |r| duration_is_since?(r, @since[:grants]) }
|
|
|
|
grant_categories = [
|
|
["Planned Applications", "planned", ["grant", "gift"]],
|
|
["Pending Applications", "submitted", ["grant", "gift"]],
|
|
["Active 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",
|
|
"start",
|
|
"end",
|
|
"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?
|
|
|
|
awarded_grants =
|
|
grants_in_category
|
|
.select { |r| r["status"] == "accepted" or r["status"] == "completed" }
|
|
|
|
total_amount =
|
|
awarded_grants.map { |r| amt = r["amount"]; if amt.is_a? Numeric then amt else 0 end }.sum
|
|
by_effort =
|
|
awarded_grants.map { |r| amt = r["amount"]; if amt.is_a? Numeric then amt * (r["effort"].sub(/%/, "").to_f/100.0) else 0 end }.sum
|
|
|
|
dollar_summary =
|
|
if total_amount > 0
|
|
"--- #{format_money total_amount} Total; #{format_money by_effort} By Effort"
|
|
else
|
|
""
|
|
end
|
|
|
|
## Header for the category
|
|
subsection!("#{sec} (#{grants_in_category.length}) #{dollar_summary}")
|
|
|
|
## 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("p{0.15\\textwidth}p{0.8\\textwidth}") 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("0.02in")
|
|
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
|
|
|
|
def render_short_grant_support
|
|
make_section("Grant Support"+since_title_subscript(:grants))
|
|
|
|
test_for_since = proc { |r| duration_is_since?(r, @since[:grants]) }
|
|
|
|
grants = @data["grants"].select { |r|
|
|
r["status"] == "accepted" and test_for_since.call(r)
|
|
}
|
|
build_itemize(grants, "noitemsep,leftmargin=5mm,label=--") do |r|
|
|
copis = r.fetch("copis", [])
|
|
copi_string = if copis.empty? then "" else "; with "+copis.oxford_comma end
|
|
puts "#{r["title"]} (#{format_money r["amount"]} from #{r["agency"].split(/:/)[0]}#{copi_string})"
|
|
end
|
|
|
|
end
|
|
############### PUBLICATIONS AND ARTEFACTS ################
|
|
|
|
|
|
def render_author_list(record)
|
|
advisors = $db["cv/#{@who}/education"].map { |r| r["Advisor"] }.compact
|
|
print_direct "#{
|
|
record["authors"].map do |name|
|
|
abbreviated_name = LabMetadata.person_name(name, :size => :short)
|
|
if LabMetadata.is_member? name
|
|
"\\textbf{#{abbreviated_name}}"
|
|
elsif advisors.include? name
|
|
"\\underline{#{abbreviated_name}}"
|
|
else
|
|
safe_text(abbreviated_name)
|
|
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 for past conferences #{
|
|
get_avg_selectivity(record)
|
|
}"
|
|
elsif not sel_data["average"].nil?
|
|
"#{rate} #{sel_data["average"]}"
|
|
else
|
|
"#{rate} unknown; average rate for past conferences #{
|
|
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)
|
|
LabMetadata.venue_selectivity(record, rounding: rounding)
|
|
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"+since_title_subscript(:publications))
|
|
|
|
center {
|
|
print_direct "\\noindent Students I advise or co-advise and I are in \\textbf{bold}, my former advisor is \\underline{underlined}"
|
|
}
|
|
|
|
# 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_author_list(r)
|
|
textit {
|
|
render_paper_title(r["title"])
|
|
}
|
|
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)
|
|
puts " / "+r["notes"].join(" / ") if r.has_key? "notes"
|
|
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_author_list(r)
|
|
textit {
|
|
render_paper_title(r["title"])
|
|
}
|
|
puts "---"
|
|
textit{ puts LabMetadata.venue_name(r) }
|
|
puts "#{r["year"]}; "
|
|
venue = LabMetadata.complete_venue(r)
|
|
raise "Unknown location for venue: #{venue["acronym"] || venue["venue"]} #{r["year"]}" unless venue.has_key? "location"
|
|
puts "#{venue["location"]}; "
|
|
puts "#{get_pub_length_string(r)}, "
|
|
puts render_selectivity(r)
|
|
puts " / "+r["notes"].join(" / ") if r.has_key? "notes"
|
|
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_author_list(r)
|
|
textit {
|
|
render_paper_title(r["title"])
|
|
}
|
|
puts "---"
|
|
textit { puts LabMetadata.venue_name(r) }
|
|
puts "#{r["year"]}; "
|
|
venue = LabMetadata.complete_venue(r)
|
|
raise "Unknown location for venue: #{venue["acronym"] || venue["venue"]} #{r["year"]}" unless venue.has_key? "location"
|
|
puts "#{venue["location"]}; "
|
|
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_author_list(r)
|
|
textit {
|
|
render_paper_title(r["title"])
|
|
}
|
|
puts "---"
|
|
textit { puts r["booktitle"] || LabMetadata.venue_name(r) }
|
|
render_editor_list(r)
|
|
publisher = get_publisher(r)
|
|
venue = LabMetadata.complete_venue(r)
|
|
series = r["series"] || venue["series"] || venue["fullname"]
|
|
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
|
|
|
|
if @include_pending_papers
|
|
render_one_pub_or_artifact(
|
|
"Submitted and Planned Publications",
|
|
@data["publications_submitted"],
|
|
proc do |r|
|
|
render_author_list(r)
|
|
textit {
|
|
render_paper_title(r["title"])
|
|
}
|
|
puts "---"
|
|
textit {
|
|
puts "#{LabMetadata.venue_name(r)} #{r["year"]}"
|
|
case r.fetch("status", "unknown")
|
|
when "submitted"
|
|
puts " (under submission)"
|
|
when "planned"
|
|
puts " (planned for submission)"
|
|
end
|
|
puts " --- "
|
|
}
|
|
# puts "#{get_pub_length_string(r)}; "
|
|
puts "Historical average acceptance rate #{get_avg_selectivity(r, 0)}"
|
|
end,
|
|
global_filter
|
|
)
|
|
end
|
|
|
|
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_author_list(r)
|
|
textit {
|
|
render_paper_title(r["title"])
|
|
}
|
|
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_author_list(r)
|
|
textit {
|
|
render_paper_title(r["title"])
|
|
}
|
|
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"
|
|
when "github" then v.to_a.map { |gh_m, gh_v| "#{gh_v.to_i} GitHub #{gh_m}" }
|
|
else puts "#{safe_text v} #{safe_text m}"
|
|
end
|
|
end.flatten
|
|
|
|
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
|
|
|
|
def render_students
|
|
make_section("Students Advised")
|
|
|
|
priority = ["PhD", "MS", "BS"]
|
|
|
|
|
|
students =
|
|
($db["lab/members"].values + $db["lab/alumni"].values).
|
|
where { |r| r["advisor"].nil? or r["joint_advisor"] }.
|
|
map { |r| [ (r["degree"] or r["status"]), r ] }.
|
|
map { |cat, r|
|
|
cat = cat.split("-")[0]
|
|
cat = cat.split(/, */).sort_by { |c| priority.index(c) }[0]
|
|
[cat, r]
|
|
}.
|
|
where { |cat, r| priority.include? cat }.
|
|
my_reduce { |cat, records| records.sort_by { |r| [(r["year"] or 100000), r["name"]] }}
|
|
|
|
render_student_list = proc { |cat, students|
|
|
current = students.where { |s| s["year"].nil? }.size
|
|
graduated = students.where { |s| s["year"] }.size
|
|
subsection!("#{cat} Students Advised (#{graduated} graduated, #{current} current)")
|
|
puts(
|
|
students.
|
|
map { |r|
|
|
comments = []
|
|
if r["year"]
|
|
comments.push("Graduated in #{r["year"]}")
|
|
else
|
|
comments.push("Current Student")
|
|
end
|
|
comments.push("Jointly advised with #{r["advisor"].oxford_comma}") if r["advisor"]
|
|
comments.push("Expected Graduation in #{r["expected"]}") if r["expected"]
|
|
|
|
"#{r["name"]} (#{comments.join("; ")})"
|
|
}.
|
|
join(", ")
|
|
)
|
|
}
|
|
|
|
render_student_list.call("PhD", students["PhD"])
|
|
render_student_list.call("MS", students["MS"])
|
|
render_student_list.call("BS", students["BS"])
|
|
|
|
theses = @data["thesis_committees"]
|
|
# Thesis Committees
|
|
subsection!("Thesis Committees (Excluding joint advisees; #{theses.size} graduated)")
|
|
puts(
|
|
theses.map { |r| "#{r["name"]} (#{r["date"]})" }.join(", ")
|
|
)
|
|
end
|
|
|
|
end |