BIG UPD: migrate to python 3.10 and django 3.2
All checks were successful
buildbot/tox Build done.

This commit is contained in:
2022-06-07 16:07:08 +02:00
parent edd4050935
commit 8610e2a557
36 changed files with 192 additions and 91 deletions

1
.gitignore vendored
View File

@@ -4,6 +4,7 @@
/env/
/tmp/
/.eggs/
*.pyc
*.mo
/.coverage

View File

@@ -10,7 +10,7 @@ disable=missing-docstring,
missing-module-docstring,
missing-class-docstring,
missing-function-docstring,
useless-object-inheritance,
consider-using-f-string,
[BASIC]
@@ -18,10 +18,17 @@ good-names=_,
c,
d,
e,
f,
i,
j,
k,
r,
t,
[FORMAT]
max-line-length=120
[SIMILARITIES]
ignore-comments=no

View File

@@ -1,6 +1,6 @@
REQUIREMENTS
============
- Python 2.7
- Python 3
- Python package virtualenv (in most cases this is distributed or installed together with python)
- Django (will be installed automatically)
- Several additional django related python packages (will be installed automatically)

View File

@@ -9,7 +9,6 @@ logger = logging.getLogger(__name__)
class LoginForm(auth_forms.AuthenticationForm):
username = auth_forms.UsernameField(
max_length=254,
label=_(u'E-Mail-Adresse'),
widget=forms.TextInput(attrs={'autofocus': True}),
)
@@ -64,7 +63,6 @@ class SetPasswordForm(forms.Form):
class CreateAndSendPasswordForm(forms.Form):
username = auth_forms.UsernameField(
max_length=254,
label=_(u'E-Mail-Adresse'),
widget=forms.TextInput(attrs={'autofocus': True}),
)

View File

@@ -3,7 +3,7 @@ from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
class SeleniumAuthMixin(object):
class SeleniumAuthMixin:
def login(self, driver, username, password):
driver.get(self.complete_url(reverse('dav_auth:login')))
username_field = self.wait_on_presence(driver, (By.ID, 'id_username'))

View File

@@ -11,7 +11,6 @@ from ..forms import LoginForm, SetPasswordForm, CreateAndSendPasswordForm
TEST_USERNAME = 'root@localhost'
TEST_PASSWORD = u'me||ön 21ABll'
TEST_EMAIL = TEST_USERNAME
USERNAME_MAX_LENGTH = 254
class LoginFormTestCase(FormsTestCase):
@@ -24,11 +23,6 @@ class LoginFormTestCase(FormsTestCase):
model = get_user_model()
self.user = model.objects.create_user(username=TEST_USERNAME, password=TEST_PASSWORD, email=TEST_EMAIL)
def test_length(self):
form = self.form_class()
self.assertEqual(form.fields['username'].max_length, USERNAME_MAX_LENGTH)
self.assertEqual(form.fields['password'].max_length, None)
def test_labels(self):
form = self.form_class()
self.assertEqual(form.fields['username'].label, ugettext(u'E-Mail-Adresse'))
@@ -53,13 +47,6 @@ class LoginFormTestCase(FormsTestCase):
]
super(LoginFormTestCase, self).test_invalid_data(data_sets=data_sets)
def test_username_too_long(self):
data_sets = [
FormDataSet({'username': 'u' * USERNAME_MAX_LENGTH + 'u', 'password': self.test_password},
expected_errors=[('username', 'max_length')]),
]
super(LoginFormTestCase, self).test_invalid_data(data_sets=data_sets)
def test_invalid_username(self):
data_sets = [
FormDataSet({'username': self.test_username[::-1], 'password': self.test_password},
@@ -202,13 +189,8 @@ class CreateAndSendPasswordFormTestCase(FormsTestCase):
)
invalid_data_sets = (
FormDataSet({'username': ''}, expected_errors=[('username', 'required')]),
FormDataSet({'username': 'u' * USERNAME_MAX_LENGTH + 'u'}, expected_errors=[('username', 'max_length')]),
)
def test_length(self):
form = self.form_class()
self.assertEqual(form.fields['username'].max_length, USERNAME_MAX_LENGTH)
def test_labels(self):
form = self.form_class()
self.assertEqual(form.fields['username'].label, ugettext(u'E-Mail-Adresse'))

View File

@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
from unittest import skip
from django.contrib.auth import get_user_model
from django.test import TestCase, Client
class ModelsTestCase(TestCase):
@skip('I do not know, why the user.save() does not raise an exception')
def test_username_length(self):
max_length = 150 # Hard coded in django.contrib.auth.models.AbstractUser
username_ok = 'u' * max_length
username_too_long = username_ok + 'u'
user_model = get_user_model()
user = user_model(username=username_too_long,
first_name='A',
last_name='User',
email='a.user@example.com')
user.save()
def test_last_login(self):
user_model = get_user_model()
user = user_model(username='auser',
first_name='A',
last_name='User',
email='a.user@example.com')
user.save()
self.assertEqual(user.last_login, None)
c = Client()
c.force_login(user)
self.assertNotEqual(user.last_login, None)

View File

@@ -40,7 +40,7 @@ class TestCase(SeleniumAuthMixin, SeleniumTestCase):
c = self.selenium
c.get(self.complete_url('/'))
try:
link = c.find_element_by_css_selector('#login-widget a')
c.find_element_by_css_selector('#login-widget a')
except NoSuchElementException as e: # pragma: no cover
self.fail(str(e))
@@ -65,4 +65,3 @@ class TestCase(SeleniumAuthMixin, SeleniumTestCase):
self.assertEqual(field.get_attribute('required'), 'true')
field = c.find_element_by_id('id_send_password_mail')
self.assertEqual(field.get_attribute('required'), None)

View File

@@ -35,7 +35,7 @@ class ViewsTestCase(TestCase):
cls.set_password_message = ugettext(u'Passwort gespeichert.')
def setUp(self):
super(TestCase, self).setUp()
super(ViewsTestCase, self).setUp()
# Need a test user
self.test_username = TEST_USERNAME
self.test_password = TEST_PASSWORD

View File

@@ -2,6 +2,8 @@ from django.conf.urls import url
from . import views
app_name = 'dav_auth'
urlpatterns = [
url(r'^login$', views.LoginView.as_view(), name='login'),
url(r'^logout$', views.LogoutView.as_view(), name='logout'),

View File

@@ -4,7 +4,7 @@ from django.core.exceptions import ValidationError
from django.utils.translation import gettext as _
class PasswordScoreValidator(object):
class PasswordScoreValidator:
def _get_score(self, password, user=None):
score = 0
char_counters = {}
@@ -98,7 +98,7 @@ class PasswordScoreValidator(object):
return text
class CustomWordlistPasswordValidator(object):
class CustomWordlistPasswordValidator:
context = 'the Sektion Karlsruhe'
words = (
u'dav',
@@ -132,7 +132,7 @@ class CustomWordlistPasswordValidator(object):
return text
class CharacterClassPasswordValidator(object):
class CharacterClassPasswordValidator:
def _is_enough_lower(self, password):
lower = re.sub(r'[^a-z]', '', password)
if len(lower) < self.minimum_lower:

View File

@@ -3,8 +3,8 @@
# Additional settings for django-dav
#
BASE_VAR_DIR = os.path.join(BASE_DIR, 'var')
BASE_SHARE_DIR = os.path.join(BASE_DIR, 'common')
BASE_VAR_DIR = BASE_DIR / 'var'
BASE_SHARE_DIR = BASE_DIR / 'common'
# Get modules config
from dav_base.config.modules import ModuleConfig
@@ -14,7 +14,7 @@ INSTALLED_APPS += [
'bootstrap3',
'datetimewidget',
'django_countries',
'django_extensions',
# 'django_extensions',
# Our main app
'dav_base',
]
@@ -45,11 +45,11 @@ TEMPLATES += [
# Add our local templates directory to the template engine configurations.
for config in TEMPLATES:
config['DIRS'].append(os.path.join(BASE_SHARE_DIR, 'templates'))
config['DIRS'].append(BASE_SHARE_DIR / 'templates')
DATABASES['default'] = {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_VAR_DIR, 'db', 'devel.sqlite3'),
'NAME': BASE_VAR_DIR / 'db' / 'devel.sqlite3',
}
AUTH_PASSWORD_VALIDATORS = [
@@ -76,7 +76,7 @@ AUTH_PASSWORD_VALIDATORS = [
},
]
STATIC_ROOT = os.path.join(BASE_VAR_DIR, 'www', 'static')
STATIC_ROOT = BASE_VAR_DIR / 'www' / 'static'
LANGUAGE_CODE = 'de'
TIME_ZONE = 'Europe/Berlin'
@@ -130,20 +130,20 @@ LOGGING = {
'default_log': {
'level': 'INFO',
'class': 'logging.FileHandler',
'filename': os.path.join(BASE_VAR_DIR, 'log', 'default.log'),
'filename': BASE_VAR_DIR / 'log' / 'default.log',
'formatter': 'default',
},
'error_log': {
'level': 'ERROR',
'class': 'logging.FileHandler',
'filename': os.path.join(BASE_VAR_DIR, 'log', 'error.log'),
'filename': BASE_VAR_DIR / 'log' / 'error.log',
'formatter': 'default',
},
'debug_log': {
'level': 'DEBUG',
'filters': ['require_debug_true'],
'class': 'logging.FileHandler',
'filename': os.path.join(BASE_VAR_DIR, 'log', 'debug.log'),
'filename': BASE_VAR_DIR / 'log' / 'debug.log',
'formatter': 'verbose',
},
},

View File

@@ -1,6 +1,6 @@
{# This template is used by software tests #}{% load dav_base %}
--{% include_if_exist './includes/include_missing.html' %}--
--{% include_if_exist './includes/include_missing.html' default 'dav_base/tests/includes/include_default.html' %}--
--{% include_if_exist './includes/include_missing3.html' %}--
--{% include_if_exist './includes/include_missing4.html' default 'dav_base/tests/includes/include_default.html' %}--
--{% include_if_exist './includes/include_optional.html' %}--
--{% include_if_exist './includes/include_optional.html' default 'dav_base/tests/includes/include_default.html' %}--
--{% include_if_exist './includes/include_with_missing_include.html' %}--

View File

@@ -1,2 +1,2 @@
{# This template is used by software tests #}{% load dav_base %}
--{% include_if_exist './includes/include_missing.html' default './includes/include_missing.html' %}--
--{% include_if_exist './includes/include_missing1.html' default './includes/include_missing2.html' %}--

View File

@@ -1,2 +1,2 @@
{# This template is used by software tests #}{% load dav_base %}
--{% include './include_missing.html' %}--
--{% include './include_missing5.html' %}--

View File

@@ -16,7 +16,7 @@ def do_include_if_exist(parser, token):
"""
bits = token.split_contents()
if len(bits) < 2:
raise template.TemplateSyntaxError("%r tag takes at least two arguments:"
raise template.TemplateSyntaxError("%r tag takes at least one argument:"
" the name of the template to be included" % bits[0])
try:
@@ -27,6 +27,10 @@ def do_include_if_exist(parser, token):
token = template.base.Token(token.token_type, ' '.join(bits))
token2 = template.base.Token(token.token_type, bits[0] + ' ' + default_template + ' '.join(bits[2:]))
default_node = template.loader_tags.do_include(parser, token2)
# TODO: I belive, this ist not the correct way to do things.
# But without setting default_node.origin here the following AttributeError will be risen within the tests:
# AttributeError: 'IncludeNode' object has no attribute 'origin'
default_node.origin = template.Origin(name='<unknown_source>', template_name=None)
except ValueError:
default_node = template.defaulttags.CommentNode()
except IndexError:

View File

@@ -28,20 +28,20 @@ class TemplateTagsTestCase(SimpleTestCase):
def test_include_if_exist(self):
template_name = 'dav_base/tests/include_if_exist.html'
template = get_template(template_name)
content = template.render()
expected_content = """
with self.settings(DEBUG=False):
template = get_template(template_name)
content = template.render()
expected_content = """
----
--DEFAULT INCLUDED HTML--
--OPTIONAL INCLUDED HTML--
--OPTIONAL INCLUDED HTML--
--
----
--
"""
self.assertEqual(content, expected_content)
self.assertEqual(content, expected_content)
def test_include_if_exist_while_debug(self):
template_name = 'dav_base/tests/include_if_exist.html'

View File

@@ -2,6 +2,8 @@ from django.conf.urls import url
from . import views
app_name = 'dav_event_office'
urlpatterns = [
url(r'^home$', views.HomeView.as_view(), name='root'),
url(r'^participants$', views.ParticipantListView.as_view(), name='participant-list'),

View File

@@ -0,0 +1,44 @@
# Generated by Django 3.2.13 on 2022-06-07 11:45
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('dav_events', '0041_auto_20210107_1220'),
]
operations = [
migrations.AlterField(
model_name='event',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='eventchange',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='eventflag',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='participant',
name='event',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='participants', to='dav_events.event'),
),
migrations.AlterField(
model_name='participant',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='trashedparticipant',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View File

@@ -10,10 +10,9 @@ import unicodedata
from babel.dates import format_date
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.db import models
from django.template.loader import get_template
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import get_language, ugettext_lazy as _
from django_countries.fields import Country, CountryField
@@ -27,7 +26,6 @@ from .eventchange import EventChange
logger = logging.getLogger(__name__)
@python_2_unicode_compatible
class Event(models.Model):
# Metadata
owner = models.ForeignKey(settings.AUTH_USER_MODEL,

View File

@@ -3,7 +3,6 @@ from __future__ import unicode_literals
from django.conf import settings
from django.db import models
from django.utils import timezone
from django.utils.encoding import python_2_unicode_compatible
from . import get_ghost_user, get_system_user
@@ -12,7 +11,6 @@ def get_system_user_id():
return get_system_user().id
@python_2_unicode_compatible
class EventChange(models.Model):
UPDATE = 'update'
RAISE_FLAG = 'set_flag'
@@ -23,7 +21,7 @@ class EventChange(models.Model):
(LOWER_FLAG, 'Lower Flag'),
)
event = models.ForeignKey('dav_events.Event', related_name='changes')
event = models.ForeignKey('dav_events.Event', related_name='changes', on_delete=models.CASCADE)
timestamp = models.DateTimeField(default=timezone.now)
user = models.ForeignKey(settings.AUTH_USER_MODEL,
default=get_system_user_id,

View File

@@ -2,7 +2,6 @@ from __future__ import unicode_literals
from django.conf import settings
from django.db import models
from django.utils import timezone
from django.utils.encoding import python_2_unicode_compatible
from . import get_ghost_user, get_system_user
@@ -11,9 +10,8 @@ def get_system_user_id():
return get_system_user().id
@python_2_unicode_compatible
class EventFlag(models.Model):
event = models.ForeignKey('dav_events.Event', related_name='flags')
event = models.ForeignKey('dav_events.Event', related_name='flags', on_delete=models.CASCADE)
status = models.ForeignKey('dav_events.EventStatus',
on_delete=models.PROTECT,
related_name='+')

View File

@@ -1,6 +1,5 @@
from __future__ import unicode_literals
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from ..validators import IdStringValidator
@@ -22,7 +21,6 @@ BOOTSTRAP_CONTEXT_CHOICES = (
)
@python_2_unicode_compatible
class EventStatus(models.Model):
code = models.CharField(primary_key=True, max_length=254, validators=[IdStringValidator])
severity = models.IntegerField(unique=True)

View File

@@ -3,10 +3,9 @@ from __future__ import unicode_literals
import logging
import uuid
from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.db import models
from django.utils import timezone
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext, ugettext_lazy as _
from .event import Event
@@ -14,7 +13,6 @@ from .event import Event
logger = logging.getLogger(__name__)
@python_2_unicode_compatible
class OneClickAction(models.Model):
COMMANDS = (
('EVENT_LIST', 'login and go to event list (user id)'),

View File

@@ -4,7 +4,6 @@ import datetime
from django.core.exceptions import ValidationError
from django.db import models
from django.utils import timezone
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from dav_base.validators import DAVNumberValidator
@@ -12,7 +11,6 @@ from dav_base.validators import DAVNumberValidator
midnight = datetime.time(00, 00, 00)
@python_2_unicode_compatible
class AbstractParticipant(models.Model):
personal_names = models.CharField(max_length=1024,
verbose_name=_('Vorname(n)'))
@@ -158,9 +156,8 @@ class AbstractParticipant(models.Model):
return timezone.make_aware(datetime.datetime.combine(purge_date, midnight))
@python_2_unicode_compatible
class Participant(AbstractParticipant):
event = models.ForeignKey('Event', related_name='participants')
event = models.ForeignKey('Event', related_name='participants', on_delete=models.PROTECT)
created_at = models.DateTimeField(auto_now_add=True)
position = models.IntegerField(verbose_name='Listennummer')

View File

@@ -1,15 +1,13 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from ..participant import AbstractParticipant
@python_2_unicode_compatible
class TrashedParticipant(AbstractParticipant):
event = models.ForeignKey('Event', related_name='trashed_participants')
event = models.ForeignKey('Event', related_name='trashed_participants', on_delete=models.CASCADE)
created_at = models.DateTimeField()
trashed_at = models.DateTimeField(auto_now_add=True)
position = models.IntegerField(verbose_name='Listennummer')

View File

@@ -2,6 +2,8 @@ from django.conf.urls import url
from . import views
app_name = 'dav_events'
urlpatterns = [
url(r'^home$', views.base.HomeView.as_view(), name='root'),
url(r'^$', views.events.EventListView.as_view(), name='list'),

View File

@@ -754,14 +754,14 @@ class EventCreateView(EventPermissionMixin, generic.FormView):
_(u'Du hast jemand anderen als Tourenleiter eingetragen.'),
_(u'Warum machst du sowas?')
))
elif owner.has_usable_password():
next_url = reverse('dav_events:list')
else:
elif owner.last_login is None:
login(self.request, owner)
next_url = reverse('dav_auth:set_password')
messages.success(self.request,
_(u'Neuen Benutzer angemeldet: %(username)s') % {'username': owner.username})
messages.warning(self.request, _(u'Bitte neues Passwort setzen!'))
else:
next_url = reverse('dav_events:list')
return HttpResponseRedirect(next_url)
def clean_session_data(self, session=None):

View File

@@ -0,0 +1,30 @@
# Generated by Django 3.2.13 on 2022-06-07 11:45
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('dav_events', '0042_auto_20220607_1345'),
('dav_registration', '0010_registration_apply_reduced_fee'),
]
operations = [
migrations.AlterField(
model_name='registration',
name='event',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='registrations', to='dav_events.event'),
),
migrations.AlterField(
model_name='registration',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='registrationstatus',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View File

@@ -6,7 +6,6 @@ from django.core.exceptions import ValidationError
from django.db import models
from django.urls import reverse
from django.utils import timezone
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from dav_base.validators import DAVNumberValidator
@@ -19,9 +18,8 @@ logger = logging.getLogger(__name__)
midnight = datetime.time(00, 00, 00)
@python_2_unicode_compatible
class Registration(models.Model):
event = models.ForeignKey(Event, related_name='registrations')
event = models.ForeignKey(Event, related_name='registrations', on_delete=models.PROTECT)
created_at = models.DateTimeField(auto_now_add=True)
personal_names = models.CharField(max_length=1024,
@@ -219,11 +217,11 @@ Anmerkung:
return timezone.make_aware(datetime.datetime.combine(purge_date, midnight))
@python_2_unicode_compatible
class RegistrationStatus(models.Model):
registration = models.OneToOneField(Registration, on_delete=models.CASCADE, related_name='status')
updated_at = models.DateTimeField(auto_now=True)
answered = models.BooleanField(_('Durch Tourleitung beantwortet'), default=False)
# TODO: NullBooleanField is marked deprecated
accepted = models.NullBooleanField(_('Zusage erteilt'))
class Meta:

View File

@@ -2,6 +2,8 @@ from django.conf.urls import url
from . import views
app_name = 'dav_registration'
urlpatterns = [
url(r'^$', views.RootView.as_view(), name='root'),
url(r'^finished', views.RegistrationSuccessView.as_view(), name='registered'),

View File

@@ -2,6 +2,8 @@ from django.conf.urls import url
from . import views
app_name = 'dav_submission'
urlpatterns = [
url(r'^$', views.UploadView.as_view(), name='root'),
url(r'^danke', views.SuccessView.as_view(), name='success'),

View File

@@ -22,12 +22,7 @@ class SetupPythonEnvironment(MyCommand):
def run(self):
python_bin = sys.executable if sys.executable else 'python'
python_ver = sys.version_info.major
if python_ver == 2:
path = os.path.join('env', 'python2')
symlink_path = os.path.join('env', 'python')
venv_module = 'virtualenv'
prompt = '(py2-dav) '
elif python_ver == 3:
if python_ver == 3:
path = os.path.join('env', 'python3')
symlink_path = os.path.join('env', 'python')
venv_module = 'venv'
@@ -97,7 +92,7 @@ class QuickSetup(MyCommand):
setup(
name='django-dav-events',
version='2.0',
version='2.1',
description='A django based web application project to organize DAV Events.',
url='https://touren.alpenverein-karlsruhe.de',
author='Jens Kleineheismann',
@@ -116,11 +111,12 @@ setup(
},
install_requires=[
'babel',
'django >= 1.11, < 2.0',
'django-extensions',
'django-bootstrap3 < 12',
'django-countries < 6',
'django-datetime-widget',
#'django >= 1.11, < 2.0',
'django >= 1.11, < 3.3',
# 'django-extensions',
'django-bootstrap3',
'django-countries',
'django-datetime-widget2',
'pytz',
'selenium',
'coverage',

5
tests/__main__.py Normal file
View File

@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
from test_suite import main
if __name__ == '__main__':
main()

View File

@@ -1,3 +1,4 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import datetime
@@ -80,3 +81,12 @@ class TestSuite(object):
def __call__(self):
sys.exit(self.run())
def main():
test_suite = TestSuite()
test_suite()
if __name__ == '__main__':
main()

View File

@@ -1,7 +1,7 @@
[tox]
envlist = py3,py2
envlist = py3
[testenv]
commands = python --version
python -m coverage run setup.py test
python -m coverage run tests/test_suite.py
coverage report --skip-covered