@@ -351,6 +351,55 @@ def count_users(self) -> int:
351351 finally :
352352 self ._put_conn (conn )
353353
354+ def count_agents_by_status (self ) -> dict :
355+ """Count agents grouped by status (claimed/pending/etc).
356+
357+ Returns dict with keys: total, claimed, pending, other.
358+ Excludes sandbox agents from counts.
359+
360+ See: https://github.com/shirtlessfounder/moltmarkets-api/issues/177
361+ """
362+ if self ._use_memory :
363+ total = claimed = pending = 0
364+ for user in self ._users .values ():
365+ if user .get ("user_type" ) != "agent" or user .get ("is_sandbox" ):
366+ continue
367+ total += 1
368+ status = user .get ("status" , "pending" )
369+ if status == "claimed" :
370+ claimed += 1
371+ elif status == "pending" :
372+ pending += 1
373+ return {
374+ "total" : total ,
375+ "claimed" : claimed ,
376+ "pending" : pending ,
377+ "other" : total - claimed - pending ,
378+ }
379+
380+ conn = self ._get_conn ()
381+ try :
382+ with conn .cursor () as cur :
383+ cur .execute ("""
384+ SELECT
385+ COUNT(*) FILTER (WHERE user_type = 'agent' AND NOT is_sandbox) AS total,
386+ COUNT(*) FILTER (WHERE user_type = 'agent' AND NOT is_sandbox AND status = 'claimed') AS claimed,
387+ COUNT(*) FILTER (WHERE user_type = 'agent' AND NOT is_sandbox AND status = 'pending') AS pending
388+ FROM users
389+ """ )
390+ row = cur .fetchone ()
391+ total = row ["total" ] or 0
392+ claimed = row ["claimed" ] or 0
393+ pending = row ["pending" ] or 0
394+ return {
395+ "total" : total ,
396+ "claimed" : claimed ,
397+ "pending" : pending ,
398+ "other" : total - claimed - pending ,
399+ }
400+ finally :
401+ self ._put_conn (conn )
402+
354403 def reset_sandbox_balance (self , user_id : str , amount : float ) -> float :
355404 """Reset a sandbox agent's balance and stats.
356405
0 commit comments