|
| 1 | +import asyncio |
| 2 | + |
1 | 3 | from django.contrib.auth import get_user_model |
2 | 4 | from django.core.management.base import BaseCommand |
3 | 5 |
|
|
8 | 10 | class Command(BaseCommand): |
9 | 11 | help = "Updates Wharton privilege status for all users." |
10 | 12 |
|
| 13 | + def add_arguments(self, parser): |
| 14 | + parser.add_argument( |
| 15 | + "--concurrency", |
| 16 | + type=int, |
| 17 | + default=20, |
| 18 | + help="Max concurrent API calls (default: 20).", |
| 19 | + ) |
| 20 | + |
11 | 21 | def handle(self, *args, **kwargs): |
12 | | - users = GroupMembership.objects.values_list("user__username", flat=True).distinct() |
13 | | - print(f"Checking {len(users)} users...") |
14 | | - wharton_wrapper = WhartonBookingWrapper() |
15 | | - updated = 0 |
16 | | - |
17 | | - for username in users: |
18 | | - user = get_user_model().objects.get(username=username) |
19 | | - is_wharton = wharton_wrapper.is_wharton(user) |
20 | | - memberships = GroupMembership.objects.filter(user__username=user) |
21 | | - for membership in memberships: |
22 | | - if membership.is_wharton != is_wharton: |
23 | | - membership.is_wharton = is_wharton |
24 | | - membership.save() |
25 | | - status = "now" if is_wharton else "no longer" |
26 | | - print(f"User {user} is {status} a Wharton user.") |
27 | | - updated += 1 |
28 | | - |
29 | | - print(f"Done updating Wharton statuses. Updated: {updated} users.") |
| 22 | + asyncio.run(self.ahandle(**kwargs)) |
| 23 | + |
| 24 | + async def ahandle(self, **kwargs): |
| 25 | + max_concurrency = kwargs["concurrency"] |
| 26 | + usernames = await asyncio.to_thread( |
| 27 | + lambda: list( |
| 28 | + GroupMembership.objects.values_list("user__username", flat=True).distinct() |
| 29 | + ) |
| 30 | + ) |
| 31 | + |
| 32 | + self.stdout.write(f"Checking {len(usernames)} users (concurrency={max_concurrency})...") |
| 33 | + |
| 34 | + semaphore = asyncio.Semaphore(max_concurrency) |
| 35 | + updated_count = 0 |
| 36 | + |
| 37 | + async def process_user(username: str) -> int: |
| 38 | + async with semaphore: |
| 39 | + wrapper = WhartonBookingWrapper() |
| 40 | + user = await asyncio.to_thread(get_user_model().objects.get, username=username) |
| 41 | + is_wharton = await asyncio.to_thread(wrapper.is_wharton, user) |
| 42 | + |
| 43 | + memberships = await asyncio.to_thread( |
| 44 | + lambda: list(GroupMembership.objects.filter(user__username=username)) |
| 45 | + ) |
| 46 | + |
| 47 | + count = 0 |
| 48 | + for membership in memberships: |
| 49 | + if membership.is_wharton != is_wharton: |
| 50 | + membership.is_wharton = is_wharton |
| 51 | + await asyncio.to_thread(membership.save) |
| 52 | + status = "now" if is_wharton else "no longer" |
| 53 | + self.stdout.write(f"User {user} is {status} a Wharton user.") |
| 54 | + count += 1 |
| 55 | + return count |
| 56 | + |
| 57 | + results = await asyncio.gather( |
| 58 | + *(process_user(u) for u in usernames), |
| 59 | + return_exceptions=True, |
| 60 | + ) |
| 61 | + |
| 62 | + for username, result in zip(usernames, results): |
| 63 | + if isinstance(result, Exception): |
| 64 | + self.stderr.write(f"Error processing {username}: {result}") |
| 65 | + else: |
| 66 | + updated_count += result |
| 67 | + |
| 68 | + self.stdout.write(f"Done updating Wharton statuses. Updated: {updated_count} users.") |
0 commit comments