prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Fix "identical-code" issue in website/education/admin.py
  Identical blocks of code found in 2 locations. Consider refactoring.
  
  https://codeclimate.com/github/svthalia/concrexit/website/education/admin.py#issue_5ecfbb80e8d0fa00010003f6
  
  </issue>
  <code>
  [start of website/education/admin.py]
  1 """
  2 This module registers admin pages for the models
  3 """
  4 import csv
  5 
  6 from django.contrib import admin
  7 from django.http import HttpResponse
  8 from django.utils.translation import gettext_lazy as _
  9 
  10 from utils.translation import TranslatedModelAdmin
  11 from . import models
  12 from .forms import SummaryAdminForm
  13 
  14 admin.site.register(models.Category)
  15 
  16 
  17 @admin.register(models.Course)
  18 class CourseAdmin(TranslatedModelAdmin):
  19     fields = (
  20         "name",
  21         "course_code",
  22         "ec",
  23         "since",
  24         "until",
  25         "categories",
  26         "old_courses",
  27     )
  28     list_filter = ("categories", "ec")
  29     search_fields = ("name", "course_code")
  30 
  31 
  32 @admin.register(models.Exam)
  33 class ExamAdmin(TranslatedModelAdmin):
  34     list_display = (
  35         "type",
  36         "course",
  37         "exam_date",
  38         "uploader",
  39         "accepted",
  40         "language",
  41         "download_count",
  42     )
  43     readonly_fields = ("download_count",)
  44     list_filter = ("accepted", "exam_date", "type", "language")
  45     search_fields = (
  46         "name",
  47         "uploader__first_name",
  48         "uploader__last_name",
  49         "course__name_nl",
  50         "course__name_en",
  51     )
  52     actions = ["accept", "reject", "reset_download_count", "download_csv"]
  53 
  54     def accept(self, request, queryset):
  55         queryset.update(accepted=True)
  56 
  57     accept.short_description = _("Mark exams as accepted")
  58 
  59     def reject(self, request, queryset):
  60         queryset.update(accepted=False)
  61 
  62     reject.short_description = _("Mark exams as rejected")
  63 
  64     def reset_download_count(self, request, queryset):
  65         queryset.update(download_count=0)
  66 
  67     reset_download_count.short_description = _("Reset the marked exams download count")
  68 
  69     def download_csv(self, request, queryset):
  70         opts = queryset.model._meta
  71         response = HttpResponse(content_type="text/csv")
  72         # force download.
  73         response["Content-Disposition"] = "attachment;filename=export.csv"
  74         # the csv writer
  75         writer = csv.writer(response)
  76         field_names = [field.name for field in opts.fields]
  77         # Write a first row with header information
  78         writer.writerow(field_names)
  79         # Write data rows
  80         for obj in queryset:
  81             writer.writerow([getattr(obj, field) for field in field_names])
  82         return response
  83 
  84     download_csv.short_description = _("Download marked as csv")
  85 
  86 
  87 @admin.register(models.Summary)
  88 class SummaryAdmin(TranslatedModelAdmin):
  89     list_display = (
  90         "name",
  91         "course",
  92         "uploader",
  93         "accepted",
  94         "language",
  95         "download_count",
  96     )
  97     readonly_fields = ("download_count",)
  98     list_filter = ("accepted", "language")
  99     search_fields = (
  100         "name",
  101         "uploader__first_name",
  102         "uploader__last_name",
  103         "course__name_nl",
  104         "course__name_en",
  105     )
  106     actions = ["accept", "reject", "reset_download_count", "download_csv"]
  107     form = SummaryAdminForm
  108 
  109     def accept(self, request, queryset):
  110         queryset.update(accepted=True)
  111 
  112     accept.short_description = _("Mark summaries as accepted")
  113 
  114     def reject(self, request, queryset):
  115         queryset.update(accepted=False)
  116 
  117     reject.short_description = _("Mark summaries as rejected")
  118 
  119     def reset_download_count(self, request, queryset):
  120         queryset.update(download_count=0)
  121 
  122     reset_download_count.short_description = _(
  123         "Reset the marked summaries download count"
  124     )
  125 
  126     def download_csv(self, request, queryset):
  127         opts = queryset.model._meta
  128         response = HttpResponse(content_type="text/csv")
  129         # force download.
  130         response["Content-Disposition"] = "attachment;filename=export.csv"
  131         # the csv writer
  132         writer = csv.writer(response)
  133         field_names = [field.name for field in opts.fields]
  134         # Write a first row with header information
  135         writer.writerow(field_names)
  136         # Write data rows
  137         for obj in queryset:
  138             writer.writerow([getattr(obj, field) for field in field_names])
  139         return response
  140 
  141     download_csv.short_description = _("Download marked as csv")
  142
  [end of website/education/admin.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/website/education/admin.py b/website/education/admin.py
  --- a/website/education/admin.py
  +++ b/website/education/admin.py
  @@ -29,8 +29,27 @@
       search_fields = ("name", "course_code")
   
   
  +class WithDownloadCsv:
  +    def download_csv(self, request, queryset):
  +        opts = queryset.model._meta
  +        response = HttpResponse(content_type="text/csv")
  +        # force download.
  +        response["Content-Disposition"] = "attachment;filename=export.csv"
  +        # the csv writer
  +        writer = csv.writer(response)
  +        field_names = [field.name for field in opts.fields]
  +        # Write a first row with header information
  +        writer.writerow(field_names)
  +        # Write data rows
  +        for obj in queryset:
  +            writer.writerow([getattr(obj, field) for field in field_names])
  +        return response
  +
  +    download_csv.short_description = _("Download marked as csv")
  +
  +
   @admin.register(models.Exam)
  -class ExamAdmin(TranslatedModelAdmin):
  +class ExamAdmin(TranslatedModelAdmin, WithDownloadCsv):
       list_display = (
           "type",
           "course",
  @@ -66,26 +85,9 @@
   
       reset_download_count.short_description = _("Reset the marked exams download count")
   
  -    def download_csv(self, request, queryset):
  -        opts = queryset.model._meta
  -        response = HttpResponse(content_type="text/csv")
  -        # force download.
  -        response["Content-Disposition"] = "attachment;filename=export.csv"
  -        # the csv writer
  -        writer = csv.writer(response)
  -        field_names = [field.name for field in opts.fields]
  -        # Write a first row with header information
  -        writer.writerow(field_names)
  -        # Write data rows
  -        for obj in queryset:
  -            writer.writerow([getattr(obj, field) for field in field_names])
  -        return response
  -
  -    download_csv.short_description = _("Download marked as csv")
  -
   
   @admin.register(models.Summary)
  -class SummaryAdmin(TranslatedModelAdmin):
  +class SummaryAdmin(TranslatedModelAdmin, WithDownloadCsv):
       list_display = (
           "name",
           "course",
  @@ -122,20 +124,3 @@
       reset_download_count.short_description = _(
           "Reset the marked summaries download count"
       )
  -
  -    def download_csv(self, request, queryset):
  -        opts = queryset.model._meta
  -        response = HttpResponse(content_type="text/csv")
  -        # force download.
  -        response["Content-Disposition"] = "attachment;filename=export.csv"
  -        # the csv writer
  -        writer = csv.writer(response)
  -        field_names = [field.name for field in opts.fields]
  -        # Write a first row with header information
  -        writer.writerow(field_names)
  -        # Write data rows
  -        for obj in queryset:
  -            writer.writerow([getattr(obj, field) for field in field_names])
  -        return response
  -
  -    download_csv.short_description = _("Download marked as csv")
  
verification_info: {"golden_diff": "diff --git a/website/education/admin.py b/website/education/admin.py\n--- a/website/education/admin.py\n+++ b/website/education/admin.py\n@@ -29,8 +29,27 @@\n     search_fields = (\"name\", \"course_code\")\n \n \n+class WithDownloadCsv:\n+    def download_csv(self, request, queryset):\n+        opts = queryset.model._meta\n+        response = HttpResponse(content_type=\"text/csv\")\n+        # force download.\n+        response[\"Content-Disposition\"] = \"attachment;filename=export.csv\"\n+        # the csv writer\n+        writer = csv.writer(response)\n+        field_names = [field.name for field in opts.fields]\n+        # Write a first row with header information\n+        writer.writerow(field_names)\n+        # Write data rows\n+        for obj in queryset:\n+            writer.writerow([getattr(obj, field) for field in field_names])\n+        return response\n+\n+    download_csv.short_description = _(\"Download marked as csv\")\n+\n+\n @admin.register(models.Exam)\n-class ExamAdmin(TranslatedModelAdmin):\n+class ExamAdmin(TranslatedModelAdmin, WithDownloadCsv):\n     list_display = (\n         \"type\",\n         \"course\",\n@@ -66,26 +85,9 @@\n \n     reset_download_count.short_description = _(\"Reset the marked exams download count\")\n \n-    def download_csv(self, request, queryset):\n-        opts = queryset.model._meta\n-        response = HttpResponse(content_type=\"text/csv\")\n-        # force download.\n-        response[\"Content-Disposition\"] = \"attachment;filename=export.csv\"\n-        # the csv writer\n-        writer = csv.writer(response)\n-        field_names = [field.name for field in opts.fields]\n-        # Write a first row with header information\n-        writer.writerow(field_names)\n-        # Write data rows\n-        for obj in queryset:\n-            writer.writerow([getattr(obj, field) for field in field_names])\n-        return response\n-\n-    download_csv.short_description = _(\"Download marked as csv\")\n-\n \n @admin.register(models.Summary)\n-class SummaryAdmin(TranslatedModelAdmin):\n+class SummaryAdmin(TranslatedModelAdmin, WithDownloadCsv):\n     list_display = (\n         \"name\",\n         \"course\",\n@@ -122,20 +124,3 @@\n     reset_download_count.short_description = _(\n         \"Reset the marked summaries download count\"\n     )\n-\n-    def download_csv(self, request, queryset):\n-        opts = queryset.model._meta\n-        response = HttpResponse(content_type=\"text/csv\")\n-        # force download.\n-        response[\"Content-Disposition\"] = \"attachment;filename=export.csv\"\n-        # the csv writer\n-        writer = csv.writer(response)\n-        field_names = [field.name for field in opts.fields]\n-        # Write a first row with header information\n-        writer.writerow(field_names)\n-        # Write data rows\n-        for obj in queryset:\n-            writer.writerow([getattr(obj, field) for field in field_names])\n-        return response\n-\n-    download_csv.short_description = _(\"Download marked as csv\")\n", "issue": "Fix \"identical-code\" issue in website/education/admin.py\nIdentical blocks of code found in 2 locations. Consider refactoring.\n\nhttps://codeclimate.com/github/svthalia/concrexit/website/education/admin.py#issue_5ecfbb80e8d0fa00010003f6\n", "before_files": [{"content": "\"\"\"\nThis module registers admin pages for the models\n\"\"\"\nimport csv\n\nfrom django.contrib import admin\nfrom django.http import HttpResponse\nfrom django.utils.translation import gettext_lazy as _\n\nfrom utils.translation import TranslatedModelAdmin\nfrom . import models\nfrom .forms import SummaryAdminForm\n\nadmin.site.register(models.Category)\n\n\n@admin.register(models.Course)\nclass CourseAdmin(TranslatedModelAdmin):\n    fields = (\n        \"name\",\n        \"course_code\",\n        \"ec\",\n        \"since\",\n        \"until\",\n        \"categories\",\n        \"old_courses\",\n    )\n    list_filter = (\"categories\", \"ec\")\n    search_fields = (\"name\", \"course_code\")\n\n\n@admin.register(models.Exam)\nclass ExamAdmin(TranslatedModelAdmin):\n    list_display = (\n        \"type\",\n        \"course\",\n        \"exam_date\",\n        \"uploader\",\n        \"accepted\",\n        \"language\",\n        \"download_count\",\n    )\n    readonly_fields = (\"download_count\",)\n    list_filter = (\"accepted\", \"exam_date\", \"type\", \"language\")\n    search_fields = (\n        \"name\",\n        \"uploader__first_name\",\n        \"uploader__last_name\",\n        \"course__name_nl\",\n        \"course__name_en\",\n    )\n    actions = [\"accept\", \"reject\", \"reset_download_count\", \"download_csv\"]\n\n    def accept(self, request, queryset):\n        queryset.update(accepted=True)\n\n    accept.short_description = _(\"Mark exams as accepted\")\n\n    def reject(self, request, queryset):\n        queryset.update(accepted=False)\n\n    reject.short_description = _(\"Mark exams as rejected\")\n\n    def reset_download_count(self, request, queryset):\n        queryset.update(download_count=0)\n\n    reset_download_count.short_description = _(\"Reset the marked exams download count\")\n\n    def download_csv(self, request, queryset):\n        opts = queryset.model._meta\n        response = HttpResponse(content_type=\"text/csv\")\n        # force download.\n        response[\"Content-Disposition\"] = \"attachment;filename=export.csv\"\n        # the csv writer\n        writer = csv.writer(response)\n        field_names = [field.name for field in opts.fields]\n        # Write a first row with header information\n        writer.writerow(field_names)\n        # Write data rows\n        for obj in queryset:\n            writer.writerow([getattr(obj, field) for field in field_names])\n        return response\n\n    download_csv.short_description = _(\"Download marked as csv\")\n\n\n@admin.register(models.Summary)\nclass SummaryAdmin(TranslatedModelAdmin):\n    list_display = (\n        \"name\",\n        \"course\",\n        \"uploader\",\n        \"accepted\",\n        \"language\",\n        \"download_count\",\n    )\n    readonly_fields = (\"download_count\",)\n    list_filter = (\"accepted\", \"language\")\n    search_fields = (\n        \"name\",\n        \"uploader__first_name\",\n        \"uploader__last_name\",\n        \"course__name_nl\",\n        \"course__name_en\",\n    )\n    actions = [\"accept\", \"reject\", \"reset_download_count\", \"download_csv\"]\n    form = SummaryAdminForm\n\n    def accept(self, request, queryset):\n        queryset.update(accepted=True)\n\n    accept.short_description = _(\"Mark summaries as accepted\")\n\n    def reject(self, request, queryset):\n        queryset.update(accepted=False)\n\n    reject.short_description = _(\"Mark summaries as rejected\")\n\n    def reset_download_count(self, request, queryset):\n        queryset.update(download_count=0)\n\n    reset_download_count.short_description = _(\n        \"Reset the marked summaries download count\"\n    )\n\n    def download_csv(self, request, queryset):\n        opts = queryset.model._meta\n        response = HttpResponse(content_type=\"text/csv\")\n        # force download.\n        response[\"Content-Disposition\"] = \"attachment;filename=export.csv\"\n        # the csv writer\n        writer = csv.writer(response)\n        field_names = [field.name for field in opts.fields]\n        # Write a first row with header information\n        writer.writerow(field_names)\n        # Write data rows\n        for obj in queryset:\n            writer.writerow([getattr(obj, field) for field in field_names])\n        return response\n\n    download_csv.short_description = _(\"Download marked as csv\")\n", "path": "website/education/admin.py"}]}