99import sys
1010import requests
1111import getpass
12+ import time
1213
13- # With authentication: up to 5000 requests per hour.
1414print ("user:" , file = sys .stderr )
1515user = input ()
1616passwd = getpass .getpass ("Password or access token:\n " )
2020LOGO_URL = 'https://avatars2.githubusercontent.com/u/365630?v=4'
2121
2222
23+ def get (url ):
24+ for sleep_time in [10 , 30 , 0 ]:
25+ reply = requests .get (url , auth = auth )
26+ api_limit = ("message" in reply .json ()
27+ and "API rate limit exceeded" in reply .json ()["message" ])
28+ if not api_limit :
29+ break
30+ print ("API rate limit exceeded, waiting.." )
31+ time .sleep (sleep_time )
32+
33+ reply .raise_for_status ()
34+ return reply
35+
36+
2337def group_iterable (iterable , size ):
2438 """Group iterable into lines"""
2539 group = []
@@ -34,73 +48,121 @@ def group_iterable(iterable, size):
3448
3549def get_contributors ():
3650 """Get the list of contributor profiles. Require admin rights."""
37- # get members of scikit-learn teams on GitHub
38- members = []
51+ # get members of scikit-learn core-dev on GitHub
52+ core_devs = []
3953 team = 11523
4054 for page in [1 , 2 ]: # 30 per page
41- reply = requests .get (
42- "https://api.github.com/teams/%d/members?page=%d"
43- % (team , page ), auth = auth )
44- reply .raise_for_status ()
55+ reply = get ("https://api.github.com/teams/%d/members?page=%d" %
56+ (team , page ))
57+ core_devs .extend (reply .json ())
58+
59+ # get members of scikit-learn on GitHub
60+ members = []
61+ for page in [1 , 2 ]: # 30 per page
62+ reply = get (
63+ "https://api.github.com/orgs/scikit-learn/members?page=%d" %
64+ (page , ))
4565 members .extend (reply .json ())
4666
4767 # keep only the logins
48- logins = [c ['login' ] for c in members ]
49- # remove duplicate
50- logins = set (logins )
68+ core_devs = [c ['login' ] for c in core_devs ]
69+ members = [c ['login' ] for c in members ]
70+
71+ # add missing contributors with GitHub accounts
72+ members .extend (['dubourg' , 'mbrucher' , 'thouis' , 'jarrodmillman' ])
73+ # add missing contributors without GitHub accounts
74+ members .extend (['Angel Soler Gollonet' ])
75+ # remove CI bots
76+ members .remove ('sklearn-ci' )
77+ members .remove ('sklearn-lgtm' )
78+ members .remove ('sklearn-wheels' )
79+
80+ # remove duplicate, and get the difference of the two sets
81+ core_devs = set (core_devs )
82+ members = set (members )
83+ emeritus = members .difference (core_devs )
5184
5285 # get profiles from GitHub
53- profiles = [get_profile (login ) for login in logins ]
86+ core_devs = [get_profile (login ) for login in core_devs ]
87+ emeritus = [get_profile (login ) for login in emeritus ]
88+
5489 # sort by last name
55- profiles = sorted (profiles , key = key )
90+ core_devs = sorted (core_devs , key = key )
91+ emeritus = sorted (emeritus , key = key )
5692
57- return profiles
93+ return core_devs , emeritus
5894
5995
6096def get_profile (login ):
6197 """Get the GitHub profile from login"""
62- profile = requests . get ( "https://api.github.com/users/%3C/span%3E%s" % login ,
63- auth = auth ). json ()
64- if 'name' not in profile :
65- # default profile if the login does not exist
98+ print ( "get profile for %s" % ( login , ))
99+ try :
100+ profile = get ( "https://api.github.com/users/%s" % login ). json ()
101+ except requests . exceptions . HTTPError :
66102 return dict (name = login , avatar_url = LOGO_URL , html_url = "" )
67- else :
68- if profile ["name" ] is None :
69- profile ["name" ] = profile ["login" ]
70103
71- # fix missing names
72- missing_names = {'bthirion' : 'Bertrand Thirion' ,
73- 'Duchesnay' : 'Edouard Duchesnay' ,
74- 'Lars' : 'Lars Buitinck' ,
75- 'MechCoder' : 'Manoj Kumar' }
76- if profile ["name" ] in missing_names :
77- profile ["name" ] = missing_names [profile ["name" ]]
78- return profile
104+ if profile ["name" ] is None :
105+ profile ["name" ] = profile ["login" ]
106+
107+ # fix missing names
108+ missing_names = {
109+ 'bthirion' : 'Bertrand Thirion' ,
110+ 'dubourg' : 'Vincent Dubourg' ,
111+ 'Duchesnay' : 'Edouard Duchesnay' ,
112+ 'Lars' : 'Lars Buitinck' ,
113+ 'MechCoder' : 'Manoj Kumar' ,
114+ 'jeremiedbb' : 'Jérémie Du Boisberranger' ,
115+ }
116+ if profile ["name" ] in missing_names :
117+ profile ["name" ] = missing_names [profile ["name" ]]
118+
119+ return profile
79120
80121
81122def key (profile ):
82123 """Get the last name in lower case"""
83124 return profile ["name" ].split (' ' )[- 1 ].lower ()
84125
85126
86- contributors = get_contributors ()
87-
88- print (".. raw :: html\n " )
89- print (" <!-- Generated by generate_authors_table.py -->" )
90- print (" <table>" )
91- print (" <col style='width:%d%%' span='%d'>"
92- % (int (100 / ROW_SIZE ), ROW_SIZE ))
93- print (" <style>" )
94- print (" img.avatar {border-radius: 10px;}" )
95- print (" td {vertical-align: top;}" )
96- print (" </style>" )
97- for row in group_iterable (contributors , size = ROW_SIZE ):
98- print (" <tr>" )
99- for contributor in row :
100- print (" <td>" )
101- print (" <a href='%s'><img src='%s' class='avatar' /></a> <br />"
102- % (contributor ["html_url" ], contributor ["avatar_url" ]))
103- print (" <p>%s</p>" % contributor ["name" ])
104- print (" </td>" )
105- print (" </tr>" )
106- print (" </table>" )
127+ def generate_table (contributors ):
128+ lines = [
129+ (".. raw :: html\n " ),
130+ (" <!-- Generated by generate_authors_table.py -->" ),
131+ (" <table>" ),
132+ (" <col style='width:%d%%' span='%d'>" %
133+ (int (100 / ROW_SIZE ), ROW_SIZE )),
134+ (" <style>" ),
135+ (" img.avatar {border-radius: 10px;}" ),
136+ (" td {vertical-align: top;}" ),
137+ (" </style>" ),
138+ ]
139+ for row in group_iterable (contributors , size = ROW_SIZE ):
140+ lines .append (" <tr>" )
141+ for contributor in row :
142+ lines .append (" <td>" )
143+ lines .append (
144+ " <a href='%s'><img src='%s' class='avatar' /></a> <br />" %
145+ (contributor ["html_url" ], contributor ["avatar_url" ]))
146+ lines .append (" <p>%s</p>" % (contributor ["name" ], ))
147+ lines .append (" </td>" )
148+ lines .append (" </tr>" )
149+ lines .append (" </table>" )
150+ return '\n ' .join (lines )
151+
152+
153+ def generate_list (contributors ):
154+ lines = []
155+ for contributor in contributors :
156+ lines .append ("- %s" % (contributor ["name" ], ))
157+ return '\n ' .join (lines )
158+
159+
160+ if __name__ == "__main__" :
161+
162+ core_devs , emeritus = get_contributors ()
163+
164+ with open ("../doc/authors.rst" , "w+" ) as rst_file :
165+ rst_file .write (generate_table (core_devs ))
166+
167+ with open ("../doc/authors_emeritus.rst" , "w+" ) as rst_file :
168+ rst_file .write (generate_list (emeritus ))
0 commit comments