prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Users should only be shown the View-Edit toggle if they have edit access for the source/chant in question
  @annamorphism made a comment on #441 that really deserves its own issue
  
  > also is there a way to not have the "Edit" tab show up for unauthorized people? it's annoying to try to edit something and then be sent to a 403.
  
  Currently, the view-edit toggle is being displayed whenever the user is logged in. Instead, we need to properly check that the user is actually allowed to edit the chant before displaying the Edit link.
  
  </issue>
  <code>
  [start of django/cantusdb_project/main_app/views/sequence.py]
  1 from django.views.generic import DetailView, ListView, UpdateView
  2 from main_app.models import Sequence
  3 from django.db.models import Q
  4 from main_app.forms import SequenceEditForm
  5 from django.contrib.auth.mixins import LoginRequiredMixin
  6 from django.contrib import messages
  7 from django.contrib.auth.mixins import UserPassesTestMixin
  8 from django.core.exceptions import PermissionDenied
  9 
  10 
  11 class SequenceDetailView(DetailView):
  12     """
  13     Displays a single Sequence object. Accessed with ``sequences/<int:pk>``
  14     """
  15 
  16     model = Sequence
  17     context_object_name = "sequence"
  18     template_name = "sequence_detail.html"
  19 
  20     def get_context_data(self, **kwargs):
  21         sequence = self.get_object()
  22         source = sequence.source
  23         # if the sequence's source isn't published,
  24         # only logged-in users should be able to view the sequence's detail page
  25         if (
  26             (source is not None)
  27             and (source.published is False)
  28             and (not self.request.user.is_authenticated)
  29         ):
  30             raise PermissionDenied()
  31 
  32         context = super().get_context_data(**kwargs)
  33         context["concordances"] = (
  34             Sequence.objects.filter(cantus_id=sequence.cantus_id)
  35             .select_related("source")
  36             .order_by("siglum")
  37         )
  38         return context
  39 
  40 
  41 class SequenceListView(ListView):
  42     """
  43     Displays a list of Sequence objects. Accessed with ``sequences/``
  44     """
  45 
  46     paginate_by = 100
  47     context_object_name = "sequences"
  48     template_name = "sequence_list.html"
  49 
  50     def get_queryset(self):
  51         queryset = Sequence.objects.select_related("source")
  52         display_unpublished = self.request.user.is_authenticated
  53         if display_unpublished:
  54             q_obj_filter = Q()
  55         else:
  56             q_obj_filter = Q(source__published=True)
  57 
  58         if self.request.GET.get("incipit"):
  59             incipit = self.request.GET.get("incipit")
  60             q_obj_filter &= Q(incipit__icontains=incipit)
  61         if self.request.GET.get("siglum"):
  62             siglum = self.request.GET.get("siglum")
  63             q_obj_filter &= Q(siglum__icontains=siglum)
  64         if self.request.GET.get("cantus_id"):
  65             cantus_id = self.request.GET.get("cantus_id")
  66             q_obj_filter &= Q(cantus_id__icontains=cantus_id)
  67 
  68         return queryset.filter(q_obj_filter).order_by("siglum", "s_sequence")
  69 
  70 
  71 class SequenceEditView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
  72     template_name = "sequence_edit.html"
  73     model = Sequence
  74     form_class = SequenceEditForm
  75     pk_url_kwarg = "sequence_id"
  76 
  77     def form_valid(self, form):
  78         form.instance.last_updated_by = self.request.user
  79         messages.success(
  80             self.request,
  81             "Sequence updated successfully!",
  82         )
  83         return super().form_valid(form)
  84 
  85     def test_func(self):
  86         user = self.request.user
  87         # checks if the user is a project manager (they should have the privilege to edit any sequence)
  88         is_project_manager = user.groups.filter(name="project manager").exists()
  89 
  90         if is_project_manager:
  91             return True
  92         else:
  93             return False
  94
  [end of django/cantusdb_project/main_app/views/sequence.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/django/cantusdb_project/main_app/views/sequence.py b/django/cantusdb_project/main_app/views/sequence.py
  --- a/django/cantusdb_project/main_app/views/sequence.py
  +++ b/django/cantusdb_project/main_app/views/sequence.py
  @@ -6,6 +6,7 @@
   from django.contrib import messages
   from django.contrib.auth.mixins import UserPassesTestMixin
   from django.core.exceptions import PermissionDenied
  +from main_app.views.chant import user_can_edit_chants_in_source
   
   
   class SequenceDetailView(DetailView):
  @@ -20,6 +21,8 @@
       def get_context_data(self, **kwargs):
           sequence = self.get_object()
           source = sequence.source
  +        user = self.request.user
  +
           # if the sequence's source isn't published,
           # only logged-in users should be able to view the sequence's detail page
           if (
  @@ -35,6 +38,7 @@
               .select_related("source")
               .order_by("siglum")
           )
  +        context["user_can_edit_sequence"] = user_can_edit_chants_in_source(user, source)
           return context
  
verification_info: {"golden_diff": "diff --git a/django/cantusdb_project/main_app/views/sequence.py b/django/cantusdb_project/main_app/views/sequence.py\n--- a/django/cantusdb_project/main_app/views/sequence.py\n+++ b/django/cantusdb_project/main_app/views/sequence.py\n@@ -6,6 +6,7 @@\n from django.contrib import messages\n from django.contrib.auth.mixins import UserPassesTestMixin\n from django.core.exceptions import PermissionDenied\n+from main_app.views.chant import user_can_edit_chants_in_source\n \n \n class SequenceDetailView(DetailView):\n@@ -20,6 +21,8 @@\n     def get_context_data(self, **kwargs):\n         sequence = self.get_object()\n         source = sequence.source\n+        user = self.request.user\n+\n         # if the sequence's source isn't published,\n         # only logged-in users should be able to view the sequence's detail page\n         if (\n@@ -35,6 +38,7 @@\n             .select_related(\"source\")\n             .order_by(\"siglum\")\n         )\n+        context[\"user_can_edit_sequence\"] = user_can_edit_chants_in_source(user, source)\n         return context\n", "issue": "Users should only be shown the View-Edit toggle if they have edit access for the source/chant in question\n@annamorphism made a comment on #441 that really deserves its own issue\r\n\r\n> also is there a way to not have the \"Edit\" tab show up for unauthorized people? it's annoying to try to edit something and then be sent to a 403.\r\n\r\nCurrently, the view-edit toggle is being displayed whenever the user is logged in. Instead, we need to properly check that the user is actually allowed to edit the chant before displaying the Edit link.\n", "before_files": [{"content": "from django.views.generic import DetailView, ListView, UpdateView\nfrom main_app.models import Sequence\nfrom django.db.models import Q\nfrom main_app.forms import SequenceEditForm\nfrom django.contrib.auth.mixins import LoginRequiredMixin\nfrom django.contrib import messages\nfrom django.contrib.auth.mixins import UserPassesTestMixin\nfrom django.core.exceptions import PermissionDenied\n\n\nclass SequenceDetailView(DetailView):\n    \"\"\"\n    Displays a single Sequence object. Accessed with ``sequences/<int:pk>``\n    \"\"\"\n\n    model = Sequence\n    context_object_name = \"sequence\"\n    template_name = \"sequence_detail.html\"\n\n    def get_context_data(self, **kwargs):\n        sequence = self.get_object()\n        source = sequence.source\n        # if the sequence's source isn't published,\n        # only logged-in users should be able to view the sequence's detail page\n        if (\n            (source is not None)\n            and (source.published is False)\n            and (not self.request.user.is_authenticated)\n        ):\n            raise PermissionDenied()\n\n        context = super().get_context_data(**kwargs)\n        context[\"concordances\"] = (\n            Sequence.objects.filter(cantus_id=sequence.cantus_id)\n            .select_related(\"source\")\n            .order_by(\"siglum\")\n        )\n        return context\n\n\nclass SequenceListView(ListView):\n    \"\"\"\n    Displays a list of Sequence objects. Accessed with ``sequences/``\n    \"\"\"\n\n    paginate_by = 100\n    context_object_name = \"sequences\"\n    template_name = \"sequence_list.html\"\n\n    def get_queryset(self):\n        queryset = Sequence.objects.select_related(\"source\")\n        display_unpublished = self.request.user.is_authenticated\n        if display_unpublished:\n            q_obj_filter = Q()\n        else:\n            q_obj_filter = Q(source__published=True)\n\n        if self.request.GET.get(\"incipit\"):\n            incipit = self.request.GET.get(\"incipit\")\n            q_obj_filter &= Q(incipit__icontains=incipit)\n        if self.request.GET.get(\"siglum\"):\n            siglum = self.request.GET.get(\"siglum\")\n            q_obj_filter &= Q(siglum__icontains=siglum)\n        if self.request.GET.get(\"cantus_id\"):\n            cantus_id = self.request.GET.get(\"cantus_id\")\n            q_obj_filter &= Q(cantus_id__icontains=cantus_id)\n\n        return queryset.filter(q_obj_filter).order_by(\"siglum\", \"s_sequence\")\n\n\nclass SequenceEditView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):\n    template_name = \"sequence_edit.html\"\n    model = Sequence\n    form_class = SequenceEditForm\n    pk_url_kwarg = \"sequence_id\"\n\n    def form_valid(self, form):\n        form.instance.last_updated_by = self.request.user\n        messages.success(\n            self.request,\n            \"Sequence updated successfully!\",\n        )\n        return super().form_valid(form)\n\n    def test_func(self):\n        user = self.request.user\n        # checks if the user is a project manager (they should have the privilege to edit any sequence)\n        is_project_manager = user.groups.filter(name=\"project manager\").exists()\n\n        if is_project_manager:\n            return True\n        else:\n            return False\n", "path": "django/cantusdb_project/main_app/views/sequence.py"}]}