CoolFace
Datasetpublic

12Parker/python-migrations

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes92downloads
web-task-instances.jsonl.all2 linesDownload Raw Back to train
1{"repo": "studentenportal/web", "pull_number": 239, "instance_id": "studentenportal__web-239", "issue_numbers": "", "base_commit": "20ab2fd057725d6a23422c46f0e62cdc5fdc41d9", "patch": "diff --git a/apps/api/urls.py b/apps/api/urls.py\n--- a/apps/api/urls.py\n+++ b/apps/api/urls.py\n@@ -6,6 +6,9 @@\n from apps.api import views\n \n \n+app_name = 'api'\n+\n+\n v1_api = [\n     url(r'^$', views.api_root, name='api_root'),\n     url(r'^users$', views.UserList.as_view(), name='user_list'),\n@@ -19,6 +22,6 @@\n ]\n \n urlpatterns = [\n-    url(r'^', include('rest_framework.urls', namespace='rest_framework')),\n+    url(r'^', include('rest_framework.urls')),\n     url(r'^v1/', include(format_suffix_patterns(v1_api))),\n ]\ndiff --git a/apps/documents/migrations/0001_initial.py b/apps/documents/migrations/0001_initial.py\n--- a/apps/documents/migrations/0001_initial.py\n+++ b/apps/documents/migrations/0001_initial.py\n@@ -59,7 +59,7 @@ class Migration(migrations.Migration):\n             fields=[\n                 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),\n                 ('rating', models.PositiveSmallIntegerField(validators=[django.core.validators.MaxValueValidator(10), django.core.validators.MinValueValidator(1)])),\n-                ('document', models.ForeignKey(related_name='DocumentRating', to='documents.Document')),\n+                ('document', models.ForeignKey(related_name='DocumentRating', to='documents.Document', on_delete=django.db.models.deletion.CASCADE)),\n             ],\n         ),\n     ]\ndiff --git a/apps/documents/migrations/0002_auto_20161124_2111.py b/apps/documents/migrations/0002_auto_20161124_2111.py\n--- a/apps/documents/migrations/0002_auto_20161124_2111.py\n+++ b/apps/documents/migrations/0002_auto_20161124_2111.py\n@@ -18,12 +18,12 @@ class Migration(migrations.Migration):\n         migrations.AddField(\n             model_name='documentrating',\n             name='user',\n-            field=models.ForeignKey(related_name='DocumentRating', to=settings.AUTH_USER_MODEL),\n+            field=models.ForeignKey(related_name='DocumentRating', to=settings.AUTH_USER_MODEL, on_delete=django.db.models.deletion.CASCADE),\n         ),\n         migrations.AddField(\n             model_name='documentdownload',\n             name='document',\n-            field=models.ForeignKey(related_name='DocumentDownload', to='documents.Document'),\n+            field=models.ForeignKey(related_name='DocumentDownload', to='documents.Document', on_delete=django.db.models.deletion.CASCADE),\n         ),\n         migrations.AddField(\n             model_name='documentcategory',\ndiff --git a/apps/documents/models.py b/apps/documents/models.py\n--- a/apps/documents/models.py\n+++ b/apps/documents/models.py\n@@ -41,7 +41,7 @@ def other_count(self):\n         excludes = [Document.DTypes.EXAM, Document.DTypes.SUMMARY]\n         return self.Document.exclude(dtype__in=excludes).count()\n \n-    def __unicode__(self):\n+    def __str__(self):\n         return self.name\n \n     class Meta:\n@@ -169,7 +169,7 @@ def save(self, *args, **kwargs):\n             self.change_date = datetime.now()\n         return super(Document, self).save(*args, **kwargs)\n \n-    def __unicode__(self):\n+    def __str__(self):\n         return self.name\n \n     class Meta:\n@@ -179,7 +179,8 @@ class Meta:\n \n class DocumentDownload(models.Model):\n     \"\"\"Tracks a download of a document.\"\"\"\n-    document = models.ForeignKey(Document, related_name='DocumentDownload', db_index=True)\n+    document = models.ForeignKey(Document, related_name='DocumentDownload', db_index=True,\n+                                 on_delete=models.CASCADE)\n     timestamp = models.DateTimeField(auto_now_add=True, editable=False)\n     ip = models.GenericIPAddressField(unpack_ipv4=True, editable=False, db_index=True)\n \n@@ -196,8 +197,10 @@ class DocumentRating(models.Model):\n \n     \"\"\"\n     RATING_VALIDATORS = [MaxValueValidator(10), MinValueValidator(1)]\n-    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='DocumentRating')\n-    document = models.ForeignKey(Document, related_name='DocumentRating')\n+    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='DocumentRating',\n+                             on_delete=models.CASCADE)\n+    document = models.ForeignKey(Document, related_name='DocumentRating',\n+                                 on_delete=models.CASCADE)\n     rating = models.PositiveSmallIntegerField(validators=RATING_VALIDATORS)\n \n     # Custom model validation\n@@ -205,7 +208,7 @@ def clean(self):\n         if self.user == self.document.uploader:\n             raise ValidationError('A user cannot rate his own uploads.')\n \n-    def __unicode__(self):\n+    def __str__(self):\n         fmt_args = self.user.username, self.document.name, self.rating\n         return 'User %s Document %s Rating %u' % fmt_args\n \ndiff --git a/apps/documents/urls.py b/apps/documents/urls.py\n--- a/apps/documents/urls.py\n+++ b/apps/documents/urls.py\n@@ -8,6 +8,8 @@\n \n admin.autodiscover()\n \n+app_name = 'documents'\n+\n # Dynamic pages\n urlpatterns = [\n     url(r'^$', views.DocumentcategoryList.as_view(), name='documentcategory_list'),\ndiff --git a/apps/documents/views.py b/apps/documents/views.py\n--- a/apps/documents/views.py\n+++ b/apps/documents/views.py\n@@ -12,7 +12,7 @@\n from django.contrib import messages\n from django.contrib.syndication.views import Feed\n from django.db.models import Count\n-from django.core.urlresolvers import reverse\n+from django.urls import reverse\n from django.core.exceptions import ObjectDoesNotExist, ValidationError\n from django.core.mail import send_mail\n from django.http import HttpResponse\n@@ -116,7 +116,7 @@ def get_queryset(self):\n \n     def get_context_data(self, **kwargs):\n         context = super(DocumentList, self).get_context_data(**kwargs)\n-        if self.request.user.is_authenticated():\n+        if self.request.user.is_authenticated:\n             ratings = models.DocumentRating.objects.filter(user=self.request.user)\n             context['ratings'] = dict([(r.document.pk, r.rating) for r in ratings])\n         return context\n@@ -159,7 +159,7 @@ def get(self, request, *args, **kwargs):\n         doc = get_object_or_404(models.Document, pk=self.kwargs.get('pk'))\n         # If document is an exam or marked as non-public, require login\n         if doc.dtype == doc.DTypes.EXAM or doc.public is False:\n-            if not self.request.user.is_authenticated():\n+            if not self.request.user.is_authenticated:\n                 return redirect('%s?next=%s' % (\n                         reverse('auth_login'),\n                         reverse('documents:document_list',\n@@ -174,7 +174,7 @@ def get(self, request, *args, **kwargs):\n         # Serve file\n         filename = unicodedata.normalize('NFKD', doc.original_filename) \\\n                               .encode('us-ascii', 'ignore')\n-        attachment = not filename.lower().endswith('.pdf')\n+        attachment = not filename.lower().endswith(b'.pdf')\n         return sendfile(request, doc.document.path,\n                 attachment=attachment, attachment_filename=filename)\n \n@@ -308,7 +308,7 @@ def dispatch(self, request, *args, **kwargs):\n         return super(DocumentReport, self).dispatch(request, *args, **kwargs)\n \n     def get_initial(self):\n-        if self.request.user.is_authenticated():\n+        if self.request.user.is_authenticated:\n             return {\n                 'name': self.request.user.name(),\n                 'email': self.request.user.email,\n@@ -343,7 +343,7 @@ def document_rating(request, category, pk):\n     to update the text after changing a rating via JavaScript.\"\"\"\n     if not request.is_ajax():\n         return HttpResponseBadRequest('XMLHttpRequest expected.')\n-    if not request.user.is_authenticated():\n+    if not request.user.is_authenticated:\n         return HttpResponseForbidden('Login required')\n     template = 'front/blocks/document_rating_summary.html'\n     context = {'doc': get_object_or_404(models.Document, pk=pk, category__name=category)}\ndiff --git a/apps/events/models.py b/apps/events/models.py\n--- a/apps/events/models.py\n+++ b/apps/events/models.py\n@@ -51,5 +51,5 @@ def days_until(self):\n         delta = self.start_date - date.today()\n         return delta.days if delta.days > 0 else None\n \n-    def __unicode__(self):\n+    def __str__(self):\n         return '%s %s' % (self.start_date, self.summary)\ndiff --git a/apps/events/urls.py b/apps/events/urls.py\n--- a/apps/events/urls.py\n+++ b/apps/events/urls.py\n@@ -8,6 +8,8 @@\n \n admin.autodiscover()\n \n+app_name = 'events'\n+\n # Dynamic pages\n urlpatterns = [\n     url(r'^$', views.EventList.as_view(), name='event_list'),\ndiff --git a/apps/events/views.py b/apps/events/views.py\n--- a/apps/events/views.py\n+++ b/apps/events/views.py\n@@ -3,13 +3,13 @@\n \n import datetime\n from dateutil.relativedelta import relativedelta\n-from urlparse import urlsplit, urlunsplit\n+from urllib.parse import urlsplit, urlunsplit\n \n from django.views.generic import View, TemplateView\n from django.views.generic.detail import DetailView\n from django.views.generic.edit import CreateView, UpdateView, DeleteView\n from django.contrib import messages\n-from django.core.urlresolvers import reverse\n+from django.urls import reverse\n from django.http import HttpResponse\n from django.http import HttpResponseForbidden\n \ndiff --git a/apps/front/migrations/0003_username_normalization.py b/apps/front/migrations/0003_username_normalization.py\nnew file mode 100644\n--- /dev/null\n+++ b/apps/front/migrations/0003_username_normalization.py\n@@ -0,0 +1,30 @@\n+# -*- coding: utf-8 -*-\n+# Generated by Django 1.11.27 on 2020-01-21 15:28\n+from __future__ import unicode_literals\n+\n+import django.contrib.auth.models\n+import django.contrib.auth.validators\n+from django.db import migrations, models\n+import django.db.models.manager\n+\n+\n+class Migration(migrations.Migration):\n+\n+    dependencies = [\n+        ('front', '0002_auto_20200119_1707'),\n+    ]\n+\n+    operations = [\n+        migrations.AlterModelManagers(\n+            name='user',\n+            managers=[\n+                ('users', django.db.models.manager.Manager()),\n+                ('objects', django.contrib.auth.models.UserManager()),\n+            ],\n+        ),\n+        migrations.AlterField(\n+            model_name='user',\n+            name='username',\n+            field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username'),\n+        ),\n+    ]\ndiff --git a/apps/front/migrations/0004_auto_20200121_1625.py b/apps/front/migrations/0004_auto_20200121_1625.py\nnew file mode 100644\n--- /dev/null\n+++ b/apps/front/migrations/0004_auto_20200121_1625.py\n@@ -0,0 +1,18 @@\n+# Generated by Django 2.2.9 on 2020-01-21 16:25\n+\n+from django.db import migrations, models\n+\n+\n+class Migration(migrations.Migration):\n+\n+    dependencies = [\n+        ('front', '0003_username_normalization'),\n+    ]\n+\n+    operations = [\n+        migrations.AlterField(\n+            model_name='user',\n+            name='last_name',\n+            field=models.CharField(blank=True, max_length=150, verbose_name='last name'),\n+        ),\n+    ]\ndiff --git a/apps/front/models.py b/apps/front/models.py\n--- a/apps/front/models.py\n+++ b/apps/front/models.py\n@@ -2,10 +2,38 @@\n from __future__ import print_function, division, absolute_import, unicode_literals\n \n from django.db import models\n-from django.contrib.auth.models import AbstractUser\n+from django.contrib.auth.models import AbstractUser, BaseUserManager\n from django.utils.safestring import mark_safe\n \n \n+def strip_mail_part(username):\n+    \"\"\"\n+    Users might try to login with their email. To support that\n+    we can simply strip the mail part from the username\n+    \"\"\"\n+    if '@' in username:\n+        return username.split('@')[0]\n+    return username\n+\n+\n+class CustomUserManager(BaseUserManager):\n+\n+    \"\"\"\n+    By default, django.contrib.auth does case _sensitive_ username authentication, which isn't what is\n+    generally expected. By defining a custom user manager, we can compare user\n+    names case insensitively and strip off the email part.\n+\n+    Sources:\n+\n+    https://djangosnippets.org/snippets/1368/\n+    https://code.djangoproject.com/ticket/2273#comment:12\n+    \"\"\"\n+\n+    def get_by_natural_key(self, username):\n+        username = strip_mail_part(username)\n+        return self.get(username__iexact=username)\n+\n+\n class User(AbstractUser):\n     \"\"\"The user model.\"\"\"\n     twitter = models.CharField('Twitter Benutzername', max_length=24, blank=True)\n@@ -13,9 +41,11 @@ class User(AbstractUser):\n             help_text=mark_safe('Falls angegeben, wird bei deinen Zusammenfassungen jeweils ein '\n             '<a href=\"https://flattr.com/\">Flattr</a> Button angezeigt.'))\n \n+    users = CustomUserManager()\n+\n     def name(self):\n         \"\"\"Return either full user first and last name or the username, if no\n         further data is found.\"\"\"\n         if self.first_name or self.last_name:\n-            return ' '.join(filter(None, [self.first_name, self.last_name]))\n+            return ' '.join(part for part in [self.first_name, self.last_name] if part)\n         return self.username\ndiff --git a/apps/front/templatetags/tags.py b/apps/front/templatetags/tags.py\n--- a/apps/front/templatetags/tags.py\n+++ b/apps/front/templatetags/tags.py\n@@ -23,13 +23,13 @@ def get_range(value):\n \n     Instead of 3 one may use the variable set in the views\n     \"\"\"\n-    return xrange(value)\n+    return range(value)\n \n \n @register.filter\n def get_range1(value):\n     \"\"\"Like get_range, but with an 1 based index.\"\"\"\n-    return xrange(1, value + 1)\n+    return range(1, value + 1)\n \n \n @register.filter\ndiff --git a/apps/front/views.py b/apps/front/views.py\n--- a/apps/front/views.py\n+++ b/apps/front/views.py\n@@ -6,7 +6,7 @@\n from django.contrib.auth import get_user_model\n from django.contrib import messages\n from django.db.models import Count\n-from django.core.urlresolvers import reverse\n+from django.urls import reverse\n from django.views.generic import TemplateView\n from django.views.generic.edit import UpdateView\n from django.views.generic.detail import DetailView\n@@ -53,7 +53,7 @@ def get_context_data(self, **kwargs):\n         user = self.get_object()\n         context['lecturerratings'] = user.LecturerRating. \\\n                 values_list('lecturer').distinct().count()\n-        if self.request.user.is_authenticated():\n+        if self.request.user.is_authenticated:\n             ratings = document_models.DocumentRating.objects.filter(user=user)\n             context['ratings'] = dict([(r.document.pk, r.rating) for r in ratings])\n         return context\ndiff --git a/apps/lecturers/management/commands/fetch_lecturers.py b/apps/lecturers/management/commands/fetch_lecturers.py\n--- a/apps/lecturers/management/commands/fetch_lecturers.py\n+++ b/apps/lecturers/management/commands/fetch_lecturers.py\n@@ -20,7 +20,7 @@\n def unicode_csv_reader(utf8_data, dialect=csv.excel, **kwargs):\n     csv_reader = csv.reader(utf8_data, dialect=dialect, **kwargs)\n     for row in csv_reader:\n-        yield [unicode(cell, 'latin1') for cell in row]\n+        yield [cell.encode('latin1') for cell in row]\n \n \n class HsrWebsite(object):\n@@ -127,7 +127,7 @@ def handle(self, **options):\n         reader = unicode_csv_reader(f, delimiter=b';')\n         # The last element added to the list is a little hack, because all\n         # rows except the title row have a trailing semicolon.\n-        titles = [t.lower().replace(' ', '_') for t in reader.next()] + ['empty']\n+        titles = [t.lower().replace(' ', '_') for t in next(reader)] + ['empty']\n         Person = namedtuple('Person', titles)\n         for p in map(Person._make, reader):\n             parsed_count += 1\ndiff --git a/apps/lecturers/migrations/0001_initial.py b/apps/lecturers/migrations/0001_initial.py\n--- a/apps/lecturers/migrations/0001_initial.py\n+++ b/apps/lecturers/migrations/0001_initial.py\n@@ -47,8 +47,8 @@ class Migration(migrations.Migration):\n                 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),\n                 ('category', models.CharField(db_index=True, max_length=1, choices=[('d', 'Didaktisch'), ('m', 'Menschlich'), ('f', 'Fachlich')])),\n                 ('rating', models.PositiveSmallIntegerField(db_index=True, validators=[django.core.validators.MaxValueValidator(10), django.core.validators.MinValueValidator(1)])),\n-                ('lecturer', models.ForeignKey(related_name='LecturerRating', to='lecturers.Lecturer')),\n-                ('user', models.ForeignKey(related_name='LecturerRating', to=settings.AUTH_USER_MODEL)),\n+                ('lecturer', models.ForeignKey(related_name='LecturerRating', to='lecturers.Lecturer', on_delete=django.db.models.deletion.CASCADE)),\n+                ('user', models.ForeignKey(related_name='LecturerRating', to=settings.AUTH_USER_MODEL, on_delete=django.db.models.deletion.CASCADE)),\n             ],\n         ),\n         migrations.CreateModel(\n@@ -59,7 +59,7 @@ class Migration(migrations.Migration):\n                 ('quote', models.TextField(verbose_name='Zitat')),\n                 ('comment', models.TextField(default='', verbose_name='Bemerkung', blank=True)),\n                 ('author', models.ForeignKey(related_name='Quote', on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, null=True)),\n-                ('lecturer', models.ForeignKey(related_name='Quote', verbose_name='Dozent', to='lecturers.Lecturer')),\n+                ('lecturer', models.ForeignKey(related_name='Quote', verbose_name='Dozent', to='lecturers.Lecturer', on_delete=django.db.models.deletion.CASCADE)),\n             ],\n             options={\n                 'ordering': ['-date'],\n@@ -71,8 +71,8 @@ class Migration(migrations.Migration):\n             fields=[\n                 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),\n                 ('vote', models.BooleanField(help_text='True = upvote, False = downvote')),\n-                ('quote', models.ForeignKey(related_name='QuoteVote', to='lecturers.Quote')),\n-                ('user', models.ForeignKey(related_name='QuoteVote', to=settings.AUTH_USER_MODEL)),\n+                ('quote', models.ForeignKey(related_name='QuoteVote', to='lecturers.Quote', on_delete=django.db.models.deletion.CASCADE)),\n+                ('user', models.ForeignKey(related_name='QuoteVote', to=settings.AUTH_USER_MODEL, on_delete=django.db.models.deletion.CASCADE)),\n             ],\n         ),\n         migrations.AlterUniqueTogether(\ndiff --git a/apps/lecturers/models.py b/apps/lecturers/models.py\n--- a/apps/lecturers/models.py\n+++ b/apps/lecturers/models.py\n@@ -35,8 +35,8 @@ class Lecturer(models.Model):\n     real_objects = managers.RealLecturerManager()\n \n     def name(self):\n-        parts = filter(None, [self.title, self.last_name, self.first_name])\n-        return ' '.join(parts)\n+        parts = [self.title, self.last_name, self.first_name]\n+        return ' '.join(p for p in parts if p)\n \n     def photo(self):\n         \"\"\"Try to see if a photo with the name <self.id>.jpg exists. If it\n@@ -64,7 +64,7 @@ def _avg_rating(self, category):\n         qs = self.LecturerRating.filter(category=category)\n         if qs.exists():\n             ratings = qs.values_list('rating', flat=True)\n-            return int(round(float(sum(ratings)) / len(ratings)))\n+            return int(sum(ratings) / len(ratings) + 0.5)  # always round .5 up\n         return 0\n \n     def _rating_count(self, category):\n@@ -88,7 +88,7 @@ def rating_count_m(self):\n     def rating_count_f(self):\n         return self._rating_count('f')\n \n-    def __unicode__(self):\n+    def __str__(self):\n         return '%s %s' % (self.last_name, self.first_name)\n \n     class Meta:\n@@ -103,12 +103,14 @@ class LecturerRating(models.Model):\n         ('f', 'Fachlich'))\n     RATING_VALIDATORS = [MaxValueValidator(10), MinValueValidator(1)]\n \n-    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='LecturerRating')\n-    lecturer = models.ForeignKey(Lecturer, related_name='LecturerRating')\n+    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='LecturerRating',\n+                             on_delete=models.CASCADE)\n+    lecturer = models.ForeignKey(Lecturer, related_name='LecturerRating',\n+                                 on_delete=models.CASCADE)\n     category = models.CharField(max_length=1, choices=CATEGORY_CHOICES, db_index=True)\n     rating = models.PositiveSmallIntegerField(validators=RATING_VALIDATORS, db_index=True)\n \n-    def __unicode__(self):\n+    def __str__(self):\n         return '%s %s%u' % (self.lecturer, self.category, self.rating)\n \n     class Meta:\n@@ -119,7 +121,8 @@ class Quote(models.Model):\n     \"\"\"Lecturer quotes.\"\"\"\n     author = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='Quote', null=True,\n             on_delete=models.SET_NULL)\n-    lecturer = models.ForeignKey(Lecturer, verbose_name='Dozent', related_name='Quote')\n+    lecturer = models.ForeignKey(Lecturer, verbose_name='Dozent', related_name='Quote',\n+                                 on_delete=models.CASCADE)\n     date = models.DateTimeField(auto_now_add=True)\n     quote = models.TextField('Zitat')\n     comment = models.TextField('Bemerkung', default='', blank=True)\n@@ -133,7 +136,7 @@ def vote_sum(self):\n         down = self.QuoteVote.filter(vote=False).count()\n         return up - down\n \n-    def __unicode__(self):\n+    def __str__(self):\n         return '[%s] %s...' % (self.lecturer, self.quote[:30])\n \n     class Meta:\n@@ -142,12 +145,14 @@ class Meta:\n \n \n class QuoteVote(models.Model):\n-    \"\"\"Lecturer quotes.\"\"\"\n-    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='QuoteVote')\n-    quote = models.ForeignKey(Quote, related_name='QuoteVote')\n+    \"\"\"Lecturer quote votes.\"\"\"\n+    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='QuoteVote',\n+                             on_delete=models.CASCADE)\n+    quote = models.ForeignKey(Quote, related_name='QuoteVote',\n+                              on_delete=models.CASCADE)\n     vote = models.BooleanField(help_text='True = upvote, False = downvote')\n \n-    def __unicode__(self):\n+    def __str__(self):\n         fmt_args = self.user.username, 'up' if self.vote else 'down', self.quote.pk\n         return 'User %s votes %s quote %s' % fmt_args\n \n@@ -156,10 +161,10 @@ class Meta:\n \n \n class Course(models.Model):\n-    \"\"\"A possible degree course. At the moment only one lecturer is possible\"\"\"\n+    \"\"\"A possible degree course. At the moment only one lecturer is possible.\"\"\"\n     id = models.IntegerField('Studiengang ID', primary_key=True)\n     abbreviation = models.CharField('Abk\u00fcrzung', max_length=10, unique=True)\n     name = models.CharField('Titel', max_length=50)\n \n-    def __unicode__(self):\n+    def __str__(self):\n         return '%s (%s)' % (self.name, self.abbreviation)\ndiff --git a/apps/lecturers/urls.py b/apps/lecturers/urls.py\n--- a/apps/lecturers/urls.py\n+++ b/apps/lecturers/urls.py\n@@ -4,6 +4,8 @@\n \n admin.autodiscover()\n \n+app_name = 'lecturers'\n+\n # Dynamic pages\n urlpatterns = [\n     url(r'^dozenten/$', views.LecturerList.as_view(), name='lecturer_list'),\ndiff --git a/apps/lecturers/views.py b/apps/lecturers/views.py\n--- a/apps/lecturers/views.py\n+++ b/apps/lecturers/views.py\n@@ -3,7 +3,7 @@\n \n from django.contrib import messages\n from django.db.models import Count\n-from django.core.urlresolvers import reverse\n+from django.urls import reverse\n from django.core.exceptions import ObjectDoesNotExist\n from django.http import HttpResponseForbidden\n from django.views.generic.edit import CreateView, DeleteView\ndiff --git a/apps/tabs/templatetags/tabs.py b/apps/tabs/templatetags/tabs.py\n--- a/apps/tabs/templatetags/tabs.py\n+++ b/apps/tabs/templatetags/tabs.py\n@@ -78,7 +78,7 @@ def render(self, context):\n def activetab(parser, token):\r\n     bits = token.contents.split()[1:]\r\n     if len(bits) not in (1, 2):\r\n-        raise template.TemplateSyntaxError, \"Invalid number of arguments\"\r\n+        raise template.TemplateSyntaxError(\"Invalid number of arguments\")\r\n     if len(bits) == 1:\r\n         namespace = None\r\n         name = bits[0]\r\n@@ -100,7 +100,7 @@ def ifactivetab(parser, token):\n     else:\r\n         nodelist_false = template.NodeList()\r\n     if len(bits) not in (1, 2):\r\n-        raise template.TemplateSyntaxError, \"Invalid number of arguments\"\r\n+        raise template.TemplateSyntaxError(\"Invalid number of arguments\")\r\n     if len(bits) == 1:\r\n         namespace = None\r\n         name = bits[0]\r\ndiff --git a/apps/tweets/management/commands/get_twitter_secrets.py b/apps/tweets/management/commands/get_twitter_secrets.py\n--- a/apps/tweets/management/commands/get_twitter_secrets.py\n+++ b/apps/tweets/management/commands/get_twitter_secrets.py\n@@ -39,7 +39,7 @@ def handle_noargs(self, **options):\n             sys.exit(1)\n \n         self.printO('Please visit {0} in your browser.'.format(redirect_url))\n-        verifier = raw_input('Enter PIN: ')\n+        verifier = input('Enter PIN: ')\n \n         try:\n             auth.get_access_token(verifier)\ndiff --git a/apps/tweets/urls.py b/apps/tweets/urls.py\n--- a/apps/tweets/urls.py\n+++ b/apps/tweets/urls.py\n@@ -8,6 +8,8 @@\n \n admin.autodiscover()\n \n+app_name = 'tweets'\n+\n # Dynamic pages\n urlpatterns = [\n     url(r'^$', views.TweetList.as_view(), name='tweet_list'),\ndiff --git a/apps/user_stats/management/commands/plot_user_registrations.py b/apps/user_stats/management/commands/plot_user_registrations.py\n--- a/apps/user_stats/management/commands/plot_user_registrations.py\n+++ b/apps/user_stats/management/commands/plot_user_registrations.py\n@@ -23,7 +23,7 @@ def handle(self, *args, **options):\n         User = get_user_model()\n         datetimes = User.objects.values_list('date_joined', flat=True) \\\n                                 .order_by('date_joined')\n-        dates = map(lambda d: d.date(), datetimes)\n+        dates = [dt.date() for dt in datetimes]\n \n         # Get some auxilliary values\n         min_date = date2num(dates[0])\ndiff --git a/config/backends.py b/config/backends.py\ndeleted file mode 100644\n--- a/config/backends.py\n+++ /dev/null\n@@ -1,33 +0,0 @@\n-from django.contrib.auth.backends import ModelBackend\n-from django.contrib.auth import get_user_model\n-\n-\n-def strip_mail_part(username):\n-    \"\"\"\n-    Users might try to login with their email. To support that\n-    we can simply strip the mail part from the username\n-    \"\"\"\n-    if '@' in username:\n-        return username.split('@')[0]\n-    return username\n-\n-\n-class CaseInsensitiveModelBackend(ModelBackend):\n-    \"\"\"\n-    By default ModelBackend does case _sensitive_ username authentication, which isn't what is\n-    generally expected.  This backend supports case insensitive username authentication.\n-\n-    Source: http://blog.shopfiber.com/?p=220\n-\n-    \"\"\"\n-    def authenticate(self, username=None, password=None):\n-        username = strip_mail_part(username)\n-        User = get_user_model()\n-        try:\n-            user = User.objects.get(username__iexact=username)\n-            if user.check_password(password):\n-                return user\n-            else:\n-                return None\n-        except User.DoesNotExist:\n-            return None\ndiff --git a/config/settings.py b/config/settings.py\n--- a/config/settings.py\n+++ b/config/settings.py\n@@ -5,7 +5,7 @@\n import sys\n import datetime\n \n-from unipath import Path\n+from pathlib import Path\n \n from django.core.exceptions import ImproperlyConfigured\n \n@@ -21,7 +21,7 @@ def require_env(name):\n     return value\n \n \n-PROJECT_ROOT = Path(__file__).ancestor(2)\n+PROJECT_ROOT = Path(__file__).parents[1]\n \n ADMINS = (\n     ('Studentenportal Team', 'team@studentenportal.ch'),\n@@ -81,7 +81,7 @@ def require_env(name):\n \n # Absolute filesystem path to the directory that will hold user-uploaded files.\n # Example: \"/home/media/media.lawrence.com/media/\"\n-MEDIA_ROOT = env('DJANGO_MEDIA_ROOT', PROJECT_ROOT.child('media'))\n+MEDIA_ROOT = env('DJANGO_MEDIA_ROOT', PROJECT_ROOT / 'media')\n \n # URL that handles the media served from MEDIA_ROOT. Make sure to use a\n # trailing slash.\n@@ -92,7 +92,7 @@ def require_env(name):\n # Don't put anything in this directory yourself; store your static files\n # in apps' \"static/\" subdirectories and in STATICFILES_DIRS.\n # Example: \"/home/media/media.lawrence.com/static/\"\n-STATIC_ROOT = env('DJANGO_STATIC_ROOT', PROJECT_ROOT.child('static'))\n+STATIC_ROOT = env('DJANGO_STATIC_ROOT', PROJECT_ROOT / 'static')\n \n # URL prefix for static files.\n # Example: \"http://media.lawrence.com/static/\"\n@@ -157,16 +157,18 @@ def require_env(name):\n     }\n }]\n \n-MIDDLEWARE_CLASSES = (\n-    'django.middleware.common.CommonMiddleware',\n+MIDDLEWARE = []\n+if DEBUG_TOOLBAR:\n+    MIDDLEWARE.append('debug_toolbar.middleware.DebugToolbarMiddleware')\n+MIDDLEWARE += [\n+    'django.middleware.security.SecurityMiddleware',\n     'django.contrib.sessions.middleware.SessionMiddleware',\n+    'django.middleware.common.CommonMiddleware',\n     'django.middleware.csrf.CsrfViewMiddleware',\n-    'django.middleware.clickjacking.XFrameOptionsMiddleware',\n     'django.contrib.auth.middleware.AuthenticationMiddleware',\n     'django.contrib.messages.middleware.MessageMiddleware',\n-)\n-if DEBUG_TOOLBAR:\n-    MIDDLEWARE_CLASSES += ('debug_toolbar.middleware.DebugToolbarMiddleware',)\n+    'django.middleware.clickjacking.XFrameOptionsMiddleware',\n+]\n \n ROOT_URLCONF = 'config.urls'\n \n@@ -272,7 +274,7 @@ def require_env(name):\n \n # Auth\n LOGIN_REDIRECT_URL = '/'\n-AUTHENTICATION_BACKENDS = ('config.backends.CaseInsensitiveModelBackend',)\n+AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.ModelBackend']\n \n # API\n REST_FRAMEWORK = {\ndiff --git a/config/urls.py b/config/urls.py\n--- a/config/urls.py\n+++ b/config/urls.py\n@@ -21,20 +21,20 @@\n     url(r'^statistiken/$', views.Stats.as_view(), name='stats'),\n \n     # Own apps\n-    url(r'^events/', include(event_urls, namespace='events')),\n-    url(r'^dokumente/', include(document_urls, namespace='documents')),\n-    url(r'tweets/', include(tweet_urls, namespace='tweets')),\n-    url(r'', include(lecturer_urls, namespace='lecturers')),\n+    url(r'^events/', include(event_urls)),\n+    url(r'^dokumente/', include(document_urls)),\n+    url(r'tweets/', include(tweet_urls)),\n+    url(r'', include(lecturer_urls)),\n \n     # Auth pages\n     url(r'^accounts/', include('registration.backends.default.urls')),\n \n     # Admin pages\n     url(r'^admin/doc/', include('django.contrib.admindocs.urls')),\n-    url(r'^admin/', include(admin.site.urls)),\n+    url(r'^admin/', admin.site.urls),\n \n     # API\n-    url(r'^api/', include(api_urls, namespace='api')),\n+    url(r'^api/', include(api_urls)),\n \n     # Static pages\n     url(r'^sitemap\\.xml$', TemplateView.as_view(template_name='front/sitemap.xml'), name='sitemap'),\n@@ -45,3 +45,9 @@\n         url(r'static/(?P<path>.*)$', serve, {'document_root': settings.STATIC_ROOT}),\n         url(r'media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),\n     ]\n+\n+if settings.DEBUG_TOOLBAR:\n+    import debug_toolbar\n+    urlpatterns += [\n+        url(r'^__debug__/', include(debug_toolbar.urls)),\n+    ]\n", "test_patch": "diff --git a/requirements/testing.txt b/requirements/testing.txt\n--- a/requirements/testing.txt\n+++ b/requirements/testing.txt\n@@ -1,8 +1,8 @@\n model-bakery==1.0.2\n \n-coverage>=4.5,<5.0\n+coverage>=5.0,<5.1\n six==1.14.0\n-pytest>=4.6,<5\n+pytest>=5.3,<5.4\n pytest-django>=3.8,<3.9\n pytest-pep8>=1.0.6,<1.1\n pytest-cov>=2.8,<2.9\ndiff --git a/tests/api/test_serializers.py b/tests/api/test_serializers.py\n--- a/tests/api/test_serializers.py\n+++ b/tests/api/test_serializers.py\n@@ -2,7 +2,7 @@\n from __future__ import print_function, division, absolute_import, unicode_literals\n \n import pytest\n-from django.core.urlresolvers import reverse\n+from django.urls import reverse\n from django.contrib.auth import get_user_model\n \n from model_bakery import baker\ndiff --git a/tests/api/test_views.py b/tests/api/test_views.py\n--- a/tests/api/test_views.py\n+++ b/tests/api/test_views.py\n@@ -6,7 +6,7 @@\n \n import pytest\n from django.test.client import Client\n-from django.core.urlresolvers import reverse, NoReverseMatch\n+from django.urls import reverse, NoReverseMatch\n from django.contrib.auth import get_user_model\n \n from model_bakery import baker\n@@ -42,7 +42,7 @@ def test_session_auth(self, auth_client):\n \n     def test_basic_auth(self, client, user, db):\n         url = reverse('api:quote_list')\n-        auth = 'Basic ' + base64.b64encode('testuser:test')\n+        auth = b'Basic ' + base64.b64encode(b'testuser:test')\n         resp = client.get(url, HTTP_AUTHORIZATION=auth)\n         assert resp.status_code == 200\n \n@@ -57,7 +57,7 @@ def test_status_code(self, user, auth_client):\n                     'Status code for %s is %d instead of 200.' % (url, resp.status_code)\n \n     def test_list_data(self, auth_client):\n-        users = [baker.make(User) for i in xrange(3)]\n+        users = [baker.make(User) for i in range(3)]\n         url = reverse('api:user_list')\n         resp = auth_client.get(url)\n         data = resp.json()\n@@ -120,7 +120,7 @@ def test_status_code(self, lecturer, auth_client):\n                     'Status code for %s is %d instead of 200.' % (url, resp.status_code)\n \n     def test_detail_data(self, lecturer, auth_client, db):\n-        [baker.make(Quote, lecturer=lecturer) for i in xrange(3)]\n+        [baker.make(Quote, lecturer=lecturer) for i in range(3)]\n \n         url = reverse('api:lecturer_detail', args=(lecturer.pk,))\n         resp = auth_client.get(url)\n@@ -336,7 +336,7 @@ def test_login_required(self, client, url):\n     def test_invalid_data(self, data, auth_client, url):\n         resp = auth_client.post(url, data)\n         assert resp.status_code == 400\n-        assert resp.content == 'Validierungsfehler'\n+        assert resp.content == b'Validierungsfehler'\n \n     @pytest.fixture\n     def rater(self, auth_client, url):\ndiff --git a/tests/documents/test_views.py b/tests/documents/test_views.py\n--- a/tests/documents/test_views.py\n+++ b/tests/documents/test_views.py\n@@ -2,7 +2,7 @@\n from __future__ import print_function, division, absolute_import, unicode_literals\n \n from django.test import TestCase\n-from django.core.urlresolvers import reverse\n+from django.urls import reverse\n from django.contrib.auth import get_user_model\n \n from bs4 import BeautifulSoup\n@@ -162,20 +162,20 @@ def testTitle(self):\n         self.assertContains(self.response, '<h1>Dokumente An1I</h1>')\n \n     def testDocumentTitle(self):\n-        soup = BeautifulSoup(self.response.content)\n+        soup = BeautifulSoup(self.response.content, 'html.parser')\n         div_details = soup.find('h3', text='Analysis 1 Theoriesammlung').find_parent('div').prettify()\n         assert '<h3 property=\"dct:title\" xmlns:dct=\"http://purl.org/dc/terms/\">\\n  Analysis 1 Theoriesammlung\\n </h3>' in div_details\n         assert '<span class=\"label-summary\">\\n   Zusammenfassung\\n  </span>' in div_details\n \n     def testDocumentLicense(self):\n-        soup = BeautifulSoup(self.response.content)\n+        soup = BeautifulSoup(self.response.content, 'html.parser')\n         div_details = soup.find('h3', text='Analysis 1 Theoriesammlung').find_parent('div').prettify()\n         assert '<a href=\"http://creativecommons.org/licenses/by-nc-sa/3.0/deed.de\" rel=\"license\" ' + \\\n                       u'title=\"Ver\u00f6ffentlicht unter der CC BY-NC-SA 3.0 Lizenz\">' in div_details\n         assert ' <span class=\"label-license\">\\n    CC BY-NC-SA 3.0\\n   </span>' in div_details\n \n     def testDocumentFlattr(self):\n-        soup = BeautifulSoup(self.response.content)\n+        soup = BeautifulSoup(self.response.content, 'html.parser')\n         div_flattr = soup.find('h3', text='Title with Flattr').find_parent('div').prettify()\n         div_noflattr = soup.find('h3', text='Title Noflattr').find_parent('div').prettify()\n         assert 'Flattr this' in div_flattr\n@@ -220,7 +220,7 @@ def testDownloadCount(self):\n \n         # No downloads yet\n         response0 = self.client.get(self.url)\n-        soup0 = BeautifulSoup(response0.content)\n+        soup0 = BeautifulSoup(response0.content, 'html.parser')\n         anchor0 = soup0.find('a', href=dl_url)\n         document0 = anchor0.find_parent('article').prettify()\n         assert \"0 Downloads\" in document0\n@@ -228,7 +228,7 @@ def testDownloadCount(self):\n         # First download\n         self.client.get(dl_url)\n         response1 = self.client.get(self.url)\n-        soup1 = BeautifulSoup(response1.content)\n+        soup1 = BeautifulSoup(response1.content, 'html.parser')\n         anchor1 = soup1.find('a', href=dl_url)\n         document1 = anchor1.find_parent('article').prettify()\n         assert \"1 Download\" in document1\n@@ -237,7 +237,7 @@ def testDownloadCount(self):\n         # done from the same IP\n         self.client.get(dl_url)\n         response2 = self.client.get(self.url)\n-        soup2 = BeautifulSoup(response2.content)\n+        soup2 = BeautifulSoup(response2.content, 'html.parser')\n         anchor2 = soup2.find('a', href=dl_url)\n         document2 = anchor2.find_parent('article').prettify()\n         assert \"1 Download\" in document2\ndiff --git a/tests/events/test_views.py b/tests/events/test_views.py\n--- a/tests/events/test_views.py\n+++ b/tests/events/test_views.py\n@@ -5,7 +5,7 @@\n \n from django.test import TestCase\n from django.contrib.auth import get_user_model\n-from django.core.urlresolvers import reverse\n+from django.urls import reverse\n \n import pytest\n from model_bakery import baker\n@@ -102,7 +102,7 @@ def testContent(self):\n @pytest.mark.django_db(transaction=True)\n def test_ical_event1(client, test_events):\n     response = client.get(reverse('events:event_calendar'))\n-    event = response.content.split('BEGIN:VEVENT')[1]\n+    event = response.content.decode('utf-8').split('BEGIN:VEVENT')[1]\n     assert 'SUMMARY:Weltuntergang' in event\n     assert 'DTSTART:20121221T200000' in event\n     assert 'DTEND:20121222T100000' in event\n@@ -112,7 +112,7 @@ def test_ical_event1(client, test_events):\n @pytest.mark.django_db(transaction=True)\n def test_ical_event2(client, test_events):\n     response = client.get(reverse('events:event_calendar'))\n-    event = response.content.split('BEGIN:VEVENT')[2]\n+    event = response.content.decode('utf-8').split('BEGIN:VEVENT')[2]\n     assert 'SUMMARY:Afterparty' in event\n     assert 'DTSTART:20121222T100000' in event\n     assert 'DTEND:20121222T235959' in event\ndiff --git a/tests/front/test_views.py b/tests/front/test_views.py\n--- a/tests/front/test_views.py\n+++ b/tests/front/test_views.py\n@@ -5,8 +5,12 @@\n \n from django.test import TestCase\n from django.core import mail\n-from django.core.urlresolvers import reverse\n+from django.urls import reverse\n from django.contrib.auth import get_user_model\n+from django.db import transaction\n+\n+from pytest_django.asserts import assertRedirects\n+\n \n from model_bakery import baker\n \n@@ -56,6 +60,29 @@ def testCaseInsensitveLogin(self):\n         assert r2.status_code == 302\n \n \n+@pytest.mark.django_db(transaction=True)\n+def test_registration(client):\n+    \"\"\"\n+    Test that a registration is successful and that an activation email is\n+    sent.\n+\n+    Needs to use a transaction because the mail is sent on on_commit.\n+    \"\"\"\n+    registration_url = '/accounts/register/'\n+\n+    response = client.post(registration_url, {\n+        'email': 'testuser@hsr.ch',\n+        'password1': 'testpass',\n+        'password2': 'testpass',\n+    })\n+    assertRedirects(response, '/accounts/register/complete/')\n+    assert User.objects.filter(username='testuser').exists()\n+\n+    transaction.commit()\n+    assert len(mail.outbox) == 1\n+    assert mail.outbox[0].subject == '[studentenportal.ch] Aktivierung'\n+\n+\n class RegistrationViewTest(TestCase):\n     registration_url = '/accounts/register/'\n \n@@ -65,21 +92,6 @@ def testRegistrationPage(self):\n         self.assertContains(response, 'Diese Registrierung ist Studenten mit einer HSR-Email-Adresse')\n         self.assertContains(response, '<form')\n \n-    def testRegistration(self):\n-        \"\"\"\n-        Test that a registration is successful and that an activation email is\n-        sent.\n-        \"\"\"\n-        response = self.client.post(self.registration_url, {\n-            'email': 'testuser@hsr.ch',\n-            'password1': 'testpass',\n-            'password2': 'testpass',\n-        })\n-        self.assertRedirects(response, '/accounts/register/complete/')\n-        assert User.objects.filter(username='testuser').exists()\n-        assert len(mail.outbox) == 1\n-        assert mail.outbox[0].subject == '[studentenportal.ch] Aktivierung'\n-\n     def testRegistrationBadUsername(self):\n         \"\"\"\n         Test that a registration with a bad username returns an error.\ndiff --git a/tests/lecturers/test_views.py b/tests/lecturers/test_views.py\n--- a/tests/lecturers/test_views.py\n+++ b/tests/lecturers/test_views.py\n@@ -2,7 +2,7 @@\n from __future__ import print_function, division, absolute_import, unicode_literals\n \n from django.test import TestCase\n-from django.core.urlresolvers import reverse\n+from django.urls import reverse\n from django.contrib.auth import get_user_model\n \n from model_bakery import baker\ndiff --git a/tests/tweets/test_views.py b/tests/tweets/test_views.py\n--- a/tests/tweets/test_views.py\n+++ b/tests/tweets/test_views.py\n@@ -5,7 +5,7 @@\n from collections import namedtuple\n \n from django.test import TestCase\n-from django.core.urlresolvers import reverse\n+from django.urls import reverse\n \n from mock import patch\n \n", "problem_statement": "", "hints_text": "", "created_at": "2020-01-21T17:36:43Z"}2