We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Course staff may create duplicate student groups
Course staff may create student groups (course/models.py class StudentGroup) that contain exactly the same group members as an existing group. Duplicate groups should not be allowed. The course staff UI for editing groups is in the URL http://localhost:8000/def/current/teachers/groups/ (in the left navigation menu, it is the "Groups" link under the heading Course staff).

Course staff may also create new groups (or edit existing groups) that are empty (no members) or only have one member. Groups should always have at least two members.

When students create groups in the "form a group" page (with user personal codes), A+ already prevents empty and duplicate groups.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `course/forms.py`
Content:
```
1 from typing import Any
2 
3 from django import forms
4 from django.contrib.humanize.templatetags.humanize import ordinal
5 from django.utils.safestring import mark_safe
6 from django.utils.text import format_lazy
7 from django.utils.translation import gettext_lazy as _
8 
9 from aplus.api import api_reverse
10 from exercise.models import SubmissionDraft
11 from lib.fields import UsersSearchSelectField
12 from .models import Enrollment, StudentGroup
13 from userprofile.models import UserProfile
14 
15 
16 class GroupsForm(forms.Form):
17 
18     def __init__(self, *args, **kwargs):
19         self.profile = kwargs.pop('profile')
20         self.instance = kwargs.pop('instance')
21         self.content = kwargs.pop('content')
22         super().__init__(*args, **kwargs)
23         total = self.content.total()
24         min_size = max(total.min_group_size, 2)
25         max_size = total.max_group_size
26 
27         for n in range(2, max_size + 1):
28             widget = forms.TextInput(attrs={'class':'form-control'})
29             field = forms.CharField(widget=widget, required=(n <= min_size))
30             field.label = mark_safe(format_lazy(_('GROUP_MEMBER_LABEL -- {num}'), num=ordinal(n)))
31             self.fields['member{:d}'.format(n)] = field
32 
33     def clean(self):
34         super().clean()
35 
36         self.member_profiles = [self.profile]
37         for key in self.fields.keys():
38             if key in self.cleaned_data and self.cleaned_data[key]:
39                 enrollment = Enrollment.objects.filter(
40                     course_instance=self.instance,
41                     personal_code=self.cleaned_data[key].upper()
42                 ).first()
43                 if not enrollment:
44                     self.add_error(key, _('ERROR_CODE_NOT_RECOGNIZED'))
45                 elif enrollment.user_profile in self.member_profiles:
46                     self.add_error(key, _('ERROR_USER_ALREADY_IN_GROUP'))
47                 else:
48                     self.member_profiles.append(enrollment.user_profile)
49 
50         if not self.errors and len(self.member_profiles) > 1:
51             if StudentGroup.get_exact(self.instance, self.member_profiles):
52                 self.add_error(None, _('ERROR_GROUP_ALREADY_EXISTS'))
53 
54         return self.cleaned_data
55 
56     def save(self):
57         group = StudentGroup(course_instance=self.instance)
58         group.save()
59         group.members.add(*self.member_profiles)
60         return group
61 
62 
63 class GroupSelectForm(forms.Form):
64     group = forms.IntegerField(required=True)
65 
66     def __init__(self, *args, **kwargs):
67         self.profile = kwargs.pop('profile')
68         self.instance = kwargs.pop('instance')
69         super().__init__(*args, **kwargs)
70 
71     def clean(self):
72         super().clean()
73         self.selected_group = None
74         if 'group' in self.cleaned_data:
75             gid = self.cleaned_data['group']
76             if gid != 0:
77                 group = self.profile.groups.filter(id=gid, course_instance=self.instance).first()
78                 if group:
79                     self.selected_group = group
80                 else:
81                     self.add_error('group', 'Invalid group id')
82         return self.cleaned_data
83 
84     def save(self) -> Enrollment:
85         enrollment = self.instance.get_enrollment_for(self.profile.user)
86         enrollment.selected_group = self.selected_group
87         enrollment.save()
88         # Deactivate all drafts when changing groups.
89         SubmissionDraft.objects.filter(
90             exercise__course_module__course_instance=self.instance,
91             submitter=self.profile,
92             active=True,
93         ).update(active=False)
94         return enrollment
95 
96 
97 class GroupEditForm(forms.ModelForm):
98 
99     members = UsersSearchSelectField(queryset=UserProfile.objects.none(),
100         initial_queryset=UserProfile.objects.none(),
101         label=_('LABEL_MEMBERS'),
102     )
103 
104     def __init__(self, *args: Any, **kwargs: Any) -> None:
105         course_instance = kwargs.get('instance').course_instance
106         super().__init__(*args, **kwargs)
107         self.fields['members'].widget.search_api_url = api_reverse(
108             "course-students-list",
109             kwargs={'course_id': course_instance.id},
110         )
111         self.fields["members"].queryset = course_instance.get_student_profiles()
112         # Course staff may use this form for modifying and creating student groups.
113         # If an existing group is being modified, its current members must be
114         # set to the initial queryset.
115         if self.instance.id:
116             self.fields["members"].initial_queryset = self.instance.members.all()
117 
118     class Meta:
119         model = StudentGroup
120         fields = ['members']
121 
122 
123 class EnrollStudentsForm(forms.Form):
124 
125     user_profiles = UsersSearchSelectField(queryset=UserProfile.objects.all(),
126         initial_queryset=UserProfile.objects.none(),
127         label=_('LABEL_USERS'),
128         required=False,
129     )
130 
131     def __init__(self, *args: Any, **kwargs: Any) -> None:
132         self.instance = kwargs.pop('instance')
133         super().__init__(*args, **kwargs)
134         self.fields['user_profiles'].widget.search_api_url = api_reverse("user-list")
135         if self.instance.sis_id:
136             self.fields['sis'] = forms.BooleanField(
137                 required=False,
138                 label=_('LABEL_ENROLL_FROM_SIS'),
139             )
140
```
--- 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()

```

diff --git a/course/forms.py b/course/forms.py
--- a/course/forms.py
+++ b/course/forms.py
@@ -5,6 +5,7 @@
 from django.utils.safestring import mark_safe
 from django.utils.text import format_lazy
 from django.utils.translation import gettext_lazy as _
+from django.db.models import Count
 
 from aplus.api import api_reverse
 from exercise.models import SubmissionDraft
@@ -115,6 +116,30 @@
         if self.instance.id:
             self.fields["members"].initial_queryset = self.instance.members.all()
 
+    def clean(self):
+        super().clean()
+        members = self.cleaned_data.get('members')
+        if members:
+            if len(members) == 1:
+                self.add_error('members', _('MUST_HAVE_TWO_MEMBERS'))
+            course_instance = self.instance.course_instance
+            # Filter all groups with course instance and that have one or more similar members as in the members list
+            filtered_groups = StudentGroup.objects.filter(course_instance=course_instance, members__in=members)
+            # Count number of members in each group
+            groups_with_member_count = filtered_groups.annotate(member_count=Count('members'))
+            # Filter only those groups that have same number of members
+            groups_with_exact_member_count = groups_with_member_count.filter(member_count=len(members))
+            # Loop through the returned groups and check if any group with exact same members exist
+            group_exists = False
+            for group in groups_with_exact_member_count:
+                group_members = group.members.all()
+                if list(group_members) == list(members):
+                    group_exists = True
+            if group_exists:
+                self.add_error('members', _('ERROR_GROUP_ALREADY_EXISTS'))
+        return self.cleaned_data
+
+
     class Meta:
         model = StudentGroup
         fields = ['members']

{"golden_diff": "diff --git a/course/forms.py b/course/forms.py\n--- a/course/forms.py\n+++ b/course/forms.py\n@@ -5,6 +5,7 @@\n from django.utils.safestring import mark_safe\n from django.utils.text import format_lazy\n from django.utils.translation import gettext_lazy as _\n+from django.db.models import Count\n \n from aplus.api import api_reverse\n from exercise.models import SubmissionDraft\n@@ -115,6 +116,30 @@\n         if self.instance.id:\n             self.fields[\"members\"].initial_queryset = self.instance.members.all()\n \n+    def clean(self):\n+        super().clean()\n+        members = self.cleaned_data.get('members')\n+        if members:\n+            if len(members) == 1:\n+                self.add_error('members', _('MUST_HAVE_TWO_MEMBERS'))\n+            course_instance = self.instance.course_instance\n+            # Filter all groups with course instance and that have one or more similar members as in the members list\n+            filtered_groups = StudentGroup.objects.filter(course_instance=course_instance, members__in=members)\n+            # Count number of members in each group\n+            groups_with_member_count = filtered_groups.annotate(member_count=Count('members'))\n+            # Filter only those groups that have same number of members\n+            groups_with_exact_member_count = groups_with_member_count.filter(member_count=len(members))\n+            # Loop through the returned groups and check if any group with exact same members exist\n+            group_exists = False\n+            for group in groups_with_exact_member_count:\n+                group_members = group.members.all()\n+                if list(group_members) == list(members):\n+                    group_exists = True\n+            if group_exists:\n+                self.add_error('members', _('ERROR_GROUP_ALREADY_EXISTS'))\n+        return self.cleaned_data\n+\n+\n     class Meta:\n         model = StudentGroup\n         fields = ['members']\n", "issue": "Course staff may create duplicate student groups\nCourse staff may create student groups (course/models.py class StudentGroup) that contain exactly the same group members as an existing group. Duplicate groups should not be allowed. The course staff UI for editing groups is in the URL http://localhost:8000/def/current/teachers/groups/ (in the left navigation menu, it is the \"Groups\" link under the heading Course staff).\r\n\r\nCourse staff may also create new groups (or edit existing groups) that are empty (no members) or only have one member. Groups should always have at least two members.\r\n\r\nWhen students create groups in the \"form a group\" page (with user personal codes), A+ already prevents empty and duplicate groups.\n", "before_files": [{"content": "from typing import Any\n\nfrom django import forms\nfrom django.contrib.humanize.templatetags.humanize import ordinal\nfrom django.utils.safestring import mark_safe\nfrom django.utils.text import format_lazy\nfrom django.utils.translation import gettext_lazy as _\n\nfrom aplus.api import api_reverse\nfrom exercise.models import SubmissionDraft\nfrom lib.fields import UsersSearchSelectField\nfrom .models import Enrollment, StudentGroup\nfrom userprofile.models import UserProfile\n\n\nclass GroupsForm(forms.Form):\n\n    def __init__(self, *args, **kwargs):\n        self.profile = kwargs.pop('profile')\n        self.instance = kwargs.pop('instance')\n        self.content = kwargs.pop('content')\n        super().__init__(*args, **kwargs)\n        total = self.content.total()\n        min_size = max(total.min_group_size, 2)\n        max_size = total.max_group_size\n\n        for n in range(2, max_size + 1):\n            widget = forms.TextInput(attrs={'class':'form-control'})\n            field = forms.CharField(widget=widget, required=(n <= min_size))\n            field.label = mark_safe(format_lazy(_('GROUP_MEMBER_LABEL -- {num}'), num=ordinal(n)))\n            self.fields['member{:d}'.format(n)] = field\n\n    def clean(self):\n        super().clean()\n\n        self.member_profiles = [self.profile]\n        for key in self.fields.keys():\n            if key in self.cleaned_data and self.cleaned_data[key]:\n                enrollment = Enrollment.objects.filter(\n                    course_instance=self.instance,\n                    personal_code=self.cleaned_data[key].upper()\n                ).first()\n                if not enrollment:\n                    self.add_error(key, _('ERROR_CODE_NOT_RECOGNIZED'))\n                elif enrollment.user_profile in self.member_profiles:\n                    self.add_error(key, _('ERROR_USER_ALREADY_IN_GROUP'))\n                else:\n                    self.member_profiles.append(enrollment.user_profile)\n\n        if not self.errors and len(self.member_profiles) > 1:\n            if StudentGroup.get_exact(self.instance, self.member_profiles):\n                self.add_error(None, _('ERROR_GROUP_ALREADY_EXISTS'))\n\n        return self.cleaned_data\n\n    def save(self):\n        group = StudentGroup(course_instance=self.instance)\n        group.save()\n        group.members.add(*self.member_profiles)\n        return group\n\n\nclass GroupSelectForm(forms.Form):\n    group = forms.IntegerField(required=True)\n\n    def __init__(self, *args, **kwargs):\n        self.profile = kwargs.pop('profile')\n        self.instance = kwargs.pop('instance')\n        super().__init__(*args, **kwargs)\n\n    def clean(self):\n        super().clean()\n        self.selected_group = None\n        if 'group' in self.cleaned_data:\n            gid = self.cleaned_data['group']\n            if gid != 0:\n                group = self.profile.groups.filter(id=gid, course_instance=self.instance).first()\n                if group:\n                    self.selected_group = group\n                else:\n                    self.add_error('group', 'Invalid group id')\n        return self.cleaned_data\n\n    def save(self) -> Enrollment:\n        enrollment = self.instance.get_enrollment_for(self.profile.user)\n        enrollment.selected_group = self.selected_group\n        enrollment.save()\n        # Deactivate all drafts when changing groups.\n        SubmissionDraft.objects.filter(\n            exercise__course_module__course_instance=self.instance,\n            submitter=self.profile,\n            active=True,\n        ).update(active=False)\n        return enrollment\n\n\nclass GroupEditForm(forms.ModelForm):\n\n    members = UsersSearchSelectField(queryset=UserProfile.objects.none(),\n        initial_queryset=UserProfile.objects.none(),\n        label=_('LABEL_MEMBERS'),\n    )\n\n    def __init__(self, *args: Any, **kwargs: Any) -> None:\n        course_instance = kwargs.get('instance').course_instance\n        super().__init__(*args, **kwargs)\n        self.fields['members'].widget.search_api_url = api_reverse(\n            \"course-students-list\",\n            kwargs={'course_id': course_instance.id},\n        )\n        self.fields[\"members\"].queryset = course_instance.get_student_profiles()\n        # Course staff may use this form for modifying and creating student groups.\n        # If an existing group is being modified, its current members must be\n        # set to the initial queryset.\n        if self.instance.id:\n            self.fields[\"members\"].initial_queryset = self.instance.members.all()\n\n    class Meta:\n        model = StudentGroup\n        fields = ['members']\n\n\nclass EnrollStudentsForm(forms.Form):\n\n    user_profiles = UsersSearchSelectField(queryset=UserProfile.objects.all(),\n        initial_queryset=UserProfile.objects.none(),\n        label=_('LABEL_USERS'),\n        required=False,\n    )\n\n    def __init__(self, *args: Any, **kwargs: Any) -> None:\n        self.instance = kwargs.pop('instance')\n        super().__init__(*args, **kwargs)\n        self.fields['user_profiles'].widget.search_api_url = api_reverse(\"user-list\")\n        if self.instance.sis_id:\n            self.fields['sis'] = forms.BooleanField(\n                required=False,\n                label=_('LABEL_ENROLL_FROM_SIS'),\n            )\n", "path": "course/forms.py"}], "after_files": [{"content": "from typing import Any\n\nfrom django import forms\nfrom django.contrib.humanize.templatetags.humanize import ordinal\nfrom django.utils.safestring import mark_safe\nfrom django.utils.text import format_lazy\nfrom django.utils.translation import gettext_lazy as _\nfrom django.db.models import Count\n\nfrom aplus.api import api_reverse\nfrom exercise.models import SubmissionDraft\nfrom lib.fields import UsersSearchSelectField\nfrom .models import Enrollment, StudentGroup\nfrom userprofile.models import UserProfile\n\n\nclass GroupsForm(forms.Form):\n\n    def __init__(self, *args, **kwargs):\n        self.profile = kwargs.pop('profile')\n        self.instance = kwargs.pop('instance')\n        self.content = kwargs.pop('content')\n        super().__init__(*args, **kwargs)\n        total = self.content.total()\n        min_size = max(total.min_group_size, 2)\n        max_size = total.max_group_size\n\n        for n in range(2, max_size + 1):\n            widget = forms.TextInput(attrs={'class':'form-control'})\n            field = forms.CharField(widget=widget, required=(n <= min_size))\n            field.label = mark_safe(format_lazy(_('GROUP_MEMBER_LABEL -- {num}'), num=ordinal(n)))\n            self.fields['member{:d}'.format(n)] = field\n\n    def clean(self):\n        super().clean()\n\n        self.member_profiles = [self.profile]\n        for key in self.fields.keys():\n            if key in self.cleaned_data and self.cleaned_data[key]:\n                enrollment = Enrollment.objects.filter(\n                    course_instance=self.instance,\n                    personal_code=self.cleaned_data[key].upper()\n                ).first()\n                if not enrollment:\n                    self.add_error(key, _('ERROR_CODE_NOT_RECOGNIZED'))\n                elif enrollment.user_profile in self.member_profiles:\n                    self.add_error(key, _('ERROR_USER_ALREADY_IN_GROUP'))\n                else:\n                    self.member_profiles.append(enrollment.user_profile)\n\n        if not self.errors and len(self.member_profiles) > 1:\n            if StudentGroup.get_exact(self.instance, self.member_profiles):\n                self.add_error(None, _('ERROR_GROUP_ALREADY_EXISTS'))\n\n        return self.cleaned_data\n\n    def save(self):\n        group = StudentGroup(course_instance=self.instance)\n        group.save()\n        group.members.add(*self.member_profiles)\n        return group\n\n\nclass GroupSelectForm(forms.Form):\n    group = forms.IntegerField(required=True)\n\n    def __init__(self, *args, **kwargs):\n        self.profile = kwargs.pop('profile')\n        self.instance = kwargs.pop('instance')\n        super().__init__(*args, **kwargs)\n\n    def clean(self):\n        super().clean()\n        self.selected_group = None\n        if 'group' in self.cleaned_data:\n            gid = self.cleaned_data['group']\n            if gid != 0:\n                group = self.profile.groups.filter(id=gid, course_instance=self.instance).first()\n                if group:\n                    self.selected_group = group\n                else:\n                    self.add_error('group', 'Invalid group id')\n        return self.cleaned_data\n\n    def save(self) -> Enrollment:\n        enrollment = self.instance.get_enrollment_for(self.profile.user)\n        enrollment.selected_group = self.selected_group\n        enrollment.save()\n        # Deactivate all drafts when changing groups.\n        SubmissionDraft.objects.filter(\n            exercise__course_module__course_instance=self.instance,\n            submitter=self.profile,\n            active=True,\n        ).update(active=False)\n        return enrollment\n\n\nclass GroupEditForm(forms.ModelForm):\n\n    members = UsersSearchSelectField(queryset=UserProfile.objects.none(),\n        initial_queryset=UserProfile.objects.none(),\n        label=_('LABEL_MEMBERS'),\n    )\n\n    def __init__(self, *args: Any, **kwargs: Any) -> None:\n        course_instance = kwargs.get('instance').course_instance\n        super().__init__(*args, **kwargs)\n        self.fields['members'].widget.search_api_url = api_reverse(\n            \"course-students-list\",\n            kwargs={'course_id': course_instance.id},\n        )\n        self.fields[\"members\"].queryset = course_instance.get_student_profiles()\n        # Course staff may use this form for modifying and creating student groups.\n        # If an existing group is being modified, its current members must be\n        # set to the initial queryset.\n        if self.instance.id:\n            self.fields[\"members\"].initial_queryset = self.instance.members.all()\n\n    def clean(self):\n        super().clean()\n        members = self.cleaned_data.get('members')\n        if members:\n            if len(members) == 1:\n                self.add_error('members', _('MUST_HAVE_TWO_MEMBERS'))\n            course_instance = self.instance.course_instance\n            # Filter all groups with course instance and that have one or more similar members as in the members list\n            filtered_groups = StudentGroup.objects.filter(course_instance=course_instance, members__in=members)\n            # Count number of members in each group\n            groups_with_member_count = filtered_groups.annotate(member_count=Count('members'))\n            # Filter only those groups that have same number of members\n            groups_with_exact_member_count = groups_with_member_count.filter(member_count=len(members))\n            # Loop through the returned groups and check if any group with exact same members exist\n            group_exists = False\n            for group in groups_with_exact_member_count:\n                group_members = group.members.all()\n                if list(group_members) == list(members):\n                    group_exists = True\n            if group_exists:\n                self.add_error('members', _('ERROR_GROUP_ALREADY_EXISTS'))\n        return self.cleaned_data\n\n\n    class Meta:\n        model = StudentGroup\n        fields = ['members']\n\n\nclass EnrollStudentsForm(forms.Form):\n\n    user_profiles = UsersSearchSelectField(queryset=UserProfile.objects.all(),\n        initial_queryset=UserProfile.objects.none(),\n        label=_('LABEL_USERS'),\n        required=False,\n    )\n\n    def __init__(self, *args: Any, **kwargs: Any) -> None:\n        self.instance = kwargs.pop('instance')\n        super().__init__(*args, **kwargs)\n        self.fields['user_profiles'].widget.search_api_url = api_reverse(\"user-list\")\n        if self.instance.sis_id:\n            self.fields['sis'] = forms.BooleanField(\n                required=False,\n                label=_('LABEL_ENROLL_FROM_SIS'),\n            )\n", "path": "course/forms.py"}]}