More tests, development instructions and small changes.
This commit is contained in:
parent
7cd70a568c
commit
ac9c9f4ec3
@ -109,3 +109,31 @@ a. Running Angular.js test cases
|
||||
''''''''''''''''''''''''''''''''
|
||||
|
||||
$ node_modules/.bin/karma start tests/karma/karma.conf.js
|
||||
|
||||
|
||||
Installation Openslides in big mode
|
||||
===================================
|
||||
|
||||
1. Install PostgreSQL und redis:
|
||||
|
||||
apt-get install postgresql redis-server libpg-dev
|
||||
|
||||
TODO: Configure postgresql
|
||||
|
||||
2. Install python dependencies
|
||||
|
||||
pip install django-redis asgi-redis psycopg2
|
||||
|
||||
3. Change settings.py
|
||||
|
||||
(See comments in the settings)
|
||||
|
||||
The relevant settings are: DATABASES, CHANNEL_LAYERS, CACHES
|
||||
|
||||
4. Start one or more workers:
|
||||
|
||||
python manage.py runworker
|
||||
|
||||
5. Start daphne. Set the DJANGO_SETTINGS_MODULE and the PYTHONPATH
|
||||
|
||||
DJANGO_SETTINGS_MODULE=settings PYTHONPATH=personal_data/var/ daphne openslides.asgi:channel_layer
|
||||
|
@ -116,7 +116,7 @@ class Projector(RESTModelMixin, models.Model):
|
||||
result[key]['error'] = str(e)
|
||||
return result
|
||||
|
||||
def get_all_requirements(self, on_slide=None): #TODO: Refactor or rename this.
|
||||
def get_all_requirements(self, on_slide=None): # TODO autoupdate: Refactor or rename this.
|
||||
"""
|
||||
Generator which returns all instances that are shown on this projector.
|
||||
"""
|
||||
|
@ -23,7 +23,7 @@ def get_logged_in_users():
|
||||
return User.objects.exclude(session=None).filter(session__expire_date__gte=timezone.now()).distinct()
|
||||
|
||||
|
||||
def get_projector_element_data(projector, on_slide=None): #TODO
|
||||
def get_projector_element_data(projector, on_slide=None): # TODO autoupdate
|
||||
"""
|
||||
Returns a list of dicts that are required for a specific projector.
|
||||
|
||||
@ -81,7 +81,7 @@ def ws_add_projector(message, projector_id):
|
||||
Group('projector-{}'.format(projector_id)).add(message.reply_channel)
|
||||
|
||||
# Send all elements that are on the projector.
|
||||
output = get_projector_element_data(projector) #TODO
|
||||
output = get_projector_element_data(projector) # TODO autoupdate
|
||||
|
||||
# Send all config elements.
|
||||
collection = Collection(config.get_collection_string())
|
||||
@ -102,7 +102,7 @@ def ws_disconnect_projector(message, projector_id):
|
||||
Group('projector-{}'.format(projector_id)).discard(message.reply_channel)
|
||||
|
||||
|
||||
def send_data(message): #TODO
|
||||
def send_data(message): # TODO autoupdate
|
||||
"""
|
||||
Informs all users about changed data.
|
||||
"""
|
||||
|
@ -35,7 +35,7 @@ class CollectionElement:
|
||||
self.instance = instance
|
||||
self.deleted = deleted
|
||||
self.full_data = full_data
|
||||
self.information = information
|
||||
self.information = information or {}
|
||||
if instance is not None:
|
||||
self.collection_string = instance.get_collection_string()
|
||||
self.id = instance.pk
|
||||
@ -56,6 +56,16 @@ class CollectionElement:
|
||||
# then update the cache.
|
||||
self.save_to_cache()
|
||||
|
||||
def __eq__(self, collection_element):
|
||||
"""
|
||||
Compares two collection_elements.
|
||||
|
||||
Two collection elements are equal, if they have the same collection_string
|
||||
and id.
|
||||
"""
|
||||
return (self.collection_string == collection_element.collection_string and
|
||||
self.id == collection_element.id)
|
||||
|
||||
def as_channels_message(self):
|
||||
"""
|
||||
Returns a dictonary that can be used to send the object through the
|
||||
@ -224,7 +234,7 @@ class Collection:
|
||||
"""
|
||||
key = get_element_list_cache_key(self.collection_string)
|
||||
if raw:
|
||||
key = self.make_key(key)
|
||||
key = cache.make_key(key)
|
||||
return key
|
||||
|
||||
def get_model(self):
|
||||
|
@ -61,15 +61,15 @@ class RESTModelMixin:
|
||||
|
||||
If skip_autoupdate is set to True, then the autoupdate system is not
|
||||
informed about the model changed. This also means, that the model cache
|
||||
is not updated. You have to do it manually. Like this:
|
||||
is not updated. You have to do this manually, by creating a collection
|
||||
element from the instance:
|
||||
|
||||
TODO HELP ME
|
||||
CollectionElement.from_instance(instance)
|
||||
|
||||
The optional argument information can be a dictionary that is given to
|
||||
the autoupdate system.
|
||||
"""
|
||||
#TODO: Add example in docstring.
|
||||
#TODO: Fix circular imports
|
||||
# We don't know how to fix this circular import
|
||||
from .autoupdate import inform_changed_data
|
||||
return_value = super().save(*args, **kwargs)
|
||||
if not skip_autoupdate:
|
||||
@ -80,16 +80,27 @@ class RESTModelMixin:
|
||||
"""
|
||||
Calls Django's delete() method and afterwards hits the autoupdate system.
|
||||
|
||||
See the save method above.
|
||||
If skip_autoupdate is set to True, then the autoupdate system is not
|
||||
informed about the model changed. This also means, that the model cache
|
||||
is not updated. You have to do this manually, by creating a collection
|
||||
element from the instance:
|
||||
|
||||
CollectionElement.from_instance(instance, deleted=True)
|
||||
|
||||
or
|
||||
|
||||
CollectionElement.from_values(collection_string, id, deleted=True)
|
||||
|
||||
The optional argument information can be a dictionary that is given to
|
||||
the autoupdate system.
|
||||
"""
|
||||
#TODO: Fix circular imports
|
||||
# We don't know how to fix this circular import
|
||||
from .autoupdate import inform_changed_data, inform_deleted_data
|
||||
instance_pk = self.pk
|
||||
return_value = super().delete(*args, **kwargs)
|
||||
if not skip_autoupdate:
|
||||
if self != self.get_root_rest_element():
|
||||
# The deletion of a included element is a change of the root element.
|
||||
#TODO: Does this work in any case with self.pk == None?
|
||||
inform_changed_data(self.get_root_rest_element(), information=information)
|
||||
else:
|
||||
inform_deleted_data(self.get_collection_string(), instance_pk, information=information)
|
||||
|
@ -46,6 +46,17 @@ DEBUG = %(debug)s
|
||||
|
||||
# Change this setting to use e. g. PostgreSQL or MySQL.
|
||||
|
||||
# DATABASES = {
|
||||
# 'default': {
|
||||
# 'ENGINE': 'django.db.backends.postgresql',
|
||||
# 'NAME': 'mydatabase',
|
||||
# 'USER': 'mydatabaseuser',
|
||||
# 'PASSWORD': 'mypassword',
|
||||
# 'HOST': '127.0.0.1',
|
||||
# 'PORT': '5432',
|
||||
# }
|
||||
# }
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
@ -64,7 +75,12 @@ DATABASES = {
|
||||
|
||||
# CHANNEL_LAYERS['default']['BACKEND'] = 'asgi_redis.RedisChannelLayer'
|
||||
|
||||
# https://niwinz.github.io/django-redis/latest/#_user_guide
|
||||
|
||||
# Caching
|
||||
|
||||
# Django uses a inmemory cache at default. This supports only one thread. If
|
||||
# you use more then one thread another caching backend is required. We recommand
|
||||
# django-redis: https://niwinz.github.io/django-redis/latest/#_user_guide
|
||||
|
||||
# CACHES = {
|
||||
# "default": {
|
||||
|
@ -40,7 +40,7 @@ class RetrieveItem(TestCase):
|
||||
permission = group.permissions.get(content_type__app_label=app_label, codename=codename)
|
||||
group.permissions.remove(permission)
|
||||
response = self.client.get(reverse('item-detail', args=[self.item.pk]))
|
||||
self.assertEqual(response.status_code, status.HTTP_403_PERMISSION_DENIED)
|
||||
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||
|
||||
|
||||
class TestDBQueries(TestCase):
|
||||
|
@ -19,7 +19,7 @@ class TestProjectorDBQueries(TestCase):
|
||||
self.client = APIClient()
|
||||
config['general_system_enable_anonymous'] = True
|
||||
for index in range(10):
|
||||
Projector.objects.create()
|
||||
Projector.objects.create(name="Projector{}".format(index))
|
||||
|
||||
def test_admin(self):
|
||||
"""
|
||||
@ -137,9 +137,9 @@ class TestConfigDBQueries(TestCase):
|
||||
* 2 requests to get the permission for anonymous (config and permissions),
|
||||
* 1 to get all config value and
|
||||
|
||||
* 55 requests to find out if anonymous is enabled.
|
||||
* 57 requests to find out if anonymous is enabled.
|
||||
|
||||
TODO: The last 55 requests are a bug.
|
||||
TODO: The last 57 requests are a bug.
|
||||
"""
|
||||
with self.assertNumQueries(58):
|
||||
with self.assertNumQueries(60):
|
||||
self.client.get(reverse('config-list'))
|
||||
|
@ -90,7 +90,8 @@ class TestCollectionElement(TestCase):
|
||||
collection_element.as_channels_message(),
|
||||
{'collection_string': 'testmodule/model',
|
||||
'id': 42,
|
||||
'deleted': False})
|
||||
'deleted': False,
|
||||
'information': {}})
|
||||
|
||||
def test_as_autoupdate_for_user(self):
|
||||
collection_element = collection.CollectionElement.from_values('testmodule/model', 42)
|
||||
@ -210,6 +211,20 @@ class TestCollectionElement(TestCase):
|
||||
mock_Collection.assert_called_once_with('testmodule/model')
|
||||
mock_Collection().add_id_to_cache.assert_called_once_with(42)
|
||||
|
||||
def test_equal(self):
|
||||
self.assertEqual(
|
||||
collection.CollectionElement.from_values('testmodule/model', 1),
|
||||
collection.CollectionElement.from_values('testmodule/model', 1))
|
||||
self.assertEqual(
|
||||
collection.CollectionElement.from_values('testmodule/model', 1),
|
||||
collection.CollectionElement.from_values('testmodule/model', 1, deleted=True))
|
||||
self.assertNotEqual(
|
||||
collection.CollectionElement.from_values('testmodule/model', 1),
|
||||
collection.CollectionElement.from_values('testmodule/model', 2))
|
||||
self.assertNotEqual(
|
||||
collection.CollectionElement.from_values('testmodule/model', 1),
|
||||
collection.CollectionElement.from_values('testmodule/other_model', 1))
|
||||
|
||||
|
||||
class TestCollection(TestCase):
|
||||
@patch('openslides.utils.collection.CollectionElement')
|
||||
|
Loading…
Reference in New Issue
Block a user