Path: blob/main/_plugins/author-page.rb
1677 views
# frozen_string_literal: true12require './_plugins/gtn/mod'3require './_plugins/gtn'45module Jekyll6module Generators7##8# This class generates the GTN's author pags9class AuthorPageGenerator < Generator10safe true1112##13# This extracts the contributions and pushes them on to an existing14# datastructure, modifying it in the process. It's pretty gross.15#16# Params17# +t+:: The tutorial, slide, or news item18# +datastructure+:: The hash of contributors that the author information should be pushed onto19# +flat+:: Whether the datastructure is a flat array or a nested array20#21# Returns22# +datastructure+:: The modified datastructure23def pusher(t, datastructure, flat)24if t.data.key?('contributors')25if flat26t.data['contributors'].each { |c| datastructure[c].push(t) }27else28t.data['contributors'].each { |c| datastructure[c].push([t, nil]) }29end30elsif t.data.key?('contributions')31t.data['contributions'].each do |contribution_type, contributor|32contributor.each do |c|33if flat34datastructure[c].push(t)35else36datastructure[c].push([t, contribution_type])37end38end39end40end4142t.data['maintainers'].each { |c| datastructure[c].push([t, 'maintainer']) } if t.data.key?('maintainers')43t.data['funding'].each { |c| datastructure[c].push([t, 'funding']) } if t.data.key?('funding')4445datastructure46end4748##49# This generates the author pages50# Params51# +site+:: The site object52def generate(site)53return unless site.layouts.key? 'contributor_index'5455dir = 'hall-of-fame'5657# pre-calculating this hash saves about 4.9 seconds off the previous58# build time of 5 seconds.59tutorials_by_author = Hash.new { |hash, key| hash[key] = [] }60learning_pathways_by_author = Hash.new { |hash, key| hash[key] = [] }61slides_by_author = Hash.new { |hash, key| hash[key] = [] }62news_by_author = Hash.new { |hash, key| hash[key] = [] }63events_by_author = Hash.new { |hash, key| hash[key] = [] }64videos_by_author = Hash.new { |hash, key| hash[key] = [] }65faqs_by_author = Hash.new { |hash, key| hash[key] = [] }66has_philosophy = Hash.new { false }6768prs_by_author = Hash.new { |hash, key| hash[key] = [] }69reviews_by_author = Hash.new { |hash, key| hash[key] = [] }7071site.data['github'].each do |num, pr|72prs_by_author[pr['author']['login']] << [num, pr['mergedAt']]7374pr['reviews'].each do |review|75reviews_by_author[review['author']['login']] << [num, review['submittedAt'], review['state']]76end77end7879site.pages.each do |t|80# Skip Symlinks81if t.data['symlink']82next83end8485# Tutorials86pusher(t, tutorials_by_author, false) if t['layout'] == 'tutorial_hands_on'8788# Slides89if !%w[base_slides introduction_slides tutorial_slides].index(t['layout']).nil?90pusher(t, slides_by_author, false)91end9293pusher(t, events_by_author, false) if t['layout'] == 'event' or t['layout'] == "event-external"9495pusher(t, faqs_by_author, false) if t['layout'] == 'faq'9697t.data.fetch('recordings', []).each do |r|98r.fetch('captioners', []).each { |ent| videos_by_author[ent].push([t, 'captioner', r]) }99r.fetch('speakers', []).each { |ent| videos_by_author[ent].push([t, 'speaker', r]) }100end101102pusher(t, learning_pathways_by_author, false) if t['layout'] == 'learning-pathway'103104# Philosophies105has_philosophy[t.data['username']] = true if t['layout'] == 'training_philosophy' && !t.data['username'].nil?106end107108site.posts.docs.each do |t|109# News110pusher(t, news_by_author, true) if t['layout'] == 'news'111end112113Gtn::Contributors.list(site).each_key do |contributor|114# Using PageWithoutAFile instead of a custom class which reads files115# from disk each time, saves some time, but it is unclear how much116# due to how the previous was accounted. But assuming 0.040s per page * 193 should be about 8 seconds.117page2 = PageWithoutAFile.new(site, '', File.join(dir, contributor), 'index.html')118page2.content = nil119name = Gtn::Contributors.fetch_contributor(site, contributor).fetch('name', contributor)120121# Their tutorials122page2.data['contributor'] = contributor123page2.data['personname'] = name124page2.data['title'] = "GTN Contributor: #{name}"125page2.data['layout'] = 'contributor_index'126127page2.data['tutorials'] = tutorials_by_author[contributor].group_by{|x| x[0] }.map{|k, v| [k, v.map{|vv| vv[1]}.compact]}128page2.data['slides'] = slides_by_author[contributor].group_by{|x| x[0] }.map{|k, v| [k, v.map{|vv| vv[1]}.compact]}129page2.data['news'] = news_by_author[contributor]130page2.data['learning_pathways'] = learning_pathways_by_author[contributor]131page2.data['events'] = events_by_author[contributor].group_by{|x| x[0] }.map{|k, v| [k, v.map{|vv| vv[1]}.compact]}132page2.data['videos'] = videos_by_author[contributor].group_by{|x| x[0] }.map{|k, v| [k, v.map{|vv| vv[1]}.uniq.compact]}133page2.data['faqs'] = faqs_by_author[contributor].group_by{|x| x[0] }.map{|k, v| [k, v.map{|vv| vv[1]}.uniq.compact]}134135page2.data['tutorials_count'] = tutorials_by_author[contributor].length136page2.data['slides_count'] = slides_by_author[contributor].length137page2.data['news_count'] = news_by_author[contributor].length138page2.data['learning_pathways_count'] = learning_pathways_by_author[contributor].length139page2.data['events_count'] = events_by_author[contributor].length140page2.data['videos_count'] = videos_by_author[contributor].length141page2.data['faqs_count'] = faqs_by_author[contributor].length142143page2.data['editors'] = Gtn::TopicFilter.enumerate_topics(site).select do |t|144t.fetch('editorial_board', []).include?(contributor)145end146# Also their learning pathways147page2.data['editors'] += site.pages.select do |t|148t['layout'] == 'learning-pathway' && t.data.fetch('editorial_board', []).include?(contributor)149end150page2.data['editor_count'] = page2.data['editors'].length151152page2.data['has_philosophy'] = has_philosophy[contributor]153154countable_reviews = reviews_by_author[contributor]155.reject{|x| x[1].nil?} # Group by PRs.156.group_by{|x| x[0]}.map{|x, r| r.sort_by{|r1| r1[1]}.max}.sort_by{|w| w[1]}.reverse157158page2.data['gh_prs_count'] = prs_by_author[contributor].count159page2.data['gh_reviews_count'] = countable_reviews.count160161page2.data['gh_prs_recent'] = prs_by_author[contributor]162.reject{|x| x[1].nil?}.sort_by { |x| x[1] }.reverse.take(5)163.map{|x| x[0]}164page2.data['gh_reviews_recent'] = countable_reviews.take(5)165.map{|x| x[0]}166167site.pages << page2168end169end170end171end172end173174Jekyll::Hooks.register :site, :post_read do |site|175if Jekyll.env == 'production'176Jekyll.logger.info "[GTN/Reviewers] Ingesting GitHub reviewer metadata"177start_time = Time.now178# Maps a lowercase version of their name to the potential mixed-case version179contribs_lower = site.data['contributors'].map{|k, v| [k.downcase, k]}.to_h180181# Annotate the from github metadata182gh_reviewers_by_path = Hash.new { |hash, key| hash[key] = [] }183# Hash of PRs by path184site.data['github'].each do |num, pr|185# Within a PR we have some reviews, let's get that set organised:186reviewers = pr['reviews'].map do |review|187# Just "people"188contribs_lower.fetch(review['author']['login'].downcase, review['author']['login'])189end.uniq.reject{|x| x == 'github-actions'}190191pr['files'].select{|p| p['path'] =~ /.(md|html)$/}.each do |file|192real_path = Gtn::PublicationTimes.chase_rename(file['path'])193gh_reviewers_by_path[real_path] += reviewers194gh_reviewers_by_path[real_path].uniq!195end196end197198# For all of our pages, if the path is mentioned above, then, tag it.199site.pages.select{|t| gh_reviewers_by_path.key?(t.path)}.each do |t|200if t['layout'] == 'tutorial_hands_on' or !%w[base_slides introduction_slides tutorial_slides].index(t['layout']).nil?201if t.data.key?('contributors')202# Automatically 'upgrade' to new structure203t.data['contributions'] = {204'authorship' => t.data['contributors'],205'reviewing' => gh_reviewers_by_path[t.path]206}207t.data.delete('contributors')208elsif t.data.key?('contributions')209if t.data['contributions'].key?('reviewing')210t.data['contributions']['reviewing'] += gh_reviewers_by_path[t.path]211else212t.data['contributions']['reviewing'] = gh_reviewers_by_path[t.path]213end214t.data['contributions']['reviewing'].uniq!215end216end217end218219site.posts.docs.select{|t| gh_reviewers_by_path.key?(t.path)}.each do |t|220if t['layout'] == 'news'221if t.data.key?('contributors')222t.data['contributions'] = {223'authorship' => t.data['contributors'],224'reviewing' => gh_reviewers_by_path[t.path]225}226t.data.delete('contributors')227elsif t.data.key?('contributions')228if t.data['contributions'].key?('reviewing')229t.data['contributions']['reviewing'] += gh_reviewers_by_path[t.path]230else231t.data['contributions']['reviewing'] = gh_reviewers_by_path[t.path]232end233t.data['contributions']['reviewing'].uniq!234end235end236end237Jekyll.logger.info "[GTN/Reviewers] Complete in #{Time.now - start_time} seconds"238end239end240241242