prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Modify domain invitation script to process "friends of the show" first, then incrementally process others
  ### Issue description
  
  We have a few domain managers that we'll invite to the registrar first. Let's modify the domain invitation script to send to a defined set of domains first. 
  
  We also shouldn't blast thousands of emails out to the internet, but incrementally roll them out.
  
  ### Acceptance criteria
  
  - [ ] Invitation script works with a product owner-specified list of domains/contacts (before sending to everyone else)
  - [ ] Script slow rolls out invitations. Could be percentage-based (1/2/5/10/20/45/75/100) or volume-based (a few hundred at a time)
  
  ### Additional context
  
  _No response_
  
  ### Links to other issues
  
  🔄 Related to PR #1038
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `src/registrar/management/commands/send_domain_invitations.py`
  Content:
  ```
  1 """Data migration: Send domain invitations once to existing customers."""
  2 
  3 import logging
  4 import copy
  5 
  6 from django.core.management import BaseCommand
  7 from registrar.models import TransitionDomain
  8 from ...utility.email import send_templated_email, EmailSendingError
  9 from typing import List
  10 
  11 logger = logging.getLogger(__name__)
  12 
  13 
  14 class Command(BaseCommand):
  15     help = "Send domain invitations once to existing customers."
  16 
  17     # this array is used to store and process the transition_domains
  18     transition_domains: List[str] = []
  19     # this array is used to store domains with errors, which are not
  20     # sent emails; this array is used to update the succesful
  21     # transition_domains to email_sent=True, and also to report
  22     # out errors
  23     domains_with_errors: List[str] = []
  24     # this array is used to store email_context; each item in the array
  25     # contains the context for a single email; single emails may be 1
  26     # or more transition_domains, as they are grouped by username
  27     emails_to_send: List[str] = []
  28 
  29     def add_arguments(self, parser):
  30         """Add command line arguments."""
  31         parser.add_argument(
  32             "-s",
  33             "--send_emails",
  34             action="store_true",
  35             default=False,
  36             dest="send_emails",
  37             help="Send emails ",
  38         )
  39 
  40     def handle(self, **options):
  41         """Process the objects in TransitionDomain."""
  42 
  43         logger.info("checking domains and preparing emails")
  44         # Get all TransitionDomain objects
  45         self.transition_domains = TransitionDomain.objects.filter(
  46             email_sent=False,
  47         ).order_by("username")
  48         logger.info("Found %d transition domains", len(self.transition_domains))
  49 
  50         self.build_emails_to_send_array()
  51         logger.info("Prepared %d emails to send", len(self.emails_to_send))
  52 
  53         if options["send_emails"]:
  54             logger.info("about to send emails")
  55             self.send_emails()
  56             logger.info("done sending emails")
  57 
  58             self.update_domains_as_sent()
  59 
  60             logger.info("done sending emails and updating transition_domains")
  61         else:
  62             logger.info("not sending emails")
  63             for email_context in self.emails_to_send:
  64                 logger.info(
  65                     "would send email to %s for %s",
  66                     email_context["email"],
  67                     email_context["domains"],
  68                 )
  69 
  70     def build_emails_to_send_array(self):
  71         """this method sends emails to distinct usernames"""
  72 
  73         # data structure to hold email context for a single email;
  74         # transition_domains ordered by username, a single email_context
  75         # may include information from more than one transition_domain
  76         email_context = {"email": ""}
  77 
  78         # loop through all transition_domains; group them by username
  79         # into emails_to_send_array
  80         for transition_domain in self.transition_domains:
  81             # attempt to get the domain from domain objects; if there is
  82             # an error getting the domain, skip this domain and add it to
  83             # domains_with_errors
  84             try:
  85                 # if prior username does not match current username
  86                 if not email_context["email"] or email_context["email"] != transition_domain.username:
  87                     # if not first in list of transition_domains
  88                     if email_context["email"]:
  89                         # append the email context to the emails_to_send array
  90                         self.emails_to_send.append(copy.deepcopy(email_context))
  91                     email_context["domains"] = []
  92                 email_context["email"] = transition_domain.username
  93                 email_context["domains"].append(transition_domain.domain_name)
  94             except Exception as err:
  95                 # error condition if domain not in database
  96                 self.domains_with_errors.append(copy.deepcopy(transition_domain.domain_name))
  97                 logger.error(f"error retrieving domain {transition_domain.domain_name}: {err}")
  98         # if there are at least one more transition domains than errors,
  99         # then append one more item
  100         if len(self.transition_domains) > len(self.domains_with_errors):
  101             self.emails_to_send.append(email_context)
  102 
  103     def send_emails(self):
  104         if len(self.emails_to_send) > 0:
  105             for email_data in self.emails_to_send:
  106                 self.send_email(email_data)
  107         else:
  108             logger.info("no emails to send")
  109 
  110     def send_email(self, email_data):
  111         try:
  112             send_templated_email(
  113                 "emails/transition_domain_invitation.txt",
  114                 "emails/transition_domain_invitation_subject.txt",
  115                 to_address=email_data["email"],
  116                 context={
  117                     "domains": email_data["domains"],
  118                 },
  119             )
  120             # success message is logged
  121             logger.info(
  122                 f"email sent successfully to {email_data['email']} for "
  123                 f"{[domain for domain in email_data['domains']]}"
  124             )
  125         except EmailSendingError as err:
  126             logger.error(
  127                 f"email did not send successfully to {email_data['email']} "
  128                 f"for {[domain for domain in email_data['domains']]}"
  129                 f": {err}"
  130             )
  131             # if email failed to send, set error in domains_with_errors for each
  132             # domain in the email so that transition domain email_sent is not set
  133             # to True
  134             for domain in email_data["domains"]:
  135                 self.domains_with_errors.append(domain)
  136 
  137     def update_domains_as_sent(self):
  138         """set email_sent to True in all transition_domains which have
  139         been processed successfully"""
  140         for transition_domain in self.transition_domains:
  141             if transition_domain.domain_name not in self.domains_with_errors:
  142                 transition_domain.email_sent = True
  143                 transition_domain.save()
  144
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/src/registrar/management/commands/send_domain_invitations.py b/src/registrar/management/commands/send_domain_invitations.py
  --- a/src/registrar/management/commands/send_domain_invitations.py
  +++ b/src/registrar/management/commands/send_domain_invitations.py
  @@ -37,14 +37,24 @@
               help="Send emails ",
           )
   
  +        parser.add_argument("emails", nargs="*", help="Email addresses to send invitations to")
  +
       def handle(self, **options):
           """Process the objects in TransitionDomain."""
   
           logger.info("checking domains and preparing emails")
  -        # Get all TransitionDomain objects
  -        self.transition_domains = TransitionDomain.objects.filter(
  -            email_sent=False,
  -        ).order_by("username")
  +
  +        if options["emails"]:
  +            # this option is a list of email addresses
  +            self.transition_domains = TransitionDomain.objects.filter(
  +                username__in=options["emails"],
  +                email_sent=False,
  +            ).order_by("username")
  +        else:
  +            # Get all TransitionDomain objects
  +            self.transition_domains = TransitionDomain.objects.filter(
  +                email_sent=False,
  +            ).order_by("username")
           logger.info("Found %d transition domains", len(self.transition_domains))
   
           self.build_emails_to_send_array()
  
verification_info: {"golden_diff": "diff --git a/src/registrar/management/commands/send_domain_invitations.py b/src/registrar/management/commands/send_domain_invitations.py\n--- a/src/registrar/management/commands/send_domain_invitations.py\n+++ b/src/registrar/management/commands/send_domain_invitations.py\n@@ -37,14 +37,24 @@\n             help=\"Send emails \",\n         )\n \n+        parser.add_argument(\"emails\", nargs=\"*\", help=\"Email addresses to send invitations to\")\n+\n     def handle(self, **options):\n         \"\"\"Process the objects in TransitionDomain.\"\"\"\n \n         logger.info(\"checking domains and preparing emails\")\n-        # Get all TransitionDomain objects\n-        self.transition_domains = TransitionDomain.objects.filter(\n-            email_sent=False,\n-        ).order_by(\"username\")\n+\n+        if options[\"emails\"]:\n+            # this option is a list of email addresses\n+            self.transition_domains = TransitionDomain.objects.filter(\n+                username__in=options[\"emails\"],\n+                email_sent=False,\n+            ).order_by(\"username\")\n+        else:\n+            # Get all TransitionDomain objects\n+            self.transition_domains = TransitionDomain.objects.filter(\n+                email_sent=False,\n+            ).order_by(\"username\")\n         logger.info(\"Found %d transition domains\", len(self.transition_domains))\n \n         self.build_emails_to_send_array()\n", "issue": "Modify domain invitation script to process \"friends of the show\" first, then incrementally process others\n### Issue description\n\nWe have a few domain managers that we'll invite to the registrar first. Let's modify the domain invitation script to send to a defined set of domains first. \r\n\r\nWe also shouldn't blast thousands of emails out to the internet, but incrementally roll them out.\n\n### Acceptance criteria\n\n- [ ] Invitation script works with a product owner-specified list of domains/contacts (before sending to everyone else)\r\n- [ ] Script slow rolls out invitations. Could be percentage-based (1/2/5/10/20/45/75/100) or volume-based (a few hundred at a time)\n\n### Additional context\n\n_No response_\n\n### Links to other issues\n\n\ud83d\udd04 Related to PR #1038\n", "before_files": [{"content": "\"\"\"Data migration: Send domain invitations once to existing customers.\"\"\"\n\nimport logging\nimport copy\n\nfrom django.core.management import BaseCommand\nfrom registrar.models import TransitionDomain\nfrom ...utility.email import send_templated_email, EmailSendingError\nfrom typing import List\n\nlogger = logging.getLogger(__name__)\n\n\nclass Command(BaseCommand):\n    help = \"Send domain invitations once to existing customers.\"\n\n    # this array is used to store and process the transition_domains\n    transition_domains: List[str] = []\n    # this array is used to store domains with errors, which are not\n    # sent emails; this array is used to update the succesful\n    # transition_domains to email_sent=True, and also to report\n    # out errors\n    domains_with_errors: List[str] = []\n    # this array is used to store email_context; each item in the array\n    # contains the context for a single email; single emails may be 1\n    # or more transition_domains, as they are grouped by username\n    emails_to_send: List[str] = []\n\n    def add_arguments(self, parser):\n        \"\"\"Add command line arguments.\"\"\"\n        parser.add_argument(\n            \"-s\",\n            \"--send_emails\",\n            action=\"store_true\",\n            default=False,\n            dest=\"send_emails\",\n            help=\"Send emails \",\n        )\n\n    def handle(self, **options):\n        \"\"\"Process the objects in TransitionDomain.\"\"\"\n\n        logger.info(\"checking domains and preparing emails\")\n        # Get all TransitionDomain objects\n        self.transition_domains = TransitionDomain.objects.filter(\n            email_sent=False,\n        ).order_by(\"username\")\n        logger.info(\"Found %d transition domains\", len(self.transition_domains))\n\n        self.build_emails_to_send_array()\n        logger.info(\"Prepared %d emails to send\", len(self.emails_to_send))\n\n        if options[\"send_emails\"]:\n            logger.info(\"about to send emails\")\n            self.send_emails()\n            logger.info(\"done sending emails\")\n\n            self.update_domains_as_sent()\n\n            logger.info(\"done sending emails and updating transition_domains\")\n        else:\n            logger.info(\"not sending emails\")\n            for email_context in self.emails_to_send:\n                logger.info(\n                    \"would send email to %s for %s\",\n                    email_context[\"email\"],\n                    email_context[\"domains\"],\n                )\n\n    def build_emails_to_send_array(self):\n        \"\"\"this method sends emails to distinct usernames\"\"\"\n\n        # data structure to hold email context for a single email;\n        # transition_domains ordered by username, a single email_context\n        # may include information from more than one transition_domain\n        email_context = {\"email\": \"\"}\n\n        # loop through all transition_domains; group them by username\n        # into emails_to_send_array\n        for transition_domain in self.transition_domains:\n            # attempt to get the domain from domain objects; if there is\n            # an error getting the domain, skip this domain and add it to\n            # domains_with_errors\n            try:\n                # if prior username does not match current username\n                if not email_context[\"email\"] or email_context[\"email\"] != transition_domain.username:\n                    # if not first in list of transition_domains\n                    if email_context[\"email\"]:\n                        # append the email context to the emails_to_send array\n                        self.emails_to_send.append(copy.deepcopy(email_context))\n                    email_context[\"domains\"] = []\n                email_context[\"email\"] = transition_domain.username\n                email_context[\"domains\"].append(transition_domain.domain_name)\n            except Exception as err:\n                # error condition if domain not in database\n                self.domains_with_errors.append(copy.deepcopy(transition_domain.domain_name))\n                logger.error(f\"error retrieving domain {transition_domain.domain_name}: {err}\")\n        # if there are at least one more transition domains than errors,\n        # then append one more item\n        if len(self.transition_domains) > len(self.domains_with_errors):\n            self.emails_to_send.append(email_context)\n\n    def send_emails(self):\n        if len(self.emails_to_send) > 0:\n            for email_data in self.emails_to_send:\n                self.send_email(email_data)\n        else:\n            logger.info(\"no emails to send\")\n\n    def send_email(self, email_data):\n        try:\n            send_templated_email(\n                \"emails/transition_domain_invitation.txt\",\n                \"emails/transition_domain_invitation_subject.txt\",\n                to_address=email_data[\"email\"],\n                context={\n                    \"domains\": email_data[\"domains\"],\n                },\n            )\n            # success message is logged\n            logger.info(\n                f\"email sent successfully to {email_data['email']} for \"\n                f\"{[domain for domain in email_data['domains']]}\"\n            )\n        except EmailSendingError as err:\n            logger.error(\n                f\"email did not send successfully to {email_data['email']} \"\n                f\"for {[domain for domain in email_data['domains']]}\"\n                f\": {err}\"\n            )\n            # if email failed to send, set error in domains_with_errors for each\n            # domain in the email so that transition domain email_sent is not set\n            # to True\n            for domain in email_data[\"domains\"]:\n                self.domains_with_errors.append(domain)\n\n    def update_domains_as_sent(self):\n        \"\"\"set email_sent to True in all transition_domains which have\n        been processed successfully\"\"\"\n        for transition_domain in self.transition_domains:\n            if transition_domain.domain_name not in self.domains_with_errors:\n                transition_domain.email_sent = True\n                transition_domain.save()\n", "path": "src/registrar/management/commands/send_domain_invitations.py"}], "after_files": [{"content": "\"\"\"Data migration: Send domain invitations once to existing customers.\"\"\"\n\nimport logging\nimport copy\n\nfrom django.core.management import BaseCommand\nfrom registrar.models import TransitionDomain\nfrom ...utility.email import send_templated_email, EmailSendingError\nfrom typing import List\n\nlogger = logging.getLogger(__name__)\n\n\nclass Command(BaseCommand):\n    help = \"Send domain invitations once to existing customers.\"\n\n    # this array is used to store and process the transition_domains\n    transition_domains: List[str] = []\n    # this array is used to store domains with errors, which are not\n    # sent emails; this array is used to update the succesful\n    # transition_domains to email_sent=True, and also to report\n    # out errors\n    domains_with_errors: List[str] = []\n    # this array is used to store email_context; each item in the array\n    # contains the context for a single email; single emails may be 1\n    # or more transition_domains, as they are grouped by username\n    emails_to_send: List[str] = []\n\n    def add_arguments(self, parser):\n        \"\"\"Add command line arguments.\"\"\"\n        parser.add_argument(\n            \"-s\",\n            \"--send_emails\",\n            action=\"store_true\",\n            default=False,\n            dest=\"send_emails\",\n            help=\"Send emails \",\n        )\n\n        parser.add_argument(\"emails\", nargs=\"*\", help=\"Email addresses to send invitations to\")\n\n    def handle(self, **options):\n        \"\"\"Process the objects in TransitionDomain.\"\"\"\n\n        logger.info(\"checking domains and preparing emails\")\n\n        if options[\"emails\"]:\n            # this option is a list of email addresses\n            self.transition_domains = TransitionDomain.objects.filter(\n                username__in=options[\"emails\"],\n                email_sent=False,\n            ).order_by(\"username\")\n        else:\n            # Get all TransitionDomain objects\n            self.transition_domains = TransitionDomain.objects.filter(\n                email_sent=False,\n            ).order_by(\"username\")\n        logger.info(\"Found %d transition domains\", len(self.transition_domains))\n\n        self.build_emails_to_send_array()\n        logger.info(\"Prepared %d emails to send\", len(self.emails_to_send))\n\n        if options[\"send_emails\"]:\n            logger.info(\"about to send emails\")\n            self.send_emails()\n            logger.info(\"done sending emails\")\n\n            self.update_domains_as_sent()\n\n            logger.info(\"done sending emails and updating transition_domains\")\n        else:\n            logger.info(\"not sending emails\")\n            for email_context in self.emails_to_send:\n                logger.info(\n                    \"would send email to %s for %s\",\n                    email_context[\"email\"],\n                    email_context[\"domains\"],\n                )\n\n    def build_emails_to_send_array(self):\n        \"\"\"this method sends emails to distinct usernames\"\"\"\n\n        # data structure to hold email context for a single email;\n        # transition_domains ordered by username, a single email_context\n        # may include information from more than one transition_domain\n        email_context = {\"email\": \"\"}\n\n        # loop through all transition_domains; group them by username\n        # into emails_to_send_array\n        for transition_domain in self.transition_domains:\n            # attempt to get the domain from domain objects; if there is\n            # an error getting the domain, skip this domain and add it to\n            # domains_with_errors\n            try:\n                # if prior username does not match current username\n                if not email_context[\"email\"] or email_context[\"email\"] != transition_domain.username:\n                    # if not first in list of transition_domains\n                    if email_context[\"email\"]:\n                        # append the email context to the emails_to_send array\n                        self.emails_to_send.append(copy.deepcopy(email_context))\n                    email_context[\"domains\"] = []\n                email_context[\"email\"] = transition_domain.username\n                email_context[\"domains\"].append(transition_domain.domain_name)\n            except Exception as err:\n                # error condition if domain not in database\n                self.domains_with_errors.append(copy.deepcopy(transition_domain.domain_name))\n                logger.error(f\"error retrieving domain {transition_domain.domain_name}: {err}\")\n        # if there are at least one more transition domains than errors,\n        # then append one more item\n        if len(self.transition_domains) > len(self.domains_with_errors):\n            self.emails_to_send.append(email_context)\n\n    def send_emails(self):\n        if len(self.emails_to_send) > 0:\n            for email_data in self.emails_to_send:\n                self.send_email(email_data)\n        else:\n            logger.info(\"no emails to send\")\n\n    def send_email(self, email_data):\n        try:\n            send_templated_email(\n                \"emails/transition_domain_invitation.txt\",\n                \"emails/transition_domain_invitation_subject.txt\",\n                to_address=email_data[\"email\"],\n                context={\n                    \"domains\": email_data[\"domains\"],\n                },\n            )\n            # success message is logged\n            logger.info(\n                f\"email sent successfully to {email_data['email']} for \"\n                f\"{[domain for domain in email_data['domains']]}\"\n            )\n        except EmailSendingError as err:\n            logger.error(\n                f\"email did not send successfully to {email_data['email']} \"\n                f\"for {[domain for domain in email_data['domains']]}\"\n                f\": {err}\"\n            )\n            # if email failed to send, set error in domains_with_errors for each\n            # domain in the email so that transition domain email_sent is not set\n            # to True\n            for domain in email_data[\"domains\"]:\n                self.domains_with_errors.append(domain)\n\n    def update_domains_as_sent(self):\n        \"\"\"set email_sent to True in all transition_domains which have\n        been processed successfully\"\"\"\n        for transition_domain in self.transition_domains:\n            if transition_domain.domain_name not in self.domains_with_errors:\n                transition_domain.email_sent = True\n                transition_domain.save()\n", "path": "src/registrar/management/commands/send_domain_invitations.py"}]}