diff --git a/CHANGELOG b/CHANGELOG index 92014d0d3..07534dc4f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -8,6 +8,9 @@ Version 2.0.1 (unreleased) ========================== [https://github.com/OpenSlides/OpenSlides/milestones/2.0.1] +Other: +- Remove config cache to support multiple threads or processes. + Version 2.0 (2016-04-18) ======================== diff --git a/openslides/agenda/apps.py b/openslides/agenda/apps.py index a0c573577..6cd114997 100644 --- a/openslides/agenda/apps.py +++ b/openslides/agenda/apps.py @@ -15,16 +15,18 @@ class AgendaAppConfig(AppConfig): # Import all required stuff. from django.db.models.signals import pre_delete, post_save - from openslides.core.signals import config_signal + from openslides.core.config import config from openslides.utils.rest_api import router + from .config_variables import get_config_variables from .signals import ( - setup_agenda_config, listen_to_related_object_post_delete, listen_to_related_object_post_save) from .views import ItemViewSet + # Define config varialbes + config.update_config_varialbes(get_config_variables()) + # Connect signals. - config_signal.connect(setup_agenda_config, dispatch_uid='setup_agenda_config') post_save.connect( listen_to_related_object_post_save, dispatch_uid='listen_to_related_object_post_save') diff --git a/openslides/agenda/config_variables.py b/openslides/agenda/config_variables.py new file mode 100644 index 000000000..5b5ba9994 --- /dev/null +++ b/openslides/agenda/config_variables.py @@ -0,0 +1,88 @@ +from datetime import datetime + +from django.core.exceptions import ValidationError as DjangoValidationError +from django.core.validators import MaxLengthValidator, MinValueValidator +from django.utils.translation import ugettext as _ +from django.utils.translation import ugettext_lazy + +from openslides.core.config import ConfigVariable + + +def validate_start_time(value): + try: + datetime.strptime(value, '%d.%m.%Y %H:%M') + except ValueError: + raise DjangoValidationError(_('Invalid input.')) + + +def get_config_variables(): + """ + Generator which yields all config variables of this app. + + It has to be evaluated during app loading (see apps.py). + """ + yield ConfigVariable( + name='agenda_number_prefix', + default_value='', + label=ugettext_lazy('Numbering prefix for agenda items'), + help_text=ugettext_lazy('This prefix will be set if you run the automatic agenda numbering.'), + weight=210, + group=ugettext_lazy('Agenda'), + subgroup=ugettext_lazy('General'), + validators=(MaxLengthValidator(20),)) + + yield ConfigVariable( + name='agenda_numeral_system', + default_value='arabic', + input_type='choice', + label=ugettext_lazy('Numeral system for agenda items'), + choices=( + {'value': 'arabic', 'display_name': ugettext_lazy('Arabic')}, + {'value': 'roman', 'display_name': ugettext_lazy('Roman')}), + weight=215, + group=ugettext_lazy('Agenda'), + subgroup=ugettext_lazy('General')) + + # TODO: Use an input type with generic datetime support. + yield ConfigVariable( + name='agenda_start_event_date_time', + default_value='', + label=ugettext_lazy('Begin of event'), + help_text=ugettext_lazy('Input format: DD.MM.YYYY HH:MM'), + weight=220, + group=ugettext_lazy('Agenda'), + subgroup=ugettext_lazy('General'), + validators=(validate_start_time,)) + + # List of speakers + + yield ConfigVariable( + name='agenda_show_last_speakers', + default_value=1, + input_type='integer', + label=ugettext_lazy('Number of last speakers to be shown on the projector'), + weight=230, + group=ugettext_lazy('Agenda'), + subgroup=ugettext_lazy('List of speakers'), + validators=(MinValueValidator(0),)) + + yield ConfigVariable( + name='agenda_countdown_warning_time', + default_value=0, + input_type='integer', + label=ugettext_lazy('Show orange countdown in the last x seconds of speaking time'), + help_text=ugettext_lazy('Enter duration in seconds. Choose 0 to disable warning color.'), + weight=235, + group=ugettext_lazy('Agenda'), + subgroup=ugettext_lazy('List of speakers'), + validators=(MinValueValidator(0),)) + + yield ConfigVariable( + name='agenda_couple_countdown_and_speakers', + default_value=False, + input_type='boolean', + label=ugettext_lazy('Couple countdown with the list of speakers'), + help_text=ugettext_lazy('[Begin speech] starts the countdown, [End speech] stops the countdown.'), + weight=240, + group=ugettext_lazy('Agenda'), + subgroup=ugettext_lazy('List of speakers')) diff --git a/openslides/agenda/signals.py b/openslides/agenda/signals.py index be46e16f5..ff8694774 100644 --- a/openslides/agenda/signals.py +++ b/openslides/agenda/signals.py @@ -1,97 +1,10 @@ -from datetime import datetime - from django.contrib.contenttypes.models import ContentType -from django.core.exceptions import ValidationError as DjangoValidationError -from django.core.validators import MaxLengthValidator, MinValueValidator -from django.utils.translation import ugettext as _ -from django.utils.translation import ugettext_lazy -from openslides.core.config import ConfigVariable from openslides.utils.autoupdate import inform_changed_data from .models import Item -def validate_start_time(value): - try: - datetime.strptime(value, '%d.%m.%Y %H:%M') - except ValueError: - raise DjangoValidationError(_('Invalid input.')) - - -def setup_agenda_config(sender, **kwargs): - """ - Receiver function to setup all agenda config variables. - This function connected to the signal openslides.core.signals.config_signal - during app loading. - """ - yield ConfigVariable( - name='agenda_number_prefix', - default_value='', - label=ugettext_lazy('Numbering prefix for agenda items'), - help_text=ugettext_lazy('This prefix will be set if you run the automatic agenda numbering.'), - weight=210, - group=ugettext_lazy('Agenda'), - subgroup=ugettext_lazy('General'), - validators=(MaxLengthValidator(20),)) - - yield ConfigVariable( - name='agenda_numeral_system', - default_value='arabic', - input_type='choice', - label=ugettext_lazy('Numeral system for agenda items'), - choices=( - {'value': 'arabic', 'display_name': ugettext_lazy('Arabic')}, - {'value': 'roman', 'display_name': ugettext_lazy('Roman')}), - weight=215, - group=ugettext_lazy('Agenda'), - subgroup=ugettext_lazy('General')) - - # TODO: Use an input type with generic datetime support. - yield ConfigVariable( - name='agenda_start_event_date_time', - default_value='', - label=ugettext_lazy('Begin of event'), - help_text=ugettext_lazy('Input format: DD.MM.YYYY HH:MM'), - weight=220, - group=ugettext_lazy('Agenda'), - subgroup=ugettext_lazy('General'), - validators=(validate_start_time,)) - - # List of speakers - - yield ConfigVariable( - name='agenda_show_last_speakers', - default_value=1, - input_type='integer', - label=ugettext_lazy('Number of last speakers to be shown on the projector'), - weight=230, - group=ugettext_lazy('Agenda'), - subgroup=ugettext_lazy('List of speakers'), - validators=(MinValueValidator(0),)) - - yield ConfigVariable( - name='agenda_countdown_warning_time', - default_value=0, - input_type='integer', - label=ugettext_lazy('Show orange countdown in the last x seconds of speaking time'), - help_text=ugettext_lazy('Enter duration in seconds. Choose 0 to disable warning color.'), - weight=235, - group=ugettext_lazy('Agenda'), - subgroup=ugettext_lazy('List of speakers'), - validators=(MinValueValidator(0),)) - - yield ConfigVariable( - name='agenda_couple_countdown_and_speakers', - default_value=False, - input_type='boolean', - label=ugettext_lazy('Couple countdown with the list of speakers'), - help_text=ugettext_lazy('[Begin speech] starts the countdown, [End speech] stops the countdown.'), - weight=240, - group=ugettext_lazy('Agenda'), - subgroup=ugettext_lazy('List of speakers')) - - def listen_to_related_object_post_save(sender, instance, created, **kwargs): """ Receiver function to create agenda items. It is connected to the signal diff --git a/openslides/assignments/apps.py b/openslides/assignments/apps.py index 67a455495..7228e4f89 100644 --- a/openslides/assignments/apps.py +++ b/openslides/assignments/apps.py @@ -14,13 +14,13 @@ class AssignmentsAppConfig(AppConfig): from . import projector # noqa # Import all required stuff. - from openslides.core.signals import config_signal + from openslides.core.config import config from openslides.utils.rest_api import router - from .signals import setup_assignment_config + from .config_variables import get_config_variables from .views import AssignmentViewSet, AssignmentPollViewSet - # Connect signals. - config_signal.connect(setup_assignment_config, dispatch_uid='setup_assignment_config') + # Define config variables + config.update_config_varialbes(get_config_variables()) # Register viewsets. router.register(self.get_model('Assignment').get_collection_string(), AssignmentViewSet) diff --git a/openslides/assignments/signals.py b/openslides/assignments/config_variables.py similarity index 92% rename from openslides/assignments/signals.py rename to openslides/assignments/config_variables.py index 17f920948..e4e3412b3 100644 --- a/openslides/assignments/signals.py +++ b/openslides/assignments/config_variables.py @@ -6,15 +6,14 @@ from openslides.core.config import ConfigVariable from openslides.poll.models import PERCENT_BASE_CHOICES -def setup_assignment_config(sender, **kwargs): +def get_config_variables(): """ - Receiver function to setup all assignment config variables. They are - grouped in 'Ballot and ballot papers' and 'PDF'. This function is - connected to the signal openslides.core.signals.config_signal during - app loading. + Generator which yields all config variables of this app. + + They are grouped in 'Ballot and ballot papers' and 'PDF'. The generator has + to be evaluated during app loading (see apps.py). """ # Ballot and ballot papers - yield ConfigVariable( name='assignments_poll_vote_values', default_value='auto', diff --git a/openslides/core/apps.py b/openslides/core/apps.py index 456d08552..569c3d72c 100644 --- a/openslides/core/apps.py +++ b/openslides/core/apps.py @@ -15,11 +15,13 @@ class CoreAppConfig(AppConfig): # Import all required stuff. from django.db.models import signals - from openslides.core.signals import config_signal, post_permission_creation + from openslides.core.config import config + from openslides.core.signals import post_permission_creation from openslides.utils.autoupdate import inform_changed_data_receiver, inform_deleted_data_receiver from openslides.utils.rest_api import router from openslides.utils.search import index_add_instance, index_del_instance - from .signals import delete_django_app_permissions, setup_general_config + from .config_variables import get_config_variables + from .signals import delete_django_app_permissions from .views import ( ChatMessageViewSet, ConfigViewSet, @@ -28,10 +30,10 @@ class CoreAppConfig(AppConfig): TagViewSet, ) + # Define config variables + config.update_config_varialbes(get_config_variables()) + # Connect signals. - config_signal.connect( - setup_general_config, - dispatch_uid='setup_general_config') post_permission_creation.connect( delete_django_app_permissions, dispatch_uid='delete_django_app_permissions') diff --git a/openslides/core/config.py b/openslides/core/config.py index d4d52113d..b0ad57e4f 100644 --- a/openslides/core/config.py +++ b/openslides/core/config.py @@ -19,33 +19,35 @@ class ConfigHandler: object. To get a config variable use x = config[...], to set it use config[...] = x. """ + + def __init__(self): + # Dict, that keeps all ConfigVariable objects. Has to be set at statup. + # See the run method in openslides.core.apps. + self.config_variables = {} + def __getitem__(self, key): """ - Returns the value of the config variable. Builds the cache if it - does not exist. + Returns the value of the config variable. Returns the default value, if + not value exists in the database. """ try: - return self._cache[key] + default_value = self.config_variables[key].default_value except KeyError: - raise ConfigNotFound(_('The config variable %s was not found.') % key) - except AttributeError: - self.setup_cache() - return self[key] + raise ConfigNotFound(_('The config variable {} was not found.').format(key)) - def setup_cache(self): - """ - Creates a cache of all config variables with their current value. - """ - self._cache = {} - for key, config_variable in self.get_config_variables().items(): - self._cache[key] = config_variable.default_value - for config_object in ConfigStore.objects.all(): - self._cache[config_object.key] = config_object.value + try: + db_value = ConfigStore.objects.get(key=key) + except ConfigStore.DoesNotExist: + return default_value + return db_value.value def __contains__(self, key): + """ + Returns True, if the config varialbe exists. + """ try: - config[key] - except ConfigNotFound: + self.config_variables[key] + except KeyError: return False else: return True @@ -56,9 +58,9 @@ class ConfigHandler: """ # Check if the variable is defined. try: - config_variable = config.get_config_variables()[key] + config_variable = self.config_variables[key] except KeyError: - raise ConfigNotFound(_('The config variable %s was not found.') % key) + raise ConfigNotFound(_('The config variable {} was not found.').format(key)) # Validate datatype and run validators. expected_type = INPUT_TYPE_MAPPING[config_variable.input_type] @@ -69,8 +71,15 @@ class ConfigHandler: except ValueError: raise ConfigError(_('Wrong datatype. Expected %(expected_type)s, got %(got_type)s.') % { 'expected_type': expected_type, 'got_type': type(value)}) - if config_variable.input_type == 'choice' and value not in map(lambda choice: choice['value'], config_variable.choices): - raise ConfigError(_('Invalid input. Choice does not match.')) + + if config_variable.input_type == 'choice': + # Choices can be a callable. In this case call it at this place + if callable(config_variable.choices): + choices = config_variable.choices() + else: + choices = config_variable.choices + if value not in map(lambda choice: choice['value'], choices): + raise ConfigError(_('Invalid input. Choice does not match.')) for validator in config_variable.validators: try: validator(value) @@ -78,50 +87,45 @@ class ConfigHandler: raise ConfigError(e.messages[0]) # Save the new value to the database. - config_store, created = ConfigStore.objects.get_or_create(key=key, defaults={'value': value}) - if not created: - config_store.value = value - config_store.save() - - # Update cache. - if hasattr(self, '_cache'): - self._cache[key] = value + ConfigStore.objects.update_or_create(key=key, defaults={'value': value}) # Call on_change callback. if config_variable.on_change: config_variable.on_change() + def update_config_varialbes(self, items): + """ + Updates the config_variables dict. + + items has to be an iterator over ConfigVariable objects. + """ + new_items = dict((variable.name, variable) for variable in items) + # Check that all ConfigVariables are unique. So no key from items can + # be in already in self.config_variables + for key in new_items.keys(): + if key in self.config_variables: + raise ConfigError(_('Too many values for config variable {} found.').format(key)) + + self.config_variables.update(new_items) + def items(self): """ - Returns key-value pairs of all config variables. + Iterates over key-value pairs of all config variables. """ - if not hasattr(self, '_cache'): - self.setup_cache() - return self._cache.items() + # Create a dict with the default values of each ConfigVariable + config_items = dict((key, variable.default_value) for key, variable in self.config_variables.items()) - def get_config_variables(self): - """ - Returns a dictionary with all ConfigVariable instances of all - signal receivers. The key is the name of the config variable. - """ - # config_signal can not be imported at global space, because - # core.signals imports this file - from .signals import config_signal - - result = {} - for receiver, config_collection in config_signal.send(sender='get_config_variables'): - for config_variable in config_collection: - if config_variable.name in result: - raise ConfigError(_('Too many values for config variable %s found.') % config_variable.name) - result[config_variable.name] = config_variable - return result + # Update the dict with all values, which are in the db + for db_value in ConfigStore.objects.all(): + config_items[db_value.key] = db_value.value + return config_items.items() def get_all_translatable(self): """ Generator to get all config variables as strings when their values are intended to be translated. """ - for config_variable in self.get_config_variables().values(): + for config_variable in self.config_variables.values(): if config_variable.translatable: yield config_variable.name diff --git a/openslides/core/config_variables.py b/openslides/core/config_variables.py new file mode 100644 index 000000000..affaeee7b --- /dev/null +++ b/openslides/core/config_variables.py @@ -0,0 +1,160 @@ +from django.core.validators import MaxLengthValidator +from django.utils.translation import ugettext as _ +from django.utils.translation import ugettext_lazy + +from openslides.core.config import ConfigVariable + + +def get_config_variables(): + """ + Generator which yields all config variables of this app. + + There are two main groups: 'General' and 'Projector'. The group 'General' + has subgroups. The generator has to be evaluated during app loading + (see apps.py). + """ + yield ConfigVariable( + name='general_event_name', + default_value='OpenSlides', + label=ugettext_lazy('Event name'), + weight=110, + group=ugettext_lazy('General'), + subgroup=ugettext_lazy('Event'), + validators=(MaxLengthValidator(50),)) + + yield ConfigVariable( + name='general_event_description', + default_value=_('Presentation and assembly system'), + label=ugettext_lazy('Short description of event'), + weight=115, + group=ugettext_lazy('General'), + subgroup=ugettext_lazy('Event'), + validators=(MaxLengthValidator(100),), + translatable=True) + + yield ConfigVariable( + name='general_event_date', + default_value='', + label=ugettext_lazy('Event date'), + weight=120, + group=ugettext_lazy('General'), + subgroup=ugettext_lazy('Event')) + + yield ConfigVariable( + name='general_event_location', + default_value='', + label=ugettext_lazy('Event location'), + weight=125, + group=ugettext_lazy('General'), + subgroup=ugettext_lazy('Event')) + + yield ConfigVariable( + name='general_event_organizer', + default_value='', + label=ugettext_lazy('Event organizer'), + weight=130, + group=ugettext_lazy('General'), + subgroup=ugettext_lazy('Event')) + + yield ConfigVariable( + name='general_event_legal_notice', + default_value=_( + 'OpenSlides is a free web based ' + 'presentation and assembly system for visualizing and controlling agenda, ' + 'motions and elections of an assembly.'), + input_type='text', + label=ugettext_lazy('Legal notice'), + weight=132, + group=ugettext_lazy('General'), + subgroup=ugettext_lazy('Event'), + translatable=True) + + yield ConfigVariable( + name='general_event_welcome_title', + default_value=_('Welcome to OpenSlides'), + label=ugettext_lazy('Front page title'), + weight=134, + group=ugettext_lazy('General'), + subgroup=ugettext_lazy('Event'), + translatable=True) + + yield ConfigVariable( + name='general_event_welcome_text', + default_value=_('[Space for your welcome text.]'), + input_type='text', + label=ugettext_lazy('Front page text'), + weight=136, + group=ugettext_lazy('General'), + subgroup=ugettext_lazy('Event'), + translatable=True) + + # General System + + yield ConfigVariable( + name='general_system_enable_anonymous', + default_value=False, + input_type='boolean', + label=ugettext_lazy('Allow access for anonymous guest users'), + weight=138, + group=ugettext_lazy('General'), + subgroup=ugettext_lazy('System')) + + yield ConfigVariable( + name='general_login_info_text', + default_value='', + label=ugettext_lazy('Show this text on the login page.'), + weight=140, + group=ugettext_lazy('General'), + subgroup=ugettext_lazy('System')) + + # Projector + + yield ConfigVariable( + name='projector_enable_logo', + default_value=True, + input_type='boolean', + label=ugettext_lazy('Show logo on projector'), + help_text=ugettext_lazy( + 'You can replace the logo. Just copy a file to ' + '"static/img/logo-projector.png" in your OpenSlides data path.'), + weight=150, + group=ugettext_lazy('Projector')) + + yield ConfigVariable( + name='projector_enable_title', + default_value=True, + input_type='boolean', + label=ugettext_lazy('Show title and description of event on projector'), + weight=155, + group=ugettext_lazy('Projector')) + + yield ConfigVariable( + name='projector_header_backgroundcolor', + default_value='#317796', + input_type='colorpicker', + label=ugettext_lazy('Background color of projector header and footer'), + weight=160, + group=ugettext_lazy('Projector')) + + yield ConfigVariable( + name='projector_header_fontcolor', + default_value='#F5F5F5', + input_type='colorpicker', + label=ugettext_lazy('Font color of projector header and footer'), + weight=165, + group=ugettext_lazy('Projector')) + + yield ConfigVariable( + name='projector_h1_fontcolor', + default_value='#317796', + input_type='colorpicker', + label=ugettext_lazy('Font color of projector headline'), + weight=170, + group=ugettext_lazy('Projector')) + + yield ConfigVariable( + name='projector_default_countdown', + default_value=60, + label=ugettext_lazy('Default countdown'), + weight=185, + group=ugettext_lazy('Projector')) diff --git a/openslides/core/middleware.py b/openslides/core/middleware.py deleted file mode 100644 index d4bc03a21..000000000 --- a/openslides/core/middleware.py +++ /dev/null @@ -1,9 +0,0 @@ -from .config import config - - -class ConfigCacheMiddleware(object): - """ - Middleware to refresh the config cache before processing any view. - """ - def process_request(self, request): - config.setup_cache() diff --git a/openslides/core/signals.py b/openslides/core/signals.py index 2f8bc101d..dc4490f5c 100644 --- a/openslides/core/signals.py +++ b/openslides/core/signals.py @@ -1,12 +1,7 @@ from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType -from django.core.validators import MaxLengthValidator from django.db.models import Q from django.dispatch import Signal -from django.utils.translation import ugettext as _ -from django.utils.translation import ugettext_lazy - -from openslides.core.config import ConfigVariable # This signal is sent when the migrate command is done. That means it is sent # after post_migrate sending and creating all Permission objects. Don't use it @@ -25,163 +20,3 @@ def delete_django_app_permissions(sender, **kwargs): Q(app_label='sessions')) for permission in Permission.objects.filter(content_type__in=contenttypes): permission.delete() - - -def setup_general_config(sender, **kwargs): - """ - Receiver function to setup general config variables for OpenSlides. - There are two main groups: 'General' and 'Projector'. The group - 'General' has subgroups. This function is connected to the signal - openslides.core.signals.config_signal during app loading. - """ - # General Event - - yield ConfigVariable( - name='general_event_name', - default_value='OpenSlides', - label=ugettext_lazy('Event name'), - weight=110, - group=ugettext_lazy('General'), - subgroup=ugettext_lazy('Event'), - validators=(MaxLengthValidator(50),)) - - yield ConfigVariable( - name='general_event_description', - default_value=_('Presentation and assembly system'), - label=ugettext_lazy('Short description of event'), - weight=115, - group=ugettext_lazy('General'), - subgroup=ugettext_lazy('Event'), - validators=(MaxLengthValidator(100),), - translatable=True) - - yield ConfigVariable( - name='general_event_date', - default_value='', - label=ugettext_lazy('Event date'), - weight=120, - group=ugettext_lazy('General'), - subgroup=ugettext_lazy('Event')) - - yield ConfigVariable( - name='general_event_location', - default_value='', - label=ugettext_lazy('Event location'), - weight=125, - group=ugettext_lazy('General'), - subgroup=ugettext_lazy('Event')) - - yield ConfigVariable( - name='general_event_organizer', - default_value='', - label=ugettext_lazy('Event organizer'), - weight=130, - group=ugettext_lazy('General'), - subgroup=ugettext_lazy('Event')) - - yield ConfigVariable( - name='general_event_legal_notice', - default_value=_( - 'OpenSlides is a free web based ' - 'presentation and assembly system for visualizing and controlling agenda, ' - 'motions and elections of an assembly.'), - input_type='text', - label=ugettext_lazy('Legal notice'), - weight=132, - group=ugettext_lazy('General'), - subgroup=ugettext_lazy('Event'), - translatable=True) - - yield ConfigVariable( - name='general_event_welcome_title', - default_value=_('Welcome to OpenSlides'), - label=ugettext_lazy('Front page title'), - weight=134, - group=ugettext_lazy('General'), - subgroup=ugettext_lazy('Event'), - translatable=True) - - yield ConfigVariable( - name='general_event_welcome_text', - default_value=_('[Space for your welcome text.]'), - input_type='text', - label=ugettext_lazy('Front page text'), - weight=136, - group=ugettext_lazy('General'), - subgroup=ugettext_lazy('Event'), - translatable=True) - - # General System - - yield ConfigVariable( - name='general_system_enable_anonymous', - default_value=False, - input_type='boolean', - label=ugettext_lazy('Allow access for anonymous guest users'), - weight=138, - group=ugettext_lazy('General'), - subgroup=ugettext_lazy('System')) - - yield ConfigVariable( - name='general_login_info_text', - default_value='', - label=ugettext_lazy('Show this text on the login page.'), - weight=140, - group=ugettext_lazy('General'), - subgroup=ugettext_lazy('System')) - - # Projector - - yield ConfigVariable( - name='projector_enable_logo', - default_value=True, - input_type='boolean', - label=ugettext_lazy('Show logo on projector'), - help_text=ugettext_lazy( - 'You can replace the logo. Just copy a file to ' - '"static/img/logo-projector.png" in your OpenSlides data path.'), - weight=150, - group=ugettext_lazy('Projector')) - - yield ConfigVariable( - name='projector_enable_title', - default_value=True, - input_type='boolean', - label=ugettext_lazy('Show title and description of event on projector'), - weight=155, - group=ugettext_lazy('Projector')) - - yield ConfigVariable( - name='projector_header_backgroundcolor', - default_value='#317796', - input_type='colorpicker', - label=ugettext_lazy('Background color of projector header and footer'), - weight=160, - group=ugettext_lazy('Projector')) - - yield ConfigVariable( - name='projector_header_fontcolor', - default_value='#F5F5F5', - input_type='colorpicker', - label=ugettext_lazy('Font color of projector header and footer'), - weight=165, - group=ugettext_lazy('Projector')) - - yield ConfigVariable( - name='projector_h1_fontcolor', - default_value='#317796', - input_type='colorpicker', - label=ugettext_lazy('Font color of projector headline'), - weight=170, - group=ugettext_lazy('Projector')) - - yield ConfigVariable( - name='projector_default_countdown', - default_value=60, - label=ugettext_lazy('Default countdown'), - weight=185, - group=ugettext_lazy('Projector')) - - -config_signal = Signal(providing_args=[]) -"""Signal to get all config tabs from all apps.""" diff --git a/openslides/core/views.py b/openslides/core/views.py index b76183d1b..ac6ee5412 100644 --- a/openslides/core/views.py +++ b/openslides/core/views.py @@ -415,12 +415,9 @@ class ConfigMetadata(SimpleMetadata): Custom metadata class to add config info to responses on OPTIONS requests. """ def determine_metadata(self, request, view): - # Sort config variables by weight. - config_variables = sorted(config.get_config_variables().values(), key=attrgetter('weight')) - # Build tree. config_groups = [] - for config_variable in config_variables: + for config_variable in sorted(config.config_variables.values(), key=attrgetter('weight')): if config_variable.is_hidden(): # Skip hidden config variables. Do not even check groups and subgroups. continue diff --git a/openslides/global_settings.py b/openslides/global_settings.py index f913e79ab..9b6ba6090 100644 --- a/openslides/global_settings.py +++ b/openslides/global_settings.py @@ -60,7 +60,6 @@ MIDDLEWARE_CLASSES = ( 'django.middleware.csrf.CsrfViewMiddleware', 'openslides.users.auth.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', - 'openslides.core.middleware.ConfigCacheMiddleware', ) ROOT_URLCONF = 'openslides.urls' diff --git a/openslides/motions/apps.py b/openslides/motions/apps.py index 582090fb5..38ad8e34f 100644 --- a/openslides/motions/apps.py +++ b/openslides/motions/apps.py @@ -15,13 +15,16 @@ class MotionsAppConfig(AppConfig): from . import projector # noqa # Import all required stuff. - from openslides.core.signals import config_signal + from openslides.core.config import config from openslides.utils.rest_api import router - from .signals import create_builtin_workflows, setup_motion_config + from .config_variables import get_config_variables + from .signals import create_builtin_workflows from .views import CategoryViewSet, MotionViewSet, MotionPollViewSet, WorkflowViewSet + # Define config varialbes + config.update_config_varialbes(get_config_variables()) + # Connect signals. - config_signal.connect(setup_motion_config, dispatch_uid='setup_motion_config') post_migrate.connect(create_builtin_workflows, dispatch_uid='motion_create_builtin_workflows') # Register viewsets. diff --git a/openslides/motions/config_variables.py b/openslides/motions/config_variables.py new file mode 100644 index 000000000..74cf3dc8c --- /dev/null +++ b/openslides/motions/config_variables.py @@ -0,0 +1,183 @@ +from django.core.validators import MinValueValidator +from django.utils.translation import ugettext as _ +from django.utils.translation import pgettext, ugettext_lazy + +from openslides.core.config import ConfigVariable +from openslides.poll.models import PERCENT_BASE_CHOICES + +from .models import Workflow + + +def get_workflow_choices(): + """ + Returns a list of all workflows to be used as choices for the config variable + 'motions_workflow'. Each list item contains the pk and the display name. + """ + return [{'value': str(workflow.pk), 'display_name': ugettext_lazy(workflow.name)} + for workflow in Workflow.objects.all()] + + +def get_config_variables(): + """ + Generator which yields all config variables of this app. + + They are grouped in 'General', 'Amendments', 'Supporters', 'Voting and ballot + papers' and 'PDF'. The generator has to be evaluated during app loading + (see apps.py). + """ + # General + yield ConfigVariable( + name='motions_workflow', + default_value='1', + input_type='choice', + label=ugettext_lazy('Workflow of new motions'), + choices=get_workflow_choices, + weight=310, + group=ugettext_lazy('Motions'), + subgroup=ugettext_lazy('General')) + + yield ConfigVariable( + name='motions_identifier', + default_value='per_category', + input_type='choice', + label=ugettext_lazy('Identifier'), + choices=( + {'value': 'per_category', 'display_name': ugettext_lazy('Numbered per category')}, + {'value': 'serially_numbered', 'display_name': ugettext_lazy('Serially numbered')}, + {'value': 'manually', 'display_name': ugettext_lazy('Set it manually')}), + weight=315, + group=ugettext_lazy('Motions'), + subgroup=ugettext_lazy('General')) + + yield ConfigVariable( + name='motions_preamble', + default_value=_('The assembly may decide,'), + label=ugettext_lazy('Motion preamble'), + weight=320, + group=ugettext_lazy('Motions'), + subgroup=ugettext_lazy('General'), + translatable=True) + + yield ConfigVariable( + name='motions_stop_submitting', + default_value=False, + input_type='boolean', + label=ugettext_lazy('Stop submitting new motions by non-staff users'), + weight=325, + group=ugettext_lazy('Motions'), + subgroup=ugettext_lazy('General')) + + yield ConfigVariable( + name='motions_allow_disable_versioning', + default_value=False, + input_type='boolean', + label=ugettext_lazy('Allow to disable versioning'), + weight=330, + group=ugettext_lazy('Motions'), + subgroup=ugettext_lazy('General')) + + # Amendments + # Amendments currently not implemented. (TODO: Implement it like in OpenSlides 1.7.) + yield ConfigVariable( + name='motions_amendments_enabled', + default_value=False, + input_type='boolean', + label=ugettext_lazy('Activate amendments'), + hidden=True, + weight=335, + group=ugettext_lazy('Motions'), + subgroup=ugettext_lazy('Amendments')) + + yield ConfigVariable( + name='motions_amendments_prefix', + default_value=pgettext('Prefix for the identifier for amendments', 'A'), + label=ugettext_lazy('Prefix for the identifier for amendments'), + hidden=True, + weight=340, + group=ugettext_lazy('Motions'), + subgroup=ugettext_lazy('Amendments')) + + # Supporters + + yield ConfigVariable( + name='motions_min_supporters', + default_value=0, + input_type='integer', + label=ugettext_lazy('Number of (minimum) required supporters for a motion'), + help_text=ugettext_lazy('Choose 0 to disable the supporting system.'), + weight=345, + group=ugettext_lazy('Motions'), + subgroup=ugettext_lazy('Supporters'), + validators=(MinValueValidator(0),)) + + yield ConfigVariable( + name='motions_remove_supporters', + default_value=False, + input_type='boolean', + label=ugettext_lazy('Remove all supporters of a motion if a submitter edits his motion in early state'), + weight=350, + group=ugettext_lazy('Motions'), + subgroup=ugettext_lazy('Supporters')) + + # Voting and ballot papers + + yield ConfigVariable( + name='motions_poll_100_percent_base', + default_value='WITHOUT_INVALID', + input_type='choice', + label=ugettext_lazy('The 100 % base of a voting result consists of'), + choices=PERCENT_BASE_CHOICES, + weight=355, + group=ugettext_lazy('Motions'), + subgroup=ugettext_lazy('Voting and ballot papers')) + + yield ConfigVariable( + name='motions_pdf_ballot_papers_selection', + default_value='CUSTOM_NUMBER', + input_type='choice', + label=ugettext_lazy('Number of ballot papers (selection)'), + choices=( + {'value': 'NUMBER_OF_DELEGATES', 'display_name': ugettext_lazy('Number of all delegates')}, + {'value': 'NUMBER_OF_ALL_PARTICIPANTS', 'display_name': ugettext_lazy('Number of all participants')}, + {'value': 'CUSTOM_NUMBER', 'display_name': ugettext_lazy('Use the following custom number')}), + weight=360, + group=ugettext_lazy('Motions'), + subgroup=ugettext_lazy('Voting and ballot papers')) + + yield ConfigVariable( + name='motions_pdf_ballot_papers_number', + default_value=8, + input_type='integer', + label=ugettext_lazy('Custom number of ballot papers'), + weight=365, + group=ugettext_lazy('Motions'), + subgroup=ugettext_lazy('Voting and ballot papers'), + validators=(MinValueValidator(1),)) + + # PDF + + yield ConfigVariable( + name='motions_pdf_title', + default_value=_('Motions'), + label=ugettext_lazy('Title for PDF document (all motions)'), + weight=370, + group=ugettext_lazy('Motions'), + subgroup=ugettext_lazy('PDF'), + translatable=True) + + yield ConfigVariable( + name='motions_pdf_preamble', + default_value='', + label=ugettext_lazy('Preamble text for PDF document (all motions)'), + weight=375, + group=ugettext_lazy('Motions'), + subgroup=ugettext_lazy('PDF')) + + yield ConfigVariable( + name='motions_pdf_paragraph_numbering', + default_value=False, + input_type='boolean', + label=ugettext_lazy('Show paragraph numbering (only in PDF)'), + weight=380, + group=ugettext_lazy('Motions'), + subgroup=ugettext_lazy('PDF')) diff --git a/openslides/motions/signals.py b/openslides/motions/signals.py index 782c94e8e..15cc0fa78 100644 --- a/openslides/motions/signals.py +++ b/openslides/motions/signals.py @@ -1,179 +1,8 @@ -from django.core.validators import MinValueValidator -from django.utils.translation import ugettext as _ -from django.utils.translation import pgettext, ugettext_lazy, ugettext_noop - -from openslides.core.config import ConfigVariable -from openslides.poll.models import PERCENT_BASE_CHOICES +from django.utils.translation import ugettext_noop from .models import State, Workflow -def setup_motion_config(sender, **kwargs): - """ - Receiver function to setup all motion config variables. They are - grouped in 'General', 'Amendments', 'Supporters', 'Voting and ballot - papers' and 'PDF'. This function connected to the signal - openslides.core.signals.config_signal during app loading. - """ - # General - - yield ConfigVariable( - name='motions_workflow', - default_value='1', - input_type='choice', - label=ugettext_lazy('Workflow of new motions'), - choices=({'value': str(workflow.pk), 'display_name': ugettext_lazy(workflow.name)} for workflow in Workflow.objects.all()), - weight=310, - group=ugettext_lazy('Motions'), - subgroup=ugettext_lazy('General')) - - yield ConfigVariable( - name='motions_identifier', - default_value='per_category', - input_type='choice', - label=ugettext_lazy('Identifier'), - choices=( - {'value': 'per_category', 'display_name': ugettext_lazy('Numbered per category')}, - {'value': 'serially_numbered', 'display_name': ugettext_lazy('Serially numbered')}, - {'value': 'manually', 'display_name': ugettext_lazy('Set it manually')}), - weight=315, - group=ugettext_lazy('Motions'), - subgroup=ugettext_lazy('General')) - - yield ConfigVariable( - name='motions_preamble', - default_value=_('The assembly may decide,'), - label=ugettext_lazy('Motion preamble'), - weight=320, - group=ugettext_lazy('Motions'), - subgroup=ugettext_lazy('General'), - translatable=True) - - yield ConfigVariable( - name='motions_stop_submitting', - default_value=False, - input_type='boolean', - label=ugettext_lazy('Stop submitting new motions by non-staff users'), - weight=325, - group=ugettext_lazy('Motions'), - subgroup=ugettext_lazy('General')) - - yield ConfigVariable( - name='motions_allow_disable_versioning', - default_value=False, - input_type='boolean', - label=ugettext_lazy('Allow to disable versioning'), - weight=330, - group=ugettext_lazy('Motions'), - subgroup=ugettext_lazy('General')) - - # Amendments - # Amendments currently not implemented. (TODO: Implement it like in OpenSlides 1.7.) - yield ConfigVariable( - name='motions_amendments_enabled', - default_value=False, - input_type='boolean', - label=ugettext_lazy('Activate amendments'), - hidden=True, - weight=335, - group=ugettext_lazy('Motions'), - subgroup=ugettext_lazy('Amendments')) - - yield ConfigVariable( - name='motions_amendments_prefix', - default_value=pgettext('Prefix for the identifier for amendments', 'A'), - label=ugettext_lazy('Prefix for the identifier for amendments'), - hidden=True, - weight=340, - group=ugettext_lazy('Motions'), - subgroup=ugettext_lazy('Amendments')) - - # Supporters - - yield ConfigVariable( - name='motions_min_supporters', - default_value=0, - input_type='integer', - label=ugettext_lazy('Number of (minimum) required supporters for a motion'), - help_text=ugettext_lazy('Choose 0 to disable the supporting system.'), - weight=345, - group=ugettext_lazy('Motions'), - subgroup=ugettext_lazy('Supporters'), - validators=(MinValueValidator(0),)) - - yield ConfigVariable( - name='motions_remove_supporters', - default_value=False, - input_type='boolean', - label=ugettext_lazy('Remove all supporters of a motion if a submitter edits his motion in early state'), - weight=350, - group=ugettext_lazy('Motions'), - subgroup=ugettext_lazy('Supporters')) - - # Voting and ballot papers - - yield ConfigVariable( - name='motions_poll_100_percent_base', - default_value='WITHOUT_INVALID', - input_type='choice', - label=ugettext_lazy('The 100 % base of a voting result consists of'), - choices=PERCENT_BASE_CHOICES, - weight=355, - group=ugettext_lazy('Motions'), - subgroup=ugettext_lazy('Voting and ballot papers')) - - yield ConfigVariable( - name='motions_pdf_ballot_papers_selection', - default_value='CUSTOM_NUMBER', - input_type='choice', - label=ugettext_lazy('Number of ballot papers (selection)'), - choices=( - {'value': 'NUMBER_OF_DELEGATES', 'display_name': ugettext_lazy('Number of all delegates')}, - {'value': 'NUMBER_OF_ALL_PARTICIPANTS', 'display_name': ugettext_lazy('Number of all participants')}, - {'value': 'CUSTOM_NUMBER', 'display_name': ugettext_lazy('Use the following custom number')}), - weight=360, - group=ugettext_lazy('Motions'), - subgroup=ugettext_lazy('Voting and ballot papers')) - - yield ConfigVariable( - name='motions_pdf_ballot_papers_number', - default_value=8, - input_type='integer', - label=ugettext_lazy('Custom number of ballot papers'), - weight=365, - group=ugettext_lazy('Motions'), - subgroup=ugettext_lazy('Voting and ballot papers'), - validators=(MinValueValidator(1),)) - - # PDF - - yield ConfigVariable( - name='motions_pdf_title', - default_value=_('Motions'), - label=ugettext_lazy('Title for PDF document (all motions)'), - weight=370, - group=ugettext_lazy('Motions'), - subgroup=ugettext_lazy('PDF'), - translatable=True) - - yield ConfigVariable( - name='motions_pdf_preamble', - default_value='', - label=ugettext_lazy('Preamble text for PDF document (all motions)'), - weight=375, - group=ugettext_lazy('Motions'), - subgroup=ugettext_lazy('PDF')) - - yield ConfigVariable( - name='motions_pdf_paragraph_numbering', - default_value=False, - input_type='boolean', - label=ugettext_lazy('Show paragraph numbering (only in PDF)'), - weight=380, - group=ugettext_lazy('Motions'), - subgroup=ugettext_lazy('PDF')) - - def create_builtin_workflows(sender, **kwargs): """ Receiver function to create a simple and a complex workflow. It is diff --git a/openslides/users/apps.py b/openslides/users/apps.py index 623adc8f0..bbd2663e3 100644 --- a/openslides/users/apps.py +++ b/openslides/users/apps.py @@ -14,15 +14,17 @@ class UsersAppConfig(AppConfig): from . import projector # noqa # Import all required stuff. - from ..core.signals import config_signal, post_permission_creation + from ..core.config import config + from ..core.signals import post_permission_creation from ..utils.rest_api import router - from .signals import create_builtin_groups_and_admin, setup_users_config + from .config_variables import get_config_variables + from .signals import create_builtin_groups_and_admin from .views import GroupViewSet, UserViewSet + # Define config variables + config.update_config_varialbes(get_config_variables()) + # Connect signals. - config_signal.connect( - setup_users_config, - dispatch_uid='setup_users_config') post_permission_creation.connect( create_builtin_groups_and_admin, dispatch_uid='create_builtin_groups_and_admin') diff --git a/openslides/users/config_variables.py b/openslides/users/config_variables.py new file mode 100644 index 000000000..171f313a7 --- /dev/null +++ b/openslides/users/config_variables.py @@ -0,0 +1,86 @@ +from django.utils.translation import ugettext as _ +from django.utils.translation import ugettext_lazy + +from openslides.core.config import ConfigVariable + + +def get_config_variables(): + """ + Generator which yields all config variables of this app. + + They are grouped in 'Sorting' and 'PDF'. The generator has to be evaluated + during app loading (see apps.py). + """ + # Sorting + yield ConfigVariable( + name='users_sort_users_by_first_name', + default_value=False, + input_type='boolean', + label=ugettext_lazy('Sort users by first name'), + help_text=ugettext_lazy('Disable for sorting by last name'), + weight=510, + group=ugettext_lazy('Users'), + subgroup=ugettext_lazy('Sorting')) + + # PDF + + yield ConfigVariable( + name='users_pdf_welcometitle', + default_value=_('Welcome to OpenSlides!'), + label=ugettext_lazy('Title for access data and welcome PDF'), + weight=520, + group=ugettext_lazy('Users'), + subgroup=ugettext_lazy('PDF'), + translatable=True) + + yield ConfigVariable( + name='users_pdf_welcometext', + default_value=_('[Place for your welcome and help text.]'), + label=ugettext_lazy('Help text for access data and welcome PDF'), + weight=530, + group=ugettext_lazy('Users'), + subgroup=ugettext_lazy('PDF'), + translatable=True) + + # TODO: Use Django's URLValidator here. + yield ConfigVariable( + name='users_pdf_url', + default_value='http://example.com:8000', + label=ugettext_lazy('System URL'), + help_text=ugettext_lazy('Used for QRCode in PDF of access data.'), + weight=540, + group=ugettext_lazy('Users'), + subgroup=ugettext_lazy('PDF')) + + yield ConfigVariable( + name='users_pdf_wlan_ssid', + default_value='', + label=ugettext_lazy('WLAN name (SSID)'), + help_text=ugettext_lazy('Used for WLAN QRCode in PDF of access data.'), + weight=550, + group=ugettext_lazy('Users'), + subgroup=ugettext_lazy('PDF')) + + yield ConfigVariable( + name='users_pdf_wlan_password', + default_value='', + label=ugettext_lazy('WLAN password'), + help_text=ugettext_lazy('Used for WLAN QRCode in PDF of access data.'), + weight=560, + group=ugettext_lazy('Users'), + subgroup=ugettext_lazy('PDF')) + + yield ConfigVariable( + name='users_pdf_wlan_encryption', + default_value='', + input_type='choice', + label=ugettext_lazy('WLAN encryption'), + help_text=ugettext_lazy('Used for WLAN QRCode in PDF of access data.'), + choices=( + {'value': '', 'display_name': '---------'}, + {'value': 'WEP', 'display_name': ugettext_lazy('WEP')}, + {'value': 'WPA', 'display_name': ugettext_lazy('WPA/WPA2')}, + {'value': 'nopass', 'display_name': ugettext_lazy('No encryption')}), + weight=570, + group=ugettext_lazy('Users'), + subgroup=ugettext_lazy('PDF')) diff --git a/openslides/users/signals.py b/openslides/users/signals.py index 34bd75ad5..8192fa28e 100644 --- a/openslides/users/signals.py +++ b/openslides/users/signals.py @@ -1,95 +1,9 @@ from django.contrib.auth.models import Permission from django.db.models import Q -from django.utils.translation import ugettext as _ -from django.utils.translation import ugettext_lazy -from ..core.config import ConfigVariable from .models import Group, User -def setup_users_config(sender, **kwargs): - """ - Receiver function to setup all users config variables. They are grouped - in 'Sorting' and 'PDF'. This function is connected to the signal - openslides.core.signals.config_signal during app loading. - """ - - # Sorting - - yield ConfigVariable( - name='users_sort_users_by_first_name', - default_value=False, - input_type='boolean', - label=ugettext_lazy('Sort users by first name'), - help_text=ugettext_lazy('Disable for sorting by last name'), - weight=510, - group=ugettext_lazy('Users'), - subgroup=ugettext_lazy('Sorting')) - - # PDF - - yield ConfigVariable( - name='users_pdf_welcometitle', - default_value=_('Welcome to OpenSlides!'), - label=ugettext_lazy('Title for access data and welcome PDF'), - weight=520, - group=ugettext_lazy('Users'), - subgroup=ugettext_lazy('PDF'), - translatable=True) - - yield ConfigVariable( - name='users_pdf_welcometext', - default_value=_('[Place for your welcome and help text.]'), - label=ugettext_lazy('Help text for access data and welcome PDF'), - weight=530, - group=ugettext_lazy('Users'), - subgroup=ugettext_lazy('PDF'), - translatable=True) - - # TODO: Use Django's URLValidator here. - yield ConfigVariable( - name='users_pdf_url', - default_value='http://example.com:8000', - label=ugettext_lazy('System URL'), - help_text=ugettext_lazy('Used for QRCode in PDF of access data.'), - weight=540, - group=ugettext_lazy('Users'), - subgroup=ugettext_lazy('PDF')) - - yield ConfigVariable( - name='users_pdf_wlan_ssid', - default_value='', - label=ugettext_lazy('WLAN name (SSID)'), - help_text=ugettext_lazy('Used for WLAN QRCode in PDF of access data.'), - weight=550, - group=ugettext_lazy('Users'), - subgroup=ugettext_lazy('PDF')) - - yield ConfigVariable( - name='users_pdf_wlan_password', - default_value='', - label=ugettext_lazy('WLAN password'), - help_text=ugettext_lazy('Used for WLAN QRCode in PDF of access data.'), - weight=560, - group=ugettext_lazy('Users'), - subgroup=ugettext_lazy('PDF')) - - yield ConfigVariable( - name='users_pdf_wlan_encryption', - default_value='', - input_type='choice', - label=ugettext_lazy('WLAN encryption'), - help_text=ugettext_lazy('Used for WLAN QRCode in PDF of access data.'), - choices=( - {'value': '', 'display_name': '---------'}, - {'value': 'WEP', 'display_name': ugettext_lazy('WEP')}, - {'value': 'WPA', 'display_name': ugettext_lazy('WPA/WPA2')}, - {'value': 'nopass', 'display_name': ugettext_lazy('No encryption')}), - weight=570, - group=ugettext_lazy('Users'), - subgroup=ugettext_lazy('PDF')) - - def create_builtin_groups_and_admin(**kwargs): """ Creates the builtin groups: Anonymous, Registered, Delegates and Staff. diff --git a/openslides/utils/test.py b/openslides/utils/test.py index 75cb0b9ff..c9f90c6de 100644 --- a/openslides/utils/test.py +++ b/openslides/utils/test.py @@ -1,8 +1,6 @@ from django.test import TestCase as _TestCase from django.test.runner import DiscoverRunner -from openslides.core.config import config - class OpenSlidesDiscoverRunner(DiscoverRunner): def run_tests(self, test_labels, extra_tests=None, **kwargs): @@ -28,15 +26,8 @@ class OpenSlidesDiscoverRunner(DiscoverRunner): class TestCase(_TestCase): """ - Overwrites Django's TestCase class to refreshs the config cache. - """ + Does nothing at the moment. - def _post_teardown(self, *args, **kwargs): - return_value = super(TestCase, self)._post_teardown(*args, **kwargs) - # Resets the config object by deleting the cache - try: - del config._cache - except AttributeError: - # The cache has only to be deleted if it exists. - pass - return return_value + Could be used in the future. Use this this for the integration test suit. + """ + pass diff --git a/tests/integration/core/test_views.py b/tests/integration/core/test_views.py index 7545f2bac..31143ca9a 100644 --- a/tests/integration/core/test_views.py +++ b/tests/integration/core/test_views.py @@ -1,17 +1,18 @@ import json +from unittest.mock import patch from django.core.urlresolvers import reverse -from django.dispatch import receiver from rest_framework import status from rest_framework.test import APIClient from openslides import __version__ as version -from openslides.core.config import ConfigVariable, config +from openslides.core.config import ConfigHandler, ConfigVariable from openslides.core.models import CustomSlide, Projector -from openslides.core.signals import config_signal from openslides.utils.rest_api import ValidationError from openslides.utils.test import TestCase +config = ConfigHandler() + class ProjectorAPI(TestCase): """ @@ -74,10 +75,19 @@ class VersionView(TestCase): 'version': 'unknown'}]}) +@patch('openslides.core.config.config', config) +@patch('openslides.core.views.config', config) class ConfigViewSet(TestCase): """ Tests requests to deal with config variables. """ + def setUp(self): + config.update_config_varialbes(set_simple_config_view_integration_config_test()) + + def tearDown(self): + # Reset the config variables + config.config_variables = {} + def test_retrieve(self): self.client.login(username='admin', password='admin') config['test_var_aeW3Quahkah1phahCheo'] = 'test_value_Oovoojieme7eephaed2A' @@ -178,8 +188,7 @@ def validator_for_testing(value): raise ValidationError({'detail': 'Invalid input.'}) -@receiver(config_signal, dispatch_uid='set_simple_config_view_integration_config_test') -def set_simple_config_view_integration_config_test(sender, **kwargs): +def set_simple_config_view_integration_config_test(): """ Sets a simple config view with some config variables but without grouping. diff --git a/tests/integration/motions/test_viewset.py b/tests/integration/motions/test_viewset.py index 32399ca7c..96465bdc3 100644 --- a/tests/integration/motions/test_viewset.py +++ b/tests/integration/motions/test_viewset.py @@ -150,7 +150,7 @@ class RetrieveMotion(TestCase): self.motion.create_poll() def test_number_of_queries(self): - with self.assertNumQueries(17): + with self.assertNumQueries(16): self.client.get(reverse('motion-detail', args=[self.motion.pk])) diff --git a/tests/old/config/test_config.py b/tests/old/config/test_config.py index 2ba3b33c8..ed4c11a53 100644 --- a/tests/old/config/test_config.py +++ b/tests/old/config/test_config.py @@ -1,12 +1,28 @@ -from django.dispatch import receiver +from unittest.mock import patch -from openslides.core.config import ConfigVariable, config +from openslides.core.config import ConfigHandler, ConfigVariable from openslides.core.exceptions import ConfigError, ConfigNotFound -from openslides.core.signals import config_signal from openslides.utils.test import TestCase +class TestConfigException(Exception): + pass + +config = ConfigHandler() + + +@patch('openslides.core.config.config', config) class HandleConfigTest(TestCase): + def setUp(self): + config.update_config_varialbes(set_grouped_config_view()) + config.update_config_varialbes(set_simple_config_view()) + config.update_config_varialbes(set_simple_config_view_multiple_vars()) + config.update_config_varialbes(set_simple_config_collection_disabled_view()) + config.update_config_varialbes(set_simple_config_collection_with_callback()) + + def tearDown(self): + # Reset the config variables + config.config_variables = {} def get_config_var(self, key): return config[key] @@ -26,24 +42,10 @@ class HandleConfigTest(TestCase): 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') - 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): - """ - Test that no database queries are send, after the cache was created. - """ - config.setup_cache() - self.assertNumQueries(0, self.get_config_var, key='string_var') + config.update_config_varialbes(set_simple_config_view_multiple_vars()) def test_setup_config_var(self): self.assertRaises(TypeError, ConfigVariable) @@ -73,9 +75,8 @@ class HandleConfigTest(TestCase): Tests that the special callback is called and raises a special message. """ - # TODO: use right exception with self.assertRaisesMessage( - Exception, + TestConfigException, 'Change callback dhcnfg34dlg06kdg successfully called.'): self.set_config_var( key='var_with_callback_ghvnfjd5768gdfkwg0hm2', @@ -86,8 +87,7 @@ class HandleConfigTest(TestCase): 'new_string_kbmbnfhdgibkdjshg452bc') -@receiver(config_signal, dispatch_uid='set_grouped_config_view_for_testing') -def set_grouped_config_view(sender, **kwargs): +def set_grouped_config_view(): """ Sets a grouped config collection. There are some variables, one variable with a string as default value, one with a boolean as default value, @@ -128,8 +128,7 @@ def set_grouped_config_view(sender, **kwargs): subgroup='Group 2 Toongai7ahyahy7B') -@receiver(config_signal, dispatch_uid='set_simple_config_view_for_testing') -def set_simple_config_view(sender, **kwargs): +def set_simple_config_view(): """ Sets a simple config view with some config variables but without grouping. @@ -139,8 +138,7 @@ def set_simple_config_view(sender, **kwargs): yield ConfigVariable(name='none_config_var', default_value=None) -# Do not connect to the signal now but later inside the test. -def set_simple_config_view_multiple_vars(sender, **kwargs): +def set_simple_config_view_multiple_vars(): """ Sets a bad config view with some multiple config vars. """ @@ -148,15 +146,13 @@ def set_simple_config_view_multiple_vars(sender, **kwargs): yield ConfigVariable(name='multiple_config_var', default_value='foobar2') -@receiver(config_signal, dispatch_uid='set_simple_config_collection_disabled_view_for_testing') -def set_simple_config_collection_disabled_view(sender, **kwargs): +def set_simple_config_collection_disabled_view(): yield ConfigVariable(name='hidden_config_var_2', default_value='') -@receiver(config_signal, dispatch_uid='set_simple_config_collection_with_callback_for_testing') -def set_simple_config_collection_with_callback(sender, **kwargs): +def set_simple_config_collection_with_callback(): def callback(): - raise Exception('Change callback dhcnfg34dlg06kdg successfully called.') + raise TestConfigException('Change callback dhcnfg34dlg06kdg successfully called.') yield ConfigVariable( name='var_with_callback_ghvnfjd5768gdfkwg0hm2', default_value='', diff --git a/tests/unit/config/test_api.py b/tests/unit/config/test_api.py index ef061efa2..d059401bc 100644 --- a/tests/unit/config/test_api.py +++ b/tests/unit/config/test_api.py @@ -1,7 +1,7 @@ from unittest import TestCase from unittest.mock import patch -from openslides.core.config import ConfigHandler, ConfigVariable, config +from openslides.core.config import ConfigVariable, config from openslides.core.exceptions import ConfigNotFound @@ -26,12 +26,7 @@ class TestConfigVariable(TestCase): class TestConfigHandler(TestCase): - def test_get_from_cache(self): - ConfigHandler._cache = {'key_eeshah4Sho6zailee4ko': 'value_chies7aCohZoo9umieph'} - self.assertEqual(config['key_eeshah4Sho6zailee4ko'], 'value_chies7aCohZoo9umieph') - def test_get_not_found(self): - ConfigHandler._cache = {} self.assertRaises( ConfigNotFound, config.__getitem__,