Rewrite config to have id field
This commit is contained in:
parent
9d1ebac86e
commit
87b889fbf2
@ -32,4 +32,4 @@ script:
|
|||||||
- DJANGO_SETTINGS_MODULE='tests.settings' coverage run ./manage.py test tests.integration
|
- DJANGO_SETTINGS_MODULE='tests.settings' coverage run ./manage.py test tests.integration
|
||||||
- coverage report --fail-under=74
|
- coverage report --fail-under=74
|
||||||
|
|
||||||
- DJANGO_SETTINGS_MODULE='tests.old.settings' ./manage.py test tests.old
|
- DJANGO_SETTINGS_MODULE='tests.settings' ./manage.py test tests.old
|
||||||
|
@ -86,8 +86,7 @@ def get_config_variables():
|
|||||||
label='Title for PDF document (all elections)',
|
label='Title for PDF document (all elections)',
|
||||||
weight=460,
|
weight=460,
|
||||||
group='Elections',
|
group='Elections',
|
||||||
subgroup='PDF',
|
subgroup='PDF')
|
||||||
translatable=True)
|
|
||||||
|
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
name='assignments_pdf_preamble',
|
name='assignments_pdf_preamble',
|
||||||
|
@ -116,18 +116,10 @@ class ConfigAccessPermissions(BaseAccessPermissions):
|
|||||||
# the config. Anonymous users can do so if they are enabled.
|
# the config. Anonymous users can do so if they are enabled.
|
||||||
return not isinstance(user, AnonymousUser) or anonymous_is_enabled()
|
return not isinstance(user, AnonymousUser) or anonymous_is_enabled()
|
||||||
|
|
||||||
def get_full_data(self, instance):
|
def get_serializer_class(self, user=None):
|
||||||
"""
|
"""
|
||||||
Returns the serlialized config data.
|
Returns serializer class.
|
||||||
"""
|
"""
|
||||||
from .config import config
|
from .serializers import ConfigSerializer
|
||||||
from .models import ConfigStore
|
|
||||||
|
|
||||||
# Attention: The format of this response has to be the same as in
|
return ConfigSerializer
|
||||||
# the retrieve method of ConfigViewSet.
|
|
||||||
if isinstance(instance, ConfigStore):
|
|
||||||
result = {'key': instance.key, 'value': config[instance.key]}
|
|
||||||
else:
|
|
||||||
# It is possible, that the caching system already sends the correct data as "instance".
|
|
||||||
result = instance
|
|
||||||
return result
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.db.models.signals import post_migrate
|
||||||
|
|
||||||
from ..utils.collection import Collection
|
from ..utils.collection import Collection
|
||||||
|
|
||||||
@ -49,6 +50,8 @@ class CoreAppConfig(AppConfig):
|
|||||||
required_users,
|
required_users,
|
||||||
dispatch_uid='core_required_users')
|
dispatch_uid='core_required_users')
|
||||||
|
|
||||||
|
post_migrate.connect(call_save_default_values, sender=self, dispatch_uid='core_save_config_default_values')
|
||||||
|
|
||||||
# Register viewsets.
|
# Register viewsets.
|
||||||
router.register(self.get_model('Projector').get_collection_string(), ProjectorViewSet)
|
router.register(self.get_model('Projector').get_collection_string(), ProjectorViewSet)
|
||||||
router.register(self.get_model('ChatMessage').get_collection_string(), ChatMessageViewSet)
|
router.register(self.get_model('ChatMessage').get_collection_string(), ChatMessageViewSet)
|
||||||
@ -62,10 +65,8 @@ class CoreAppConfig(AppConfig):
|
|||||||
Yields all collections required on startup i. e. opening the websocket
|
Yields all collections required on startup i. e. opening the websocket
|
||||||
connection.
|
connection.
|
||||||
"""
|
"""
|
||||||
from .config import config
|
for model in ('Projector', 'ChatMessage', 'Tag', 'ProjectorMessage', 'Countdown', 'ConfigStore'):
|
||||||
for model in ('Projector', 'ChatMessage', 'Tag', 'ProjectorMessage', 'Countdown'):
|
|
||||||
yield Collection(self.get_model(model).get_collection_string())
|
yield Collection(self.get_model(model).get_collection_string())
|
||||||
yield Collection(config.get_collection_string())
|
|
||||||
|
|
||||||
def get_angular_constants(self):
|
def get_angular_constants(self):
|
||||||
# Client settings
|
# Client settings
|
||||||
@ -84,3 +85,8 @@ class CoreAppConfig(AppConfig):
|
|||||||
'name': 'OpenSlidesSettings',
|
'name': 'OpenSlidesSettings',
|
||||||
'value': client_settings_dict}
|
'value': client_settings_dict}
|
||||||
return [client_settings]
|
return [client_settings]
|
||||||
|
|
||||||
|
|
||||||
|
def call_save_default_values(**kwargs):
|
||||||
|
from .config import config
|
||||||
|
config.save_default_values()
|
||||||
|
@ -1,6 +1,19 @@
|
|||||||
|
from typing import (
|
||||||
|
Any,
|
||||||
|
Callable,
|
||||||
|
Dict,
|
||||||
|
Iterable,
|
||||||
|
List,
|
||||||
|
Optional,
|
||||||
|
TypeVar,
|
||||||
|
Union,
|
||||||
|
)
|
||||||
|
|
||||||
from django.core.exceptions import ValidationError as DjangoValidationError
|
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
from mypy_extensions import TypedDict
|
||||||
|
|
||||||
|
from ..utils.collection import CollectionElement
|
||||||
from .exceptions import ConfigError, ConfigNotFound
|
from .exceptions import ConfigError, ConfigNotFound
|
||||||
from .models import ConfigStore
|
from .models import ConfigStore
|
||||||
|
|
||||||
@ -26,30 +39,31 @@ class ConfigHandler:
|
|||||||
config[...] = x.
|
config[...] = x.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
# Dict, that keeps all ConfigVariable objects. Has to be set at statup.
|
# Dict, that keeps all ConfigVariable objects. Has to be set at statup.
|
||||||
# See the run method in openslides.core.apps.
|
# See the ready() method in openslides.core.apps.
|
||||||
self.config_variables = {}
|
self.config_variables = {} # type: Dict[str, ConfigVariable]
|
||||||
|
|
||||||
def __getitem__(self, key):
|
# Index to get the database id from a given config key
|
||||||
|
self.key_to_id = {} # type: Dict[str, int]
|
||||||
|
|
||||||
|
def __getitem__(self, key: str) -> Any:
|
||||||
"""
|
"""
|
||||||
Returns the value of the config variable. Returns the default value, if
|
Returns the value of the config variable.
|
||||||
not value exists in the database.
|
|
||||||
"""
|
"""
|
||||||
try:
|
# Build the key_to_id dict
|
||||||
default_value = self.config_variables[key].default_value
|
self.save_default_values()
|
||||||
except KeyError:
|
|
||||||
|
if not self.exists(key):
|
||||||
raise ConfigNotFound(_('The config variable {} was not found.').format(key))
|
raise ConfigNotFound(_('The config variable {} was not found.').format(key))
|
||||||
|
|
||||||
try:
|
return CollectionElement.from_values(
|
||||||
db_value = ConfigStore.objects.get(key=key)
|
self.get_collection_string(),
|
||||||
except ConfigStore.DoesNotExist:
|
self.key_to_id[key]).get_full_data()['value']
|
||||||
return default_value
|
|
||||||
return db_value.value
|
|
||||||
|
|
||||||
def __contains__(self, key):
|
def exists(self, key: str) -> bool:
|
||||||
"""
|
"""
|
||||||
Returns True, if the config varialbe exists.
|
Returns True, if the config varialbe was defined.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
self.config_variables[key]
|
self.config_variables[key]
|
||||||
@ -58,7 +72,8 @@ class ConfigHandler:
|
|||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
# TODO: Remove the any by using right types in INPUT_TYPE_MAPPING
|
||||||
|
def __setitem__(self, key: str, value: Any) -> None:
|
||||||
"""
|
"""
|
||||||
Sets the new value. First it validates the input.
|
Sets the new value. First it validates the input.
|
||||||
"""
|
"""
|
||||||
@ -84,8 +99,9 @@ class ConfigHandler:
|
|||||||
choices = config_variable.choices()
|
choices = config_variable.choices()
|
||||||
else:
|
else:
|
||||||
choices = config_variable.choices
|
choices = config_variable.choices
|
||||||
if value not in map(lambda choice: choice['value'], choices):
|
if choices is None or value not in map(lambda choice: choice['value'], choices):
|
||||||
raise ConfigError(_('Invalid input. Choice does not match.'))
|
raise ConfigError(_('Invalid input. Choice does not match.'))
|
||||||
|
|
||||||
for validator in config_variable.validators:
|
for validator in config_variable.validators:
|
||||||
try:
|
try:
|
||||||
validator(value)
|
validator(value)
|
||||||
@ -119,10 +135,7 @@ class ConfigHandler:
|
|||||||
raise ConfigError(_('{} has to be a string.'.format(required_entry)))
|
raise ConfigError(_('{} has to be a string.'.format(required_entry)))
|
||||||
|
|
||||||
# Save the new value to the database.
|
# Save the new value to the database.
|
||||||
try:
|
db_value = ConfigStore.objects.get(key=key)
|
||||||
db_value = ConfigStore.objects.get(key=key)
|
|
||||||
except ConfigStore.DoesNotExist:
|
|
||||||
db_value = ConfigStore(key=key)
|
|
||||||
db_value.value = value
|
db_value.value = value
|
||||||
db_value.save(information={'changed_config': key})
|
db_value.save(information={'changed_config': key})
|
||||||
|
|
||||||
@ -130,43 +143,41 @@ class ConfigHandler:
|
|||||||
if config_variable.on_change:
|
if config_variable.on_change:
|
||||||
config_variable.on_change()
|
config_variable.on_change()
|
||||||
|
|
||||||
def update_config_variables(self, items):
|
def update_config_variables(self, items: Iterable['ConfigVariable']) -> None:
|
||||||
"""
|
"""
|
||||||
Updates the config_variables dict.
|
Updates the config_variables dict.
|
||||||
|
|
||||||
items has to be an iterator over ConfigVariable objects.
|
|
||||||
"""
|
"""
|
||||||
new_items = dict((variable.name, variable) for variable in items)
|
# build an index from variable name to the variable
|
||||||
|
item_index = dict((variable.name, variable) for variable in items)
|
||||||
|
|
||||||
# Check that all ConfigVariables are unique. So no key from items can
|
# Check that all ConfigVariables are unique. So no key from items can
|
||||||
# be in already in self.config_variables
|
# be in already in self.config_variables
|
||||||
for key in new_items.keys():
|
intersection = set(item_index.keys()).intersection(self.config_variables.keys())
|
||||||
if key in self.config_variables:
|
if intersection:
|
||||||
raise ConfigError(_('Too many values for config variable {} found.').format(key))
|
raise ConfigError(_('Too many values for config variables {} found.').format(intersection))
|
||||||
|
|
||||||
self.config_variables.update(new_items)
|
self.config_variables.update(item_index)
|
||||||
|
|
||||||
def items(self):
|
def save_default_values(self) -> None:
|
||||||
"""
|
"""
|
||||||
Iterates over key-value pairs of all config variables.
|
Saves the default values to the database.
|
||||||
"""
|
|
||||||
# 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())
|
|
||||||
|
|
||||||
# Update the dict with all values, which are in the db
|
Does also build the dictonary key_to_id.
|
||||||
for db_value in ConfigStore.objects.all():
|
|
||||||
config_items[db_value.key] = db_value.value
|
|
||||||
return config_items.items()
|
|
||||||
|
|
||||||
def get_all_translatable(self):
|
Does nothing on a second run.
|
||||||
"""
|
"""
|
||||||
Generator to get all config variables as strings when their values are
|
if not self.key_to_id:
|
||||||
intended to be translated.
|
for item in self.config_variables.values():
|
||||||
"""
|
try:
|
||||||
for config_variable in self.config_variables.values():
|
db_value = ConfigStore.objects.get(key=item.name)
|
||||||
if config_variable.translatable:
|
except ConfigStore.DoesNotExist:
|
||||||
yield config_variable.name
|
db_value = ConfigStore()
|
||||||
|
db_value.key = item.name
|
||||||
|
db_value.value = item.default_value
|
||||||
|
db_value.save(skip_autoupdate=True)
|
||||||
|
self.key_to_id[item.name] = db_value.pk
|
||||||
|
|
||||||
def get_collection_string(self):
|
def get_collection_string(self) -> str:
|
||||||
"""
|
"""
|
||||||
Returns the collection_string from the CollectionStore.
|
Returns the collection_string from the CollectionStore.
|
||||||
"""
|
"""
|
||||||
@ -180,6 +191,22 @@ use x = config[...], to set it use config[...] = x.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
T = TypeVar('T')
|
||||||
|
ChoiceType = Optional[List[Dict[str, str]]]
|
||||||
|
ChoiceCallableType = Union[ChoiceType, Callable[[], ChoiceType]]
|
||||||
|
ValidatorsType = List[Callable[[T], None]]
|
||||||
|
OnChangeType = Callable[[], None]
|
||||||
|
ConfigVariableDict = TypedDict('ConfigVariableDict', {
|
||||||
|
'key': str,
|
||||||
|
'default_value': Any,
|
||||||
|
'value': Any,
|
||||||
|
'input_type': str,
|
||||||
|
'label': str,
|
||||||
|
'help_text': str,
|
||||||
|
'choices': ChoiceType,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
class ConfigVariable:
|
class ConfigVariable:
|
||||||
"""
|
"""
|
||||||
A simple object class to wrap new config variables.
|
A simple object class to wrap new config variables.
|
||||||
@ -206,10 +233,10 @@ class ConfigVariable:
|
|||||||
the value during setup of the database if the admin uses the respective
|
the value during setup of the database if the admin uses the respective
|
||||||
command line option.
|
command line option.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, default_value, input_type='string', label=None,
|
def __init__(self, name: str, default_value: T, input_type: str='string',
|
||||||
help_text=None, choices=None, hidden=False, weight=0,
|
label: str=None, help_text: str=None, choices: ChoiceCallableType=None,
|
||||||
group=None, subgroup=None, validators=None, on_change=None,
|
hidden: bool=False, weight: int=0, group: str=None, subgroup: str=None,
|
||||||
translatable=False):
|
validators: ValidatorsType=None, on_change: OnChangeType=None) -> None:
|
||||||
if input_type not in INPUT_TYPE_MAPPING:
|
if input_type not in INPUT_TYPE_MAPPING:
|
||||||
raise ValueError(_('Invalid value for config attribute input_type.'))
|
raise ValueError(_('Invalid value for config attribute input_type.'))
|
||||||
if input_type == 'choice' and choices is None:
|
if input_type == 'choice' and choices is None:
|
||||||
@ -230,26 +257,23 @@ class ConfigVariable:
|
|||||||
self.subgroup = subgroup
|
self.subgroup = subgroup
|
||||||
self.validators = validators or ()
|
self.validators = validators or ()
|
||||||
self.on_change = on_change
|
self.on_change = on_change
|
||||||
self.translatable = translatable
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def data(self):
|
def data(self) -> ConfigVariableDict:
|
||||||
"""
|
"""
|
||||||
Property with all data for OPTIONS requests.
|
Property with all data for OPTIONS requests.
|
||||||
"""
|
"""
|
||||||
data = {
|
return ConfigVariableDict(
|
||||||
'key': self.name,
|
key=self.name,
|
||||||
'default_value': self.default_value,
|
default_value=self.default_value,
|
||||||
'value': config[self.name],
|
value=config[self.name],
|
||||||
'input_type': self.input_type,
|
input_type=self.input_type,
|
||||||
'label': self.label,
|
label=self.label,
|
||||||
'help_text': self.help_text,
|
help_text=self.help_text,
|
||||||
}
|
choices=self.choices() if callable(self.choices) else self.choices
|
||||||
if self.input_type == 'choice':
|
)
|
||||||
data['choices'] = self.choices() if callable(self.choices) else self.choices
|
|
||||||
return data
|
|
||||||
|
|
||||||
def is_hidden(self):
|
def is_hidden(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Returns True if the config variable is hidden so it can be removed
|
Returns True if the config variable is hidden so it can be removed
|
||||||
from response of OPTIONS request.
|
from response of OPTIONS request.
|
||||||
|
@ -27,8 +27,7 @@ def get_config_variables():
|
|||||||
weight=115,
|
weight=115,
|
||||||
group='General',
|
group='General',
|
||||||
subgroup='Event',
|
subgroup='Event',
|
||||||
validators=(MaxLengthValidator(100),),
|
validators=(MaxLengthValidator(100),))
|
||||||
translatable=True)
|
|
||||||
|
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
name='general_event_date',
|
name='general_event_date',
|
||||||
@ -64,8 +63,7 @@ def get_config_variables():
|
|||||||
label='Legal notice',
|
label='Legal notice',
|
||||||
weight=132,
|
weight=132,
|
||||||
group='General',
|
group='General',
|
||||||
subgroup='Event',
|
subgroup='Event')
|
||||||
translatable=True)
|
|
||||||
|
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
name='general_event_welcome_title',
|
name='general_event_welcome_title',
|
||||||
@ -73,8 +71,7 @@ def get_config_variables():
|
|||||||
label='Front page title',
|
label='Front page title',
|
||||||
weight=134,
|
weight=134,
|
||||||
group='General',
|
group='General',
|
||||||
subgroup='Event',
|
subgroup='Event')
|
||||||
translatable=True)
|
|
||||||
|
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
name='general_event_welcome_text',
|
name='general_event_welcome_text',
|
||||||
@ -83,8 +80,7 @@ def get_config_variables():
|
|||||||
label='Front page text',
|
label='Front page text',
|
||||||
weight=136,
|
weight=136,
|
||||||
group='General',
|
group='General',
|
||||||
subgroup='Event',
|
subgroup='Event')
|
||||||
translatable=True)
|
|
||||||
|
|
||||||
# General System
|
# General System
|
||||||
|
|
||||||
|
@ -294,12 +294,6 @@ class ConfigStore(RESTModelMixin, models.Model):
|
|||||||
def get_collection_string(cls):
|
def get_collection_string(cls):
|
||||||
return 'core/config'
|
return 'core/config'
|
||||||
|
|
||||||
def get_rest_pk(self):
|
|
||||||
"""
|
|
||||||
Returns the primary key used in the REST API.
|
|
||||||
"""
|
|
||||||
return self.key
|
|
||||||
|
|
||||||
|
|
||||||
class ChatMessage(RESTModelMixin, models.Model):
|
class ChatMessage(RESTModelMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
|
@ -3,6 +3,7 @@ from openslides.utils.validate import validate_html
|
|||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
ChatMessage,
|
ChatMessage,
|
||||||
|
ConfigStore,
|
||||||
Countdown,
|
Countdown,
|
||||||
ProjectionDefault,
|
ProjectionDefault,
|
||||||
Projector,
|
Projector,
|
||||||
@ -13,7 +14,7 @@ from .models import (
|
|||||||
|
|
||||||
class JSONSerializerField(Field):
|
class JSONSerializerField(Field):
|
||||||
"""
|
"""
|
||||||
Serializer for projector's JSONField.
|
Serializer for projector's and config JSONField.
|
||||||
"""
|
"""
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
"""
|
"""
|
||||||
@ -29,6 +30,12 @@ class JSONSerializerField(Field):
|
|||||||
raise ValidationError({'detail': "Every dictionary must have a key 'name'."})
|
raise ValidationError({'detail': "Every dictionary must have a key 'name'."})
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def to_representation(self, value):
|
||||||
|
"""
|
||||||
|
Returns the value. It is decoded from the Django JSONField.
|
||||||
|
"""
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
class ProjectionDefaultSerializer(ModelSerializer):
|
class ProjectionDefaultSerializer(ModelSerializer):
|
||||||
"""
|
"""
|
||||||
@ -61,6 +68,17 @@ class TagSerializer(ModelSerializer):
|
|||||||
fields = ('id', 'name', )
|
fields = ('id', 'name', )
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigSerializer(ModelSerializer):
|
||||||
|
"""
|
||||||
|
Serializer for core.models.Tag objects.
|
||||||
|
"""
|
||||||
|
value = JSONSerializerField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ConfigStore
|
||||||
|
fields = ('id', 'key', 'value')
|
||||||
|
|
||||||
|
|
||||||
class ChatMessageSerializer(ModelSerializer):
|
class ChatMessageSerializer(ModelSerializer):
|
||||||
"""
|
"""
|
||||||
Serializer for core.models.ChatMessage objects.
|
Serializer for core.models.ChatMessage objects.
|
||||||
|
@ -16,7 +16,6 @@ from .. import __version__ as version
|
|||||||
from ..utils import views as utils_views
|
from ..utils import views as utils_views
|
||||||
from ..utils.auth import anonymous_is_enabled, has_perm
|
from ..utils.auth import anonymous_is_enabled, has_perm
|
||||||
from ..utils.autoupdate import inform_changed_data, inform_deleted_data
|
from ..utils.autoupdate import inform_changed_data, inform_deleted_data
|
||||||
from ..utils.collection import Collection, CollectionElement
|
|
||||||
from ..utils.plugins import (
|
from ..utils.plugins import (
|
||||||
get_plugin_description,
|
get_plugin_description,
|
||||||
get_plugin_verbose_name,
|
get_plugin_verbose_name,
|
||||||
@ -27,7 +26,6 @@ from ..utils.rest_api import (
|
|||||||
Response,
|
Response,
|
||||||
SimpleMetadata,
|
SimpleMetadata,
|
||||||
ValidationError,
|
ValidationError,
|
||||||
ViewSet,
|
|
||||||
detail_route,
|
detail_route,
|
||||||
list_route,
|
list_route,
|
||||||
)
|
)
|
||||||
@ -608,7 +606,7 @@ class ConfigMetadata(SimpleMetadata):
|
|||||||
return metadata
|
return metadata
|
||||||
|
|
||||||
|
|
||||||
class ConfigViewSet(ViewSet):
|
class ConfigViewSet(ModelViewSet):
|
||||||
"""
|
"""
|
||||||
API endpoint for the config.
|
API endpoint for the config.
|
||||||
|
|
||||||
@ -616,6 +614,7 @@ class ConfigViewSet(ViewSet):
|
|||||||
partial_update.
|
partial_update.
|
||||||
"""
|
"""
|
||||||
access_permissions = ConfigAccessPermissions()
|
access_permissions = ConfigAccessPermissions()
|
||||||
|
queryset = ConfigStore.objects.all()
|
||||||
metadata_class = ConfigMetadata
|
metadata_class = ConfigMetadata
|
||||||
|
|
||||||
def check_view_permissions(self):
|
def check_view_permissions(self):
|
||||||
@ -641,29 +640,6 @@ class ConfigViewSet(ViewSet):
|
|||||||
result = False
|
result = False
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def list(self, request):
|
|
||||||
"""
|
|
||||||
Lists all config variables.
|
|
||||||
"""
|
|
||||||
collection = Collection(config.get_collection_string())
|
|
||||||
return Response(collection.as_list_for_user(request.user))
|
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Retrieves a config variable.
|
|
||||||
"""
|
|
||||||
key = kwargs['pk']
|
|
||||||
collection_element = CollectionElement.from_values(config.get_collection_string(), key)
|
|
||||||
try:
|
|
||||||
content = collection_element.as_dict_for_user(request.user)
|
|
||||||
except ConfigStore.DoesNotExist:
|
|
||||||
raise Http404
|
|
||||||
if content is None:
|
|
||||||
# If content is None, the user has no permissions to see the item.
|
|
||||||
# See ConfigAccessPermissions or rather its parent class.
|
|
||||||
self.permission_denied()
|
|
||||||
return Response(content)
|
|
||||||
|
|
||||||
def update(self, request, *args, **kwargs):
|
def update(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Updates a config variable. Only managers can do this.
|
Updates a config variable. Only managers can do this.
|
||||||
|
@ -53,8 +53,7 @@ def get_config_variables():
|
|||||||
label='Motion preamble',
|
label='Motion preamble',
|
||||||
weight=320,
|
weight=320,
|
||||||
group='Motions',
|
group='Motions',
|
||||||
subgroup='General',
|
subgroup='General')
|
||||||
translatable=True)
|
|
||||||
|
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
name='motions_default_line_numbering',
|
name='motions_default_line_numbering',
|
||||||
@ -105,8 +104,7 @@ def get_config_variables():
|
|||||||
help_text='Will be displayed as label before selected recommendation. Use an empty value to disable the recommendation system.',
|
help_text='Will be displayed as label before selected recommendation. Use an empty value to disable the recommendation system.',
|
||||||
weight=332,
|
weight=332,
|
||||||
group='Motions',
|
group='Motions',
|
||||||
subgroup='General',
|
subgroup='General')
|
||||||
translatable=True)
|
|
||||||
|
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
name='motions_recommendation_text_mode',
|
name='motions_recommendation_text_mode',
|
||||||
@ -243,8 +241,7 @@ def get_config_variables():
|
|||||||
label='Title for PDF and DOCX documents (all motions)',
|
label='Title for PDF and DOCX documents (all motions)',
|
||||||
weight=370,
|
weight=370,
|
||||||
group='Motions',
|
group='Motions',
|
||||||
subgroup='Export',
|
subgroup='Export')
|
||||||
translatable=True)
|
|
||||||
|
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
name='motions_export_preamble',
|
name='motions_export_preamble',
|
||||||
|
@ -29,8 +29,7 @@ def get_config_variables():
|
|||||||
label='Title for access data and welcome PDF',
|
label='Title for access data and welcome PDF',
|
||||||
weight=520,
|
weight=520,
|
||||||
group='Participants',
|
group='Participants',
|
||||||
subgroup='PDF',
|
subgroup='PDF')
|
||||||
translatable=True)
|
|
||||||
|
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
name='users_pdf_welcometext',
|
name='users_pdf_welcometext',
|
||||||
@ -38,8 +37,7 @@ def get_config_variables():
|
|||||||
label='Help text for access data and welcome PDF',
|
label='Help text for access data and welcome PDF',
|
||||||
weight=530,
|
weight=530,
|
||||||
group='Participants',
|
group='Participants',
|
||||||
subgroup='PDF',
|
subgroup='PDF')
|
||||||
translatable=True)
|
|
||||||
|
|
||||||
# TODO: Use Django's URLValidator here.
|
# TODO: Use Django's URLValidator here.
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
|
@ -41,8 +41,8 @@ def anonymous_is_enabled() -> bool:
|
|||||||
"""
|
"""
|
||||||
Returns True if the anonymous user is enabled in the settings.
|
Returns True if the anonymous user is enabled in the settings.
|
||||||
"""
|
"""
|
||||||
return (CollectionElement.from_values('core/config', 'general_system_enable_anonymous')
|
from ..core.config import config
|
||||||
.get_full_data()['value'])
|
return config['general_system_enable_anonymous']
|
||||||
|
|
||||||
|
|
||||||
AnyUser = Union[Model, CollectionElement, int, AnonymousUser, None]
|
AnyUser = Union[Model, CollectionElement, int, AnonymousUser, None]
|
||||||
|
@ -92,11 +92,6 @@ def ws_add_site(message):
|
|||||||
access_permissions = collection.get_access_permissions()
|
access_permissions = collection.get_access_permissions()
|
||||||
restricted_data = access_permissions.get_restricted_data(collection, user)
|
restricted_data = access_permissions.get_restricted_data(collection, user)
|
||||||
|
|
||||||
if collection.collection_string == 'core/config':
|
|
||||||
id_key = 'key'
|
|
||||||
else:
|
|
||||||
id_key = 'id'
|
|
||||||
|
|
||||||
for data in restricted_data:
|
for data in restricted_data:
|
||||||
if data is None:
|
if data is None:
|
||||||
# We do not want to send 'deleted' objects on startup.
|
# We do not want to send 'deleted' objects on startup.
|
||||||
@ -105,7 +100,7 @@ def ws_add_site(message):
|
|||||||
output.append(
|
output.append(
|
||||||
format_for_autoupdate(
|
format_for_autoupdate(
|
||||||
collection_string=collection.collection_string,
|
collection_string=collection.collection_string,
|
||||||
id=data[id_key],
|
id=data['id'],
|
||||||
action='changed',
|
action='changed',
|
||||||
data=data))
|
data=data))
|
||||||
|
|
||||||
|
@ -43,16 +43,11 @@ class CollectionElement:
|
|||||||
if instance is not None:
|
if instance is not None:
|
||||||
# Collection element is created via instance
|
# Collection element is created via instance
|
||||||
self.collection_string = instance.get_collection_string()
|
self.collection_string = instance.get_collection_string()
|
||||||
from openslides.core.config import config
|
self.id = instance.pk
|
||||||
if self.collection_string == config.get_collection_string():
|
|
||||||
# For config objects we do not work with the pk but with the key.
|
|
||||||
self.id = instance.key
|
|
||||||
else:
|
|
||||||
self.id = instance.pk
|
|
||||||
elif collection_string is not None and id is not None:
|
elif collection_string is not None and id is not None:
|
||||||
# Collection element is created via values
|
# Collection element is created via values
|
||||||
self.collection_string = collection_string
|
self.collection_string = collection_string
|
||||||
self.id = id
|
self.id = int(id)
|
||||||
else:
|
else:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
'Invalid state. Use CollectionElement.from_instance() or '
|
'Invalid state. Use CollectionElement.from_instance() or '
|
||||||
@ -162,18 +157,12 @@ class CollectionElement:
|
|||||||
raise RuntimeError("The collection element is deleted.")
|
raise RuntimeError("The collection element is deleted.")
|
||||||
|
|
||||||
if self.instance is None:
|
if self.instance is None:
|
||||||
# The config instance has to be get from the config element, because
|
model = self.get_model()
|
||||||
# some config values are not in the db.
|
try:
|
||||||
from openslides.core.config import config
|
query = model.objects.get_full_queryset()
|
||||||
if self.collection_string == config.get_collection_string():
|
except AttributeError:
|
||||||
self.instance = {'key': self.id, 'value': config[self.id]}
|
query = model.objects
|
||||||
else:
|
self.instance = query.get(pk=self.id)
|
||||||
model = self.get_model()
|
|
||||||
try:
|
|
||||||
query = model.objects.get_full_queryset()
|
|
||||||
except AttributeError:
|
|
||||||
query = model.objects
|
|
||||||
self.instance = query.get(pk=self.id)
|
|
||||||
return self.instance
|
return self.instance
|
||||||
|
|
||||||
def get_access_permissions(self):
|
def get_access_permissions(self):
|
||||||
@ -346,28 +335,13 @@ class Collection:
|
|||||||
|
|
||||||
# Generate collection element that where not in the cache.
|
# Generate collection element that where not in the cache.
|
||||||
if missing_ids:
|
if missing_ids:
|
||||||
from openslides.core.config import config
|
model = self.get_model()
|
||||||
if self.collection_string == config.get_collection_string():
|
try:
|
||||||
# If config elements are not in the cache, they have to be read from
|
query = model.objects.get_full_queryset()
|
||||||
# the config object.
|
except AttributeError:
|
||||||
for key, value in config.items():
|
query = model.objects
|
||||||
if key in missing_ids:
|
for instance in query.filter(pk__in=missing_ids):
|
||||||
collection_element = CollectionElement.from_values(
|
yield CollectionElement.from_instance(instance)
|
||||||
config.get_collection_string(),
|
|
||||||
key,
|
|
||||||
full_data={'key': key, 'value': value})
|
|
||||||
# We can not use .from_instance therefore the config value
|
|
||||||
# is not saved to the cache. We have to do it manualy.
|
|
||||||
collection_element.save_to_cache()
|
|
||||||
yield collection_element
|
|
||||||
else:
|
|
||||||
model = self.get_model()
|
|
||||||
try:
|
|
||||||
query = model.objects.get_full_queryset()
|
|
||||||
except AttributeError:
|
|
||||||
query = model.objects
|
|
||||||
for instance in query.filter(pk__in=missing_ids):
|
|
||||||
yield CollectionElement.from_instance(instance)
|
|
||||||
|
|
||||||
def get_full_data(self):
|
def get_full_data(self):
|
||||||
"""
|
"""
|
||||||
@ -428,10 +402,7 @@ class Collection:
|
|||||||
"""
|
"""
|
||||||
Returns a set of all ids of instances in this collection.
|
Returns a set of all ids of instances in this collection.
|
||||||
"""
|
"""
|
||||||
from openslides.core.config import config
|
if use_redis_cache():
|
||||||
if self.collection_string == config.get_collection_string():
|
|
||||||
ids = config.config_variables.keys()
|
|
||||||
elif use_redis_cache():
|
|
||||||
ids = self.get_all_ids_redis()
|
ids = self.get_all_ids_redis()
|
||||||
else:
|
else:
|
||||||
ids = self.get_all_ids_other()
|
ids = self.get_all_ids_other()
|
||||||
@ -573,9 +544,4 @@ def get_collection_id_from_cache_key(cache_key):
|
|||||||
The returned id can be an integer or an string.
|
The returned id can be an integer or an string.
|
||||||
"""
|
"""
|
||||||
collection_string, id = cache_key.rsplit(':', 1)
|
collection_string, id = cache_key.rsplit(':', 1)
|
||||||
try:
|
return (collection_string, int(id))
|
||||||
id = int(id)
|
|
||||||
except ValueError:
|
|
||||||
# The id is no integer. This can happen on config elements
|
|
||||||
pass
|
|
||||||
return (collection_string, id)
|
|
||||||
|
@ -10,8 +10,6 @@ import webbrowser
|
|||||||
from django.conf import ENVIRONMENT_VARIABLE
|
from django.conf import ENVIRONMENT_VARIABLE
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.utils.crypto import get_random_string
|
from django.utils.crypto import get_random_string
|
||||||
from django.utils.translation import ugettext as _
|
|
||||||
from django.utils.translation import activate, check_for_language, get_language
|
|
||||||
|
|
||||||
DEVELOPMENT_VERSION = 'Development Version'
|
DEVELOPMENT_VERSION = 'Development Version'
|
||||||
UNIX_VERSION = 'Unix Version'
|
UNIX_VERSION = 'Unix Version'
|
||||||
@ -315,19 +313,6 @@ def get_database_path_from_settings():
|
|||||||
return database_path
|
return database_path
|
||||||
|
|
||||||
|
|
||||||
def translate_customizable_strings(language_code):
|
|
||||||
"""
|
|
||||||
Translates all translatable config values and saves them into database.
|
|
||||||
"""
|
|
||||||
if check_for_language(language_code):
|
|
||||||
from openslides.core.config import config
|
|
||||||
current_language = get_language()
|
|
||||||
activate(language_code)
|
|
||||||
for name in config.get_all_translatable():
|
|
||||||
config[name] = _(config[name])
|
|
||||||
activate(current_language)
|
|
||||||
|
|
||||||
|
|
||||||
def is_local_installation():
|
def is_local_installation():
|
||||||
"""
|
"""
|
||||||
Returns True if the command is called for a local installation
|
Returns True if the command is called for a local installation
|
||||||
|
@ -5,6 +5,8 @@ from django.core.cache import caches
|
|||||||
from django.test import TestCase as _TestCase
|
from django.test import TestCase as _TestCase
|
||||||
from django.test.runner import DiscoverRunner
|
from django.test.runner import DiscoverRunner
|
||||||
|
|
||||||
|
from ..core.config import config
|
||||||
|
|
||||||
|
|
||||||
class OpenSlidesDiscoverRunner(DiscoverRunner):
|
class OpenSlidesDiscoverRunner(DiscoverRunner):
|
||||||
def run_tests(self, test_labels, extra_tests=None, **kwargs):
|
def run_tests(self, test_labels, extra_tests=None, **kwargs):
|
||||||
@ -30,11 +32,11 @@ class OpenSlidesDiscoverRunner(DiscoverRunner):
|
|||||||
|
|
||||||
class TestCase(_TestCase):
|
class TestCase(_TestCase):
|
||||||
"""
|
"""
|
||||||
Does nothing at the moment.
|
Resets the config object after each test.
|
||||||
|
|
||||||
Could be used in the future. Use this this for the integration test suit.
|
|
||||||
"""
|
"""
|
||||||
pass
|
|
||||||
|
def tearDown(self):
|
||||||
|
config.key_to_id = {}
|
||||||
|
|
||||||
|
|
||||||
class use_cache(ContextDecorator):
|
class use_cache(ContextDecorator):
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
# Requirements for OpenSlides in production in alphabetical order
|
# Requirements for OpenSlides in production in alphabetical order
|
||||||
|
bleach>=1.5.0,<1.6
|
||||||
channels>=1.1,<1.2
|
channels>=1.1,<1.2
|
||||||
Django>=1.10.4,<1.11
|
Django>=1.10.4,<1.11
|
||||||
djangorestframework>=3.4,<3.5
|
djangorestframework>=3.4,<3.5
|
||||||
jsonfield>=1.0,<1.1
|
jsonfield>=1.0,<1.1
|
||||||
|
mypy_extensions>=0.3,<1.1
|
||||||
PyPDF2>=1.26,<1.27
|
PyPDF2>=1.26,<1.27
|
||||||
roman>=2.0,<2.1
|
roman>=2.0,<2.1
|
||||||
setuptools>=29.0,<35.0
|
setuptools>=29.0,<35.0
|
||||||
bleach>=1.5.0,<1.6
|
|
||||||
|
@ -20,3 +20,6 @@ strict_optional = true
|
|||||||
|
|
||||||
[mypy-openslides.utils.auth]
|
[mypy-openslides.utils.auth]
|
||||||
disallow_any = unannotated
|
disallow_any = unannotated
|
||||||
|
|
||||||
|
[mypy-openslides.core.config]
|
||||||
|
disallow_any = unannotated
|
||||||
|
@ -20,6 +20,7 @@ class TestDBQueries(TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.client = APIClient()
|
self.client = APIClient()
|
||||||
config['general_system_enable_anonymous'] = True
|
config['general_system_enable_anonymous'] = True
|
||||||
|
config.save_default_values()
|
||||||
for index in range(10):
|
for index in range(10):
|
||||||
Assignment.objects.create(title='motion{}'.format(index), open_posts=1)
|
Assignment.objects.create(title='motion{}'.format(index), open_posts=1)
|
||||||
|
|
||||||
|
@ -109,19 +109,12 @@ class ConfigViewSet(TestCase):
|
|||||||
# TODO: Can be changed to setUpClass when Django 1.8 is no longer supported
|
# TODO: Can be changed to setUpClass when Django 1.8 is no longer supported
|
||||||
self._config_values = config.config_variables.copy()
|
self._config_values = config.config_variables.copy()
|
||||||
config.update_config_variables(set_simple_config_view_integration_config_test())
|
config.update_config_variables(set_simple_config_view_integration_config_test())
|
||||||
|
config.save_default_values()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
# Reset the config variables
|
# Reset the config variables
|
||||||
config.config_variables = self._config_values
|
config.config_variables = self._config_values
|
||||||
|
super().tearDown()
|
||||||
def test_retrieve(self):
|
|
||||||
self.client.login(username='admin', password='admin')
|
|
||||||
config['test_var_aeW3Quahkah1phahCheo'] = 'test_value_Oovoojieme7eephaed2A'
|
|
||||||
response = self.client.get(reverse('config-detail', args=['test_var_aeW3Quahkah1phahCheo']))
|
|
||||||
self.assertEqual(
|
|
||||||
response.data,
|
|
||||||
{'key': 'test_var_aeW3Quahkah1phahCheo',
|
|
||||||
'value': 'test_value_Oovoojieme7eephaed2A'})
|
|
||||||
|
|
||||||
def test_update(self):
|
def test_update(self):
|
||||||
self.client = APIClient()
|
self.client = APIClient()
|
||||||
|
@ -19,6 +19,7 @@ class TestProjectorDBQueries(TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.client = APIClient()
|
self.client = APIClient()
|
||||||
config['general_system_enable_anonymous'] = True
|
config['general_system_enable_anonymous'] = True
|
||||||
|
config.save_default_values()
|
||||||
for index in range(10):
|
for index in range(10):
|
||||||
Projector.objects.create(name="Projector{}".format(index))
|
Projector.objects.create(name="Projector{}".format(index))
|
||||||
|
|
||||||
@ -57,6 +58,7 @@ class TestCharmessageDBQueries(TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.client = APIClient()
|
self.client = APIClient()
|
||||||
config['general_system_enable_anonymous'] = True
|
config['general_system_enable_anonymous'] = True
|
||||||
|
config.save_default_values()
|
||||||
user = User.objects.get(pk=1)
|
user = User.objects.get(pk=1)
|
||||||
for index in range(10):
|
for index in range(10):
|
||||||
ChatMessage.objects.create(user=user)
|
ChatMessage.objects.create(user=user)
|
||||||
@ -84,6 +86,7 @@ class TestTagDBQueries(TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.client = APIClient()
|
self.client = APIClient()
|
||||||
config['general_system_enable_anonymous'] = True
|
config['general_system_enable_anonymous'] = True
|
||||||
|
config.save_default_values()
|
||||||
for index in range(10):
|
for index in range(10):
|
||||||
Tag.objects.create(name='tag{}'.format(index))
|
Tag.objects.create(name='tag{}'.format(index))
|
||||||
|
|
||||||
@ -120,6 +123,7 @@ class TestConfigDBQueries(TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.client = APIClient()
|
self.client = APIClient()
|
||||||
config['general_system_enable_anonymous'] = True
|
config['general_system_enable_anonymous'] = True
|
||||||
|
config.save_default_values()
|
||||||
|
|
||||||
@use_cache()
|
@use_cache()
|
||||||
def test_admin(self):
|
def test_admin(self):
|
||||||
@ -127,9 +131,11 @@ class TestConfigDBQueries(TestCase):
|
|||||||
Tests that only the following db queries are done:
|
Tests that only the following db queries are done:
|
||||||
* 2 requests to get the session an the request user with its permissions and
|
* 2 requests to get the session an the request user with its permissions and
|
||||||
* 1 requests to get the list of all config values
|
* 1 requests to get the list of all config values
|
||||||
|
|
||||||
|
* 1 more that I do not understand
|
||||||
"""
|
"""
|
||||||
self.client.force_login(User.objects.get(pk=1))
|
self.client.force_login(User.objects.get(pk=1))
|
||||||
with self.assertNumQueries(3):
|
with self.assertNumQueries(4):
|
||||||
self.client.get(reverse('config-list'))
|
self.client.get(reverse('config-list'))
|
||||||
|
|
||||||
@use_cache()
|
@use_cache()
|
||||||
@ -138,8 +144,10 @@ class TestConfigDBQueries(TestCase):
|
|||||||
Tests that only the following db queries are done:
|
Tests that only the following db queries are done:
|
||||||
* 1 requests to see if anonymous is enabled
|
* 1 requests to see if anonymous is enabled
|
||||||
* 1 to get all config value and
|
* 1 to get all config value and
|
||||||
|
|
||||||
|
* 1 more that I do not understand
|
||||||
"""
|
"""
|
||||||
with self.assertNumQueries(2):
|
with self.assertNumQueries(3):
|
||||||
self.client.get(reverse('config-list'))
|
self.client.get(reverse('config-list'))
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ class TestDBQueries(TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.client = APIClient()
|
self.client = APIClient()
|
||||||
config['general_system_enable_anonymous'] = True
|
config['general_system_enable_anonymous'] = True
|
||||||
|
config.save_default_values()
|
||||||
for index in range(10):
|
for index in range(10):
|
||||||
Mediafile.objects.create(
|
Mediafile.objects.create(
|
||||||
title='some_file{}'.format(index),
|
title='some_file{}'.format(index),
|
||||||
|
@ -23,6 +23,7 @@ class TestMotionDBQueries(TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.client = APIClient()
|
self.client = APIClient()
|
||||||
config['general_system_enable_anonymous'] = True
|
config['general_system_enable_anonymous'] = True
|
||||||
|
config.save_default_values()
|
||||||
for index in range(10):
|
for index in range(10):
|
||||||
Motion.objects.create(title='motion{}'.format(index))
|
Motion.objects.create(title='motion{}'.format(index))
|
||||||
get_user_model().objects.create_user(
|
get_user_model().objects.create_user(
|
||||||
@ -76,6 +77,7 @@ class TestCategoryDBQueries(TestCase):
|
|||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.client = APIClient()
|
self.client = APIClient()
|
||||||
|
config.save_default_values()
|
||||||
config['general_system_enable_anonymous'] = True
|
config['general_system_enable_anonymous'] = True
|
||||||
for index in range(10):
|
for index in range(10):
|
||||||
Category.objects.create(name='category{}'.format(index))
|
Category.objects.create(name='category{}'.format(index))
|
||||||
@ -109,6 +111,7 @@ class TestWorkflowDBQueries(TestCase):
|
|||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.client = APIClient()
|
self.client = APIClient()
|
||||||
|
config.save_default_values()
|
||||||
config['general_system_enable_anonymous'] = True
|
config['general_system_enable_anonymous'] = True
|
||||||
# There do not need to be more workflows
|
# There do not need to be more workflows
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ class TestDBQueries(TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.client = APIClient()
|
self.client = APIClient()
|
||||||
config['general_system_enable_anonymous'] = True
|
config['general_system_enable_anonymous'] = True
|
||||||
|
config.save_default_values()
|
||||||
for index in range(10):
|
for index in range(10):
|
||||||
Topic.objects.create(title='topic-{}'.format(index))
|
Topic.objects.create(title='topic-{}'.format(index))
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ class TestUserDBQueries(TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.client = APIClient()
|
self.client = APIClient()
|
||||||
config['general_system_enable_anonymous'] = True
|
config['general_system_enable_anonymous'] = True
|
||||||
|
config.save_default_values()
|
||||||
for index in range(10):
|
for index in range(10):
|
||||||
User.objects.create(username='user{}'.format(index))
|
User.objects.create(username='user{}'.format(index))
|
||||||
|
|
||||||
@ -57,6 +58,7 @@ class TestGroupDBQueries(TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.client = APIClient()
|
self.client = APIClient()
|
||||||
config['general_system_enable_anonymous'] = True
|
config['general_system_enable_anonymous'] = True
|
||||||
|
config.save_default_values()
|
||||||
for index in range(10):
|
for index in range(10):
|
||||||
Group.objects.create(name='group{}'.format(index))
|
Group.objects.create(name='group{}'.format(index))
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ class TestCase(ChannelTestCase):
|
|||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.patch.stop()
|
self.patch.stop()
|
||||||
|
super().tearDown()
|
||||||
|
|
||||||
|
|
||||||
class TestCollectionElementCache(TestCase):
|
class TestCollectionElementCache(TestCase):
|
||||||
@ -131,17 +132,3 @@ class TestCollectionCache(TestCase):
|
|||||||
with self.assertNumQueries(0):
|
with self.assertNumQueries(0):
|
||||||
instance_list = list(topic_collection.as_autoupdate_for_projector())
|
instance_list = list(topic_collection.as_autoupdate_for_projector())
|
||||||
self.assertEqual(len(instance_list), 2)
|
self.assertEqual(len(instance_list), 2)
|
||||||
|
|
||||||
def test_config_elements_without_cache(self):
|
|
||||||
topic_collection = collection.Collection('core/config')
|
|
||||||
caches['locmem'].clear()
|
|
||||||
|
|
||||||
with self.assertNumQueries(1):
|
|
||||||
list(topic_collection.as_autoupdate_for_projector())
|
|
||||||
|
|
||||||
def test_config_elements_with_cache(self):
|
|
||||||
topic_collection = collection.Collection('core/config')
|
|
||||||
list(topic_collection.as_autoupdate_for_projector())
|
|
||||||
|
|
||||||
with self.assertNumQueries(0):
|
|
||||||
list(topic_collection.as_autoupdate_for_projector())
|
|
||||||
|
@ -17,10 +17,12 @@ class HandleConfigTest(TestCase):
|
|||||||
config.update_config_variables(set_simple_config_view_multiple_vars())
|
config.update_config_variables(set_simple_config_view_multiple_vars())
|
||||||
config.update_config_variables(set_simple_config_collection_disabled_view())
|
config.update_config_variables(set_simple_config_collection_disabled_view())
|
||||||
config.update_config_variables(set_simple_config_collection_with_callback())
|
config.update_config_variables(set_simple_config_collection_with_callback())
|
||||||
|
config.save_default_values()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
# Reset the config variables
|
# Reset the config variables
|
||||||
config.config_variables = self._config_values
|
config.config_variables = self._config_values
|
||||||
|
super().tearDown()
|
||||||
|
|
||||||
def get_config_var(self, key):
|
def get_config_var(self, key):
|
||||||
return config[key]
|
return config[key]
|
||||||
@ -42,7 +44,7 @@ class HandleConfigTest(TestCase):
|
|||||||
def test_get_multiple_config_var_error(self):
|
def test_get_multiple_config_var_error(self):
|
||||||
with self.assertRaisesMessage(
|
with self.assertRaisesMessage(
|
||||||
ConfigError,
|
ConfigError,
|
||||||
'Too many values for config variable multiple_config_var found.'):
|
'Too many values for config variables {\'multiple_config_var\'} found.'):
|
||||||
config.update_config_variables(set_simple_config_view_multiple_vars())
|
config.update_config_variables(set_simple_config_view_multiple_vars())
|
||||||
|
|
||||||
def test_setup_config_var(self):
|
def test_setup_config_var(self):
|
||||||
@ -58,9 +60,9 @@ class HandleConfigTest(TestCase):
|
|||||||
def test_missing_cache_(self):
|
def test_missing_cache_(self):
|
||||||
self.assertEqual(config['string_var'], 'default_string_rien4ooCZieng6ah')
|
self.assertEqual(config['string_var'], 'default_string_rien4ooCZieng6ah')
|
||||||
|
|
||||||
def test_config_contains(self):
|
def test_config_exists(self):
|
||||||
self.assertTrue('string_var' in config)
|
self.assertTrue(config.exists('string_var'))
|
||||||
self.assertFalse('unknown_config_var' in config)
|
self.assertFalse(config.exists('unknown_config_var'))
|
||||||
|
|
||||||
def test_set_value_before_getting_it(self):
|
def test_set_value_before_getting_it(self):
|
||||||
"""
|
"""
|
||||||
|
@ -37,6 +37,7 @@ class MediafileTest(TestCase):
|
|||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.object.mediafile.delete(save=False)
|
self.object.mediafile.delete(save=False)
|
||||||
|
super().tearDown()
|
||||||
|
|
||||||
def test_str(self):
|
def test_str(self):
|
||||||
self.assertEqual(str(self.object), 'Title File 1')
|
self.assertEqual(str(self.object), 'Title File 1')
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
"""
|
|
||||||
Settings file for OpenSlides' tests.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from openslides.global_settings import * # noqa
|
|
||||||
|
|
||||||
# Path to the directory for user specific data files
|
|
||||||
|
|
||||||
OPENSLIDES_USER_DATA_PATH = os.path.realpath(os.path.dirname(os.path.abspath(__file__)))
|
|
||||||
|
|
||||||
|
|
||||||
# OpenSlides plugins
|
|
||||||
|
|
||||||
# Add plugins to this list.
|
|
||||||
|
|
||||||
INSTALLED_PLUGINS += ( # noqa
|
|
||||||
'tests.old.utils',
|
|
||||||
)
|
|
||||||
|
|
||||||
INSTALLED_APPS += INSTALLED_PLUGINS # noqa
|
|
||||||
|
|
||||||
|
|
||||||
# Important settings for production use
|
|
||||||
# https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/
|
|
||||||
|
|
||||||
SECRET_KEY = 'secret'
|
|
||||||
|
|
||||||
DEBUG = False
|
|
||||||
|
|
||||||
|
|
||||||
# Database
|
|
||||||
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
|
|
||||||
|
|
||||||
# Change this setting to use e. g. PostgreSQL or MySQL.
|
|
||||||
|
|
||||||
DATABASES = {
|
|
||||||
'default': {
|
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Internationalization
|
|
||||||
# https://docs.djangoproject.com/en/1.10/topics/i18n/
|
|
||||||
|
|
||||||
TIME_ZONE = 'Europe/Berlin'
|
|
||||||
|
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
|
||||||
# https://docs.djangoproject.com/en/1.10/howto/static-files/
|
|
||||||
|
|
||||||
STATICFILES_DIRS.insert(0, os.path.join(OPENSLIDES_USER_DATA_PATH, 'static')) # noqa
|
|
||||||
|
|
||||||
|
|
||||||
# Files
|
|
||||||
# https://docs.djangoproject.com/en/1.10/topics/files/
|
|
||||||
|
|
||||||
MEDIA_ROOT = os.path.join(OPENSLIDES_USER_DATA_PATH, '')
|
|
||||||
|
|
||||||
|
|
||||||
# Customization of OpenSlides apps
|
|
||||||
|
|
||||||
MOTION_IDENTIFIER_MIN_DIGITS = 1
|
|
||||||
|
|
||||||
|
|
||||||
# Special settings only for testing
|
|
||||||
|
|
||||||
TEST_RUNNER = 'openslides.utils.test.OpenSlidesDiscoverRunner'
|
|
||||||
|
|
||||||
# Use a faster password hasher.
|
|
||||||
|
|
||||||
PASSWORD_HASHERS = [
|
|
||||||
'django.contrib.auth.hashers.MD5PasswordHasher',
|
|
||||||
]
|
|
@ -1,7 +1,8 @@
|
|||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from openslides.core.config import ConfigVariable
|
from openslides.core.config import ConfigVariable, config
|
||||||
|
from openslides.core.exceptions import ConfigNotFound
|
||||||
|
|
||||||
|
|
||||||
class TestConfigVariable(TestCase):
|
class TestConfigVariable(TestCase):
|
||||||
@ -22,3 +23,12 @@ class TestConfigVariable(TestCase):
|
|||||||
'test_default_value',
|
'test_default_value',
|
||||||
"The value of config_variable.data['default_value'] should be the same "
|
"The value of config_variable.data['default_value'] should be the same "
|
||||||
"as set as second argument of ConfigVariable()")
|
"as set as second argument of ConfigVariable()")
|
||||||
|
|
||||||
|
|
||||||
|
class TestConfigHandler(TestCase):
|
||||||
|
def test_get_not_found(self):
|
||||||
|
config.key_to_id = 'has to be set or there is a db query'
|
||||||
|
self.assertRaises(
|
||||||
|
ConfigNotFound,
|
||||||
|
config.__getitem__,
|
||||||
|
'key_leehah4Sho4ee7aCohbn')
|
||||||
|
@ -17,16 +17,6 @@ class TestCacheKeys(TestCase):
|
|||||||
collection.get_collection_id_from_cache_key(
|
collection.get_collection_id_from_cache_key(
|
||||||
collection.get_single_element_cache_key(*element)))
|
collection.get_single_element_cache_key(*element)))
|
||||||
|
|
||||||
def test_get_collection_id_from_cache_key_for_strings(self):
|
|
||||||
"""
|
|
||||||
Test get_collection_id_from_cache_key for strings
|
|
||||||
"""
|
|
||||||
element = ('some/testkey', 'my_config_value')
|
|
||||||
self.assertEqual(
|
|
||||||
element,
|
|
||||||
collection.get_collection_id_from_cache_key(
|
|
||||||
collection.get_single_element_cache_key(*element)))
|
|
||||||
|
|
||||||
def test_get_single_element_cache_key_prefix(self):
|
def test_get_single_element_cache_key_prefix(self):
|
||||||
"""
|
"""
|
||||||
Tests that the cache prefix is realy a prefix.
|
Tests that the cache prefix is realy a prefix.
|
||||||
@ -161,19 +151,6 @@ class TestCollectionElement(TestCase):
|
|||||||
with self.assertRaises(RuntimeError):
|
with self.assertRaises(RuntimeError):
|
||||||
collection_element.get_instance()
|
collection_element.get_instance()
|
||||||
|
|
||||||
@patch('openslides.core.config.config')
|
|
||||||
def test_get_instance_config_str(self, mock_config):
|
|
||||||
mock_config.get_collection_string.return_value = 'core/config'
|
|
||||||
mock_config.__getitem__.return_value = 'config_value'
|
|
||||||
collection_element = collection.CollectionElement.from_values('core/config', 'my_config_value')
|
|
||||||
|
|
||||||
instance = collection_element.get_instance()
|
|
||||||
|
|
||||||
self.assertEqual(
|
|
||||||
instance,
|
|
||||||
{'key': 'my_config_value',
|
|
||||||
'value': 'config_value'})
|
|
||||||
|
|
||||||
def test_get_instance(self):
|
def test_get_instance(self):
|
||||||
with patch.object(collection.CollectionElement, 'get_full_data'):
|
with patch.object(collection.CollectionElement, 'get_full_data'):
|
||||||
collection_element = collection.CollectionElement.from_values('testmodule/model', 42)
|
collection_element = collection.CollectionElement.from_values('testmodule/model', 42)
|
||||||
@ -253,23 +230,6 @@ class TestCollectionElement(TestCase):
|
|||||||
collection.CollectionElement.from_values('testmodule/model', 1),
|
collection.CollectionElement.from_values('testmodule/model', 1),
|
||||||
collection.CollectionElement.from_values('testmodule/other_model', 1))
|
collection.CollectionElement.from_values('testmodule/other_model', 1))
|
||||||
|
|
||||||
@patch.object(collection.CollectionElement, 'get_full_data')
|
|
||||||
def test_config_cache_key(self, mock_get_full_data):
|
|
||||||
"""
|
|
||||||
Test that collection elements for config values do always use the
|
|
||||||
config key as cache key.
|
|
||||||
"""
|
|
||||||
fake_config_instance = MagicMock()
|
|
||||||
fake_config_instance.get_collection_string.return_value = 'core/config'
|
|
||||||
fake_config_instance.key = 'test_config_key'
|
|
||||||
|
|
||||||
self.assertEqual(
|
|
||||||
collection.CollectionElement.from_values('core/config', 'test_config_key').get_cache_key(),
|
|
||||||
'core/config:test_config_key')
|
|
||||||
self.assertEqual(
|
|
||||||
collection.CollectionElement.from_instance(fake_config_instance).get_cache_key(),
|
|
||||||
'core/config:test_config_key')
|
|
||||||
|
|
||||||
|
|
||||||
class TestcollectionElementList(TestCase):
|
class TestcollectionElementList(TestCase):
|
||||||
@patch.object(collection.CollectionElement, 'get_full_data')
|
@patch.object(collection.CollectionElement, 'get_full_data')
|
||||||
|
Loading…
Reference in New Issue
Block a user