Refactored projector requirements system for autoupdate.
This commit is contained in:
parent
ac9c9f4ec3
commit
039795beb7
@ -1,5 +1,6 @@
|
||||
from ..core.config import config
|
||||
from ..core.exceptions import ProjectorException
|
||||
from ..utils.collection import CollectionElement
|
||||
from ..utils.projector import ProjectorElement
|
||||
from .models import Item
|
||||
|
||||
@ -60,10 +61,13 @@ class ListOfSpeakersSlide(ProjectorElement):
|
||||
# Yield last speakers
|
||||
yield speaker.user
|
||||
|
||||
def need_full_update_for_this(self, collection_element):
|
||||
# Full update if item changes because then we may have new speakers
|
||||
# and therefor need new users.
|
||||
return collection_element.collection_string == Item.get_collection_string()
|
||||
def get_collection_elements_required_for_this(self, collection_element, config_entry):
|
||||
output = super().get_collections_required_for_this(collection_element, config_entry)
|
||||
# Full update if item changes because then we may have new
|
||||
# candidates and therefor need new users.
|
||||
if collection_element == CollectionElement.from_values(Item.get_collection_string(), config_entry.get('id')):
|
||||
output.extend(self.get_requirements_as_collection_elements(config_entry))
|
||||
return output
|
||||
|
||||
|
||||
class CurrentListOfSpeakersSlide(ProjectorElement):
|
||||
|
@ -1,4 +1,5 @@
|
||||
from ..core.exceptions import ProjectorException
|
||||
from ..utils.collection import CollectionElement
|
||||
from ..utils.projector import ProjectorElement
|
||||
from .models import Assignment, AssignmentPoll
|
||||
|
||||
@ -46,7 +47,10 @@ class AssignmentSlide(ProjectorElement):
|
||||
for option in poll.options.all():
|
||||
yield option.candidate
|
||||
|
||||
def need_full_update_for_this(self, collection_element):
|
||||
def get_collection_elements_required_for_this(self, collection_element, config_entry):
|
||||
output = super().get_collection_elements_required_for_this(collection_element, config_entry)
|
||||
# Full update if assignment changes because then we may have new
|
||||
# candidates and therefor need new users.
|
||||
return collection_element.collection_string == Assignment.get_collection_string()
|
||||
if collection_element == CollectionElement.from_values(Assignment.get_collection_string(), config_entry.get('id')):
|
||||
output.extend(self.get_requirements_as_collection_elements(config_entry))
|
||||
return output
|
||||
|
@ -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 autoupdate: Refactor or rename this.
|
||||
def get_all_requirements(self):
|
||||
"""
|
||||
Generator which returns all instances that are shown on this projector.
|
||||
"""
|
||||
@ -131,50 +131,35 @@ class Projector(RESTModelMixin, models.Model):
|
||||
if element is not None:
|
||||
yield from element.get_requirements(value)
|
||||
|
||||
def collection_element_is_shown(self, collection_element):
|
||||
def get_collection_elements_required_for_this(self, collection_element):
|
||||
"""
|
||||
Returns True if this collection element is shown on this projector.
|
||||
Returns an iterable of CollectionElements that have to be sent to this
|
||||
projector according to the given collection_element and information.
|
||||
"""
|
||||
for requirement in self.get_all_requirements():
|
||||
if (requirement.get_collection_string() == collection_element.collection_string and
|
||||
requirement.pk == collection_element.id):
|
||||
result = True
|
||||
break
|
||||
from .config import config
|
||||
|
||||
output = []
|
||||
changed_fields = collection_element.information.get('changed_fields', [])
|
||||
if (collection_element.collection_string == self.get_collection_string() and
|
||||
changed_fields and
|
||||
'config' not in changed_fields):
|
||||
# Projector model changed without changeing the projector config. So we just send this data.
|
||||
output.append(collection_element)
|
||||
else:
|
||||
result = False
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def get_projectors_that_show_this(cls, collection_element):
|
||||
"""
|
||||
Returns a list of the projectors that show this collection element.
|
||||
"""
|
||||
result = []
|
||||
for projector in cls.objects.all():
|
||||
if projector.collection_element_is_shown(collection_element):
|
||||
result.append(projector)
|
||||
return result
|
||||
|
||||
def need_full_update_for_this(self, collection_element):
|
||||
"""
|
||||
Returns True if this projector needs to be updated with all
|
||||
instances as defined in get_all_requirements() because one active
|
||||
projector element requires this.
|
||||
"""
|
||||
# Get all elements from all apps.
|
||||
# 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
|
||||
collection_element.information['this_projector'] = this_projector
|
||||
elements = {}
|
||||
for element in ProjectorElement.get_all():
|
||||
elements[element.name] = element
|
||||
|
||||
for key, value in self.config.items():
|
||||
element = elements.get(value['name'])
|
||||
if element is not None and element.need_full_update_for_this(collection_element):
|
||||
result = True
|
||||
break
|
||||
else:
|
||||
result = False
|
||||
|
||||
return result
|
||||
if element is not None:
|
||||
output.extend(element.get_collection_elements_required_for_this(collection_element, value))
|
||||
# If config changed, send also this to the projector.
|
||||
if collection_element.collection_string == config.get_collection_string():
|
||||
output.append(collection_element)
|
||||
return output
|
||||
|
||||
|
||||
class ProjectionDefault(RESTModelMixin, models.Model):
|
||||
|
@ -1,4 +1,5 @@
|
||||
from ..core.exceptions import ProjectorException
|
||||
from ..utils.collection import CollectionElement
|
||||
from ..utils.projector import ProjectorElement
|
||||
from .models import Motion
|
||||
|
||||
@ -26,10 +27,13 @@ class MotionSlide(ProjectorElement):
|
||||
yield from motion.submitters.all()
|
||||
yield from motion.supporters.all()
|
||||
|
||||
def need_full_update_for_this(self, collection_element):
|
||||
def get_collection_elements_required_for_this(self, collection_element, config_entry):
|
||||
output = super().get_collection_elements_required_for_this(collection_element, config_entry)
|
||||
# Full update if motion changes because then we may have new
|
||||
# submitters or supporters and therefor need new users.
|
||||
#
|
||||
# Add some logic here if we support live changing of workflows later.
|
||||
#
|
||||
return collection_element.collection_string == Motion.get_collection_string()
|
||||
if collection_element == CollectionElement.from_values(Motion.get_collection_string(), config_entry.get('id')):
|
||||
output.extend(self.get_requirements_as_collection_elements(config_entry))
|
||||
return output
|
||||
|
@ -23,22 +23,6 @@ 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 autoupdate
|
||||
"""
|
||||
Returns a list of dicts that are required for a specific projector.
|
||||
|
||||
The argument projector has to be a projector instance.
|
||||
|
||||
If on_slide is a string that matches an slide on the projector, then only
|
||||
elements on this slide are returned.
|
||||
"""
|
||||
output = []
|
||||
for requirement in projector.get_all_requirements(on_slide):
|
||||
required_collection_element = CollectionElement.from_instance(requirement)
|
||||
output.append(required_collection_element.as_autoupdate_for_projector())
|
||||
return output
|
||||
|
||||
|
||||
@channel_session_user_from_http
|
||||
def ws_add_site(message):
|
||||
"""
|
||||
@ -81,7 +65,10 @@ 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 autoupdate
|
||||
output = []
|
||||
for requirement in projector.get_all_requirements():
|
||||
required_collection_element = CollectionElement.from_instance(requirement)
|
||||
output.append(required_collection_element.as_autoupdate_for_projector())
|
||||
|
||||
# Send all config elements.
|
||||
collection = Collection(config.get_collection_string())
|
||||
@ -102,59 +89,25 @@ def ws_disconnect_projector(message, projector_id):
|
||||
Group('projector-{}'.format(projector_id)).discard(message.reply_channel)
|
||||
|
||||
|
||||
def send_data(message): # TODO autoupdate
|
||||
def send_data(message):
|
||||
"""
|
||||
Informs all users about changed data.
|
||||
Informs all site users and projector clients about changed data.
|
||||
"""
|
||||
collection_element = CollectionElement.from_values(**message)
|
||||
|
||||
# Loop over all logged in users and the anonymous user.
|
||||
# Loop over all logged in site users and the anonymous user and send changed data.
|
||||
for user in itertools.chain(get_logged_in_users(), [AnonymousUser()]):
|
||||
channel = Group('user-{}'.format(user.id))
|
||||
output = collection_element.as_autoupdate_for_user(user)
|
||||
channel.send({'text': json.dumps([output])})
|
||||
output = [collection_element.as_autoupdate_for_user(user)]
|
||||
channel.send({'text': json.dumps(output)})
|
||||
|
||||
# Get the projector elements where data have to be sent and if whole projector
|
||||
# has to be updated.
|
||||
if collection_element.collection_string == config.get_collection_string():
|
||||
# Config elements are always send to each projector
|
||||
projectors = Projector.objects.all()
|
||||
send_all = None # The decission is done later
|
||||
elif collection_element.collection_string == Projector.get_collection_string():
|
||||
# Update a projector, when the projector element is updated.
|
||||
projectors = [collection_element.get_instance()]
|
||||
send_all = True
|
||||
elif collection_element.is_deleted():
|
||||
projectors = Projector.objects.all()
|
||||
send_all = False
|
||||
# Loop over all projectors and send data that they need.
|
||||
for projector in Projector.objects.all():
|
||||
if collection_element.is_deleted():
|
||||
output = [collection_element.as_autoupdate_for_projector()]
|
||||
else:
|
||||
# Other elements are only send to the projector they are currently shown
|
||||
projectors = Projector.get_projectors_that_show_this(collection_element)
|
||||
send_all = None # The decission is done later
|
||||
|
||||
broadcast_id = config['projector_broadcast']
|
||||
if broadcast_id > 0:
|
||||
projectors = Projector.objects.all() # Also the broadcasted projector should get his data
|
||||
send_all = True
|
||||
broadcast_projector_data = get_projector_element_data(Projector.objects.get(pk=broadcast_id))
|
||||
broadcast_projector_data.append(CollectionElement.from_values(
|
||||
collection_string=Projector.get_collection_string(), id=broadcast_id).as_autoupdate_for_projector())
|
||||
else:
|
||||
broadcast_projector_data = None
|
||||
|
||||
for projector in projectors:
|
||||
if send_all is None:
|
||||
send_all = projector.need_full_update_for_this(collection_element)
|
||||
if send_all:
|
||||
if broadcast_projector_data is None:
|
||||
output = get_projector_element_data(projector)
|
||||
else:
|
||||
output = broadcast_projector_data
|
||||
else:
|
||||
# The list will be filled in the next lines.
|
||||
output = []
|
||||
|
||||
output.append(collection_element.as_autoupdate_for_projector())
|
||||
collection_elements = projector.get_collection_elements_required_for_this(collection_element)
|
||||
output = [collection_element.as_autoupdate_for_projector() for collection_element in collection_elements]
|
||||
if output:
|
||||
Group('projector-{}'.format(projector.pk)).send(
|
||||
{'text': json.dumps(output)})
|
||||
|
@ -1,5 +1,6 @@
|
||||
from django.dispatch import Signal
|
||||
|
||||
from .collection import CollectionElement
|
||||
from .dispatch import SignalConnectMetaClass
|
||||
|
||||
|
||||
@ -73,11 +74,31 @@ class ProjectorElement(object, metaclass=SignalConnectMetaClass):
|
||||
"""
|
||||
return ()
|
||||
|
||||
def need_full_update_for_this(self, collection_element):
|
||||
def get_requirements_as_collection_elements(self, config_entry):
|
||||
"""
|
||||
Returns True if this projector element needs to be updated with all
|
||||
instances as defined in get_requirements(). The given
|
||||
collection_element contains information about the changed instance.
|
||||
Default is False.
|
||||
Returns an iterable of collection elements that are required for this
|
||||
projector element. The config_entry has to be given.
|
||||
"""
|
||||
return False
|
||||
return (CollectionElement.from_instance(instance) for instance in self.get_requirements(config_entry))
|
||||
|
||||
def get_collection_elements_required_for_this(self, collection_element, config_entry):
|
||||
"""
|
||||
Returns a list of CollectionElements that have to be sent to every
|
||||
projector that shows this projector element according to the given
|
||||
collection_element and information.
|
||||
|
||||
Default: Returns only the collection_element if it belongs to the
|
||||
requirements but return all requirements if the projector changes.
|
||||
"""
|
||||
requirements_as_collection_elements = list(self.get_requirements_as_collection_elements(config_entry))
|
||||
for requirement in requirements_as_collection_elements:
|
||||
if collection_element == requirement:
|
||||
output = [collection_element]
|
||||
break
|
||||
else:
|
||||
if collection_element.information.get('this_projector'):
|
||||
output = [collection_element]
|
||||
output.extend(requirements_as_collection_elements)
|
||||
else:
|
||||
output = []
|
||||
return output
|
||||
|
Loading…
Reference in New Issue
Block a user