prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <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.
  
  </issue>
  <code>
  [start of course/forms.py]
  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 of course/forms.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: 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']
  
verification_info: {"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"}]}