Skip to content

Instantly share code, notes, and snippets.

@petertseng
Last active May 10, 2017 06:48
Show Gist options
  • Save petertseng/460b93436817ee02f34a919216566966 to your computer and use it in GitHub Desktop.
Save petertseng/460b93436817ee02f34a919216566966 to your computer and use it in GitHub Desktop.
Exercism teams
require 'json'
require 'set'
# Create a personal access token at https://github.com/settings/tokens
# It needs only one scope: read:org ("Read org and team membership").
TOKEN_FILE = 'secrets/token.txt'.freeze
SLEEP_TIME = 2
global_name = 'track-maintainers'.freeze
language_names = Set.new(%w(
bash
c
ceylon
clojure
coffeescript
coldfusion
cpp
crystal
csharp
dlang
elixir
elm
erlang
factor
fsharp
golang
groovy
haskell
haxe
idris
java
javascript
julia
kotlin
lisp
lua
mips
nim
objective-c
ocaml
pascal
perl
php
plsql
pony
powershell
prolog
purescript
python
r
racket
ruby
rust
scala
smalltalk
swift
tcl
teco
vb-net
)).freeze
not_language_names = Set.new(%w(
alumni
analysis
api
cli-committers
contributors
ember
gui
io
product-design
project-management
proofs
rikki
styleguide
synopsis
triage
windows-installer
xruby
)).freeze
CACHE = 'cache'.freeze
Dir.mkdir(CACHE) unless File.exist?(CACHE)
LAST_REQUEST = [0]
def req(url)
cache = "#{CACHE}/#{url.gsub(/\W/, ?_)}"
if File.exist?(cache)
res = File.read(cache)
else
if Time.now.to_i - LAST_REQUEST.first < SLEEP_TIME
puts "#{url}: sleeping to not overload."
Kernel.sleep(SLEEP_TIME)
end
LAST_REQUEST[0] = Time.now.to_i
tok = File.read(TOKEN_FILE).chomp
res = `curl -H "Authorization: token #{tok}" #{url}`
File.open(cache, ?w) { |f| f.puts(res) }
end
JSON.parse(res)
rescue => e
puts res
puts "No parse: #{e}"
end
def req_members(team)
members = req(team['members_url'].split(?{).first + '?per_page=100')
team[:members] = members.freeze
end
teams = req('https://api.github.com/orgs/exercism/teams?per_page=100')
new_teams = []
language_teams = []
global_teams = []
teams.each { |t|
slug = t['slug']
if slug == global_name
global_teams << t
elsif language_names.include?(slug)
language_teams << t
elsif !not_language_names.include?(slug)
new_teams << t
end
}
unless new_teams.empty?
puts "There are #{new_teams.size} new teams! Let's classify them."
puts new_teams
exit 1
end
unless global_teams.size == 1
puts "There were #{global_teams.size} global teams! How did this happen?"
puts global_teams
exit 1
end
global_team = global_teams.first
unseen_languages = language_names - language_teams.map { |t| t['slug'] }
puts "#{teams.size} teams"
puts "#{language_teams.size} language teams (#{unseen_languages.size} unseen: #{unseen_languages.to_a})"
global_members = req_members(global_team)
global_usernames = Set.new(global_members.map { |u| u['login'] })
people = Hash.new { |h, k| h[k] = [] }
union_usernames = Set.new
language_teams.each { |lt|
members = req_members(lt)
union_usernames |= members.map { |u| u['login'] }
members.each { |m| people[m['login']] << lt['slug'] }
}
puts "Global maintainers who aren't in any track: #{(global_usernames - union_usernames).to_a}"
puts "Track maintainers who aren't in global: #{(union_usernames - global_usernames).to_a}"
people.map { |name, tracks| [name, tracks, tracks.size] }.sort_by(&:last).reverse_each { |p|
puts '%1$20s: %3$d %2$s' % p
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment