Rewrite projector_element without dispatch

This commit is contained in:
Oskar Hahn 2017-08-30 00:07:54 +02:00
parent f1d7f85be9
commit b80e95a321
27 changed files with 116 additions and 594 deletions

View File

@ -1,6 +1,7 @@
from django.apps import AppConfig from django.apps import AppConfig
from ..utils.collection import Collection from ..utils.collection import Collection
from ..utils.projector import register_projector_elements
class AgendaAppConfig(AppConfig): class AgendaAppConfig(AppConfig):
@ -10,16 +11,13 @@ class AgendaAppConfig(AppConfig):
angular_projector_module = True angular_projector_module = True
def ready(self): def ready(self):
# Load projector elements.
# Do this by just importing all from these files.
from . import projector # noqa
# Import all required stuff. # Import all required stuff.
from django.db.models.signals import pre_delete, post_save from django.db.models.signals import pre_delete, post_save
from openslides.core.config import config from ..core.config import config
from openslides.core.signals import permission_change, user_data_required from ..core.signals import permission_change, user_data_required
from openslides.utils.rest_api import router from ..utils.rest_api import router
from .config_variables import get_config_variables from .config_variables import get_config_variables
from .projector import get_projector_elements
from .signals import ( from .signals import (
get_permission_change_data, get_permission_change_data,
listen_to_related_object_post_delete, listen_to_related_object_post_delete,
@ -27,8 +25,9 @@ class AgendaAppConfig(AppConfig):
required_users) required_users)
from .views import ItemViewSet from .views import ItemViewSet
# Define config variables # Define config variables and projector elements.
config.update_config_variables(get_config_variables()) config.update_config_variables(get_config_variables())
register_projector_elements(get_projector_elements())
# Connect signals. # Connect signals.
post_save.connect( post_save.connect(

View File

@ -1,3 +1,5 @@
from typing import Generator, Type
from ..core.config import config from ..core.config import config
from ..core.exceptions import ProjectorException from ..core.exceptions import ProjectorException
from ..core.models import Projector from ..core.models import Projector
@ -136,3 +138,9 @@ class CurrentListOfSpeakersSlide(ProjectorElement):
output.extend(self.get_requirements_as_collection_elements(config_entry)) output.extend(self.get_requirements_as_collection_elements(config_entry))
break break
return output return output
def get_projector_elements() -> Generator[Type[ProjectorElement], None, None]:
yield ItemListSlide
yield ListOfSpeakersSlide
yield CurrentListOfSpeakersSlide

View File

@ -4,6 +4,7 @@ from django.apps import AppConfig
from mypy_extensions import TypedDict from mypy_extensions import TypedDict
from ..utils.collection import Collection from ..utils.collection import Collection
from ..utils.projector import register_projector_elements
class AssignmentsAppConfig(AppConfig): class AssignmentsAppConfig(AppConfig):
@ -13,20 +14,18 @@ class AssignmentsAppConfig(AppConfig):
angular_projector_module = True angular_projector_module = True
def ready(self): def ready(self):
# Load projector elements.
# Do this by just importing all from these files.
from . import projector # noqa
# Import all required stuff. # Import all required stuff.
from openslides.core.config import config from ..core.config import config
from openslides.core.signals import permission_change, user_data_required from ..core.signals import permission_change, user_data_required
from openslides.utils.rest_api import router from ..utils.rest_api import router
from .config_variables import get_config_variables from .config_variables import get_config_variables
from .projector import get_projector_elements
from .signals import get_permission_change_data, required_users from .signals import get_permission_change_data, required_users
from .views import AssignmentViewSet, AssignmentPollViewSet from .views import AssignmentViewSet, AssignmentPollViewSet
# Define config variables # Define config variables and projector elements.
config.update_config_variables(get_config_variables()) config.update_config_variables(get_config_variables())
register_projector_elements(get_projector_elements())
# Connect signals. # Connect signals.
permission_change.connect( permission_change.connect(

View File

@ -1,3 +1,5 @@
from typing import Generator, Type
from ..core.exceptions import ProjectorException from ..core.exceptions import ProjectorException
from ..utils.projector import ProjectorElement from ..utils.projector import ProjectorElement
from .models import Assignment, AssignmentPoll from .models import Assignment, AssignmentPoll
@ -64,3 +66,7 @@ class AssignmentSlide(ProjectorElement):
else: else:
data = {'agenda_item_id': assignment.agenda_item_id} data = {'agenda_item_id': assignment.agenda_item_id}
return data return data
def get_projector_elements() -> Generator[Type[ProjectorElement], None, None]:
yield AssignmentSlide

View File

@ -3,6 +3,7 @@ from django.conf import settings
from django.db.models.signals import post_migrate from django.db.models.signals import post_migrate
from ..utils.collection import Collection from ..utils.collection import Collection
from ..utils.projector import register_projector_elements
class CoreAppConfig(AppConfig): class CoreAppConfig(AppConfig):
@ -12,15 +13,12 @@ class CoreAppConfig(AppConfig):
angular_projector_module = True angular_projector_module = True
def ready(self): def ready(self):
# Load projector elements.
# Do this by just importing all from these files.
from . import projector # noqa
# Import all required stuff. # Import all required stuff.
from .config import config from .config import config
from .signals import post_permission_creation from .signals import post_permission_creation
from ..utils.rest_api import router from ..utils.rest_api import router
from .config_variables import get_config_variables from .config_variables import get_config_variables
from .projector import get_projector_elements
from .signals import ( from .signals import (
delete_django_app_permissions, delete_django_app_permissions,
get_permission_change_data, get_permission_change_data,
@ -36,8 +34,9 @@ class CoreAppConfig(AppConfig):
TagViewSet, TagViewSet,
) )
# Define config variables # Define config variables and projector elements.
config.update_config_variables(get_config_variables()) config.update_config_variables(get_config_variables())
register_projector_elements(get_projector_elements())
# Connect signals. # Connect signals.
post_permission_creation.connect( post_permission_creation.connect(

View File

@ -5,7 +5,7 @@ from jsonfield import JSONField
from ..utils.collection import CollectionElement from ..utils.collection import CollectionElement
from ..utils.models import RESTModelMixin from ..utils.models import RESTModelMixin
from ..utils.projector import ProjectorElement from ..utils.projector import get_all_projector_elements
from .access_permissions import ( from .access_permissions import (
ChatMessageAccessPermissions, ChatMessageAccessPermissions,
ConfigAccessPermissions, ConfigAccessPermissions,
@ -110,9 +110,7 @@ class Projector(RESTModelMixin, models.Model):
result is also used. result is also used.
""" """
# Get all elements from all apps. # Get all elements from all apps.
elements = {} elements = get_all_projector_elements()
for element in ProjectorElement.get_all(): # type: ignore
elements[element.name] = element
# Parse result # Parse result
result = {} result = {}
@ -137,9 +135,7 @@ class Projector(RESTModelMixin, models.Model):
Generator which returns all instances that are shown on this projector. Generator which returns all instances that are shown on this projector.
""" """
# Get all elements from all apps. # Get all elements from all apps.
elements = {} elements = get_all_projector_elements()
for element in ProjectorElement.get_all(): # type: ignore
elements[element.name] = element
# Generator # Generator
for key, value in self.config.items(): for key, value in self.config.items():
@ -166,11 +162,8 @@ class Projector(RESTModelMixin, models.Model):
# It is necessary to parse all active projector elements to check whether they require some data. # It is necessary to parse all active projector elements to check whether they require some data.
this_projector = collection_element.collection_string == self.get_collection_string() and collection_element.id == self.pk this_projector = collection_element.collection_string == self.get_collection_string() and collection_element.id == self.pk
collection_element.information['this_projector'] = this_projector collection_element.information['this_projector'] = this_projector
elements = {}
# Build projector elements. elements = get_all_projector_elements()
for element in ProjectorElement.get_all(): # type: ignore
elements[element.name] = element
# Iterate over all active projector elements. # Iterate over all active projector elements.
for key, value in self.config.items(): for key, value in self.config.items():

View File

@ -1,3 +1,5 @@
from typing import Generator, Type
from ..utils.projector import ProjectorElement from ..utils.projector import ProjectorElement
from .exceptions import ProjectorException from .exceptions import ProjectorException
from .models import Countdown, ProjectorMessage from .models import Countdown, ProjectorMessage
@ -48,3 +50,9 @@ class ProjectorMessageElement(ProjectorElement):
pass pass
else: else:
yield message yield message
def get_projector_elements() -> Generator[Type[ProjectorElement], None, None]:
yield Clock
yield CountdownElement
yield ProjectorMessageElement

View File

@ -1,6 +1,7 @@
from django.apps import AppConfig from django.apps import AppConfig
from ..utils.collection import Collection from ..utils.collection import Collection
from ..utils.projector import register_projector_elements
class MediafilesAppConfig(AppConfig): class MediafilesAppConfig(AppConfig):
@ -10,16 +11,16 @@ class MediafilesAppConfig(AppConfig):
angular_projector_module = True angular_projector_module = True
def ready(self): def ready(self):
# Load projector elements.
# Do this by just importing all from these files.
from . import projector # noqa
# Import all required stuff. # Import all required stuff.
from openslides.core.signals import permission_change, user_data_required from openslides.core.signals import permission_change, user_data_required
from openslides.utils.rest_api import router from openslides.utils.rest_api import router
from .projector import get_projector_elements
from .signals import get_permission_change_data, required_users from .signals import get_permission_change_data, required_users
from .views import MediafileViewSet from .views import MediafileViewSet
# Define projector elements.
register_projector_elements(get_projector_elements())
# Connect signals. # Connect signals.
permission_change.connect( permission_change.connect(
get_permission_change_data, get_permission_change_data,

View File

@ -1,3 +1,5 @@
from typing import Generator, Type
from ..core.exceptions import ProjectorException from ..core.exceptions import ProjectorException
from ..utils.projector import ProjectorElement from ..utils.projector import ProjectorElement
from .models import Mediafile from .models import Mediafile
@ -21,3 +23,7 @@ class MediafileSlide(ProjectorElement):
pass pass
else: else:
yield mediafile yield mediafile
def get_projector_elements() -> Generator[Type[ProjectorElement], None, None]:
yield MediafileSlide

View File

@ -2,6 +2,7 @@ from django.apps import AppConfig
from django.db.models.signals import post_migrate from django.db.models.signals import post_migrate
from ..utils.collection import Collection from ..utils.collection import Collection
from ..utils.projector import register_projector_elements
class MotionsAppConfig(AppConfig): class MotionsAppConfig(AppConfig):
@ -11,20 +12,18 @@ class MotionsAppConfig(AppConfig):
angular_projector_module = True angular_projector_module = True
def ready(self): def ready(self):
# Load projector elements.
# Do this by just importing all from these files.
from . import projector # noqa
# Import all required stuff. # Import all required stuff.
from openslides.core.config import config from openslides.core.config import config
from openslides.core.signals import permission_change, user_data_required from openslides.core.signals import permission_change, user_data_required
from openslides.utils.rest_api import router from openslides.utils.rest_api import router
from .config_variables import get_config_variables from .config_variables import get_config_variables
from .projector import get_projector_elements
from .signals import create_builtin_workflows, get_permission_change_data, required_users from .signals import create_builtin_workflows, get_permission_change_data, required_users
from .views import CategoryViewSet, MotionViewSet, MotionBlockViewSet, MotionPollViewSet, MotionChangeRecommendationViewSet, WorkflowViewSet from .views import CategoryViewSet, MotionViewSet, MotionBlockViewSet, MotionPollViewSet, MotionChangeRecommendationViewSet, WorkflowViewSet
# Define config variables # Define config variables and projector elements.
config.update_config_variables(get_config_variables()) config.update_config_variables(get_config_variables())
register_projector_elements(get_projector_elements())
# Connect signals. # Connect signals.
post_migrate.connect(create_builtin_workflows, dispatch_uid='motion_create_builtin_workflows') post_migrate.connect(create_builtin_workflows, dispatch_uid='motion_create_builtin_workflows')

View File

@ -1,3 +1,5 @@
from typing import Generator, Type
from ..core.exceptions import ProjectorException from ..core.exceptions import ProjectorException
from ..utils.projector import ProjectorElement from ..utils.projector import ProjectorElement
from .models import Motion, MotionBlock, MotionChangeRecommendation, Workflow from .models import Motion, MotionBlock, MotionChangeRecommendation, Workflow
@ -90,3 +92,8 @@ class MotionBlockSlide(ProjectorElement):
else: else:
data = {'agenda_item_id': motion_block.agenda_item_id} data = {'agenda_item_id': motion_block.agenda_item_id}
return data return data
def get_projector_elements() -> Generator[Type[ProjectorElement], None, None]:
yield MotionSlide
yield MotionBlockSlide

View File

@ -1,6 +1,7 @@
from django.apps import AppConfig from django.apps import AppConfig
from ..utils.collection import Collection from ..utils.collection import Collection
from ..utils.projector import register_projector_elements
class TopicsAppConfig(AppConfig): class TopicsAppConfig(AppConfig):
@ -10,16 +11,16 @@ class TopicsAppConfig(AppConfig):
angular_projector_module = True angular_projector_module = True
def ready(self): def ready(self):
# Load projector elements.
# Do this by just importing all from these files.
from . import projector # noqa
# Import all required stuff. # Import all required stuff.
from openslides.core.signals import permission_change from openslides.core.signals import permission_change
from ..utils.rest_api import router from ..utils.rest_api import router
from .projector import get_projector_elements
from .signals import get_permission_change_data from .signals import get_permission_change_data
from .views import TopicViewSet from .views import TopicViewSet
# Define projector elements.
register_projector_elements(get_projector_elements())
# Connect signals. # Connect signals.
permission_change.connect( permission_change.connect(
get_permission_change_data, get_permission_change_data,

View File

@ -1,3 +1,5 @@
from typing import Generator, Type
from ..core.exceptions import ProjectorException from ..core.exceptions import ProjectorException
from ..utils.projector import ProjectorElement from ..utils.projector import ProjectorElement
from .models import Topic from .models import Topic
@ -33,3 +35,7 @@ class TopicSlide(ProjectorElement):
else: else:
data = {'agenda_item_id': topic.agenda_item_id} data = {'agenda_item_id': topic.agenda_item_id}
return data return data
def get_projector_elements() -> Generator[Type[ProjectorElement], None, None]:
yield TopicSlide

View File

@ -1,6 +1,7 @@
from django.apps import AppConfig from django.apps import AppConfig
from ..utils.collection import Collection from ..utils.collection import Collection
from ..utils.projector import register_projector_elements
class UsersAppConfig(AppConfig): class UsersAppConfig(AppConfig):
@ -10,20 +11,18 @@ class UsersAppConfig(AppConfig):
angular_projector_module = True angular_projector_module = True
def ready(self): def ready(self):
# Load projector elements.
# Just import this file.
from . import projector # noqa
# Import all required stuff. # Import all required stuff.
from ..core.config import config from ..core.config import config
from ..core.signals import post_permission_creation, permission_change from ..core.signals import post_permission_creation, permission_change
from ..utils.rest_api import router from ..utils.rest_api import router
from .config_variables import get_config_variables from .config_variables import get_config_variables
from .projector import get_projector_elements
from .signals import create_builtin_groups_and_admin, get_permission_change_data from .signals import create_builtin_groups_and_admin, get_permission_change_data
from .views import GroupViewSet, PersonalNoteViewSet, UserViewSet from .views import GroupViewSet, PersonalNoteViewSet, UserViewSet
# Define config variables # Define config variables and projector elements.
config.update_config_variables(get_config_variables()) config.update_config_variables(get_config_variables())
register_projector_elements(get_projector_elements())
# Connect signals. # Connect signals.
post_permission_creation.connect( post_permission_creation.connect(

View File

@ -1,3 +1,5 @@
from typing import Generator, Type
from ..core.exceptions import ProjectorException from ..core.exceptions import ProjectorException
from ..utils.projector import ProjectorElement from ..utils.projector import ProjectorElement
from .models import User from .models import User
@ -21,3 +23,7 @@ class UserSlide(ProjectorElement):
pass pass
else: else:
yield user yield user
def get_projector_elements() -> Generator[Type[ProjectorElement], None, None]:
yield UserSlide

View File

@ -1,112 +0,0 @@
class SignalConnectMetaClass(type):
"""
Metaclass to connect the children of a base class to a Django signal.
Classes must have a signal argument and a get_dispatch_uid classmethod.
The signal argument must be the Django signal the class should be
connected to. The get_dispatch_uid classmethod must return a unique
value for each child class and None for base classes because they will
not be connected to the signal.
The classmethod get_all is added to every class using this metaclass.
Calling this on a base class or on child classes will retrieve all
connected children, one instance for each child class.
These instances will have a check_permission method which returns True
by default. You can override this method to return False on runtime if
you want to filter some children.
They will also have a get_default_weight method which returns the value
of the default_weight attribute which is 0 by default. You can override
the attribute or the method to sort the children.
Don't forget to set up the __init__ method so that it is able to receive
wildcard keyword arguments (see example below). This is necessary
because of Django's signal API.
Example:
class Base(object, metaclass=SignalConnectMetaClass):
signal = django.dispatch.Signal()
def __init__(self, **kwargs):
pass
@classmethod
def get_dispatch_uid(cls):
if not cls.__name__ == 'Base':
return cls.__name__
class Child(Base):
pass
child = Base.get_all(request)[0]
assert Child == type(child)
"""
def __new__(metaclass, class_name, class_parents, class_attributes):
"""
Creates the class and connects it to the signal if so. Adds all
default attributes and methods.
"""
class_attributes['get_all'] = get_all
new_class = super().__new__(
metaclass, class_name, class_parents, class_attributes)
try:
dispatch_uid = new_class.get_dispatch_uid()
except AttributeError:
raise NotImplementedError('Your class %s must have a get_dispatch_uid classmethod.' % class_name)
if dispatch_uid is not None:
try:
signal = new_class.signal
except AttributeError:
raise NotImplementedError('Your class %s must have a signal argument, which must be a Django Signal instance.' % class_name)
else:
signal.connect(new_class, dispatch_uid=dispatch_uid)
attributes = {'check_permission': check_permission,
'get_default_weight': get_default_weight,
'default_weight': 0}
for name, attribute in attributes.items():
if not hasattr(new_class, name):
setattr(new_class, name, attribute)
return new_class
@classmethod # type: ignore
def get_all(cls, request=None):
"""
Collects all objects of the class created by the SignalConnectMetaClass
from all apps via signal. They are sorted using the get_default_weight
method. Does not return objects where check_permission returns False.
A django.http.HttpRequest object can optionally be given.
This classmethod is added as get_all classmethod to every class using
the SignalConnectMetaClass.
"""
kwargs = {'sender': cls}
if request is not None:
kwargs['request'] = request
all_objects = [obj for __, obj in cls.signal.send(**kwargs) if obj.check_permission()]
all_objects.sort(key=lambda obj: obj.get_default_weight())
return all_objects
def check_permission(self):
"""
Returns True by default. Override this to filter some children on runtime.
This method is added to every instance of classes using the
SignalConnectMetaClass.
"""
return True
def get_default_weight(self):
"""
Returns the value of the default_weight attribute by default. Override
this to sort some children on runtime.
This method is added to every instance of classes using the
SignalConnectMetaClass.
"""
return self.default_weight

View File

@ -1,42 +1,17 @@
from typing import Any, Dict, Iterable, List, Optional # noqa from typing import Any, Dict, Generator, Iterable, List, Type
from django.dispatch import Signal
from .collection import CollectionElement from .collection import CollectionElement
from .dispatch import SignalConnectMetaClass
class ProjectorElement(object, metaclass=SignalConnectMetaClass): class ProjectorElement:
""" """
Base class for an element on the projector. Base class for an element on the projector.
Every app which wants to add projector elements has to create classes Every app which wants to add projector elements has to create classes
subclassing from this base class with different names. The name attribute subclassing from this base class with different names. The name attribute
has to be set. The metaclass (SignalConnectMetaClass) does the rest of the has to be set.
magic.
""" """
signal = Signal() name = None # type: str
name = None # type: Optional[str]
def __init__(self, **kwargs: str) -> None:
"""
Initializes the projector element instance. This is done when the
signal is sent.
Because of Django's signal API, we have to take wildcard keyword
arguments. But they are not used here.
"""
pass
@classmethod
def get_dispatch_uid(cls) -> Optional[str]:
"""
Returns the classname as a unique string for each class. Returns None
for the base class so it will not be connected to the signal.
"""
if not cls.__name__ == 'ProjectorElement':
return cls.__name__
return None
def check_and_update_data(self, projector_object: Any, config_entry: Any) -> Any: def check_and_update_data(self, projector_object: Any, config_entry: Any) -> Any:
""" """
@ -107,3 +82,25 @@ class ProjectorElement(object, metaclass=SignalConnectMetaClass):
else: else:
output = [] output = []
return output return output
projector_elements = {} # type: Dict[str, ProjectorElement]
def register_projector_elements(elements: Generator[Type[ProjectorElement], None, None]) -> None:
"""
Registers projector elements for later use.
Has to be called in the app.ready method.
"""
for Element in elements:
element = Element()
projector_elements[element.name] = element
def get_all_projector_elements() -> Dict[str, ProjectorElement]:
"""
Returns all projector elements that where registered with
register_projector_elements()
"""
return projector_elements

View File

@ -19,9 +19,6 @@ ignore_missing_imports = true
strict_optional = true strict_optional = true
check_untyped_defs = true check_untyped_defs = true
[mypy-openslides.utils.dispatch]
ignore_errors = true
[mypy-openslides.utils.*] [mypy-openslides.utils.*]
disallow_any = unannotated disallow_any = unannotated

View File

@ -4,6 +4,9 @@ from . import __description__, __verbose_name__
class TestPluginAppConfig(AppConfig): class TestPluginAppConfig(AppConfig):
"""
Test Plugin for the test tests.integration.core.test_views.VersionView
"""
name = 'tests.integration.test_plugin' name = 'tests.integration.test_plugin'
label = 'tests.integration.test_plugin' label = 'tests.integration.test_plugin'
verbose_name = __verbose_name__ verbose_name = __verbose_name__

View File

@ -1,28 +0,0 @@
from django.db import models
from openslides.projector.models import SlideMixin
class RelatedItem(SlideMixin, models.Model):
slide_callback_name = 'test_related_item'
name = models.CharField(max_length='255')
class Meta:
verbose_name = 'Related Item CHFNGEJ5634DJ34F'
def get_agenda_title(self):
return self.name
def get_agenda_title_supplement(self):
return 'test item'
def get_absolute_url(self, link=None):
if link is None:
value = '/absolute-url-here/'
else:
value = super(RelatedItem, self).get_absolute_url(link)
return value
class BadRelatedItem(models.Model):
name = models.CharField(max_length='255')

View File

@ -1,26 +0,0 @@
from django.test.client import Client
from openslides.assignments.models import Assignment
from openslides.users.models import User
from openslides.utils.test import TestCase
class AssignmentPDFTest(TestCase):
"""
Tests for assignment PDF.
"""
def setUp(self):
# Admin
self.admin = User.objects.get(pk=1)
self.admin_client = Client()
self.admin_client.login(username='admin', password='admin')
def test_render_pdf(self):
Assignment.objects.create(title='assignment_name_ith8qua1Eiferoqu5ju2', description="test", open_posts=1)
response = self.admin_client.get('/assignments/print/')
self.assertEqual(response.status_code, 200)
def test_render_many_posts(self):
Assignment.objects.create(title='assignment_name_cohZ9shaipee3Phaing4', description="test", open_posts=20)
response = self.admin_client.get('/assignments/print/')
self.assertEqual(response.status_code, 200)

View File

@ -1,263 +0,0 @@
import os
import tempfile
from unittest import skip
from django.conf import settings
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test.client import Client
from openslides.mediafiles.models import Mediafile
from openslides.users.models import User
from openslides.utils.test import TestCase
class MediafileTest(TestCase):
"""
Unit test for the mediafile model.
"""
def setUp(self):
# Setup the three permissions
ct = ContentType.objects.get(app_label='mediafiles', model='mediafile')
perm_1 = Permission.objects.get(content_type=ct, codename='can_see')
perm_2 = Permission.objects.get(content_type=ct, codename='can_upload')
# Setup three different users
self.manager = User.objects.get(pk=1)
self.vip_user = User.objects.create_user('mediafile_test_vip_user', 'default')
self.vip_user.user_permissions.add(perm_1, perm_2)
self.normal_user = User.objects.create_user('mediafile_test_normal_user', 'default')
# Setup a mediafile object
self.tmp_dir = settings.MEDIA_ROOT
tmpfile_no, mediafile_path = tempfile.mkstemp(prefix='tmp_openslides_test_', dir=self.tmp_dir)
self.object = Mediafile.objects.create(title='Title File 1', mediafile=mediafile_path, uploader=self.normal_user)
os.close(tmpfile_no)
def tearDown(self):
self.object.mediafile.delete(save=False)
super().tearDown()
def test_str(self):
self.assertEqual(str(self.object), 'Title File 1')
@skip
def test_absolute_url(self):
self.assertEqual(self.object.get_absolute_url(), '/mediafiles/1/edit/')
self.assertEqual(self.object.get_absolute_url('update'), '/mediafiles/1/edit/')
self.assertEqual(self.object.get_absolute_url(link='delete'), '/mediafiles/1/del/')
def login_clients(self):
"""
Helper function to login all three test users.
"""
client_manager = Client()
client_manager.login(username='admin', password='admin')
client_vip_user = Client()
client_vip_user.login(username='mediafile_test_vip_user', password='default')
client_normal_user = Client()
client_normal_user.login(username='mediafile_test_normal_user', password='default')
return {'client_manager': client_manager,
'client_vip_user': client_vip_user,
'client_normal_user': client_normal_user}
@skip
def test_see_mediafilelist(self):
for client in self.login_clients().values():
response = client.get('/mediafiles/')
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'mediafiles/mediafile_list.html')
@skip
def test_upload_mediafile_get_request(self):
clients = self.login_clients()
response = clients['client_manager'].get('/mediafiles/new/')
self.assertContains(response, '---------', status_code=200)
self.assertContains(response, '<option value="1" selected="selected">Administrator</option>', status_code=200)
self.assertTemplateUsed(response, 'mediafiles/mediafile_form.html')
response = clients['client_vip_user'].get('/mediafiles/new/')
self.assertNotContains(response, '<select id="id_uploader" name="uploader">', status_code=200)
self.assertTemplateUsed(response, 'mediafiles/mediafile_form.html')
response = clients['client_normal_user'].get('/mediafiles/new/')
self.assertEqual(response.status_code, 403)
@skip
def test_upload_mediafile_post_request(self):
# Test first user
client_1 = self.login_clients()['client_manager']
new_file_1 = SimpleUploadedFile(name='new_test_file.txt', content=bytes('test content hello manager', 'UTF-8'))
response_1 = client_1.post('/mediafiles/new/',
{'title': 'new_test_file_title_1',
'mediafile': new_file_1,
'uploader': self.normal_user.pk})
self.assertRedirects(response_1, expected_url='/mediafiles/', status_code=302, target_status_code=200)
object_1 = Mediafile.objects.latest('timestamp')
self.assertEqual(object_1.mediafile.url, '/media/file/new_test_file.txt')
self.assertEqual(object_1.uploader, self.normal_user)
path_1 = object_1.mediafile.path
object_1.mediafile.delete()
self.assertFalse(os.path.exists(path_1))
# Test second user
client_2 = self.login_clients()['client_vip_user']
new_file_2 = SimpleUploadedFile(name='new_test_file.txt', content=bytes('test content hello vip_user', 'UTF-8'))
response_2 = client_2.post('/mediafiles/new/',
{'title': 'new_test_file_title_2',
'mediafile': new_file_2})
self.assertEqual(response_2.status_code, 302)
# TODO: Check, why this does not work.
# self.assertRedirects(response_2, expected_url='/mediafiles/', status_code=302, target_status_code=200)
object_2 = Mediafile.objects.latest('timestamp')
self.assertEqual(object_2.mediafile.url, '/media/file/new_test_file.txt')
self.assertEqual(object_2.uploader, self.vip_user)
path_2 = object_2.mediafile.path
object_2.mediafile.delete()
self.assertFalse(os.path.exists(path_2))
# Test third user
client_3 = self.login_clients()['client_normal_user']
new_file_3 = SimpleUploadedFile(name='new_test_file.txt', content=bytes('test content hello vip_user', 'UTF-8'))
response_3 = client_3.post('/mediafiles/new/',
{'title': 'new_test_file_title_2',
'mediafile': new_file_3})
self.assertEqual(response_3.status_code, 403)
@skip
def test_edit_mediafile_get_request(self):
clients = self.login_clients()
response = clients['client_manager'].get('/mediafiles/1/edit/')
self.assertContains(response, '---------', status_code=200)
self.assertContains(response, '<option value="3" selected="selected">mediafile_test_normal_user</option>', status_code=200)
self.assertTemplateUsed(response, 'mediafiles/mediafile_form.html')
response = clients['client_vip_user'].get('/mediafiles/1/edit/')
self.assertEqual(response.status_code, 403)
response = clients['client_normal_user'].get('/mediafiles/1/edit/')
self.assertEqual(response.status_code, 403)
@skip
def test_edit_mediafile_get_request_own_file(self):
clients = self.login_clients()
self.object.uploader = self.vip_user
self.object.save()
response = clients['client_vip_user'].get('/mediafiles/1/edit/')
self.assertNotContains(response, '---------', status_code=200)
self.assertNotContains(response, '<option value="2" selected="selected">mediafile_test_vip_user</option>', status_code=200)
self.assertTemplateUsed(response, 'mediafiles/mediafile_form.html')
@skip
def test_edit_mediafile_post_request(self):
# Test only one user
tmpfile_no, mediafile_2_path = tempfile.mkstemp(prefix='tmp_openslides_test_', dir=self.tmp_dir)
os.close(tmpfile_no)
object_2 = Mediafile.objects.create(title='Title File 2', mediafile=mediafile_2_path, uploader=self.vip_user)
client_1 = self.login_clients()['client_manager']
new_file_1 = SimpleUploadedFile(name='new_test_file.txt', content=bytes('test content hello manager', 'UTF-8'))
response_1 = client_1.post('/mediafiles/2/edit/',
{'title': 'new_test_file_title_1',
'mediafile': new_file_1,
'uploader': self.manager.pk})
self.assertEqual(response_1.status_code, 302)
object_2 = Mediafile.objects.get(pk=2)
self.assertEqual(object_2.mediafile.url, '/media/file/new_test_file.txt')
self.assertEqual(object_2.uploader, self.manager)
path_2 = object_2.mediafile.path
object_2.mediafile.delete()
self.assertFalse(os.path.exists(path_2))
@skip
def test_edit_mediafile_post_request_own_file(self):
tmpfile_no, mediafile_2_path = tempfile.mkstemp(prefix='tmp_openslides_test_', dir=self.tmp_dir)
os.close(tmpfile_no)
object_2 = Mediafile.objects.create(title='Title File 2b', mediafile=mediafile_2_path, uploader=self.vip_user)
client = self.login_clients()['client_vip_user']
new_file_1 = SimpleUploadedFile(name='new_test_file.txt', content=bytes('test content hello vip user', 'UTF-8'))
response_1 = client.post('/mediafiles/2/edit/',
{'title': 'new_test_file_title_2b',
'mediafile': new_file_1})
self.assertEqual(response_1.status_code, 302)
object_2 = Mediafile.objects.get(pk=2)
self.assertEqual(object_2.mediafile.url, '/media/file/new_test_file.txt')
self.assertEqual(object_2.uploader, self.vip_user)
path_2 = object_2.mediafile.path
object_2.mediafile.delete()
self.assertFalse(os.path.exists(path_2))
@skip
def test_edit_mediafile_post_request_another_file(self):
client = self.login_clients()['client_vip_user']
new_file_1 = SimpleUploadedFile(name='new_test_file.txt', content=bytes('test content hello vip user', 'UTF-8'))
response = client.post('/mediafiles/1/edit/',
{'title': 'new_test_file_title_2c',
'mediafile': new_file_1})
self.assertEqual(response.status_code, 403)
@skip
def test_delete_mediafile_get_request(self):
clients = self.login_clients()
response = clients['client_manager'].get('/mediafiles/1/del/')
self.assertRedirects(response, expected_url='/mediafiles/1/edit/', status_code=302, target_status_code=200)
response = clients['client_vip_user'].get('/mediafiles/1/del/')
self.assertEqual(response.status_code, 403)
response = clients['client_normal_user'].get('/mediafiles/1/del/')
self.assertEqual(response.status_code, 403)
@skip
def test_delete_mediafile_get_request_own_file(self):
self.object.uploader = self.vip_user
self.object.save()
response = self.login_clients()['client_vip_user'].get('/mediafiles/1/del/')
self.assertRedirects(response, expected_url='/mediafiles/1/edit/', status_code=302, target_status_code=200)
@skip
def test_delete_mediafile_post_request(self):
tmpfile_no, mediafile_3_path = tempfile.mkstemp(prefix='tmp_openslides_test_', dir=self.tmp_dir)
os.close(tmpfile_no)
object_3 = Mediafile.objects.create(title='Title File 3', mediafile=mediafile_3_path)
client_1 = self.login_clients()['client_manager']
response_1 = client_1.post('/mediafiles/2/del/', {'yes': 'foo'})
self.assertRedirects(response_1, expected_url='/mediafiles/', status_code=302, target_status_code=200)
self.assertFalse(os.path.exists(object_3.mediafile.path))
@skip
def test_delete_mediafile_post_request_own_file(self):
tmpfile_no, mediafile_3_path = tempfile.mkstemp(prefix='tmp_openslides_test_', dir=self.tmp_dir)
os.close(tmpfile_no)
object_3 = Mediafile.objects.create(title='Title File 3b', mediafile=mediafile_3_path, uploader=self.vip_user)
client_1 = self.login_clients()['client_vip_user']
response_1 = client_1.post('/mediafiles/2/del/', {'yes': 'foo'})
self.assertRedirects(response_1, expected_url='/mediafiles/', status_code=302, target_status_code=200)
self.assertFalse(os.path.exists(object_3.mediafile.path))
@skip
def test_delete_mediafile_post_request_another_file(self):
tmpfile_no, mediafile_3_path = tempfile.mkstemp(prefix='tmp_openslides_test_', dir=self.tmp_dir)
os.close(tmpfile_no)
object_3 = Mediafile.objects.create(title='Title File 3c', mediafile=mediafile_3_path, uploader=self.normal_user)
client_1 = self.login_clients()['client_vip_user']
response = client_1.post('/mediafiles/2/del/', {'yes': 'foo'})
self.assertEqual(response.status_code, 403)
path_3 = object_3.mediafile.path
self.assertTrue(os.path.exists(path_3))
object_3.mediafile.delete()
self.assertFalse(os.path.exists(path_3))
def test_filesize(self):
tmpfile_no, mediafile_4_path = tempfile.mkstemp(prefix='tmp_openslides_test_', dir=self.tmp_dir)
os.close(tmpfile_no)
object_4 = Mediafile.objects.create(title='Title File 4', mediafile=mediafile_4_path)
self.assertEqual(object_4.get_filesize(), '< 1 kB')
with open(object_4.mediafile.path, 'wb') as bigfile:
bigfile.seek(2047)
bigfile.write(b'0')
self.assertEqual(object_4.get_filesize(), '2 kB')
with open(object_4.mediafile.path, 'wb') as bigfile:
bigfile.seek(1048575)
bigfile.write(b'0')
self.assertEqual(object_4.get_filesize(), '1 MB')
os.remove(mediafile_4_path)
self.assertEqual(object_4.get_filesize(), 'unknown')

View File

@ -1 +0,0 @@
default_app_config = 'tests.old.utils.apps.TestPluginAppConfig'

View File

@ -1,6 +0,0 @@
from django.apps import AppConfig
class TestPluginAppConfig(AppConfig):
name = 'tests.old.utils'
label = 'tests.old.utils'

View File

@ -1,76 +0,0 @@
from unittest.mock import patch
from django.dispatch import Signal
from django.test.client import RequestFactory
from openslides.utils.dispatch import SignalConnectMetaClass
from openslides.utils.test import TestCase
class TestBaseOne(object, metaclass=SignalConnectMetaClass):
signal = Signal()
@classmethod
def get_dispatch_uid(cls):
if not cls.__name__ == 'TestBaseOne':
return 'test_vieM1eingi6luish5Sei'
class TestBaseTwo(object, metaclass=SignalConnectMetaClass):
signal = Signal()
@classmethod
def get_dispatch_uid(cls):
pass
class TestSignalConnectMetaClass(TestCase):
request_factory = RequestFactory()
@patch('tests.old.utils.test_dispatch.TestBaseOne.signal')
def test_call_signal_send(self, mock_signal):
TestBaseOne.get_all(self.request_factory.request)
self.assertTrue(mock_signal.send.called)
@patch('tests.old.utils.test_dispatch.TestBaseOne.signal')
def test_call_signal_connect(self, mock_signal):
class TestChildOne(TestBaseOne):
pass
self.assertTrue(mock_signal.connect.called)
self.assertEqual(mock_signal.connect.call_args[0][0], TestChildOne)
self.assertEqual(mock_signal.connect.call_args[1], dict(dispatch_uid='test_vieM1eingi6luish5Sei'))
def test_bad_base_class(self):
def wrapper():
class BadClass1(object, metaclass=SignalConnectMetaClass):
pass
self.assertRaisesMessage(
NotImplementedError,
'Your class BadClass1 must have a get_dispatch_uid classmethod.',
wrapper)
def test_bad_base_class_without_signal(self):
def wrapper():
class BadClass2(object, metaclass=SignalConnectMetaClass):
@classmethod
def get_dispatch_uid(cls):
return True
self.assertRaisesMessage(
NotImplementedError,
'Your class BadClass2 must have a signal argument, which must be a Django Signal instance.',
wrapper)
def test_receive_signal(self):
class TestChildTwo(TestBaseTwo):
def __init__(self, sender, **kwargs):
pass
@classmethod
def get_dispatch_uid(self):
return 'test_leeve5eighahT3zooxe5'
childtwo = TestBaseTwo.get_all(self.request_factory.request)[0]
self.assertEqual(type(childtwo), TestChildTwo)