Skip to content

Commit 51053aa

Browse files
author
Thomas Efer
committed
initial code commit
1 parent 51734ae commit 51053aa

File tree

4 files changed

+78
-0
lines changed

4 files changed

+78
-0
lines changed

Gemfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
source 'https://rubygems.org'
2+
3+
gem 'json'

Gemfile.lock

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
GEM
2+
remote: https://rubygems.org/
3+
specs:
4+
json (1.8.2)
5+
6+
PLATFORMS
7+
ruby
8+
9+
DEPENDENCIES
10+
json

discovery.rb

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# encoding: utf-8
2+
Encoding.default_external = 'utf-8'
3+
Encoding.default_internal = 'utf-8'
4+
require 'rubygems'
5+
require 'bundler'
6+
Bundler.require
7+
8+
require 'open-uri'
9+
require 'set'
10+
11+
unless (@api_key = ARGV.first)
12+
puts "usage: ruby #{__FILE__} GITHUB_OAUTH_TOKEN # Generate yours at https://github.com/settings/applications"
13+
exit
14+
end
15+
16+
@directions = [:followers, :following] # the traversable relation directions
17+
@checked_users = [] # an ever growing list of login names
18+
@unchecked_users = [:efi] # setup the center(s) of your (graph) universe
19+
@delay = 0.8 # 800ms is enough to stay well below the rate limit of 5k req/hour
20+
21+
@directions.each{|d| Dir.mkdir("./#{d}") unless File.exists?("./#{d}")}
22+
23+
def neighbours_of user_login, force_api_loading=false
24+
result = Hash.new []
25+
@directions.each do |type|
26+
if !File.exists?(cache="./#{type}/#{user_login}.json") || force_api_loading
27+
File.open(cache,"w"){|c| c.write begin;open("https://api.github.com/users/#{user_login}/#{type}",http_basic_authentication:[@api_key,"x-oauth-basic"]){|i|i.read};rescue;end||"[]"}
28+
sleep @delay
29+
end
30+
result[type] = begin;JSON.load(File.open(cache).read).map{|u|u["login"]};rescue;end||[]
31+
end
32+
return result
33+
end
34+
35+
while !@unchecked_users.empty? do
36+
user = @unchecked_users.shift
37+
puts "#{@checked_users.size} / #{@unchecked_users.size} - #{user}"
38+
user_neighbours = neighbours_of user
39+
@unchecked_users |= @directions.map{|d| user_neighbours[d].map(&:to_sym)}.inject(&:|)
40+
@checked_users |= [user]
41+
end

graphbuilder.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# encoding: utf-8
2+
Encoding.default_external = 'utf-8'
3+
Encoding.default_internal = 'utf-8'
4+
require 'rubygems'
5+
require 'bundler'
6+
Bundler.require
7+
8+
@data={}
9+
10+
[:followers,:following].each do |type|
11+
@e_id=-1
12+
@data[type] = Hash[Dir["./#{type}/*.json"].map{|j| [j.gsub(/^.*[\\\/]|\.json$/,""), JSON.parse((content=File.open(j,&:read)).strip==""?"{}":content).map{|u|u["login"]}] }]
13+
File.open("#{type}.gexf","w"){|f| f.write <<-GEXF}
14+
<?xml version="1.0" encoding="UTF-8"?>
15+
<gexf xmlns="http://www.gexf.net/1.2draft" version="1.2"><graph mode="static" defaultedgetype="directed">
16+
<nodes>
17+
#{ (@data[type].keys+@data[type].values).flatten.uniq.map{|n| "<node id=\"#{n}\" label=\"#{n}\"/>"}.join("\n") }
18+
</nodes>
19+
<edges>
20+
#{ @data[type].map{|k,v| v.map{|c| "<edge id=\"#{@e_id+=1}\" source=\"#{type==:followers ? c:k}\" target=\"#{type==:followers ? k:c}\"/>" } }.flatten.join("\n") }
21+
</edges>
22+
</graph></gexf>
23+
GEXF
24+
end

0 commit comments

Comments
 (0)