@@ -139,6 +139,12 @@ async def get_org_members(
139
139
140
140
members .extend (page_members )
141
141
142
+ tasks = [check_user_admin (session , org , member ["login" ]) for member in members ]
143
+ admin_statuses = await asyncio .gather (* tasks )
144
+
145
+ for member , is_owner in zip (members , admin_statuses ):
146
+ member ["is_owner" ] = is_owner
147
+
142
148
# Cache the results
143
149
cache [cache_key ] = members # Using __setitem__ instead of set()
144
150
print (f"[green]Cached { len (members )} members for { org } [/green]" )
@@ -226,15 +232,19 @@ def clear_cache() -> None:
226
232
print (f"[red]Error clearing cache: { str (e )} [/red]" )
227
233
228
234
229
- async def check_user_admin (session : aiohttp .ClientSession , org : str , username : str ) -> bool :
235
+ async def check_user_admin (
236
+ session : aiohttp .ClientSession , org : str , username : str
237
+ ) -> bool :
230
238
url = f"https://api.github.com/orgs/{ org } /memberships/{ username } "
231
239
async with session .get (url , headers = headers ) as response :
232
240
if response .status == 404 :
233
241
return False
234
242
elif response .status != 200 :
235
- print (f"[red]Error fetching membership for { username } in { org } : { response .status } [/red]" )
243
+ print (
244
+ f"[red]Error fetching membership for { username } in { org } : { response .status } [/red]"
245
+ )
236
246
return False
237
- return (await response .json ())[' role' ] == ' admin'
247
+ return (await response .json ())[" role" ] == " admin"
238
248
239
249
240
250
async def main (orgs , debug : bool , timelimit_days : int ):
@@ -245,7 +255,9 @@ async def main(orgs, debug: bool, timelimit_days: int):
245
255
246
256
# check who the current user is
247
257
async with aiohttp .ClientSession () as session :
248
- async with session .get ("https://api.github.com/user" , headers = headers ) as response :
258
+ async with session .get (
259
+ "https://api.github.com/user" , headers = headers
260
+ ) as response :
249
261
if response .status == 200 :
250
262
user_data = await response .json ()
251
263
current_user = user_data ["login" ]
@@ -277,12 +289,15 @@ async def main(orgs, debug: bool, timelimit_days: int):
277
289
278
290
# Get all members from all orgs
279
291
all_members = {}
292
+ org_owners = {}
280
293
for org in orgs :
281
294
members = await get_org_members (session , org , debug )
282
295
for member in members :
283
296
if member ["login" ] not in all_members :
284
297
all_members [member ["login" ]] = []
285
298
all_members [member ["login" ]].append (org )
299
+ if member ["is_owner" ]:
300
+ org_owners .setdefault (org , []).append (member ["login" ])
286
301
287
302
# Get activity for each user
288
303
tasks = []
@@ -311,32 +326,42 @@ async def main(orgs, debug: bool, timelimit_days: int):
311
326
if debug :
312
327
print (f" [green]{ current_user } is an admin in { org } [/green]" )
313
328
else :
314
- print (f" [yellow]{ current_user } is not an admin in { org } , list of users will be incomplete (limited to public membership)[/yellow]" )
329
+ print (
330
+ f" [yellow]{ current_user } is not an admin in { org } , list of users will be incomplete (limited to public membership)[/yellow]"
331
+ )
315
332
n_active = 0
316
333
n_inactive = 0
317
334
for username , last_activity , user_orgs in sorted (
318
335
user_activities ,
319
- key = lambda x : (x [1 ], x [0 ])
320
- if x [1 ] is not None
321
- else (datetime .fromtimestamp (0 ).replace (tzinfo = timezone .utc ), x [0 ]),
336
+ key = lambda x : (
337
+ (x [1 ], x [0 ])
338
+ if x [1 ] is not None
339
+ else (datetime .fromtimestamp (0 ).replace (tzinfo = timezone .utc ), x [0 ])
340
+ ),
322
341
reverse = True ,
323
342
):
324
343
if org not in user_orgs :
325
344
continue
326
- if last_activity is not None and last_activity > (datetime .now ().replace (tzinfo = timezone .utc ) - timedelta (days = timelimit_days )):
345
+ if last_activity is not None and last_activity > (
346
+ datetime .now ().replace (tzinfo = timezone .utc )
347
+ - timedelta (days = timelimit_days )
348
+ ):
327
349
n_active += 1
328
350
continue
329
351
n_inactive += 1
330
352
last_activity_ago = (
331
- humanize .naturaltime (datetime .now (last_activity .tzinfo ) - last_activity )
353
+ humanize .naturaltime (
354
+ datetime .now (last_activity .tzinfo ) - last_activity
355
+ )
332
356
if last_activity
333
357
else "[red]never[/red]"
334
358
)
335
359
orgs_str = ", " .join (user_orgs )
336
- print (
337
- f" { username :<20} : Last activity { last_activity_ago } "
338
- )
339
- print (f" Found [red]{ n_inactive } inactive[/red] and [green]{ n_active } active[/green] users in { org } with last activity more recent than { timelimit_days } days." )
360
+ u_owner = " (owner)" if username in org_owners .get (org , []) else ""
361
+ print (f" { username + u_owner :<20} : Last activity { last_activity_ago } " )
362
+ print (
363
+ f" Found [red]{ n_inactive } inactive[/red] and [green]{ n_active } active[/green] users in { org } with last activity more recent than { timelimit_days } days."
364
+ )
340
365
341
366
342
367
if __name__ == "__main__" :
@@ -346,7 +371,6 @@ async def main(orgs, debug: bool, timelimit_days: int):
346
371
)
347
372
parser .add_argument ("--debug" , action = "store_true" , help = "Show debug information" )
348
373
349
-
350
374
parser .add_argument (
351
375
"--timelimit-days" ,
352
376
type = int ,
@@ -361,7 +385,6 @@ async def main(orgs, debug: bool, timelimit_days: int):
361
385
)
362
386
args = parser .parse_args ()
363
387
364
-
365
388
if args .clear_cache :
366
389
clear_cache ()
367
390
0 commit comments