diff --git a/.github/workflows/anytask.yml b/.github/workflows/anytask.yml index dc468a7bf..e6ca5f125 100644 --- a/.github/workflows/anytask.yml +++ b/.github/workflows/anytask.yml @@ -29,7 +29,7 @@ jobs: pip install pip>=9.0.1 pip install --upgrade flake8 'setuptools-scm==5.0.2' pip install -r requirements_local.txt - sudo apt-get install p7zip-full tar xz-utils bzip2 gzip + sudo apt-get install -y p7zip-full tar xz-utils bzip2 gzip gettext - name: Lint with flake8 run: | cd anytask @@ -43,4 +43,5 @@ jobs: - name: Run django tests run: | cd anytask + python manage.py compilemessages python manage.py test diff --git a/anytask/admission/migrations/0001_initial.py b/anytask/admission/migrations/0001_initial.py index ec9df47f5..91991258e 100644 --- a/anytask/admission/migrations/0001_initial.py +++ b/anytask/admission/migrations/0001_initial.py @@ -20,7 +20,7 @@ class Migration(migrations.Migration): ('old_activation_key', models.CharField(max_length=40, null=True, blank=True)), ('is_updating', models.BooleanField(default=False)), ('user_info', models.TextField(null=True, blank=True)), - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING)), ], options={ }, diff --git a/anytask/admission/models.py b/anytask/admission/models.py index ef5bda4fc..62e237def 100644 --- a/anytask/admission/models.py +++ b/anytask/admission/models.py @@ -172,7 +172,7 @@ def decline_user(self, activation_key): class AdmissionRegistrationProfile(models.Model): - user = models.ForeignKey(User, unique=False, null=False, blank=False) + user = models.ForeignKey(User, unique=False, null=False, blank=False, on_delete=models.DO_NOTHING) activation_key = models.CharField(max_length=40, null=True, blank=True) old_activation_key = models.CharField(max_length=40, null=True, blank=True) is_updating = models.BooleanField(default=False) diff --git a/anytask/admission/tests.py b/anytask/admission/tests.py index 31135c540..f79e7da9b 100644 --- a/anytask/admission/tests.py +++ b/anytask/admission/tests.py @@ -3,7 +3,7 @@ from django.test import TestCase from mock import patch -from django.core.urlresolvers import reverse +from django.urls import reverse from django.conf import settings from users.model_user_status import UserStatus diff --git a/anytask/anycontest/migrations/0001_initial.py b/anytask/anycontest/migrations/0001_initial.py index 9337707f7..fa5beccfb 100644 --- a/anytask/anycontest/migrations/0001_initial.py +++ b/anytask/anycontest/migrations/0001_initial.py @@ -32,7 +32,7 @@ class Migration(migrations.Migration): ('create_time', models.DateTimeField(auto_now_add=True)), ('update_time', models.DateTimeField(auto_now=True)), ('sended_notify', models.BooleanField(default=False)), - ('author', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ('author', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING)), ], options={ }, diff --git a/anytask/anycontest/migrations/0002_auto_20200328_1939.py b/anytask/anycontest/migrations/0002_auto_20200328_1939.py index 942c12f26..99366e92e 100644 --- a/anytask/anycontest/migrations/0002_auto_20200328_1939.py +++ b/anytask/anycontest/migrations/0002_auto_20200328_1939.py @@ -15,13 +15,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='contestsubmission', name='file', - field=models.ForeignKey(to='issues.File'), + field=models.ForeignKey(to='issues.File', on_delete=models.DO_NOTHING), preserve_default=True, ), migrations.AddField( model_name='contestsubmission', name='issue', - field=models.ForeignKey(to='issues.Issue'), + field=models.ForeignKey(to='issues.Issue', on_delete=models.DO_NOTHING), preserve_default=True, ), ] diff --git a/anytask/anycontest/models.py b/anytask/anycontest/models.py index 03ec2b382..f499f82e4 100644 --- a/anytask/anycontest/models.py +++ b/anytask/anycontest/models.py @@ -25,9 +25,9 @@ class ContestSubmissionWaiting(Exception): class ContestSubmission(models.Model): - issue = models.ForeignKey('issues.Issue', db_index=True, null=False, blank=False) - author = models.ForeignKey(User, null=False, blank=False) - file = models.ForeignKey('issues.File', null=False, blank=False) + issue = models.ForeignKey('issues.Issue', db_index=True, null=False, blank=False, on_delete=models.DO_NOTHING) + author = models.ForeignKey(User, null=False, blank=False, on_delete=models.DO_NOTHING) + file = models.ForeignKey('issues.File', null=False, blank=False, on_delete=models.DO_NOTHING) run_id = models.CharField(max_length=191, blank=True) compiler_id = models.CharField(max_length=191, blank=True) diff --git a/anytask/anycontest/tests.py b/anytask/anycontest/tests.py index d78af046e..35f334ae0 100644 --- a/anytask/anycontest/tests.py +++ b/anytask/anycontest/tests.py @@ -189,7 +189,7 @@ def setUp(self): self.group = Group.objects.create(name='name_groups', year=self.year) self.course = Course.objects.create(name='course_name', year=self.year) - self.course.groups = [self.group] + self.course.groups.set([self.group]) self.course.save() self.task = Task.objects.create(title='task', diff --git a/anytask/api/tests.py b/anytask/api/tests.py index 85bf16a6f..ba2e1ef8e 100644 --- a/anytask/api/tests.py +++ b/anytask/api/tests.py @@ -5,8 +5,8 @@ from django.contrib.auth.models import User from django.core.files.uploadedfile import SimpleUploadedFile -from django.core.urlresolvers import reverse -from django.test import TestCase +from django.urls import reverse +from django.test import TestCase, override_settings from courses.models import Course from issues.model_issue_status import IssueStatus @@ -18,6 +18,7 @@ import api.views +@override_settings(LANGUAGE_CODE='en-EN', LANGUAGES=(('en', 'English'),)) class ApiTest(TestCase): maxDiff = None @@ -63,15 +64,15 @@ def setUp(self): self.group = Group.objects.create(name='group_name', year=self.year) - self.group.students = [self.student] + self.group.students.set([self.student]) self.group.save() self.course = Course.objects.create(name='course_name', year=self.year) - self.course.groups = [self.group] - self.course.teachers = [self.teacher] - self.course.issue_fields = IssueField.objects.exclude(id=10).exclude(id=11).exclude(id=12) - self.course.issue_status_system.statuses = IssueStatus.objects.all() + self.course.groups.set([self.group]) + self.course.teachers.set([self.teacher]) + self.course.issue_fields.set(IssueField.objects.exclude(id=10).exclude(id=11).exclude(id=12)) + self.course.issue_status_system.statuses.set(IssueStatus.objects.all()) self.course.save() self.task1 = Task.objects.create(title='task_title1', @@ -492,7 +493,7 @@ def test_post_issue__status(self, status=None): }, { 'files': [], - 'message': 'status_izmenen' + 'message': 'Status updated:' ' \u0417\u0430\u0447\u0442\u0435\u043d\u043e \u043f\u043e\u0441\u043b\u0435' ' \u0434\u0435\u0434\u043b\u0430\u0439\u043d\u0430', 'id': 2, diff --git a/anytask/api/views.py b/anytask/api/views.py index c389b8704..81ab8f756 100644 --- a/anytask/api/views.py +++ b/anytask/api/views.py @@ -4,7 +4,7 @@ import json from django.conf import settings -from django.contrib.auth import authenticate, login +from django.contrib.auth import authenticate from django.http import HttpResponse, HttpResponseForbidden, HttpResponseBadRequest, HttpResponseNotFound from django.shortcuts import get_object_or_404 from django.views.decorators.csrf import csrf_exempt @@ -38,11 +38,10 @@ def check_auth(request, *args, **kwargs): return get_401_response() username, password = base64.b64decode(auth_str_parts[1].encode('utf8')).decode('utf8').split(":", 1) - user = authenticate(username=username, password=password) + user = authenticate(request=request, username=username, password=password) if user is None or not user.is_active: return get_401_response() - login(request, user) request.user = user return view(request, *args, **kwargs) diff --git a/anytask/courses/migrations/0001_initial.py b/anytask/courses/migrations/0001_initial.py index bbdd198f0..fa7925940 100644 --- a/anytask/courses/migrations/0001_initial.py +++ b/anytask/courses/migrations/0001_initial.py @@ -62,9 +62,9 @@ class Migration(migrations.Migration): name='DefaultTeacher', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('course', models.ForeignKey(to='courses.Course')), - ('group', models.ForeignKey(blank=True, to='groups.Group', null=True)), - ('teacher', models.ForeignKey(to=settings.AUTH_USER_MODEL, blank=True, null=True, db_index=False)), + ('course', models.ForeignKey(to='courses.Course', on_delete=models.DO_NOTHING)), + ('group', models.ForeignKey(blank=True, to='groups.Group', null=True, on_delete=models.DO_NOTHING)), + ('teacher', models.ForeignKey(to=settings.AUTH_USER_MODEL, blank=True, null=True, db_index=False, on_delete=models.DO_NOTHING)), ], options={ }, @@ -97,10 +97,10 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('update_time', models.DateTimeField(default=django.utils.timezone.now, auto_now=True)), - ('course', models.ForeignKey(to='courses.Course', db_index=False)), - ('mark', models.ForeignKey(to='courses.MarkField', blank=True, null=True, db_index=False)), - ('student', models.ForeignKey(to=settings.AUTH_USER_MODEL)), - ('teacher', models.ForeignKey(related_name=b'teacher_change_mark', blank=True, to=settings.AUTH_USER_MODEL, null=True, db_index=False)), + ('course', models.ForeignKey(to='courses.Course', db_index=False, on_delete=models.DO_NOTHING)), + ('mark', models.ForeignKey(to='courses.MarkField', blank=True, null=True, db_index=False, on_delete=models.DO_NOTHING)), + ('student', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING)), + ('teacher', models.ForeignKey(related_name='teacher_change_mark', blank=True, to=settings.AUTH_USER_MODEL, null=True, db_index=False, on_delete=models.DO_NOTHING)), ], options={ }, @@ -123,13 +123,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='course', name='filename_extensions', - field=models.ManyToManyField(related_name=b'filename_extensions_set', null=True, to='courses.FilenameExtension', blank=True), + field=models.ManyToManyField(related_name='filename_extensions_set', null=True, to='courses.FilenameExtension', blank=True), preserve_default=True, ), migrations.AddField( model_name='course', name='group_with_extern', - field=models.ForeignKey(related_name=b'course_with_extern', blank=True, to='groups.Group', null=True, db_index=False), + field=models.ForeignKey(related_name='course_with_extern', blank=True, to='groups.Group', null=True, db_index=False, on_delete=models.DO_NOTHING), preserve_default=True, ), migrations.AddField( diff --git a/anytask/courses/migrations/0002_auto_20200328_1939.py b/anytask/courses/migrations/0002_auto_20200328_1939.py index b66f58941..f639d5edc 100644 --- a/anytask/courses/migrations/0002_auto_20200328_1939.py +++ b/anytask/courses/migrations/0002_auto_20200328_1939.py @@ -24,25 +24,25 @@ class Migration(migrations.Migration): migrations.AddField( model_name='course', name='issue_status_system', - field=models.ForeignKey(default=1, to='issues.IssueStatusSystem', db_index=False), + field=models.ForeignKey(default=1, to='issues.IssueStatusSystem', db_index=False, on_delete=models.DO_NOTHING), preserve_default=True, ), migrations.AddField( model_name='course', name='mark_system', - field=models.ForeignKey(to='courses.CourseMarkSystem', blank=True, null=True, db_index=False), + field=models.ForeignKey(to='courses.CourseMarkSystem', blank=True, null=True, db_index=False, on_delete=models.DO_NOTHING), preserve_default=True, ), migrations.AddField( model_name='course', name='teachers', - field=models.ManyToManyField(related_name=b'course_teachers_set', null=True, to=settings.AUTH_USER_MODEL, blank=True), + field=models.ManyToManyField(related_name='course_teachers_set', null=True, to=settings.AUTH_USER_MODEL, blank=True), preserve_default=True, ), migrations.AddField( model_name='course', name='year', - field=models.ForeignKey(default=2020, to='years.Year'), + field=models.ForeignKey(default=2020, to='years.Year', on_delete=models.DO_NOTHING), preserve_default=True, ), ] diff --git a/anytask/courses/migrations/0006_auto_20210319_2321.py b/anytask/courses/migrations/0006_auto_20210319_2321.py index 9c1a1a7c0..30187e4c6 100644 --- a/anytask/courses/migrations/0006_auto_20210319_2321.py +++ b/anytask/courses/migrations/0006_auto_20210319_2321.py @@ -6,6 +6,7 @@ class Migration(migrations.Migration): + atomic = False dependencies = [ ('courses', '0005_auto_20210319_2320'), diff --git a/anytask/courses/models.py b/anytask/courses/models.py index 25f098d40..7ffb9db0a 100644 --- a/anytask/courses/models.py +++ b/anytask/courses/models.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -from django.core.urlresolvers import reverse +from django.urls import reverse from django.db import models from django.utils import timezone from django.contrib.auth.models import User @@ -95,7 +95,14 @@ class Course(models.Model): information = models.TextField(db_index=False, null=True, blank=True) - year = models.ForeignKey(Year, db_index=True, null=False, blank=False, default=timezone.now().year) + year = models.ForeignKey( + Year, + db_index=True, + null=False, + blank=False, + default=timezone.now().year, + on_delete=models.DO_NOTHING, + ) is_active = models.BooleanField(db_index=True, null=False, blank=False, default=False) @@ -126,10 +133,21 @@ class Course(models.Model): can_be_chosen_by_extern = models.BooleanField(db_index=False, null=False, blank=False, default=False) group_with_extern = models.ForeignKey( - Group, related_name="course_with_extern", db_index=False, null=True, blank=True + Group, + related_name="course_with_extern", + db_index=False, + null=True, + blank=True, + on_delete=models.DO_NOTHING, ) - mark_system = models.ForeignKey(CourseMarkSystem, db_index=False, null=True, blank=True) + mark_system = models.ForeignKey( + CourseMarkSystem, + db_index=False, + null=True, + blank=True, + on_delete=models.DO_NOTHING, + ) show_accepted_after_contest_ok = models.BooleanField(db_index=False, null=False, blank=False, default=False) default_accepted_after_contest_ok = models.BooleanField(db_index=False, null=False, blank=False, default=False) @@ -139,7 +157,14 @@ class Course(models.Model): default_task_send_to_users = models.BooleanField(db_index=False, null=False, blank=False, default=False) - issue_status_system = models.ForeignKey(IssueStatusSystem, db_index=False, null=False, blank=False, default=1) + issue_status_system = models.ForeignKey( + IssueStatusSystem, + db_index=False, + null=False, + blank=False, + default=1, + on_delete=models.DO_NOTHING, + ) is_python_task = models.BooleanField(db_index=False, null=False, blank=False, default=False) max_students_per_task = models.IntegerField(null=False, blank=False, default=0) @@ -162,7 +187,7 @@ def get_absolute_url(self): return reverse('courses.views.course_page', args=[str(self.id)]) def user_can_edit_course(self, user): - if user.is_anonymous(): + if user.is_anonymous: return False if user.is_superuser: return True @@ -183,7 +208,7 @@ def get_user_group(self, user): return None def user_is_attended(self, user): - if user.is_anonymous(): + if user.is_anonymous: return False if self.user_is_teacher(user): @@ -195,7 +220,7 @@ def user_is_attended(self, user): return False def user_can_see_transcript(self, user, student): - if user.is_anonymous(): + if user.is_anonymous: return not self.private and self.full_transcript if self.user_is_teacher(user): return True @@ -205,21 +230,21 @@ def user_can_see_transcript(self, user, student): return user.id == student.id def user_can_see_queue(self, user): - if user.is_anonymous(): + if user.is_anonymous: return False if self.user_is_teacher(user): return True return False def user_can_see_contest_run_id(self, user): - if user.is_anonymous(): + if user.is_anonymous: return False if self.send_to_contest_from_users and (self.user_is_teacher(user) or self.show_contest_run_id): return True return False def user_can_see_attendance_log(self, user): - if user.is_anonymous(): + if user.is_anonymous: return False return self.has_attendance_log and self.user_is_teacher(user) @@ -276,9 +301,15 @@ def is_contest_integrated(self): class DefaultTeacher(models.Model): - teacher = models.ForeignKey(User, db_index=False, null=True, blank=True) - course = models.ForeignKey(Course, db_index=True, null=False, blank=False) - group = models.ForeignKey(Group, db_index=True, null=True, blank=True) + teacher = models.ForeignKey( + User, db_index=False, null=True, blank=True, on_delete=models.DO_NOTHING + ) + course = models.ForeignKey( + Course, db_index=True, null=False, blank=False, on_delete=models.DO_NOTHING + ) + group = models.ForeignKey( + Group, db_index=True, null=True, blank=True, on_delete=models.DO_NOTHING + ) def __str__(self): return u"|".join((self.course.name, self.group.name, self.teacher.username)) @@ -288,11 +319,24 @@ class Meta: class StudentCourseMark(models.Model): - student = models.ForeignKey(User, db_index=True, null=False, blank=False) - course = models.ForeignKey(Course, db_index=False, null=False, blank=False) - mark = models.ForeignKey(MarkField, db_index=False, null=True, blank=True) + student = models.ForeignKey( + User, db_index=True, null=False, blank=False, on_delete=models.DO_NOTHING + ) + course = models.ForeignKey( + Course, db_index=False, null=False, blank=False, on_delete=models.DO_NOTHING + ) + mark = models.ForeignKey( + MarkField, db_index=False, null=True, blank=True, on_delete=models.DO_NOTHING + ) - teacher = models.ForeignKey(User, related_name='teacher_change_mark', db_index=False, null=True, blank=True) + teacher = models.ForeignKey( + User, + related_name="teacher_change_mark", + db_index=False, + null=True, + blank=True, + on_delete=models.DO_NOTHING, + ) update_time = models.DateTimeField(auto_now=True) # remove default=timezone.now def __str__(self): diff --git a/anytask/courses/tests.py b/anytask/courses/tests.py index 3c4bedd61..eb0e83d8f 100644 --- a/anytask/courses/tests.py +++ b/anytask/courses/tests.py @@ -2,7 +2,7 @@ import datetime -from django.test import TestCase +from django.test import TestCase, override_settings from django.contrib.auth.models import User from django.conf import settings from schools.models import School @@ -15,7 +15,7 @@ from tasks.management.commands.check_task_taken_expires import Command as CheckTastTakenExpiresCommand from bs4 import BeautifulSoup -from django.core.urlresolvers import reverse +from django.urls import reverse import courses.pythontask import courses.views import issues.views @@ -49,13 +49,13 @@ def test_course_create_filled(self): course.is_active = True course.year = year course.save() - course.teachers = teachers - course.groups = groups + course.teachers.set(teachers) + course.groups.set(groups) # course.contest_integrated = True # course.send_rb_and_contest_together = True # course.rb_integrated = True course.send_to_contest_from_users = True - course.filename_extensions = filename_extensions + course.filename_extensions.set(filename_extensions) course.full_transcript = False course.private = True course.can_be_chosen_by_extern = True @@ -90,6 +90,7 @@ def test_course_create_filled(self): self.assertEqual(course.mark_system, mark_system) +@override_settings(LANGUAGE_CODE='en-EN', LANGUAGES=(('en', 'English'),)) class ViewsTest(TestCase): def setUp(self): self.teacher_password = 'password1' @@ -110,18 +111,18 @@ def setUp(self): self.group = Group.objects.create(name='group_name', year=self.year) - self.group.students = [self.student] + self.group.students.set([self.student]) self.group.save() self.course = Course.objects.create(name='course_name', year=self.year) - self.course.groups = [self.group] - self.course.teachers = [self.teacher] + self.course.groups.set([self.group]) + self.course.teachers.set([self.teacher]) self.course.save() self.school = School.objects.create(name='school_name', link='school_link') - self.school.courses = [self.course] + self.school.courses.set([self.course]) self.school.save() def test_gradebook_anonymously(self): @@ -210,7 +211,7 @@ def test_gradebook_with_teacher(self): response = client.get(reverse(courses.views.gradebook, kwargs={'course_id': self.course.id})) self.assertEqual(response.status_code, 200) - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container-fluid', recursive=False) # title @@ -274,7 +275,6 @@ def test_gradebook_with_teacher(self): self.assertEqual(table_body_rows_cells[3].span.string.strip().strip('\n'), u'0') def test_queue_page_with_teacher(self): - return client = self.client # login @@ -284,8 +284,9 @@ def test_queue_page_with_teacher(self): response = client.get(reverse(courses.views.queue_page, kwargs={'course_id': self.course.id})) self.assertEqual(response.status_code, 200) - html = BeautifulSoup(response.content) - container = html.body.find('div', 'container', recursive=False) + html = BeautifulSoup(response.content, features="lxml") + container = html.body.find('div', 'container-fluid', recursive=False) + self.assertIsNotNone(container) # title self.assertEqual(html.find('title').string.strip().strip('\n'), u'course_name | 2016-2017') @@ -304,23 +305,20 @@ def test_queue_page_with_teacher(self): self.assertEqual(len(breadcrumbs), 4) self.assertEqual(breadcrumbs[0].a['href'], u'/') self.assertEqual(breadcrumbs[1].a['href'], u'/school/school_link') - self.assertEqual(breadcrumbs[1].a.string.strip().strip('\n'), u'school_name') + self.assertEqual(breadcrumbs[1].a.string.strip().strip('\n'), 'school_name') self.assertEqual(breadcrumbs[2].a['href'], u'/course/1') - self.assertEqual(breadcrumbs[2].a.string.strip().strip('\n'), u'course_name') - self.assertEqual(breadcrumbs[3].string.strip().strip('\n'), u'ochered_na_proverku') + self.assertEqual(breadcrumbs[2].a.string.strip().strip('\n'), 'course_name') + self.assertEqual(breadcrumbs[3].string.strip().strip('\n'), 'Check up queue') # filter form = container.form self.assertIsNotNone(form) form_id_responsible = form.find('select', {'id': 'id_responsible'})('option') - self.assertEqual(len(form_id_responsible), 2) - self.assertEqual(form_id_responsible[0]['value'], '') - self.assertTrue('selected' in dict(form_id_responsible[0].attrs)) - self.assertEqual(form_id_responsible[0].string.strip().strip('\n'), u'luboj') - self.assertEqual(form_id_responsible[1]['value'], '1') - self.assertFalse('selected' in form_id_responsible[1]) - self.assertEqual(form_id_responsible[1].string.strip().strip('\n'), u'teacher_name teacher_last_name') + self.assertEqual(len(form_id_responsible), 1) + self.assertEqual(form_id_responsible[0]['value'], '1') + self.assertFalse('selected' in form_id_responsible[0]) + self.assertEqual(form_id_responsible[0].string.strip().strip('\n'), u'teacher_name teacher_last_name') form_id_followers = form.find('div', {'id': 'div_id_followers'}).find('select')('option') self.assertEqual(len(form_id_followers), 1) @@ -329,10 +327,9 @@ def test_queue_page_with_teacher(self): self.assertEqual(form_id_followers[0].next.string.strip().strip('\n'), u'teacher_name teacher_last_name') # table queue - table = container('table', 'table_queue') - - table_body_rows = table[0].tbody('tr') - self.assertEqual(len(table_body_rows), 0) + table = container.find('table', {'id': 'table_queue'}) + table_body_rows = table.find_all('tr') + self.assertEqual(len(table_body_rows), 1) # 1 because of head row def test_edit_course_information_with_teacher(self): client = self.client @@ -356,7 +353,7 @@ def test_edit_course_information_with_teacher(self): self.assertEqual(response.status_code, 200) # # course information - # html = BeautifulSoup(response.content) + # html = BeautifulSoup(response.content, features="lxml") # container = html.body.find('div', 'container', recursive=False) # self.assertEqual(container.find('div', {'id': 'course-information'}).find('div', 'not-sanitize') # .string.strip().strip('\n'), 'course_information') @@ -371,7 +368,7 @@ def test_course_settings_with_teacher(self): response = client.get(reverse(courses.views.course_settings, kwargs={'course_id': self.course.id})) self.assertEqual(response.status_code, 200) - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container', recursive=False) # title @@ -391,10 +388,10 @@ def test_course_settings_with_teacher(self): self.assertEqual(len(breadcrumbs), 4) self.assertEqual(breadcrumbs[0].a['href'], u'/') self.assertEqual(breadcrumbs[1].a['href'], u'/school/school_link') - self.assertEqual(breadcrumbs[1].a.string.strip().strip('\n'), u'school_name') + self.assertEqual(breadcrumbs[1].a.string.strip().strip('\n'), 'school_name') self.assertEqual(breadcrumbs[2].a['href'], u'/course/1') - self.assertEqual(breadcrumbs[2].a.string.strip().strip('\n'), u'course_name') - self.assertEqual(breadcrumbs[3].string.strip().strip('\n'), u'nastrojki') + self.assertEqual(breadcrumbs[2].a.string.strip().strip('\n'), 'course_name') + self.assertEqual(breadcrumbs[3].string.strip().strip('\n'), 'Settings') # form form = container.form @@ -420,7 +417,7 @@ def test_course_settings_with_teacher(self): response = client.get(reverse(courses.views.course_settings, kwargs={'course_id': self.course.id})) self.assertEqual(response.status_code, 200) - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container', recursive=False) # form @@ -450,7 +447,7 @@ def test_change_visibility_hidden_tasks_with_teacher(self): response = client.get(reverse(courses.views.gradebook, kwargs={'course_id': self.course.id})) self.assertEqual(response.status_code, 200) - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container-fluid', recursive=False) # edit course button @@ -460,7 +457,7 @@ def test_change_visibility_hidden_tasks_with_teacher(self): self.assertEqual(len(btn_group), 2) self.assertEqual(btn_group[0]['id'], u'status_btn') self.assertEqual(btn_group[1]['href'], u'javascript:change_visibility_hidden_tasks(1);') - self.assertEqual(btn_group[1].string.strip().strip('\n'), u'pokazat_skrytye_zadachi') + self.assertEqual(btn_group[1].string.strip().strip('\n'), 'Show hidden assignments') # table results table = container.find('table', 'table-results') @@ -482,7 +479,7 @@ def test_change_visibility_hidden_tasks_with_teacher(self): response = client.get(reverse(courses.views.gradebook, kwargs={'course_id': self.course.id})) self.assertEqual(response.status_code, 200) - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container-fluid', recursive=False) # edit course button @@ -492,7 +489,7 @@ def test_change_visibility_hidden_tasks_with_teacher(self): self.assertEqual(len(btn_group), 2) self.assertEqual(btn_group[0]['id'], u'status_btn') self.assertEqual(btn_group[1]['href'], u'javascript:change_visibility_hidden_tasks(1);') - self.assertEqual(btn_group[1].string.strip().strip('\n'), u'ne_pokazyvat_skrytye_zadachi') + self.assertEqual(btn_group[1].string.strip().strip('\n'), 'Do not show hidden assignments') # table results table = container.find('table', 'table-results') @@ -516,7 +513,7 @@ def test_set_course_mark_with_teacher(self): mark_fields = [MarkField.objects.create(name='mark1'), MarkField.objects.create(name='mark2')] course_mark_system = CourseMarkSystem.objects.create(name='course_mark_system') - course_mark_system.marks = mark_fields + course_mark_system.marks.set(mark_fields) course_mark_system.save() self.course.mark_system = course_mark_system self.course.save() @@ -525,7 +522,7 @@ def test_set_course_mark_with_teacher(self): response = client.get(reverse(courses.views.gradebook, kwargs={'course_id': self.course.id})) self.assertEqual(response.status_code, 200) - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container-fluid', recursive=False) # table results @@ -567,7 +564,7 @@ def test_set_course_mark_with_teacher(self): response = client.get(reverse(courses.views.gradebook, kwargs={'course_id': self.course.id})) self.assertEqual(response.status_code, 200) - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container-fluid', recursive=False) # table results @@ -617,7 +614,7 @@ def test_set_task_mark_with_teacher(self): response = client.get(reverse(courses.views.gradebook, kwargs={'course_id': self.course.id})) self.assertEqual(response.status_code, 200) - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container-fluid', recursive=False) # table results @@ -660,7 +657,7 @@ def test_set_task_mark_with_teacher(self): response = client.get(reverse(courses.views.gradebook, kwargs={'course_id': self.course.id})) self.assertEqual(response.status_code, 200) - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container-fluid', recursive=False) # table results @@ -699,7 +696,7 @@ def test_gradebook_with_student(self): response = client.get(reverse(courses.views.gradebook, kwargs={'course_id': self.course.id})) self.assertEqual(response.status_code, 200) - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container-fluid', recursive=False) # title @@ -825,7 +822,7 @@ def test_set_course_mark_with_student(self): mark_fields = [MarkField.objects.create(name='mark1'), MarkField.objects.create(name='mark2')] course_mark_system = CourseMarkSystem.objects.create(name='course_mark_system') - course_mark_system.marks = mark_fields + course_mark_system.marks.set(mark_fields) course_mark_system.save() self.course.mark_system = course_mark_system self.course.save() @@ -834,7 +831,7 @@ def test_set_course_mark_with_student(self): response = client.get(reverse(courses.views.gradebook, kwargs={'course_id': self.course.id})) self.assertEqual(response.status_code, 200) - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container-fluid', recursive=False) # table results @@ -871,7 +868,7 @@ def test_set_course_mark_with_student(self): response = client.get(reverse(courses.views.gradebook, kwargs={'course_id': self.course.id})) self.assertEqual(response.status_code, 200) - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container-fluid', recursive=False) # table results @@ -903,7 +900,7 @@ def test_set_task_mark_with_student(self): response = client.get(reverse(courses.views.gradebook, kwargs={'course_id': self.course.id})) self.assertEqual(response.status_code, 200) - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container-fluid', recursive=False) # table results @@ -947,7 +944,7 @@ def test_set_task_mark_with_student(self): response = client.get(reverse(courses.views.gradebook, kwargs={'course_id': self.course.id})) self.assertEqual(response.status_code, 200) - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container-fluid', recursive=False) # table results diff --git a/anytask/groups/migrations/0001_initial.py b/anytask/groups/migrations/0001_initial.py index 1c2e0bb59..4a2ca54f1 100644 --- a/anytask/groups/migrations/0001_initial.py +++ b/anytask/groups/migrations/0001_initial.py @@ -22,7 +22,7 @@ class Migration(migrations.Migration): ('added_time', models.DateTimeField(default=django.utils.timezone.now, auto_now_add=True)), ('update_time', models.DateTimeField(default=django.utils.timezone.now, auto_now=True)), ('students', models.ManyToManyField(to=settings.AUTH_USER_MODEL, null=True, blank=True)), - ('year', models.ForeignKey(to='years.Year', blank=True)), + ('year', models.ForeignKey(to='years.Year', blank=True, on_delete=models.DO_NOTHING)), ], options={ }, diff --git a/anytask/groups/models.py b/anytask/groups/models.py index aa22b16cc..11b55c4df 100644 --- a/anytask/groups/models.py +++ b/anytask/groups/models.py @@ -5,7 +5,7 @@ class Group(models.Model): - year = models.ForeignKey(Year, db_index=True, null=False, blank=True) + year = models.ForeignKey(Year, db_index=True, null=False, blank=True, on_delete=models.DO_NOTHING) name = models.CharField(max_length=191, db_index=True, null=False, blank=True) students = models.ManyToManyField(User, blank=True) diff --git a/anytask/invites/migrations/0001_initial.py b/anytask/invites/migrations/0001_initial.py index 8c19510f7..6ae28d2bc 100644 --- a/anytask/invites/migrations/0001_initial.py +++ b/anytask/invites/migrations/0001_initial.py @@ -21,8 +21,8 @@ class Migration(migrations.Migration): ('key', models.CharField(unique=True, max_length=10, db_index=True)), ('added_time', models.DateTimeField(default=django.utils.timezone.now, auto_now_add=True)), ('update_time', models.DateTimeField(default=django.utils.timezone.now, auto_now=True)), - ('generated_by', models.ForeignKey(related_name=b'invite_generated_by', to=settings.AUTH_USER_MODEL, db_index=False)), - ('group', models.ForeignKey(to='groups.Group', blank=True, null=True, db_index=False)), + ('generated_by', models.ForeignKey(related_name='invite_generated_by', to=settings.AUTH_USER_MODEL, db_index=False, on_delete=models.DO_NOTHING)), + ('group', models.ForeignKey(to='groups.Group', blank=True, null=True, db_index=False, on_delete=models.DO_NOTHING)), ('invited_users', models.ManyToManyField(to=settings.AUTH_USER_MODEL, null=True, blank=True)), ], options={ diff --git a/anytask/invites/models.py b/anytask/invites/models.py index 9a910fc89..06b954179 100644 --- a/anytask/invites/models.py +++ b/anytask/invites/models.py @@ -14,8 +14,17 @@ class Invite(models.Model): - generated_by = models.ForeignKey(User, db_index=False, null=False, blank=False, related_name='invite_generated_by') - group = models.ForeignKey(Group, db_index=False, null=True, blank=True) + generated_by = models.ForeignKey( + User, + db_index=False, + null=False, + blank=False, + related_name="invite_generated_by", + on_delete=models.DO_NOTHING, + ) + group = models.ForeignKey( + Group, db_index=False, null=True, blank=True, on_delete=models.DO_NOTHING + ) invited_users = models.ManyToManyField(User, blank=True) key = models.CharField(max_length=10, db_index=True, null=False, blank=False, unique=True) @@ -28,7 +37,7 @@ def __str__(self): @staticmethod def user_can_generate_invite(generative_user): - if generative_user.is_anonymous(): + if generative_user.is_anonymous: return False if generative_user.is_superuser: diff --git a/anytask/issues/forms.py b/anytask/issues/forms.py index e460f07ce..5d40ad0da 100644 --- a/anytask/issues/forms.py +++ b/anytask/issues/forms.py @@ -47,15 +47,19 @@ def get_users_choise(issue, field=None): def get_responsible_form(field_name, request, issue, data=None, *args, **kwargs): class _form(DefaultForm): - responsible_name = forms.TypedChoiceField(get_users_choise(issue, 'responsible'), coerce=user_id2user, label='', - required=False) + responsible_name = forms.TypedChoiceField( + choices=get_users_choise(issue, "responsible"), + coerce=user_id2user, + label="", + required=False, + ) return _form(field_name, request, issue, data, *args, **kwargs) def get_followers_form(field_name, request, issue, data=None, *args, **kwargs): class _form(DefaultForm): - followers_names = forms.MultipleChoiceField(get_users_choise(issue, 'followers'), required=False, + followers_names = forms.MultipleChoiceField(choices=get_users_choise(issue, 'followers'), required=False, label='') # we dont need coerce function here # because add user id to m2m field is ok. @@ -64,7 +68,7 @@ class _form(DefaultForm): def get_costudents_form(field_name, request, issue, data=None, *args, **kwargs): class _form(DefaultForm): - costudents_names = forms.MultipleChoiceField(get_users_choise(issue, 'costudents'), required=False, + costudents_names = forms.MultipleChoiceField(choices=get_users_choise(issue, 'costudents'), required=False, label='') # we dont need coerce function here # because add user id to m2m field is ok. @@ -91,7 +95,7 @@ def status_id2status(status_id): def get_status_form(field_name, request, issue, data=None, *args, **kwargs): class _form(DefaultForm): lang = request.user.profile.language - status = forms.TypedChoiceField(get_status_choice(issue, lang), + status = forms.TypedChoiceField(choices=get_status_choice(issue, lang), coerce=status_id2status, label='', required=False) return _form(field_name, request, issue, data, *args, **kwargs) diff --git a/anytask/issues/migrations/0001_initial.py b/anytask/issues/migrations/0001_initial.py index d422dacdd..78b6f3a06 100644 --- a/anytask/issues/migrations/0001_initial.py +++ b/anytask/issues/migrations/0001_initial.py @@ -46,9 +46,9 @@ class Migration(migrations.Migration): ('mark', models.FloatField(default=0)), ('create_time', models.DateTimeField(default=django.utils.timezone.now, auto_now_add=True)), ('update_time', models.DateTimeField(default=django.utils.timezone.now)), - ('status', models.CharField(default=b'new', max_length=20, choices=[(b'new', 'novyj'), (b'rework', 'na_dorabotke'), (b'verification', 'na_proverke'), (b'accepted', 'zachteno'), (b'auto_verification', 'na_avtomaticheskoj_proverke'), (b'need_info', 'trebuetsja_informacija')])), + ('status', models.CharField(default='new', max_length=20, choices=[('new', 'novyj'), ('rework', 'na_dorabotke'), ('verification', 'na_proverke'), ('accepted', 'zachteno'), ('auto_verification', 'na_avtomaticheskoj_proverke'), ('need_info', 'trebuetsja_informacija')])), ('followers', models.ManyToManyField(to=settings.AUTH_USER_MODEL, null=True, blank=True)), - ('responsible', models.ForeignKey(related_name=b'responsible', blank=True, to=settings.AUTH_USER_MODEL, null=True)), + ('responsible', models.ForeignKey(related_name='responsible', blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.DO_NOTHING)), ], options={ }, @@ -61,8 +61,8 @@ class Migration(migrations.Migration): ('name', models.CharField(max_length=191)), ('title', models.CharField(max_length=191, blank=True)), ('history_message', models.CharField(max_length=191, blank=True)), - ('plugin', models.CharField(default=b'FieldDefaultPlugin', max_length=191)), - ('plugin_version', models.CharField(default=b'0.1', max_length=50)), + ('plugin', models.CharField(default='FieldDefaultPlugin', max_length=191)), + ('plugin_version', models.CharField(default='0.1', max_length=50)), ], options={ }, @@ -73,8 +73,8 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('name', models.CharField(help_text='Format is {"ru": "C\u0435\u043c\u0438\u043d\u0430\u0440", "en": "Seminar", etc.} or {"ru": "C\u0435\u043c\u0438\u043d\u0430\u0440"}', max_length=191, db_index=True, validators=[common.locale_funcs.validate_json])), - ('tag', models.CharField(blank=True, max_length=191, null=True, choices=[(b'rework', 'rework'), (b'verification', 'verification'), (b'accepted', 'accepted'), (b'seminar', 'seminar'), (b'accepted_after_deadline', 'accepted_after_deadline')])), - ('color', colorfield.fields.ColorField(default=b'#818A91', max_length=18)), + ('tag', models.CharField(blank=True, max_length=191, null=True, choices=[('rework', 'rework'), ('verification', 'verification'), ('accepted', 'accepted'), ('seminar', 'seminar'), ('accepted_after_deadline', 'accepted_after_deadline')])), + ('color', colorfield.fields.ColorField(default='#818A91', max_length=18)), ('hidden', models.BooleanField(default=False)), ], options={ @@ -97,13 +97,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='issue', name='status_field', - field=models.ForeignKey(default=1, to='issues.IssueStatus'), + field=models.ForeignKey(default=1, to='issues.IssueStatus', on_delete=models.DO_NOTHING), preserve_default=True, ), migrations.AddField( model_name='issue', name='student', - field=models.ForeignKey(related_name=b'student', to=settings.AUTH_USER_MODEL), + field=models.ForeignKey(related_name='student', to=settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING), preserve_default=True, ), ] diff --git a/anytask/issues/migrations/0002_auto_20200328_1939.py b/anytask/issues/migrations/0002_auto_20200328_1939.py index 798d8c9a9..ef81c74b2 100644 --- a/anytask/issues/migrations/0002_auto_20200328_1939.py +++ b/anytask/issues/migrations/0002_auto_20200328_1939.py @@ -17,7 +17,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='issue', name='task', - field=models.ForeignKey(to='tasks.Task', null=True), + field=models.ForeignKey(to='tasks.Task', null=True, on_delete=models.DO_NOTHING), preserve_default=True, ), migrations.AlterUniqueTogether( @@ -27,25 +27,25 @@ class Migration(migrations.Migration): migrations.AddField( model_name='file', name='event', - field=models.ForeignKey(to='issues.Event'), + field=models.ForeignKey(to='issues.Event', on_delete=models.DO_NOTHING), preserve_default=True, ), migrations.AddField( model_name='event', name='author', - field=models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True), + field=models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.DO_NOTHING), preserve_default=True, ), migrations.AddField( model_name='event', name='field', - field=models.ForeignKey(default=1, to='issues.IssueField'), + field=models.ForeignKey(default=1, to='issues.IssueField', on_delete=models.DO_NOTHING), preserve_default=True, ), migrations.AddField( model_name='event', name='issue', - field=models.ForeignKey(to='issues.Issue'), + field=models.ForeignKey(to='issues.Issue', on_delete=models.DO_NOTHING), preserve_default=True, ), ] diff --git a/anytask/issues/migrations/0004_initial_data_issuestatusfield.py b/anytask/issues/migrations/0004_initial_data_issuestatusfield.py index 9dafb0e95..224988c01 100644 --- a/anytask/issues/migrations/0004_initial_data_issuestatusfield.py +++ b/anytask/issues/migrations/0004_initial_data_issuestatusfield.py @@ -41,9 +41,9 @@ def forward(apps, schema_editor): # default IssueStatusSystem IssueStatusSystem(**{'name': u'Стандартная система'}).save() issue_status_system = IssueStatusSystem.objects.get(pk=1) - issue_status_system.statuses = [IssueStatus.objects.get(pk=3), + issue_status_system.statuses.set([IssueStatus.objects.get(pk=3), IssueStatus.objects.get(pk=4), - IssueStatus.objects.get(pk=5)] + IssueStatus.objects.get(pk=5)]) issue_status_system.save() diff --git a/anytask/issues/models.py b/anytask/issues/models.py index eac595660..8d417f226 100644 --- a/anytask/issues/models.py +++ b/anytask/issues/models.py @@ -8,7 +8,7 @@ from anyrb.common import update_status_review_request from django.conf import settings from django.contrib.auth.models import User -from django.core.urlresolvers import reverse +from django.urls import reverse from django.db import models from django.db.models import Q from django.dispatch import receiver @@ -39,7 +39,7 @@ def normalize_decimal(number): class File(models.Model): file = models.FileField(upload_to=get_file_path, null=True, blank=True, max_length=500) - event = models.ForeignKey('Event') + event = models.ForeignKey("Event", on_delete=models.DO_NOTHING) deleted = models.BooleanField(default=False) def filename(self): @@ -47,17 +47,33 @@ def filename(self): class Issue(models.Model): - student = models.ForeignKey(User, db_index=True, null=False, blank=False, related_name='student') + student = models.ForeignKey( + User, + db_index=True, + null=False, + blank=False, + related_name="student", + on_delete=models.DO_NOTHING, + ) costudents = models.ManyToManyField(User, blank=True, db_index=True, related_name='costudents') - task = models.ForeignKey(Task, db_index=True, null=True, blank=False) + task = models.ForeignKey( + Task, db_index=True, null=True, blank=False, on_delete=models.DO_NOTHING + ) mark = models.FloatField(db_index=False, null=False, blank=False, default=0) create_time = models.DateTimeField(auto_now_add=True) # remove default=timezone.now update_time = models.DateTimeField(default=timezone.now) - responsible = models.ForeignKey(User, db_index=True, null=True, blank=True, related_name='responsible') + responsible = models.ForeignKey( + User, + db_index=True, + null=True, + blank=True, + related_name="responsible", + on_delete=models.DO_NOTHING, + ) followers = models.ManyToManyField(User, blank=True) STATUS_NEW = 'new' @@ -81,7 +97,14 @@ class Issue(models.Model): ) status = models.CharField(max_length=20, choices=ISSUE_STATUSES, default=STATUS_NEW) - status_field = models.ForeignKey(IssueStatus, db_index=True, null=False, blank=False, default=1) + status_field = models.ForeignKey( + IssueStatus, + db_index=True, + null=False, + blank=False, + default=1, + on_delete=models.DO_NOTHING, + ) def is_status_accepted(self): return self.status_field.tag in [IssueStatus.STATUS_ACCEPTED, IssueStatus.STATUS_ACCEPTED_AFTER_DEADLINE] @@ -466,7 +489,7 @@ def set_field_followers_names(self, value): deleted_followers = [get_user_fullname(follower) for follower in set(self.followers.all()).difference(set(new_followers))] add_followers = [get_user_fullname(follower) for follower in new_followers.all()] - self.followers = value + self.followers.set(value) value = ', '.join(add_followers) + '\n' + ', '.join(deleted_followers) return delete_event, value @@ -491,7 +514,7 @@ def set_field_responsible_name(self, value): if self.responsible: new_followers.append(self.responsible) self.responsible = new_responsible - self.followers = new_followers + self.followers.set(new_followers) else: delete_event = True value = get_user_fullname(value) @@ -534,9 +557,15 @@ class Meta: class Event(models.Model): - issue = models.ForeignKey(Issue, null=False, blank=False) - author = models.ForeignKey(User, db_index=True, null=True, blank=True) - field = models.ForeignKey(IssueField, blank=False, default=1) + issue = models.ForeignKey( + Issue, null=False, blank=False, on_delete=models.DO_NOTHING + ) + author = models.ForeignKey( + User, db_index=True, null=True, blank=True, on_delete=models.DO_NOTHING + ) + field = models.ForeignKey( + IssueField, blank=False, default=1, on_delete=models.DO_NOTHING + ) value = models.TextField(max_length=2500, blank=True) timestamp = models.DateTimeField(auto_now_add=True) diff --git a/anytask/issues/tests.py b/anytask/issues/tests.py index 14665dff5..75c08f5e1 100644 --- a/anytask/issues/tests.py +++ b/anytask/issues/tests.py @@ -11,7 +11,7 @@ from django.core.management import call_command from django.conf import settings -from django.test import TestCase +from django.test import TestCase, override_settings from django.contrib.auth.models import User from django.test.testcases import SerializeMixin @@ -27,7 +27,7 @@ from mock import patch from bs4 import BeautifulSoup from datetime import datetime, timedelta -from django.core.urlresolvers import reverse +from django.urls import reverse from storages.backends.s3boto3 import S3Boto3Storage import issues.views @@ -57,7 +57,7 @@ def test_issue_create_filled(self): group = Group.objects.create(name='name_groups', year=year) course = Course.objects.create(name='course_name', year=year) - course.groups = [group] + course.groups.set([group]) course.save() task = Task.objects.create(title='task', course=course) @@ -77,7 +77,7 @@ def test_issue_create_filled(self): issue.responsible = responsible issue.status_field = status issue.save() - issue.followers = followers + issue.followers.set(followers) issue.save() issue_id = issue.id @@ -92,6 +92,7 @@ def test_issue_create_filled(self): self.assertCountEqual(issue.followers.all(), followers) +@override_settings(LANGUAGE_CODE='en-EN', LANGUAGES=(('en', 'English'),)) class ViewsTest(TestCase): def setUp(self): self.teacher_password = 'password1' @@ -112,19 +113,19 @@ def setUp(self): self.group = Group.objects.create(name='group_name', year=self.year) - self.group.students = [self.student] + self.group.students.set([self.student]) self.group.save() self.course = Course.objects.create(name='course_name', year=self.year) - self.course.groups = [self.group] - self.course.teachers = [self.teacher] - self.course.issue_fields = IssueField.objects.exclude(id=10).exclude(id=11).exclude(id=12) + self.course.groups.set([self.group]) + self.course.teachers.set([self.teacher]) + self.course.issue_fields.set(IssueField.objects.exclude(id=10).exclude(id=11).exclude(id=12)) self.course.save() self.school = School.objects.create(name='school_name', link='school_link') - self.school.courses = [self.course] + self.school.courses.set([self.course]) self.school.save() self.task = Task.objects.create(title='task_title', @@ -162,7 +163,7 @@ def test_get_or_create_with_teacher(self): self.assertEqual(response.status_code, 200, "Can't get get_or_create via teacher") self.assertEqual(len(response.redirect_chain), 1, "Must be redirect from get_or_create") - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container', recursive=False) # title @@ -198,20 +199,20 @@ def test_get_or_create_with_teacher(self): results = info('div', 'accordion2-result') forms = info('form') self.assertEqual(len(forms), 4, 'Issue field forms len is not 8') - self.assertEqual(labels[0].string.strip().strip('\n'), u'kurs:', '1st issue field label wrong') + self.assertEqual(labels[0].string.strip().strip('\n'), 'Course:', '1st issue field label wrong') self.assertEqual(results[0].a['href'], '/course/1', '1st issue field link wrong') self.assertEqual(results[0].a.string.strip().strip('\n'), 'course_name', '1st issue field link text wrong') - self.assertEqual(labels[1].string.strip().strip('\n'), u'zadacha:', '2nd issue field label wrong') + self.assertEqual(labels[1].string.strip().strip('\n'), 'Problem:', '2nd issue field label wrong') self.assertEqual(results[1].a.string.strip().strip('\n'), 'task_title', '2nd issue field text wrong') - self.assertEqual(labels[2].string.strip().strip('\n'), u'student:', '3rd issue field label wrong') + self.assertEqual(labels[2].string.strip().strip('\n'), 'Student:', '3rd issue field label wrong') self.assertEqual(results[2].a['href'], '/users/student/', '3rd issue field link wrong') self.assertEqual(results[2].a.string.strip().strip('\n'), 'student_last_name student_name', '3rd issue field link text wrong') - self.assertEqual(labels[3].a.string.strip().strip('\n'), u'proverjaushij', '4th issue field label text wrong') + self.assertEqual(labels[3].a.string.strip().strip('\n'), 'TA', '4th issue field label text wrong') self.assertEqual(labels[3].a['data-target'], u'#collapse5', '4th issue field label data-target wrong') self.assertEqual(results[3].string.strip().strip('\n'), '---', '4th issue field text wrong') self.assertEqual(forms[0].find('input', {'name': 'form_name'})['value'], @@ -224,13 +225,13 @@ def test_get_or_create_with_teacher(self): '4th issue field select option text wrong') self.assertEqual(len(forms[0]('button')), 2, '4th issue field button len is not 2') self.assertEqual(forms[0]('button')[0].string.strip().strip('\n'), - u'sohranit', + 'Save', '4th issue field 1st button wrong ') self.assertEqual(forms[0]('button')[1].string.strip().strip('\n'), - u'ja', + 'Me', '4th issue field 2st button wrong ') - self.assertEqual(labels[4].a.string.strip().strip('\n'), u'nabludateli', '5th issue field label text wrong') + self.assertEqual(labels[4].a.string.strip().strip('\n'), 'Viewers', '5th issue field label text wrong') self.assertEqual(labels[4].a['data-target'], u'#collapse6', '5th issue field label data-target wrong') self.assertEqual(results[4].string.strip().strip('\n'), '---', '5th issue field text wrong') self.assertEqual(forms[1].find('input', {'name': 'form_name'})['value'], @@ -243,13 +244,13 @@ def test_get_or_create_with_teacher(self): '5th issue field select option text wrong') self.assertEqual(len(forms[1]('button')), 2, '5th issue field button len is not 2') self.assertEqual(forms[1]('button')[0].string.strip().strip('\n'), - u'sohranit', + 'Save', '5th issue field 1st button wrong ') self.assertEqual(forms[1]('button')[1].string.strip().strip('\n'), - u'ja', + 'Me', '5th issue field 2st button wrong ') - self.assertEqual(labels[5].a.string.strip().strip('\n'), u'статус', '6th issue field label text wrong') + self.assertEqual(labels[5].a.string.strip().strip('\n'), 'Status', '6th issue field label text wrong') self.assertEqual(labels[5].a['data-target'], u'#collapse7', '6th issue field label data-target wrong') self.assertEqual(results[5].string.strip().strip('\n'), u'Новый', '6th issue field text wrong') self.assertEqual(forms[2].find('input', {'name': 'form_name'})['value'], @@ -270,12 +271,12 @@ def test_get_or_create_with_teacher(self): '6th issue field select 3st option text wrong') self.assertEqual(len(forms[2]('button')), 1, '6th issue field button len is not 1') self.assertEqual(forms[2]('button')[0].string.strip().strip('\n'), - u'sohranit', + 'Save', '6th issue field 1st button wrong ') - self.assertEqual(labels[6].a.string.strip().strip('\n'), u'ocenka', '7th issue field label text wrong') + self.assertEqual(labels[6].a.string.strip().strip('\n'), 'Mark', '7th issue field label text wrong') self.assertEqual(labels[6].a['data-target'], u'#collapse8', '7th issue field label data-target wrong') - self.assertEqual(results[6].string.strip().strip('\n'), '0 iz 10', '7th issue field text wrong') + self.assertEqual(results[6].string.strip().strip('\n'), '0 out of 10', '7th issue field text wrong') self.assertEqual(forms[3].find('input', {'name': 'form_name'})['value'], 'mark_form', '7th issue field input form_name wrong') @@ -285,13 +286,13 @@ def test_get_or_create_with_teacher(self): self.assertIsNotNone(forms[3].find('input', {'name': 'mark'}), '7th issue field mark input wrong') self.assertEqual(len(forms[3]('button')), 2, '7th issue field button len is not 2') self.assertEqual(forms[3]('button')[0].string.strip().strip('\n'), - u'sohranit', + 'Save', '7th issue field 1st button wrong ') self.assertEqual(forms[3]('button')[1].string.strip().strip('\n'), - u'Зачтено', + 'Зачтено', '7th issue field 2st button wrong ') - self.assertEqual(labels[7].string.strip().strip('\n'), u'data_sdachi:', '8th issue field label wrong') + self.assertEqual(labels[7].string.strip().strip('\n'), 'Submit due:', '8th issue field label wrong') self.assertEqual(results[7].string.strip().strip('\n'), '', '8th issue field text wrong') def test_post_responsible_name_form_send_button_with_teacher(self): @@ -310,7 +311,7 @@ def test_post_responsible_name_form_send_button_with_teacher(self): self.assertEqual(response.status_code, 200, "Can't get issue_page via teacher") self.assertEqual(len(response.redirect_chain), 1, "Must be redirect") - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container', recursive=False) # history @@ -323,7 +324,7 @@ def test_post_responsible_name_form_send_button_with_teacher(self): 'teacher_last_name teacher_name', 'Wrong comment author name') self.assertEqual(history[0].find('div', 'history-body').find('p').string.strip().strip('\n'), - u'zadachu_proveriaet teacher_last_name teacher_name', + 'Task reviewer changed: teacher_last_name teacher_name', 'Wrong comment text') # info @@ -358,7 +359,7 @@ def test_post_responsible_name_form_me_button_with_teacher(self): self.assertEqual(response.status_code, 200, "Can't get issue_page via teacher") self.assertEqual(len(response.redirect_chain), 1, "Must be redirect") - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container', recursive=False) # history @@ -371,7 +372,7 @@ def test_post_responsible_name_form_me_button_with_teacher(self): 'teacher_last_name teacher_name', 'Wrong comment author name') self.assertEqual(history[0].find('div', 'history-body').find('p').string.strip().strip('\n'), - u'zadachu_proveriaet teacher_last_name teacher_name', + 'Task reviewer changed: teacher_last_name teacher_name', 'Wrong comment text') # info @@ -406,7 +407,7 @@ def test_post_followers_names_form_send_button_with_teacher(self): self.assertEqual(response.status_code, 200, "Can't get issue_page via teacher") self.assertEqual(len(response.redirect_chain), 1, "Must be redirect") - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container', recursive=False) # history @@ -419,7 +420,7 @@ def test_post_followers_names_form_send_button_with_teacher(self): 'teacher_last_name teacher_name', 'Wrong comment author name') self.assertEqual(history[0].find('div', 'history-body').find('p').string.strip().strip('\n'), - u'nabludaiut teacher_last_name teacher_name', + 'Viewers teacher_last_name teacher_name', 'Wrong comment text') # info @@ -454,7 +455,7 @@ def test_post_followers_names_form_me_button_with_teacher(self): self.assertEqual(response.status_code, 200, "Can't get issue_page via teacher") self.assertEqual(len(response.redirect_chain), 1, "Must be redirect") - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container', recursive=False) # history @@ -467,7 +468,7 @@ def test_post_followers_names_form_me_button_with_teacher(self): 'teacher_last_name teacher_name', 'Wrong comment author name') self.assertEqual(history[0].find('div', 'history-body').find('p').string.strip().strip('\n'), - u'nabludaiut teacher_last_name teacher_name', + 'Viewers teacher_last_name teacher_name', 'Wrong comment text') # info @@ -503,7 +504,7 @@ def test_post_status_send_button_with_teacher(self): self.assertEqual(response.status_code, 200, "Can't get issue_page via teacher") self.assertEqual(len(response.redirect_chain), 1, "Must be redirect") - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container', recursive=False) # history @@ -516,7 +517,7 @@ def test_post_status_send_button_with_teacher(self): 'teacher_last_name teacher_name', 'Wrong comment author name') self.assertEqual(history[0].find('div', 'history-body').find('p').string.strip().strip('\n'), - u'status_izmenen На доработке', + 'Status updated: На доработке', 'Wrong comment text') # info @@ -539,7 +540,7 @@ def test_post_mark_form_send_button_with_teacher(self): self.assertEqual(response.status_code, 200, "Can't get issue_page via teacher") self.assertEqual(len(response.redirect_chain), 1, "Must be redirect") - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container', recursive=False) # history @@ -552,14 +553,14 @@ def test_post_mark_form_send_button_with_teacher(self): 'teacher_last_name teacher_name', 'Wrong comment author name') self.assertEqual(history[0].find('div', 'history-body').find('p').string.strip().strip('\n'), - u'ocenka_izmenena 3', + 'Grade mark changed to 3', 'Wrong comment text') # info info = container.find('div', {'id': 'accordion2'}) results = info('div', 'accordion2-result') - self.assertEqual(results[5].string.strip().strip('\n'), u'Новый', '6th issue field text wrong') - self.assertEqual(results[6].string.strip().strip('\n'), u'3 iz 10', '7th issue field text wrong') + self.assertEqual(results[5].string.strip().strip('\n'), 'Новый', '6th issue field text wrong') + self.assertEqual(results[6].string.strip().strip('\n'), '3 out of 10', '7th issue field text wrong') def test_post_mark_form_accept_button_with_teacher(self): client = self.client @@ -577,7 +578,7 @@ def test_post_mark_form_accept_button_with_teacher(self): self.assertEqual(response.status_code, 200, "Can't get issue_page via teacher") self.assertEqual(len(response.redirect_chain), 1, "Must be redirect") - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container', recursive=False) # history @@ -590,7 +591,7 @@ def test_post_mark_form_accept_button_with_teacher(self): 'teacher_last_name teacher_name', 'Wrong comment author name') self.assertEqual(history[0].find('div', 'history-body').find('p').string.strip().strip('\n'), - u'status_izmenen Зачтено', + 'Status updated: Зачтено', 'Wrong comment text') self.assertEqual(history[1].strong.a['href'], '/users/teacher/', @@ -599,14 +600,14 @@ def test_post_mark_form_accept_button_with_teacher(self): 'teacher_last_name teacher_name', 'Wrong comment author name') self.assertEqual(history[1].find('div', 'history-body').find('p').string.strip().strip('\n'), - u'ocenka_izmenena 3', + 'Grade mark changed to 3', 'Wrong comment text') # info info = container.find('div', {'id': 'accordion2'}) results = info('div', 'accordion2-result') - self.assertEqual(results[5].string.strip().strip('\n'), u'Зачтено', '6th issue field text wrong') - self.assertEqual(results[6].string.strip().strip('\n'), u'3 iz 10', '7th issue field text wrong') + self.assertEqual(results[5].string.strip().strip('\n'), 'Зачтено', '6th issue field text wrong') + self.assertEqual(results[6].string.strip().strip('\n'), '3 out of 10', '7th issue field text wrong') def test_comment_with_teacher(self): client = self.client @@ -626,7 +627,7 @@ def test_comment_with_teacher(self): self.assertEqual(response.status_code, 200, "Can't get issue_page via teacher") self.assertEqual(len(response.redirect_chain), 1, "Must be redirect") - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container', recursive=False) # history @@ -655,7 +656,7 @@ def test_get_or_create_with_student(self): self.assertEqual(response.status_code, 200, "Can't get get_or_create via teacher") self.assertEqual(len(response.redirect_chain), 1, "Must be redirect from get_or_create") - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container', recursive=False) # title @@ -690,32 +691,32 @@ def test_get_or_create_with_student(self): results = info('div', 'accordion2-result') forms = info('form') self.assertEqual(len(forms), 0, 'Issue field forms len is not 0') - self.assertEqual(labels[0].string.strip().strip('\n'), u'kurs:', '1st issue field label wrong') + self.assertEqual(labels[0].string.strip().strip('\n'), 'Course:', '1st issue field label wrong') self.assertEqual(results[0].a['href'], '/course/1', '1st issue field link wrong') self.assertEqual(results[0].a.string.strip().strip('\n'), 'course_name', '1st issue field link text wrong') - self.assertEqual(labels[1].string.strip().strip('\n'), u'zadacha:', '2nd issue field label wrong') + self.assertEqual(labels[1].string.strip().strip('\n'), 'Problem:', '2nd issue field label wrong') self.assertEqual(results[1].a.string.strip().strip('\n'), 'task_title', '2nd issue field text wrong') - self.assertEqual(labels[2].string.strip().strip('\n'), u'student:', '3rd issue field label wrong') + self.assertEqual(labels[2].string.strip().strip('\n'), 'Student:', '3rd issue field label wrong') self.assertEqual(results[2].a['href'], '/users/student/', '3rd issue field link wrong') self.assertEqual(results[2].a.string.strip().strip('\n'), 'student_last_name student_name', '3rd issue field link text wrong') - self.assertEqual(labels[3].string.strip().strip('\n'), u'proverjaushij:', '4th issue field label text wrong') + self.assertEqual(labels[3].string.strip().strip('\n'), 'TA:', '4th issue field label text wrong') self.assertEqual(results[3].string.strip().strip('\n'), '---', '4th issue field text wrong') - self.assertEqual(labels[4].string.strip().strip('\n'), u'nabludateli:', '5th issue field label text wrong') + self.assertEqual(labels[4].string.strip().strip('\n'), 'Viewers:', '5th issue field label text wrong') self.assertEqual(results[4].string.strip().strip('\n'), '---', '5th issue field text wrong') - self.assertEqual(labels[5].string.strip().strip('\n'), u'статус:', '6th issue field label text wrong') - self.assertEqual(results[5].string.strip().strip('\n'), u'Новый', '6th issue field text wrong') + self.assertEqual(labels[5].string.strip().strip('\n'), 'Status:', '6th issue field label text wrong') + self.assertEqual(results[5].string.strip().strip('\n'), 'Новый', '6th issue field text wrong') - self.assertEqual(labels[6].string.strip().strip('\n'), u'ocenka:', '7th issue field label text wrong') - self.assertEqual(results[6].string.strip().strip('\n'), '0 iz 10', '7th issue field text wrong') + self.assertEqual(labels[6].string.strip().strip('\n'), 'Mark:', '7th issue field label text wrong') + self.assertEqual(results[6].string.strip().strip('\n'), '0 out of 10', '7th issue field text wrong') - self.assertEqual(labels[7].string.strip().strip('\n'), u'data_sdachi:', '8th issue field label wrong') + self.assertEqual(labels[7].string.strip().strip('\n'), 'Submit due:', '8th issue field label wrong') self.assertEqual(results[7].string.strip().strip('\n'), '', '8th issue field text wrong') def test_comment_with_student(self): @@ -736,7 +737,7 @@ def test_comment_with_student(self): self.assertEqual(response.status_code, 200, "Can't get issue_page via student") self.assertEqual(len(response.redirect_chain), 1, "Must be redirect") - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container', recursive=False) # history @@ -753,7 +754,7 @@ def test_comment_with_student(self): 'Wrong comment text') def _extract_history_from_response(self, issue_page_response): - html = BeautifulSoup(issue_page_response.content) + html = BeautifulSoup(issue_page_response.content, features="lxml") container = html.body.find('div', 'container', recursive=False) history = container.find('ul', 'history')('li') return history @@ -804,7 +805,7 @@ def test_deadline(self): self.assertEqual(response.status_code, 200, "Can't get issue_page via teacher") self.assertEqual(len(response.redirect_chain), 1, "Must be redirect") - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container', recursive=False) # history @@ -829,7 +830,7 @@ def test_deadline(self): self.assertEqual(response.status_code, 200, "Can't get issue_page via teacher") self.assertEqual(len(response.redirect_chain), 1, "Must be redirect") - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container', recursive=False) # history @@ -855,7 +856,7 @@ def test_deadline(self): kwargs={'issue_id': issue.id})) self.assertEqual(response.status_code, 200, "Can't get issue_page via teacher") - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container', recursive=False) # history @@ -895,7 +896,7 @@ def test_upload_review_with_student(self, mock_upload_review): self.assertEqual(response.status_code, 200, "Can't get upload via student") self.assertEqual(len(response.redirect_chain), 1, "Must be redirect") - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container', recursive=False) # history @@ -913,7 +914,7 @@ def test_upload_review_with_student(self, mock_upload_review): 'Wrong comment text') comment_body = comment_body.next.next self.assertEqual(comment_body.string.strip().strip('\n'), - u'oshibka_otpravki_v_rb', + 'Sending to RB failed', 'Wrong comment text about RB') comment_body = history[0].find('div', 'history-body').find('div', 'files') self.assertEqual(comment_body.a.string.strip().strip('\n'), @@ -932,7 +933,7 @@ def test_upload_review_with_student(self, mock_upload_review): self.assertEqual(response.status_code, 200, "Can't get upload via student") self.assertEqual(len(response.redirect_chain), 1, "Must be redirect") - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container', recursive=False) # history @@ -965,7 +966,7 @@ def test_upload_review_with_student(self, mock_upload_review): kwargs={'issue_id': issue.id})) self.assertEqual(response.status_code, 200, "Can't get upload via student") - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container', recursive=False) # history @@ -979,7 +980,7 @@ def test_upload_review_with_student(self, mock_upload_review): 'Wrong comment author name') comment_body = history[2].find('div', 'history-body').strong self.assertEqual(comment_body.next.string.strip().strip('\n'), - u'novyj_kommentarij', + 'New comment', 'Wrong comment text') self.assertEqual(comment_body.a.string.strip().strip('\n'), u'Review request 1', @@ -994,7 +995,9 @@ def test_attached_file_in_s3(self): self.task.rb_integrated = False self.task.save() - call_command('s3migrate_issue_attachments', '--execute', '--do-rewrite-url') + stdout = StringIO() + stderr = StringIO() + call_command('s3migrate_issue_attachments', '--execute', '--do-rewrite-url', stdout=stdout, stderr=stderr) # login self.assertTrue(client.login(username=self.student.username, password=self.student_password), @@ -1010,7 +1013,7 @@ def test_attached_file_in_s3(self): self.assertEqual(response.status_code, 200, "Can't get upload via student") self.assertEqual(len(response.redirect_chain), 1, "Must be redirect") - html = BeautifulSoup(response.content) + html = BeautifulSoup(response.content, features="lxml") container = html.body.find('div', 'container', recursive=False) # history @@ -1056,19 +1059,19 @@ def setUp(self): self.group = Group.objects.create(name='group_name', year=self.year) - self.group.students = [self.student] + self.group.students.set([self.student]) self.group.save() self.course = Course.objects.create(name='course_name', year=self.year) - self.course.groups = [self.group] - self.course.teachers = [self.teacher] - self.course.issue_fields = IssueField.objects.exclude(id=10).exclude(id=11) + self.course.groups.set([self.group]) + self.course.teachers.set([self.teacher]) + self.course.issue_fields.set(IssueField.objects.exclude(id=10).exclude(id=11)) self.course.save() self.school = School.objects.create(name='school_name', link='school_link') - self.school.courses = [self.course] + self.school.courses.set([self.course]) self.school.save() self.task = Task.objects.create(title='task_title', @@ -1198,21 +1201,26 @@ def test_rewrite_url_only_existing_many_refs(self): out = StringIO() err = StringIO() call_command('s3migrate_issue_attachments', '--execute', '--rewrite-only-existing', stdout=out, stderr=err) + cmd_out = out.getvalue() + expected_stdout = [] for file in [file_first, file_second]: expected_s3_path = S3OverlayStorage.append_s3_prefix(file.file.name) - expected_stdout = ('''Note: destination already exists: {}\n''' - '''Note: updating model: {}, {}\n''' - '''Note: updated model: {}, {}\n''' - ).format( - expected_s3_path, file, expected_s3_path, file, expected_s3_path) - expected_stdout = (expected_stdout * 2).strip() + expected_stdout.append( + """Note: destination already exists: {}\n""" + """Note: updating model: {}, {}\n""" + """Note: updated model: {}, {}\n""".format( + expected_s3_path, file, expected_s3_path, file, expected_s3_path + ) + ) + file = File.objects.get(pk=file.pk) - cmd_out = out.getvalue().strip() - self.assertEqual(expected_stdout, cmd_out) self.assertTrue(S3OverlayStorage.is_s3_stored(file.file.name)) self.assertTrue(self.s3_storage.exists(file.file.name)) + expected_stdout = "".join(expected_stdout) + self.assertEqual(expected_stdout, cmd_out) + def test_upload_archives(self): for ext in anyrb.unpacker.get_supported_extensions(): fail_msg = 'Failed for extension: {}'.format(ext) diff --git a/anytask/issues/views.py b/anytask/issues/views.py index 2a8e1c1c2..642589d48 100644 --- a/anytask/issues/views.py +++ b/anytask/issues/views.py @@ -7,7 +7,7 @@ from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User from django.core.exceptions import PermissionDenied -from django.core.urlresolvers import reverse +from django.urls import reverse from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect from django.shortcuts import render, get_object_or_404, redirect from django.utils.translation import ugettext as _ diff --git a/anytask/lessons/migrations/0001_initial.py b/anytask/lessons/migrations/0001_initial.py index af1a46b2b..e956254d9 100644 --- a/anytask/lessons/migrations/0001_initial.py +++ b/anytask/lessons/migrations/0001_initial.py @@ -19,18 +19,18 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('title', models.CharField(max_length=100, null=True, blank=True)), - ('description', models.TextField(default=b'', null=True, blank=True)), + ('description', models.TextField(default='', null=True, blank=True)), ('date_starttime', models.DateTimeField(default=None, null=True)), ('date_endtime', models.DateTimeField(default=None, null=True)), ('schedule_id', models.CharField(db_index=True, max_length=100, null=True, blank=True)), ('position', models.IntegerField(db_index=True, null=True, blank=True)), - ('period', models.CharField(default=b'Once', max_length=128, choices=[(b'Once', 'odin_raz'), (b'Weekly', 'ezhenedelno')])), + ('period', models.CharField(default='Once', max_length=128, choices=[('Once', 'odin_raz'), ('Weekly', 'ezhenedelno')])), ('date_end', models.DateTimeField(default=None, null=True)), ('days', models.CharField(db_index=True, max_length=100, null=True, blank=True)), - ('course', models.ForeignKey(to='courses.Course')), - ('group', models.ForeignKey(to='groups.Group')), + ('course', models.ForeignKey(to='courses.Course', on_delete=models.DO_NOTHING)), + ('group', models.ForeignKey(to='groups.Group', on_delete=models.DO_NOTHING)), ('not_visited_students', models.ManyToManyField(to=settings.AUTH_USER_MODEL, null=True, blank=True)), - ('updated_by', models.ForeignKey(related_name=b'authors', blank=True, to=settings.AUTH_USER_MODEL, null=True, db_index=False)), + ('updated_by', models.ForeignKey(related_name='authors', blank=True, to=settings.AUTH_USER_MODEL, null=True, db_index=False, on_delete=models.DO_NOTHING)), ], options={ }, diff --git a/anytask/lessons/models.py b/anytask/lessons/models.py index 1c8b73fd3..0453e7a0a 100644 --- a/anytask/lessons/models.py +++ b/anytask/lessons/models.py @@ -13,10 +13,17 @@ class Lesson(models.Model): description = models.TextField(null=True, blank=True, default='') date_starttime = models.DateTimeField(auto_now=False, null=True, default=None) date_endtime = models.DateTimeField(auto_now=False, null=True, default=None) - course = models.ForeignKey(Course, db_index=True, null=False, blank=False) - group = models.ForeignKey(Group, null=False, blank=False) + course = models.ForeignKey(Course, db_index=True, null=False, blank=False, on_delete=models.DO_NOTHING) + group = models.ForeignKey(Group, null=False, blank=False, on_delete=models.DO_NOTHING) not_visited_students = models.ManyToManyField(User, blank=True) - updated_by = models.ForeignKey(User, db_index=False, null=True, blank=True, related_name='authors') + updated_by = models.ForeignKey( + User, + db_index=False, + null=True, + blank=True, + related_name="authors", + on_delete=models.DO_NOTHING, + ) schedule_id = models.CharField(max_length=100, db_index=True, null=True, blank=True) position = models.IntegerField(db_index=True, null=True, blank=True) diff --git a/anytask/mail/migrations/0001_initial.py b/anytask/mail/migrations/0001_initial.py index ba8051436..efecd1e93 100644 --- a/anytask/mail/migrations/0001_initial.py +++ b/anytask/mail/migrations/0001_initial.py @@ -24,7 +24,7 @@ class Migration(migrations.Migration): ('hidden_copy', models.BooleanField(default=False)), ('variable', models.BooleanField(default=False)), ('create_time', models.DateTimeField(default=django.utils.timezone.now, auto_now_add=True)), - ('recipients', models.ManyToManyField(related_name=b'recipients+', to=settings.AUTH_USER_MODEL)), + ('recipients', models.ManyToManyField(related_name='recipients+', to=settings.AUTH_USER_MODEL)), ('recipients_course', models.ManyToManyField(to='courses.Course', null=True, blank=True)), ('recipients_group', models.ManyToManyField(to='groups.Group', null=True, blank=True)), ], diff --git a/anytask/mail/migrations/0002_auto_20200328_1939.py b/anytask/mail/migrations/0002_auto_20200328_1939.py index 1e7fd6f20..847868180 100644 --- a/anytask/mail/migrations/0002_auto_20200328_1939.py +++ b/anytask/mail/migrations/0002_auto_20200328_1939.py @@ -23,13 +23,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='message', name='recipients_user', - field=models.ManyToManyField(related_name=b'recipients_user+', null=True, to=settings.AUTH_USER_MODEL, blank=True), + field=models.ManyToManyField(related_name='recipients_user+', null=True, to=settings.AUTH_USER_MODEL, blank=True), preserve_default=True, ), migrations.AddField( model_name='message', name='sender', - field=models.ForeignKey(related_name=b'sender+', to=settings.AUTH_USER_MODEL, db_index=False), + field=models.ForeignKey(related_name='sender+', to=settings.AUTH_USER_MODEL, db_index=False, on_delete=models.DO_NOTHING), preserve_default=True, ), ] diff --git a/anytask/mail/models.py b/anytask/mail/models.py index 4ca0a1109..c7607522c 100644 --- a/anytask/mail/models.py +++ b/anytask/mail/models.py @@ -11,7 +11,14 @@ class Message(models.Model): - sender = models.ForeignKey(User, db_index=False, null=False, blank=False, related_name='sender+') + sender = models.ForeignKey( + User, + db_index=False, + null=False, + blank=False, + related_name="sender+", + on_delete=models.DO_NOTHING, + ) recipients = models.ManyToManyField(User, db_index=False, blank=False, related_name='recipients+') recipients_user = models.ManyToManyField(User, db_index=False, blank=True, related_name='recipients_user+') diff --git a/anytask/mail/tests.py b/anytask/mail/tests.py index 42c59142a..1588362d2 100644 --- a/anytask/mail/tests.py +++ b/anytask/mail/tests.py @@ -7,7 +7,7 @@ from groups.models import Group from years.models import Year -from django.core.urlresolvers import reverse +from django.urls import reverse from mail.views import format_date from pytz import timezone as timezone_pytz import json @@ -42,10 +42,10 @@ def test_message_create_filled(self): message.save() - message.recipients = self.recipients - message.recipients_user = self.recipients - message.recipients_course = self.recipients_course - message.recipients_group = self.recipients_group + message.recipients.set(self.recipients) + message.recipients_user.set(self.recipients) + message.recipients_course.set(self.recipients_course) + message.recipients_group.set(self.recipients_group) message_id = message.id @@ -85,14 +85,14 @@ def setUp(self): self.recipients_group = [Group.objects.create(name='group1_name', year=self.year)] - self.recipients_group[0].students = [self.user_in_group] + self.recipients_group[0].students.set([self.user_in_group]) self.recipients_course = [Course.objects.create(name='course_name', year=self.year)] self.group_in_course = Group.objects.create(name='group2_name', year=self.year) - self.group_in_course.students = [self.user_in_course] - self.recipients_course[0].groups = [self.group_in_course] + self.group_in_course.students.set([self.user_in_course]) + self.recipients_course[0].groups.set([self.group_in_course]) self.recipients = [self.user, self.user_in_group, self.user_in_course] @@ -175,10 +175,10 @@ def test_ajax_get_message_user(self): message.title = u"title" message.text = u"text" message.save() - message.recipients = self.recipients - message.recipients_user = self.recipients_user - message.recipients_group = self.recipients_group - message.recipients_course = self.recipients_course + message.recipients.set(self.recipients) + message.recipients_user.set(self.recipients_user) + message.recipients_group.set(self.recipients_group) + message.recipients_course.set(self.recipients_course) get_data = { u'unread_count': 0, diff --git a/anytask/mail/views.py b/anytask/mail/views.py index 3622180a2..02ba3c232 100644 --- a/anytask/mail/views.py +++ b/anytask/mail/views.py @@ -83,26 +83,26 @@ def ajax_get_mailbox(request): user_profile.unread_messages.clear() user_profile.send_notify_messages.clear() else: - user_profile.unread_messages = list( + user_profile.unread_messages.set(list( user_profile.unread_messages .exclude(id__in=datatable_data["make_read[]"]) .values_list("id", flat=True) - ) - user_profile.send_notify_messages = list( + )) + user_profile.send_notify_messages.set(list( user_profile.send_notify_messages .exclude(id__in=datatable_data["make_read[]"]) .values_list("id", flat=True) - ) + )) if "make_unread[]" in datatable_data: user_profile.unread_messages.add(*Message.objects.filter(id__in=datatable_data["make_unread[]"])) if "make_delete[]" in datatable_data: user_profile.deleted_messages.add(*Message.objects.filter(id__in=datatable_data["make_delete[]"])) if "make_undelete[]" in datatable_data: - user_profile.deleted_messages = list( + user_profile.deleted_messages.set(list( user_profile.deleted_messages .exclude(id__in=datatable_data["make_undelete[]"]) .values_list("id", flat=True) - ) + )) messages = Message.objects.none() messages_deleted = user_profile.deleted_messages.all() @@ -266,19 +266,19 @@ def ajax_send_message(request): users = data.get("new_recipients_user[]", []) if "new_recipients_preinit[]" in data: users += request.session.get('user_ids_send_mail_' + data["new_recipients_preinit[]"][0], []) - message.recipients_user = users + message.recipients_user.set(users) recipients_ids.update(message.recipients_user.values_list('id', flat=True)) group_ids = [] if "new_recipients_group[]" in data: - message.recipients_group = data["new_recipients_group[]"] + message.recipients_group.set(data["new_recipients_group[]"]) for group in Group.objects.filter(id__in=data["new_recipients_group[]"]): recipients_ids.update(group.students.exclude(id=user.id).values_list('id', flat=True)) group_ids.append(group.id) if "new_recipients_course[]" in data: - message.recipients_course = data["new_recipients_course[]"] + message.recipients_course.set(data["new_recipients_course[]"]) for course in Course.objects.filter(id__in=data["new_recipients_course[]"]): for group in course.groups.exclude(id__in=group_ids).distinct(): @@ -290,6 +290,6 @@ def ajax_send_message(request): recipients_ids.update(UserProfile.objects.filter(user_status__in=data["new_recipients_status[]"]) .values_list('user__id', flat=True)) - message.recipients = list(recipients_ids) + message.recipients.set(list(recipients_ids)) return HttpResponse("OK") diff --git a/anytask/manage.py b/anytask/manage.py index 315bc6ea6..2ea39d8ac 100755 --- a/anytask/manage.py +++ b/anytask/manage.py @@ -1,31 +1,30 @@ #!/usr/bin/env python - +"""Django's command-line utility for administrative tasks.""" import locale -import sys import os -import imp +import sys sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) +locale.setlocale(locale.LC_ALL, "") -locale.setlocale(locale.LC_ALL, '') - -try: - imp.find_module('settings') # Assumed to be in the same directory. -except ImportError: - sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. " - "It appears you've customized things.\n" - "You'll have to run django-admin.py, passing it your settings module.\n" % __file__) - sys.exit(1) +def main(): + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "anytask.settings") + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) # To skip test_site_profile_not_available from django.contrib.auth.tests.models.ProfileTestCase # see https://code.djangoproject.com/ticket/17966 -sys.modules['django.contrib.auth.tests'] = None +sys.modules["django.contrib.auth.tests"] = None -if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "anytask.settings") - - from django.core.management import execute_from_command_line - execute_from_command_line(sys.argv) +if __name__ == "__main__": + main() diff --git a/anytask/middleware/lang_middleware.py b/anytask/middleware/lang_middleware.py index d70849801..db8afa2b5 100644 --- a/anytask/middleware/lang_middleware.py +++ b/anytask/middleware/lang_middleware.py @@ -1,13 +1,14 @@ from django.http import HttpResponseBadRequest, Http404, HttpResponse from django.utils import translation from django.views.decorators.http import require_POST +from django.utils.deprecation import MiddlewareMixin COOKIE_NAME = "anytask_lang" # Recipe from https://stackoverflow.com/questions/36859854/use-cookie-only-for-user-language-instead-of-opening-session -class LanguageCookieMiddleware(object): +class LanguageCookieMiddleware(MiddlewareMixin): def process_request(self, request): lang = request.COOKIES.get(COOKIE_NAME) if not lang: diff --git a/anytask/middleware/timezone_middleware.py b/anytask/middleware/timezone_middleware.py index 0042acbe0..4ebd8cd78 100644 --- a/anytask/middleware/timezone_middleware.py +++ b/anytask/middleware/timezone_middleware.py @@ -1,11 +1,12 @@ from django.conf import settings from django.utils import timezone +from django.utils.deprecation import MiddlewareMixin -class TimezoneMiddleware(object): +class TimezoneMiddleware(MiddlewareMixin): def process_request(self, request): - if request.user.is_authenticated(): + if request.user.is_authenticated: tz = request.session.get('django_timezone', default=request.user.profile.time_zone) or settings.TIME_ZONE timezone.activate(tz) diff --git a/anytask/schools/models.py b/anytask/schools/models.py index 842673cf7..68d759208 100644 --- a/anytask/schools/models.py +++ b/anytask/schools/models.py @@ -1,5 +1,5 @@ from django.db import models -from django.core.urlresolvers import reverse +from django.urls import reverse from courses.models import Course diff --git a/anytask/settings_common.py b/anytask/settings_common.py index 3dd903dd3..f295c08dc 100644 --- a/anytask/settings_common.py +++ b/anytask/settings_common.py @@ -3,6 +3,7 @@ import os from django.contrib.messages import constants as messages + MESSAGE_TAGS = { messages.DEBUG: 'alert-secondary', messages.INFO: 'alert-info', @@ -16,25 +17,22 @@ TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [ - # insert your TEMPLATE_DIRS here - ], + 'DIRS': [os.path.join(PROJECT_PATH, 'templates')], + 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ - # Insert your TEMPLATE_CONTEXT_PROCESSORS here or use this - # list if you haven't customized them: + 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', - 'django.template.context_processors.debug', 'django.template.context_processors.i18n', 'django.template.context_processors.media', 'django.template.context_processors.static', 'django.template.context_processors.tz', 'django.contrib.messages.context_processors.messages', ], - 'loaders': [ - # insert your TEMPLATE_LOADERS here - ] + # 'loaders': [ + # # insert your TEMPLATE_LOADERS here + # ] }, }, ] @@ -120,7 +118,7 @@ # List of callables that know how to import templates from various sources. -MIDDLEWARE_CLASSES = ( +MIDDLEWARE = [ 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', @@ -131,39 +129,37 @@ 'django.middleware.locale.LocaleMiddleware', 'anytask.middleware.timezone_middleware.TimezoneMiddleware', 'anytask.middleware.lang_middleware.LanguageCookieMiddleware', -) +] ROOT_URLCONF = 'anytask.urls' -TEMPLATES = [ - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [ - # Always use forward slashes, even on Windows. - # Don't forget to use absolute paths, not relative paths. - os.path.join(PROJECT_PATH, 'templates') - ], - 'OPTIONS': { - 'context_processors': [ - # Insert your TEMPLATE_CONTEXT_PROCESSORS here or use this - # list if you haven't customized them: - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.template.context_processors.debug', - 'django.template.context_processors.i18n', - 'django.template.context_processors.media', - 'django.template.context_processors.static', - 'django.template.context_processors.tz', - 'django.contrib.messages.context_processors.messages', - ], - 'loaders': [ - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', - # 'django.template.loaders.eggs.Loader' - ] - }, - }, -] +# TEMPLATES = [ +# { +# 'BACKEND': 'django.template.backends.django.DjangoTemplates', +# 'DIRS': [ +# # Always use forward slashes, even on Windows. +# # Don't forget to use absolute paths, not relative paths. +# os.path.join(PROJECT_PATH, 'templates') +# ], +# 'OPTIONS': { +# 'context_processors': [ +# 'django.template.context_processors.request', +# 'django.contrib.auth.context_processors.auth', +# 'django.template.context_processors.debug', +# 'django.template.context_processors.i18n', +# 'django.template.context_processors.media', +# 'django.template.context_processors.static', +# 'django.template.context_processors.tz', +# 'django.contrib.messages.context_processors.messages', +# ], +# # 'loaders': [ +# # 'django.template.loaders.filesystem.Loader', +# # 'django.template.loaders.app_directories.Loader', +# # # 'django.template.loaders.eggs.Loader' +# # ] +# }, +# }, +# ] INSTALLED_APPS = ( 'django.contrib.auth', @@ -190,7 +186,6 @@ 'issues', 'anyrb', 'django_extensions', - 'django_bootstrap_breadcrumbs', 'filemanager', 'schools', 'jfu', @@ -204,6 +199,7 @@ 'lessons', 'api', 'django_premailer', + 'django_bootstrap_breadcrumbs', ) AUTH_PROFILE_MODULE = "users.UserProfile" diff --git a/anytask/settings_docker.py b/anytask/settings_docker.py index 681ead22d..7fda90c02 100644 --- a/anytask/settings_docker.py +++ b/anytask/settings_docker.py @@ -82,10 +82,11 @@ COURSES_WITH_CONTEST_MARKS = [11, 10, 65, 85, 93, 82, 79, 80, 104, 137, 160, 155, 237, 214, 202, 294, 307, 277] -ALL_PAGES_MESSAGE = "" -#ALL_PAGES_MESSAGE = """