diff --git a/.travis.yml b/.travis.yml index 81e94e68b..74feb4ac1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,6 @@ cache: - $HOME/virtualenv/python$TRAVIS_PYTHON_VERSION/lib/python$TRAVIS_PYTHON_VERSION/site-packages/ - $HOME/virtualenv/python$TRAVIS_PYTHON_VERSION/bin/ python: - - "3.3" - "3.4" - "3.5" install: diff --git a/CHANGELOG b/CHANGELOG index d22c56df9..2a4c17f23 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -29,7 +29,7 @@ Other: - New OpenSlides logo. - New design for web interface. - Added multiple countdown support. -- Changed supported Python version to >= 3.3. +- Changed supported Python version to >= 3.4. - Used Django 1.7 as lowest requirement. - Added Django's application configuration. Refactored loading of signals and projector elements/slides. @@ -56,6 +56,7 @@ Other: - Used setup.cfg for development tools. - Removed code for Windows portable version with GUI. Used new repository for this. +- Django 1.9 is supported Translations: - Added DE and FR translations. diff --git a/README.rst b/README.rst index bc46f1f51..0fe08a848 100644 --- a/README.rst +++ b/README.rst @@ -26,7 +26,7 @@ Installation on GNU/Linux or Mac OS X 1. Check requirements - Make sure that you have installed Python Programming Language 3 (>= 3.3) + Make sure that you have installed Python Programming Language 3 (>= 3.4) on your system. You will also need the Python development headers. For example for Ubuntu run:: @@ -73,7 +73,7 @@ portable version you should observe the following install steps.* 1. Check requirements - Make sure that you have installed Python Programming Language 3 (>= 3.3) + Make sure that you have installed Python Programming Language 3 (>= 3.4) and Setuptools on your system. a. Download and run the latest `Python 3.4 32-bit MSI installer @@ -170,7 +170,7 @@ Installation and start of the development version 1. Check requirements - You need to have `Python 3 (>=3.3) `_, `Node.js + You need to have `Python 3 (>=3.4) `_, `Node.js (>=0.10) `_ and `Git `_ installed. See also step 1 in the correspondent instruction in section III. diff --git a/openslides/agenda/migrations/0006_auto_20160109_1145.py b/openslides/agenda/migrations/0006_auto_20160109_1145.py new file mode 100644 index 000000000..e8627c51d --- /dev/null +++ b/openslides/agenda/migrations/0006_auto_20160109_1145.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.1 on 2016-01-09 11:45 +from __future__ import unicode_literals + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('agenda', '0005_auto_20151210_0016'), + ] + + operations = [ + migrations.AlterField( + model_name='item', + name='content_type', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='contenttypes.ContentType'), + ), + migrations.AlterField( + model_name='item', + name='parent', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='children', to='agenda.Item'), + ), + ] diff --git a/openslides/agenda/models.py b/openslides/agenda/models.py index 047e9cfb4..4bd241d62 100644 --- a/openslides/agenda/models.py +++ b/openslides/agenda/models.py @@ -190,7 +190,12 @@ class Item(RESTModelMixin, models.Model): The intended duration for the topic. """ - parent = models.ForeignKey('self', null=True, blank=True, related_name='children') + parent = models.ForeignKey( + 'self', + on_delete=models.SET_NULL, + null=True, + blank=True, + related_name='children') """ The parent item in the agenda tree. """ @@ -200,7 +205,11 @@ class Item(RESTModelMixin, models.Model): Weight to sort the item in the agenda. """ - content_type = models.ForeignKey(ContentType, null=True, blank=True) + content_type = models.ForeignKey( + ContentType, + on_delete=models.SET_NULL, + null=True, + blank=True) """ Field for generic relation to a related object. Type of the object. """ @@ -318,12 +327,17 @@ class Speaker(RESTModelMixin, models.Model): objects = SpeakerManager() - user = models.ForeignKey(settings.AUTH_USER_MODEL) + user = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE) """ ForeinKey to the user who speaks. """ - item = models.ForeignKey(Item, related_name='speakers') + item = models.ForeignKey( + Item, + on_delete=models.CASCADE, + related_name='speakers') """ ForeinKey to the agenda item to which the user want to speak. """ diff --git a/openslides/agenda/urls.py b/openslides/agenda/urls.py index 0167c21a9..954255738 100644 --- a/openslides/agenda/urls.py +++ b/openslides/agenda/urls.py @@ -1,10 +1,9 @@ -from django.conf.urls import patterns, url +from django.conf.urls import url from . import views -urlpatterns = patterns( - '', +urlpatterns = [ url(r'^print/$', views.AgendaPDF.as_view(), name='agenda_pdf'), -) +] diff --git a/openslides/agenda/views.py b/openslides/agenda/views.py index b71c5d44f..3eaed36a2 100644 --- a/openslides/agenda/views.py +++ b/openslides/agenda/views.py @@ -1,4 +1,4 @@ -from cgi import escape +from html import escape from django.contrib.auth import get_user_model from django.db import transaction diff --git a/openslides/assignments/models.py b/openslides/assignments/models.py index 96d8f138b..2131124ea 100644 --- a/openslides/assignments/models.py +++ b/openslides/assignments/models.py @@ -1,7 +1,8 @@ +from collections import OrderedDict + from django.conf import settings from django.contrib.contenttypes.models import ContentType from django.db import models -from django.utils.datastructures import SortedDict from django.utils.translation import ugettext_lazy, ugettext_noop from openslides.agenda.models import Item, Speaker @@ -34,9 +35,11 @@ class AssignmentRelatedUser(RESTModelMixin, models.Model): assignment = models.ForeignKey( 'Assignment', - db_index=True, + on_delete=models.CASCADE, related_name='assignment_related_users') - user = models.ForeignKey(settings.AUTH_USER_MODEL, db_index=True) + user = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE) status = models.IntegerField( choices=STATUSES, default=STATUS_CANDIDATE) @@ -272,7 +275,7 @@ class Assignment(RESTModelMixin, models.Model): Returns a table represented as a list with all candidates from all related polls and their vote results. """ - vote_results_dict = SortedDict() + vote_results_dict = OrderedDict() polls = self.polls.all() if only_published: @@ -331,7 +334,10 @@ class Assignment(RESTModelMixin, models.Model): class AssignmentVote(RESTModelMixin, BaseVote): - option = models.ForeignKey('AssignmentOption', related_name='votes') + option = models.ForeignKey( + 'AssignmentOption', + on_delete=models.CASCADE, + related_name='votes') class Meta: default_permissions = () @@ -344,8 +350,13 @@ class AssignmentVote(RESTModelMixin, BaseVote): class AssignmentOption(RESTModelMixin, BaseOption): - poll = models.ForeignKey('AssignmentPoll', related_name='options') - candidate = models.ForeignKey(settings.AUTH_USER_MODEL) + poll = models.ForeignKey( + 'AssignmentPoll', + on_delete=models.CASCADE, + related_name='options') + candidate = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE) vote_class = AssignmentVote class Meta: @@ -366,7 +377,10 @@ class AssignmentPoll(RESTModelMixin, CollectDefaultVotesMixin, slide_callback_name = 'assignmentpoll' option_class = AssignmentOption - assignment = models.ForeignKey(Assignment, related_name='polls') + assignment = models.ForeignKey( + Assignment, + on_delete=models.CASCADE, + related_name='polls') yesnoabstain = models.BooleanField(default=False) description = models.CharField( max_length=79, diff --git a/openslides/assignments/urls.py b/openslides/assignments/urls.py index ff5f8d511..1352510f5 100644 --- a/openslides/assignments/urls.py +++ b/openslides/assignments/urls.py @@ -1,9 +1,8 @@ -from django.conf.urls import patterns, url +from django.conf.urls import url from . import views -urlpatterns = patterns( - '', +urlpatterns = [ url(r'^print/$', views.AssignmentPDF.as_view(), name='assignments_pdf'), @@ -15,4 +14,4 @@ urlpatterns = patterns( url(r'^poll/(?P\d+)/print/$', views.AssignmentPollPDF.as_view(), name='assignmentpoll_pdf'), -) +] diff --git a/openslides/assignments/views.py b/openslides/assignments/views.py index 3d7afe639..f0c45c325 100644 --- a/openslides/assignments/views.py +++ b/openslides/assignments/views.py @@ -1,4 +1,4 @@ -from cgi import escape +from html import escape from django.conf import settings from django.contrib.auth import get_user_model diff --git a/openslides/core/management/commands/runserver.py b/openslides/core/management/commands/runserver.py index 6760426e7..6d21cab06 100644 --- a/openslides/core/management/commands/runserver.py +++ b/openslides/core/management/commands/runserver.py @@ -1,12 +1,13 @@ import errno +import os import socket import sys from datetime import datetime -from django.core.exceptions import ImproperlyConfigured +from django.conf import settings from django.core.management.commands.runserver import Command as _Command -from django.utils import translation -from django.utils.encoding import force_text +from django.utils import six +from django.utils.encoding import force_text, get_system_encoding from openslides.utils.autoupdate import run_tornado @@ -17,40 +18,40 @@ class Command(_Command): Only the line to run tornado has changed from the django default implementation. + + The Code is from django 1.9 """ # TODO: do not start tornado when the settings says so def inner_run(self, *args, **options): - from django.conf import settings - # From the base class: - self.stdout.write("Performing system checks...\n\n") - self.validate(display_num_errors=True) - - try: - self.check_migrations() - except ImproperlyConfigured: - pass - - now = datetime.now().strftime('%B %d, %Y - %X') + # If an exception was silenced in ManagementUtility.execute in order + # to be raised in the child process, raise it now. + # OPENSLIDES: We do not use the django autoreload command + # autoreload.raise_last_exception() + # OPENSLIDES: This line is not needed by tornado + # threading = options.get('use_threading') shutdown_message = options.get('shutdown_message', '') quit_command = 'CTRL-BREAK' if sys.platform == 'win32' else 'CONTROL-C' + self.stdout.write("Performing system checks...\n\n") + self.check(display_num_errors=True) + self.check_migrations() + now = datetime.now().strftime('%B %d, %Y - %X') + if six.PY2: + now = now.decode(get_system_encoding()) + self.stdout.write(now) self.stdout.write(( - "%(started_at)s\n" "Django version %(version)s, using settings %(settings)r\n" "Starting development server at http://%(addr)s:%(port)s/\n" "Quit the server with %(quit_command)s.\n" - ) % { - "started_at": now, + ) % { "version": self.get_version(), "settings": settings.SETTINGS_MODULE, "addr": '[%s]' % self.addr if self._raw_ipv6 else self.addr, "port": self.port, "quit_command": quit_command, - }) - - translation.activate(settings.LANGUAGE_CODE) + }) try: handler = self.get_handler(*args, **options) @@ -64,15 +65,16 @@ class Command(_Command): ERRORS = { errno.EACCES: "You don't have permission to access that port.", errno.EADDRINUSE: "That port is already in use.", - errno.EADDRNOTAVAIL: "That IP address can't be assigned-to.", + errno.EADDRNOTAVAIL: "That IP address can't be assigned to.", } try: error_text = ERRORS[e.errno] except KeyError: error_text = force_text(e) - self.stderr.write("Error: %s" % error_text) - sys.exit(0) - except KeyboardInterrupt: - if shutdown_message: - self.stdout.write(shutdown_message) - sys.exit(0) + self.stderr.write("Error: %s" % error_text) + # Need to use an OS exit because sys.exit doesn't work in a thread + os._exit(1) + except KeyboardInterrupt: + if shutdown_message: + self.stdout.write(shutdown_message) + sys.exit(0) diff --git a/openslides/core/models.py b/openslides/core/models.py index a3577cd64..55683df37 100644 --- a/openslides/core/models.py +++ b/openslides/core/models.py @@ -217,6 +217,7 @@ class ChatMessage(RESTModelMixin, models.Model): user = models.ForeignKey( settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, verbose_name=ugettext_lazy('User')) class Meta: diff --git a/openslides/global_settings.py b/openslides/global_settings.py index f79a3a546..f5004597e 100644 --- a/openslides/global_settings.py +++ b/openslides/global_settings.py @@ -54,12 +54,6 @@ STATICFILES_FINDERS = ( STATICFILES_DIRS = [ os.path.join(SITE_ROOT, 'static')] -# List of callables that know how to import templates from various sources. -TEMPLATE_LOADERS = ( - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', -) - MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', diff --git a/openslides/mediafiles/migrations/0005_auto_20160109_1145.py b/openslides/mediafiles/migrations/0005_auto_20160109_1145.py new file mode 100644 index 000000000..cfe476ace --- /dev/null +++ b/openslides/mediafiles/migrations/0005_auto_20160109_1145.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.1 on 2016-01-09 11:45 +from __future__ import unicode_literals + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mediafiles', '0004_auto_20151210_0016'), + ] + + operations = [ + migrations.AlterField( + model_name='mediafile', + name='uploader', + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + verbose_name='Uploaded by'), + ), + ] diff --git a/openslides/mediafiles/models.py b/openslides/mediafiles/models.py index 3e518e0d5..f7d479759 100644 --- a/openslides/mediafiles/models.py +++ b/openslides/mediafiles/models.py @@ -21,7 +21,12 @@ class Mediafile(RESTModelMixin, models.Model): title = models.CharField(max_length=255, unique=True, blank=True, verbose_name=ugettext_lazy('Title')) """A string representing the title of the file.""" - uploader = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, verbose_name=ugettext_lazy('Uploaded by')) + uploader = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.SET_NULL, + null=True, + blank=True, + verbose_name=ugettext_lazy('Uploaded by')) """A user – the uploader of a file.""" timestamp = models.DateTimeField(auto_now_add=True) diff --git a/openslides/mediafiles/templates/search/indexes/mediafiles/mediafile_text.txt b/openslides/mediafiles/templates/search/indexes/mediafiles/mediafile_text.txt deleted file mode 100644 index 1cebe9df6..000000000 --- a/openslides/mediafiles/templates/search/indexes/mediafiles/mediafile_text.txt +++ /dev/null @@ -1 +0,0 @@ -{{ object.title }} diff --git a/openslides/mediafiles/templates/search/mediafile-results.html b/openslides/mediafiles/templates/search/mediafile-results.html deleted file mode 100644 index 2871e1e01..000000000 --- a/openslides/mediafiles/templates/search/mediafile-results.html +++ /dev/null @@ -1,10 +0,0 @@ -{% load i18n %} -{% load highlight %} - -{% if perms.mediafiles.can_see %} -
  • - {{ result.object }}
    - {% trans "File" %}
    - {% highlight result.text with request.GET.q %} -
  • -{% endif %} diff --git a/openslides/motions/migrations/0006_auto_20160109_1145.py b/openslides/motions/migrations/0006_auto_20160109_1145.py new file mode 100644 index 000000000..49363b465 --- /dev/null +++ b/openslides/motions/migrations/0006_auto_20160109_1145.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.1 on 2016-01-09 11:45 +from __future__ import unicode_literals + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('motions', '0005_auto_20151210_0019'), + ] + + operations = [ + migrations.AlterField( + model_name='motion', + name='category', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='motions.Category'), + ), + migrations.AlterField( + model_name='motion', + name='parent', + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='amendments', + to='motions.Motion'), + ), + migrations.AlterField( + model_name='motion', + name='state', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='motions.State'), + ), + migrations.AlterField( + model_name='motionlog', + name='person', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='workflow', + name='first_state', + field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='motions.State'), + ), + ] diff --git a/openslides/motions/models.py b/openslides/motions/models.py index 831dfb1ae..76fc8d261 100644 --- a/openslides/motions/models.py +++ b/openslides/motions/models.py @@ -35,9 +35,11 @@ class Motion(RESTModelMixin, models.Model): Name of the callback for the slide-system. """ - active_version = models.ForeignKey('MotionVersion', null=True, - related_name="active_version", - on_delete=models.SET_NULL) + active_version = models.ForeignKey( + 'MotionVersion', + on_delete=models.SET_NULL, + null=True, + related_name="active_version") """ Points to a specific version. @@ -46,7 +48,10 @@ class Motion(RESTModelMixin, models.Model): version. Like the sighted versions on Wikipedia. """ - state = models.ForeignKey('State', null=True) # TODO: Check whether null=True is necessary. + state = models.ForeignKey( + 'State', + on_delete=models.SET_NULL, + null=True) # TODO: Check whether null=True is necessary. """ The related state object. @@ -66,7 +71,11 @@ class Motion(RESTModelMixin, models.Model): Needed to find the next free motion identifier. """ - category = models.ForeignKey('Category', null=True, blank=True) + category = models.ForeignKey( + 'Category', + on_delete=models.SET_NULL, + null=True, + blank=True) """ ForeignKey to one category of motions. """ @@ -76,7 +85,12 @@ class Motion(RESTModelMixin, models.Model): Many to many relation to mediafile objects. """ - parent = models.ForeignKey('self', null=True, blank=True, related_name='amendments') + parent = models.ForeignKey( + 'self', + on_delete=models.SET_NULL, + null=True, + blank=True, + related_name='amendments') """ Field for amendments to reference to the motion that should be altered. @@ -557,7 +571,10 @@ class MotionVersion(RESTModelMixin, models.Model): A MotionVersion object saves some date of the motion. """ - motion = models.ForeignKey(Motion, related_name='versions') + motion = models.ForeignKey( + Motion, + on_delete=models.CASCADE, + related_name='versions') """The motion to which the version belongs.""" version_number = models.PositiveIntegerField(default=1) @@ -623,7 +640,10 @@ class Category(RESTModelMixin, models.Model): class MotionLog(RESTModelMixin, models.Model): """Save a logmessage for a motion.""" - motion = models.ForeignKey(Motion, related_name='log_messages') + motion = models.ForeignKey( + Motion, + on_delete=models.CASCADE, + related_name='log_messages') """The motion to witch the object belongs.""" message_list = JSONField() @@ -631,7 +651,10 @@ class MotionLog(RESTModelMixin, models.Model): The log message. It should be a list of strings in English. """ - person = models.ForeignKey(settings.AUTH_USER_MODEL, null=True) + person = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.SET_NULL, + null=True) """A user object, who created the log message. Optional.""" time = models.DateTimeField(auto_now=True) @@ -665,7 +688,9 @@ class MotionVote(RESTModelMixin, BaseVote): There should allways be three MotionVote objects for each poll, one for 'yes', 'no', and 'abstain'.""" - option = models.ForeignKey('MotionOption') + option = models.ForeignKey( + 'MotionOption', + on_delete=models.CASCADE) """The option object, to witch the vote belongs.""" class Meta: @@ -683,7 +708,9 @@ class MotionOption(RESTModelMixin, BaseOption): There should be one MotionOption object for each poll.""" - poll = models.ForeignKey('MotionPoll') + poll = models.ForeignKey( + 'MotionPoll', + on_delete=models.CASCADE) """The poll object, to witch the object belongs.""" vote_class = MotionVote @@ -705,7 +732,10 @@ class MotionPoll(RESTModelMixin, CollectDefaultVotesMixin, BasePoll): slide_callback_name = 'motionpoll' """Name of the callback for the slide-system.""" - motion = models.ForeignKey(Motion, related_name='polls') + motion = models.ForeignKey( + Motion, + on_delete=models.CASCADE, + related_name='polls') """The motion to witch the object belongs.""" option_class = MotionOption @@ -759,7 +789,10 @@ class State(RESTModelMixin, models.Model): action_word = models.CharField(max_length=255) """An alternative string to be used for a button to switch to this state.""" - workflow = models.ForeignKey('Workflow', related_name='states') + workflow = models.ForeignKey( + 'Workflow', + on_delete=models.CASCADE, + related_name='states') """A many-to-one relation to a workflow.""" next_states = models.ManyToManyField('self', symmetrical=False) @@ -850,7 +883,11 @@ class Workflow(RESTModelMixin, models.Model): name = models.CharField(max_length=255) """A string representing the workflow.""" - first_state = models.OneToOneField(State, related_name='+', null=True) + first_state = models.OneToOneField( + State, + on_delete=models.SET_NULL, + related_name='+', + null=True) """A one-to-one relation to a state, the starting point for the workflow.""" class Meta: diff --git a/openslides/motions/pdf.py b/openslides/motions/pdf.py index 524ccb12d..173f81621 100644 --- a/openslides/motions/pdf.py +++ b/openslides/motions/pdf.py @@ -1,5 +1,5 @@ import random -from cgi import escape +from html import escape from operator import attrgetter from bs4 import BeautifulSoup diff --git a/openslides/motions/urls.py b/openslides/motions/urls.py index 1fbd5a01c..c707d67c2 100644 --- a/openslides/motions/urls.py +++ b/openslides/motions/urls.py @@ -1,9 +1,8 @@ -from django.conf.urls import patterns, url +from django.conf.urls import url from . import views -urlpatterns = patterns( - '', +urlpatterns = [ url(r'^pdf/$', views.MotionPDFView.as_view(print_all_motions=True), name='motions_pdf'), @@ -11,4 +10,4 @@ urlpatterns = patterns( url(r'^(?P\d+)/pdf/$', views.MotionPDFView.as_view(print_all_motions=False), name='motions_single_pdf'), -) +] diff --git a/openslides/urls.py b/openslides/urls.py index 2f7f24404..0c48c4e2d 100644 --- a/openslides/urls.py +++ b/openslides/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls import include, patterns, url +from django.conf.urls import include, url from django.views.generic import RedirectView from openslides.utils.plugins import get_all_plugin_urlpatterns @@ -6,8 +6,7 @@ from openslides.utils.rest_api import router urlpatterns = get_all_plugin_urlpatterns() -urlpatterns += patterns( - '', +urlpatterns += [ url(r'^(?P.*[^/])$', RedirectView.as_view(url='/%(url)s/', permanent=True)), url(r'^rest/', include(router.urls)), url(r'^agenda/', include('openslides.agenda.urls')), @@ -18,4 +17,4 @@ urlpatterns += patterns( # The urls.py of the core app has to be the last entry. It contains the # main entry points for OpenSlides' browser clients. url(r'^', include('openslides.core.urls')), -) +] diff --git a/openslides/users/urls.py b/openslides/users/urls.py index 1e3742b83..a50683ef0 100644 --- a/openslides/users/urls.py +++ b/openslides/users/urls.py @@ -1,10 +1,8 @@ -from django.conf.urls import patterns, url +from django.conf.urls import url from . import views -urlpatterns = patterns( - '', - +urlpatterns = [ # Auth url(r'^login/$', views.UserLoginView.as_view(), @@ -30,4 +28,4 @@ urlpatterns = patterns( url(r'^passwords/print/$', views.UsersPasswordsPDF.as_view(), name='user_passwordspdf'), -) +] diff --git a/openslides/utils/plugins.py b/openslides/utils/plugins.py index 2d1ae47fe..f2ce9c3c9 100644 --- a/openslides/utils/plugins.py +++ b/openslides/utils/plugins.py @@ -1,9 +1,9 @@ import os import pkgutil import sys +from importlib import import_module from django.conf import settings -from django.utils.importlib import import_module from pkg_resources import iter_entry_points from openslides.utils.main import ( diff --git a/requirements.txt b/requirements.txt index 86a1d06c0..ea61ad4ad 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,6 @@ -r requirements_production.txt # Requirements for development and tests in alphabetical order -coverage==4.0a5 +coverage flake8 isort diff --git a/requirements_production.txt b/requirements_production.txt index 24f296921..65abb73fe 100644 --- a/requirements_production.txt +++ b/requirements_production.txt @@ -1,12 +1,12 @@ # Requirements for OpenSlides in production in alphabetical order -Django>=1.7.1,<1.9 -beautifulsoup4>=4.1,<4.5 -djangorestframework>=3.2.0,<3.3.0 +Django>=1.8,<1.10 +beautifulsoup4>=4.4,<4.5 +djangorestframework>=3.2.0,<3.4.0 html5lib>=0.9,<1.0 jsonfield>=0.9.19,<1.1 natsort>=3.2,<4.1 reportlab>=3.0,<3.3 roman>=2.0,<2.1 -setuptools>=2.2,<19.0 +setuptools>=2.2,<20.0 sockjs-tornado>=1.0,<1.1 Whoosh>=2.7.0,<2.8 diff --git a/tests/old/config/test_config.py b/tests/old/config/test_config.py index 1ca93863b..2ba3b33c8 100644 --- a/tests/old/config/test_config.py +++ b/tests/old/config/test_config.py @@ -20,16 +20,23 @@ class HandleConfigTest(TestCase): self.assertEqual(config['integer_var'], 3) self.assertEqual(config['choices_var'], '1') self.assertEqual(config['none_config_var'], None) - self.assertRaisesMessage(expected_exception=ConfigNotFound, - expected_message='The config variable unknown_config_var was not found.', - callable_obj=self.get_config_var, key='unknown_config_var') + with self.assertRaisesMessage( + ConfigNotFound, + 'The config variable unknown_config_var was not found.'): + self.get_config_var('unknown_config_var') def test_get_multiple_config_var_error(self): - config_signal.connect(set_simple_config_view_multiple_vars, dispatch_uid='set_simple_config_view_multiple_vars_for_testing') - self.assertRaisesMessage(expected_exception=ConfigError, - expected_message='Too many values for config variable multiple_config_var found.', - callable_obj=config.setup_cache) - config_signal.disconnect(set_simple_config_view_multiple_vars, dispatch_uid='set_simple_config_view_multiple_vars_for_testing') + config_signal.connect( + set_simple_config_view_multiple_vars, + dispatch_uid='set_simple_config_view_multiple_vars_for_testing') + + with self.assertRaisesMessage( + ConfigError, + 'Too many values for config variable multiple_config_var found.'): + config.setup_cache() + config_signal.disconnect( + set_simple_config_view_multiple_vars, + dispatch_uid='set_simple_config_view_multiple_vars_for_testing') def test_database_queries(self): """ @@ -67,13 +74,16 @@ class HandleConfigTest(TestCase): message. """ # TODO: use right exception - self.assertRaisesMessage( - Exception, - 'Change callback dhcnfg34dlg06kdg successfully called.', - self.set_config_var, - key='var_with_callback_ghvnfjd5768gdfkwg0hm2', - value='new_string_kbmbnfhdgibkdjshg452bc') - self.assertEqual(config['var_with_callback_ghvnfjd5768gdfkwg0hm2'], 'new_string_kbmbnfhdgibkdjshg452bc') + with self.assertRaisesMessage( + Exception, + 'Change callback dhcnfg34dlg06kdg successfully called.'): + self.set_config_var( + key='var_with_callback_ghvnfjd5768gdfkwg0hm2', + value='new_string_kbmbnfhdgibkdjshg452bc') + + self.assertEqual( + config['var_with_callback_ghvnfjd5768gdfkwg0hm2'], + 'new_string_kbmbnfhdgibkdjshg452bc') @receiver(config_signal, dispatch_uid='set_grouped_config_view_for_testing') diff --git a/tests/old/settings.py b/tests/old/settings.py index b26e2b13d..be58c6922 100644 --- a/tests/old/settings.py +++ b/tests/old/settings.py @@ -40,10 +40,6 @@ TIME_ZONE = 'Europe/Berlin' MEDIA_ROOT = os.path.join(OPENSLIDES_USER_DATA_PATH, '') -TEMPLATE_DIRS = ( - os.path.join(OPENSLIDES_USER_DATA_PATH, 'templates'), -) - STATICFILES_DIRS.insert(0, os.path.join(OPENSLIDES_USER_DATA_PATH, 'static')) SEARCH_INDEX = 'ram' diff --git a/tests/settings.py b/tests/settings.py index b26e2b13d..be58c6922 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -40,10 +40,6 @@ TIME_ZONE = 'Europe/Berlin' MEDIA_ROOT = os.path.join(OPENSLIDES_USER_DATA_PATH, '') -TEMPLATE_DIRS = ( - os.path.join(OPENSLIDES_USER_DATA_PATH, 'templates'), -) - STATICFILES_DIRS.insert(0, os.path.join(OPENSLIDES_USER_DATA_PATH, 'static')) SEARCH_INDEX = 'ram'