Exemplo n.º 1
0
    def sort(self, request):
        """
        Sort agenda items. Also checks parent field to prevent hierarchical
        loops.
        """
        nodes = request.data.get("nodes", [])
        parent_id = request.data.get("parent_id")
        items = []
        with transaction.atomic():
            for index, node in enumerate(nodes):
                item = Item.objects.get(pk=node["id"])
                item.parent_id = parent_id
                item.weight = index
                item.save(skip_autoupdate=True)
                items.append(item)

                # Now check consistency. TODO: Try to use less DB queries.
                item = Item.objects.get(pk=node["id"])
                ancestor = item.parent
                while ancestor is not None:
                    if ancestor == item:
                        raise ValidationError(
                            {
                                "detail": "There must not be a hierarchical loop. Please reload the page."
                            }
                        )
                    ancestor = ancestor.parent

        inform_changed_data(items)
        return Response({"detail": "The agenda has been sorted."})
Exemplo n.º 2
0
 def delete(self, *args, **kwargs):
     # Hotfix for #4491: Trigger extra autoupdate for motion so that the serializer
     # field vor motion change recommendations gets updated too.
     motion = self.motion
     result = super().delete(*args, **kwargs)
     inform_changed_data(motion)
     return result
Exemplo n.º 3
0
    def begin_speech(self):
        """
        Let the user speak.

        Set the weight to None and the time to now. If anyone is still
        speaking, end his speech.
        """
        try:
            current_speaker = (Speaker.objects.filter(item=self.item, end_time=None)
                                              .exclude(begin_time=None).get())
        except Speaker.DoesNotExist:
            pass
        else:
            # Do not send an autoupdate for the countdown and the item. This is done
            # by saving the item and countdown later.
            current_speaker.end_speech(skip_autoupdate=True)
        self.weight = None
        self.begin_time = timezone.now()
        self.save()  # Here, the item is saved and causes an autoupdate.
        if config['agenda_couple_countdown_and_speakers']:
            countdown, created = Countdown.objects.get_or_create(pk=1, defaults={
                'default_time': config['projector_default_countdown'],
                'countdown_time': config['projector_default_countdown']})
            if not created:
                countdown.control(action='reset', skip_autoupdate=True)
            countdown.control(action='start', skip_autoupdate=True)

            inform_changed_data(countdown)  # Here, the autoupdate for the countdown is triggered.
Exemplo n.º 4
0
def listen_to_related_object_post_save(sender, instance, created, **kwargs):
    """
    Receiver function to create agenda items. It is connected to the signal
    django.db.models.signals.post_save during app loading.
    """
    if created and hasattr(instance, 'get_agenda_title'):
        Item.objects.create(content_object=instance)
        inform_changed_data(False, instance)
Exemplo n.º 5
0
    def post(self, request, poll_id):
        # Get poll instance.
        vc = VoteCollector.objects.get(id=1)
        if vc.voting_mode == 'MotionPoll':
            poll_model = MotionPoll
            conn_model = MotionPollKeypadConnection
        else:
            poll_model = AssignmentPoll
            conn_model = AssignmentPollKeypadConnection
        try:
            poll = poll_model.objects.get(id=poll_id)
        except poll_model.DoesNotExist:
            return HttpResponse('')

        # Load json list from request body.
        votes = json.loads(request.body.decode('utf-8'))
        keypad_set = set()
        connections = []
        for vote in votes:
            keypad_id = vote['id']
            try:
                keypad = Keypad.objects.get(keypad_id=keypad_id)
            except Keypad.DoesNotExist:
                continue

            # Mark keypad as in range and update battery level.
            keypad.in_range = True
            keypad.battery_level = vote['bl']
            keypad.save(skip_autoupdate=True)

            # Validate vote value.
            value = vote['value']
            if value not in ('Y', 'N', 'A'):
                continue

            # Write poll keypad connection.
            try:
                conn = conn_model.objects.get(poll=poll, keypad=keypad)
            except conn_model.DoesNotExist:
                conn = conn_model(poll=poll, keypad=keypad)
            conn.serial_number = vote['sn']
            conn.value = value
            if conn.pk:
                # Save updated connection.
                conn.save(skip_autoupdate=True)
            else:
                # Add new connection to bulk create list.
                connections.append(conn)
            keypad_set.add(keypad.id)

        # Bulk create connections.
        conn_model.objects.bulk_create(connections)

        # Trigger auto update.
        connections = conn_model.objects.filter(poll=poll, keypad_id__in=keypad_set)
        inform_changed_data(connections)

        return HttpResponse()
Exemplo n.º 6
0
    def test_change_one_element(self):
        topic = Topic.objects.create(title="test_topic")
        channel_layers[DEFAULT_CHANNEL_LAYER].flush()

        inform_changed_data(topic)

        channel_message = self.get_next_message("autoupdate.send_data", require=True)
        self.assertEqual(len(channel_message["elements"]), 1)
        self.assertEqual(channel_message["elements"][0]["collection_string"], "topics/topic")
Exemplo n.º 7
0
 def test_hidden_by_anonymous_with_manage_perms(self):
     group = Group.objects.get(pk=1)  # Group with pk 1 is for anonymous users.
     permission_string = "agenda.can_manage"
     app_label, codename = permission_string.split(".")
     permission = Permission.objects.get(
         content_type__app_label=app_label, codename=codename
     )
     group.permissions.add(permission)
     inform_changed_data(group)
     response = self.client.get(reverse("item-detail", args=[self.item.pk]))
     self.assertEqual(response.status_code, status.HTTP_200_OK)
Exemplo n.º 8
0
    def test_change_many_elements(self):
        topics = (
            Topic.objects.create(title='test_topic1'),
            Topic.objects.create(title='test_topic2'),
            Topic.objects.create(title='test_topic3'))
        channel_layers[DEFAULT_CHANNEL_LAYER].flush()

        inform_changed_data(topics)

        channel_message = self.get_next_message('autoupdate.send_data', require=True)
        self.assertEqual(len(channel_message['elements']), 3)
Exemplo n.º 9
0
    def test_change_one_element(self):
        topic = Topic.objects.create(title='test_topic')
        channel_layers[DEFAULT_CHANNEL_LAYER].flush()

        inform_changed_data(topic)

        channel_message = self.get_next_message('autoupdate.send_data', require=True)
        self.assertEqual(len(channel_message['elements']), 1)
        self.assertEqual(
            channel_message['elements'][0]['collection_string'],
            'topics/topic')
Exemplo n.º 10
0
    def test_add_someone_else_non_admin(self):
        admin = get_user_model().objects.get(username="******")
        group_admin = admin.groups.get(name="Admin")
        group_delegates = type(group_admin).objects.get(name="Delegates")
        admin.groups.add(group_delegates)
        admin.groups.remove(group_admin)
        inform_changed_data(admin)

        response = self.client.post(
            reverse("item-manage-speaker", args=[self.item.pk]), {"user": self.user.pk}
        )
        self.assertEqual(response.status_code, 403)
Exemplo n.º 11
0
 def nominate_self(self, request, assignment):
     if assignment.phase == assignment.PHASE_FINISHED:
         raise ValidationError({'detail': _('You can not candidate to this election because it is finished.')})
     if assignment.phase == assignment.PHASE_VOTING and not has_perm(request.user, 'assignments.can_manage'):
         # To nominate self during voting you have to be a manager.
         self.permission_denied(request)
     # If the request.user is already a candidate he can nominate himself nevertheless.
     assignment.set_candidate(request.user)
     # Send new candidate via autoupdate because users without permission
     # to see users may not have it but can get it now.
     inform_changed_data([request.user])
     return _('You were nominated successfully.')
Exemplo n.º 12
0
    def create_poll(self):
        """
        Creates a new poll for the assignment and adds all candidates to all
        lists of speakers of related agenda items.
        """
        candidates = self.candidates.all()

        # Find out the method of the election
        if config["assignments_poll_vote_values"] == "votes":
            pollmethod = "votes"
        elif config["assignments_poll_vote_values"] == "yesnoabstain":
            pollmethod = "yna"
        elif config["assignments_poll_vote_values"] == "yesno":
            pollmethod = "yn"
        else:
            # config['assignments_poll_vote_values'] == 'auto'
            # candidates <= available posts -> yes/no/abstain
            if len(candidates) <= (self.open_posts - self.elected.count()):
                pollmethod = "yna"
            else:
                pollmethod = "votes"

        # Create the poll with the candidates.
        poll = self.polls.create(
            description=self.poll_description_default, pollmethod=pollmethod
        )
        options = []
        related_users = AssignmentRelatedUser.objects.filter(
            assignment__id=self.id
        ).exclude(elected=True)
        for related_user in related_users:
            options.append(
                {"candidate": related_user.user, "weight": related_user.weight}
            )
        poll.set_options(options, skip_autoupdate=True)
        inform_changed_data(self)

        # Add all candidates to list of speakers of related agenda item
        # TODO: Try to do this in a bulk create
        if config["assignments_add_candidates_to_list_of_speakers"]:
            for candidate in self.candidates:
                try:
                    Speaker.objects.add(
                        candidate, self.agenda_item, skip_autoupdate=True
                    )
                except OpenSlidesError:
                    # The Speaker is already on the list. Do nothing.
                    # TODO: Find a smart way not to catch the error concerning AnonymousUser.
                    pass
            inform_changed_data(self.agenda_item)

        return poll
Exemplo n.º 13
0
    def test_change_only_non_root_rest_element(self):
        """
        Tests that if only a non root_rest_element is called, then only the
        root_rest_element is in the channel.
        """
        assignment = Assignment.objects.create(title='test_assignment', open_posts=1)
        poll = assignment.create_poll()
        channel_layers[DEFAULT_CHANNEL_LAYER].flush()

        inform_changed_data(poll)

        channel_message = self.get_next_message('autoupdate.send_data', require=True)
        self.assertEqual(len(channel_message['elements']), 1)
Exemplo n.º 14
0
    def test_change_with_non_root_rest_elements(self):
        """
        Tests that if an root_rest_element is called together with one of its
        child elements, then there is only the root_rest_element in the channel
        message.
        """
        assignment = Assignment.objects.create(title='test_assignment', open_posts=1)
        poll = assignment.create_poll()
        channel_layers[DEFAULT_CHANNEL_LAYER].flush()

        inform_changed_data((assignment, poll))

        channel_message = self.get_next_message('autoupdate.send_data', require=True)
        self.assertEqual(len(channel_message['elements']), 1)
Exemplo n.º 15
0
    def test_change_no_autoupdate_model(self):
        """
        Tests that if inform_changed_data() is called with a model that does
        not support autoupdate, nothing happens.
        """
        group = Group.objects.create(name="test_group")
        channel_layers[DEFAULT_CHANNEL_LAYER].flush()

        inform_changed_data(group)

        with self.assertRaises(AssertionError):
            # self.get_next_message() with require=True raises a AssertionError
            # if there is no message in the channel
            self.get_next_message("autoupdate.send_data", require=True)
Exemplo n.º 16
0
    def test_get_with_user_without_permissions(self):
        group = Group.objects.get(pk=1)
        permission_string = "users.can_see_name"
        app_label, codename = permission_string.split(".")
        permission = group.permissions.get(
            content_type__app_label=app_label, codename=codename
        )
        group.permissions.remove(permission)
        inform_changed_data(group)
        config["general_system_enable_anonymous"] = True
        guest_client = APIClient()

        response = guest_client.get("/rest/users/user/1/")

        self.assertEqual(response.status_code, 404)
Exemplo n.º 17
0
    def test_nominate_self_during_voting_non_admin(self):
        self.assignment.set_phase(Assignment.PHASE_VOTING)
        self.assignment.save()
        admin = get_user_model().objects.get(username="******")
        group_admin = admin.groups.get(name="Admin")
        group_delegates = type(group_admin).objects.get(name="Delegates")
        admin.groups.add(group_delegates)
        admin.groups.remove(group_admin)
        inform_changed_data(admin)

        response = self.client.post(
            reverse("assignment-candidature-self", args=[self.assignment.pk])
        )

        self.assertEqual(response.status_code, 403)
Exemplo n.º 18
0
    def test_delete_other_during_voting_non_admin(self):
        self.assignment.set_candidate(self.user)
        self.assignment.set_phase(Assignment.PHASE_VOTING)
        self.assignment.save()
        admin = get_user_model().objects.get(username='******')
        group_staff = admin.groups.get(name='Staff')
        group_delegates = type(group_staff).objects.get(name='Delegates')
        admin.groups.add(group_delegates)
        admin.groups.remove(group_staff)
        inform_changed_data(admin)

        response = self.client.delete(
            reverse('assignment-candidature-other', args=[self.assignment.pk]),
            {'user': self.user.pk})

        self.assertEqual(response.status_code, 403)
Exemplo n.º 19
0
    def save(self, *args, **kwargs):
        recommendations = MotionChangeRecommendation.objects.filter(
            motion=self.motion).exclude(pk=self.pk)

        if self.collides_with_other_recommendation(recommendations):
            raise ValidationError(
                f"The recommendation collides with an existing one (line {self.line_from} - {self.line_to})."
            )

        result = super().save(*args, **kwargs)

        # Hotfix for #4491: Trigger extra autoupdate for motion so that the serializer
        # field vor motion change recommendations gets updated too.
        inform_changed_data(self.motion)

        return result
Exemplo n.º 20
0
    def test_withdraw_self_during_voting_non_admin(self):
        self.assignment.set_candidate(
            get_user_model().objects.get(username='******'))
        self.assignment.set_phase(Assignment.PHASE_VOTING)
        self.assignment.save()
        admin = get_user_model().objects.get(username='******')
        group_admin = admin.groups.get(name='Admin')
        group_delegates = type(group_admin).objects.get(name='Delegates')
        admin.groups.add(group_delegates)
        admin.groups.remove(group_admin)
        inform_changed_data(admin)

        response = self.client.delete(
            reverse('assignment-candidature-self', args=[self.assignment.pk]))

        self.assertEqual(response.status_code, 403)
Exemplo n.º 21
0
 def nominate_other(self, request, user, assignment):
     if assignment.is_elected(user):
         raise ValidationError({'detail': _('User %s is already elected.') % user})
     if assignment.phase == assignment.PHASE_FINISHED:
         detail = _('You can not nominate someone to this election because it is finished.')
         raise ValidationError({'detail': detail})
     if assignment.phase == assignment.PHASE_VOTING and not has_perm(request.user, 'assignments.can_manage'):
         # To nominate another user during voting you have to be a manager.
         self.permission_denied(request)
     if assignment.is_candidate(user):
         raise ValidationError({'detail': _('User %s is already nominated.') % user})
     assignment.set_candidate(user)
     # Send new candidate via autoupdate because users without permission
     # to see users may not have it but can get it now.
     inform_changed_data(user)
     return _('User %s was nominated successfully.') % user
Exemplo n.º 22
0
    def test_change_with_non_root_rest_elements(self):
        """
        Tests that if an root_rest_element is called together with one of its
        child elements, then there is only the root_rest_element in the channel
        message.
        """
        assignment = Assignment.objects.create(title='test_assignment',
                                               open_posts=1)
        poll = assignment.create_poll()
        channel_layers[DEFAULT_CHANNEL_LAYER].flush()

        inform_changed_data((assignment, poll))

        channel_message = self.get_next_message('autoupdate.send_data',
                                                require=True)
        self.assertEqual(len(channel_message['elements']), 1)
Exemplo n.º 23
0
 def test_set_no_manage_perms(self):
     admin = User.objects.get()
     admin.groups.add(GROUP_DELEGATE_PK)
     admin.groups.remove(GROUP_ADMIN_PK)
     inform_changed_data(admin)
     response = self.admin_client.post(
         reverse("user_setpassword"),
         {
             "old_password": "******",
             "new_password": "******",
         },
     )
     self.assertEqual(response.status_code, status.HTTP_200_OK)
     admin = User.objects.get()
     self.assertTrue(
         admin.check_password("new_password_ou0wei3tae5ahr7oa1Fu"))
Exemplo n.º 24
0
 def nominate_self(self, request, assignment):
     if assignment.phase == assignment.PHASE_FINISHED:
         raise ValidationError({
             "detail":
             "You can not candidate to this election because it is finished."
         })
     if assignment.phase == assignment.PHASE_VOTING and not has_perm(
             request.user, "assignments.can_manage"):
         # To nominate self during voting you have to be a manager.
         self.permission_denied(request)
     # If the request.user is already a candidate he can nominate himself nevertheless.
     assignment.add_candidate(request.user)
     # Send new candidate via autoupdate because users without permission
     # to see users may not have it but can get it now.
     inform_changed_data([request.user])
     return "You were nominated successfully."
Exemplo n.º 25
0
def listen_to_related_object_post_save(sender, instance, created, **kwargs):
    """
    Receiver function to create agenda items. It is connected to the signal
    django.db.models.signals.post_save during app loading.

    Do not run caching and autoupdate if the instance as an attribute
    skip_autoupdate (regardless of its truthy or falsy conent).
    """
    if hasattr(instance, 'get_agenda_title'):
        if created:
            # If the object is created, the related_object has to be sent again.
            Item.objects.create(content_object=instance)
            if not hasattr(instance, 'skip_autoupdate'):
                inform_changed_data(instance)
        elif not hasattr(instance, 'skip_autoupdate'):
            # If the object has changed, then also the agenda item has to be sent.
            inform_changed_data(instance.agenda_item)
Exemplo n.º 26
0
    def save(self, *args, **kwargs):
        recommendations = MotionChangeRecommendation.objects.filter(
            motion=self.motion
        ).exclude(pk=self.pk)

        if self.collides_with_other_recommendation(recommendations):
            raise ValidationError(
                f"The recommendation collides with an existing one (line {self.line_from} - {self.line_to})."
            )

        result = super().save(*args, **kwargs)

        # Hotfix for #4491: Trigger extra autoupdate for motion so that the serializer
        # field vor motion change recommendations gets updated too.
        inform_changed_data(self.motion)

        return result
Exemplo n.º 27
0
    def test_delete_other_during_voting_non_admin(self):
        self.assignment.set_candidate(self.user)
        self.assignment.set_phase(Assignment.PHASE_VOTING)
        self.assignment.save()
        admin = get_user_model().objects.get(username="******")
        group_admin = admin.groups.get(name="Admin")
        group_delegates = type(group_admin).objects.get(name="Delegates")
        admin.groups.add(group_delegates)
        admin.groups.remove(group_admin)
        inform_changed_data(admin)

        response = self.client.delete(
            reverse("assignment-candidature-other", args=[self.assignment.pk]),
            {"user": self.user.pk},
        )

        self.assertEqual(response.status_code, 403)
Exemplo n.º 28
0
    def stop(self, request, pk):
        poll = self.get_object()
        # Analog polls could not be stopped; they are stopped when
        # the results are entered.
        if poll.type == BasePoll.TYPE_ANALOG:
            raise ValidationError(
                {"detail": "Analog polls can not be stopped. Please enter votes."}
            )

        if poll.state != BasePoll.STATE_STARTED:
            raise ValidationError({"detail": "Wrong poll state"})

        poll.state = BasePoll.STATE_FINISHED
        poll.save()
        inform_changed_data(poll.get_votes())
        inform_changed_data(poll.get_options())
        return Response()
Exemplo n.º 29
0
    def stop(self, request, pk):
        poll = self.get_locked_object()
        # Analog polls cannot be stopped; they are stopped when
        # the results are entered.
        if poll.type == BasePoll.TYPE_ANALOG:
            raise ValidationError({
                "detail":
                "Analog polls can not be stopped. Please enter votes."
            })

        if poll.state != BasePoll.STATE_STARTED:
            raise ValidationError({"detail": "Wrong poll state"})

        poll.stop()
        inform_changed_data(poll.get_votes())
        inform_changed_data(poll.get_options())
        self.extend_history_information(["Voting stopped"])
        return Response()
Exemplo n.º 30
0
    def test_add_without_permission(self):
        admin = get_user_model().objects.get(username="******")
        admin.groups.add(GROUP_DELEGATE_PK)
        admin.groups.remove(GROUP_ADMIN_PK)
        inform_changed_data(admin)

        response = self.client.post(
            reverse("motion-manage-multiple-submitters"),
            {
                "motions": [{
                    "id": self.motion1.id,
                    "submitters": [self.admin.pk]
                }]
            },
        )
        self.assertEqual(response.status_code, 403)
        self.assertEqual(self.motion1.submitters.count(), 0)
        self.assertEqual(self.motion2.submitters.count(), 0)
Exemplo n.º 31
0
    def test_non_admin(self):
        """
        Test to create a motion by a delegate, non staff user.
        """
        self.admin = get_user_model().objects.get(username="******")
        self.admin.groups.add(GROUP_DELEGATE_PK)
        self.admin.groups.remove(GROUP_ADMIN_PK)
        inform_changed_data(self.admin)

        response = self.client.post(
            reverse("motion-list"),
            {
                "title": "test_title_peiJozae0luew9EeL8bo",
                "text": "test_text_eHohS8ohr5ahshoah8Oh",
            },
        )

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
Exemplo n.º 32
0
    def update_keypads_from_votes(self, votes, voting_type):
        """
        Updates the keypds from votes. The voting type has to be a VoteCollector one.
        The votes has to be validated first.
        """
        if voting_type.startswith('votecollector'):
            keypads = []
            for vote in votes:
                keypad = vote['keypad']
                # Mark keypad as in range and update battery level.
                if keypad:
                    keypad.in_range = True
                    keypad.battery_level = vote['bl']
                    keypad.save(skip_autoupdate=True)
                    keypads.append(keypad)

            # Trigger auto-update for keypads.
            inform_changed_data(keypads)
Exemplo n.º 33
0
    def create_votes_types_yn_yna(self, data, poll, check_user, vote_user):
        """
        check_user is used for the voted-array and weight of the vote,
        vote_user is the one put into the vote
        """
        options = poll.get_options()
        weight = (check_user.vote_weight
                  if config["users_activate_vote_weight"] else Decimal(1))
        for option_id, result in data.items():
            option = options.get(pk=option_id)
            vote = AssignmentVote.objects.create(option=option,
                                                 user=vote_user,
                                                 value=result,
                                                 weight=weight)
            inform_changed_data(vote, no_delete_on_restriction=True)
            inform_changed_data(option, no_delete_on_restriction=True)

        poll.voted.add(check_user)
Exemplo n.º 34
0
    def create_votes_type_named_pseudoanonymous(self, data, poll, check_user,
                                                vote_user):
        """
        check_user is used for the voted-array and weight of the vote,
        vote_user is the one put into the vote
        """
        options = poll.get_options()
        for option_id, result in data.items():
            option = options.get(pk=option_id)
            vote = AssignmentVote.objects.create(
                option=option,
                user=vote_user,
                value=result,
                weight=check_user.vote_weight,
            )
            inform_changed_data(vote, no_delete_on_restriction=True)
            inform_changed_data(option, no_delete_on_restriction=True)

        poll.voted.add(check_user)
Exemplo n.º 35
0
    def assert_can_vote(self, poll, request, vote_user):
        """
        Raises a permission denied, if the user is not allowed to vote (or has already voted).
        Adds the user to the voted array, so this needs to be reverted if a later error happens!
        Analog:                     has to have manage permissions
        Named & Pseudoanonymous:    has to be in a poll group and present
        """
        # if the request user is not the vote user, the delegation must be right
        if request.user != vote_user and request.user != vote_user.vote_delegated_to:
            raise ValidationError(
                {
                    "detail": f"You cannot vote for {vote_user.id} since the vote right was not delegated to you."
                }
            )

        # If the request user is the vote user, this user must not have any delegation.
        # It is not allowed to vote for oneself, if the vote is delegated
        if request.user == vote_user and request.user.vote_delegated_to is not None:
            raise ValidationError(
                {"detail": "You cannot vote since your vote right is delegated."}
            )

        if poll.type == BasePoll.TYPE_ANALOG:
            if not self.has_manage_permissions():
                self.permission_denied(request)
        else:
            if poll.state != BasePoll.STATE_STARTED:
                raise ValidationError(
                    {"detail": "You can only vote on a started poll."}
                )

            if not request.user.is_present or not in_some_groups(
                vote_user.id,
                list(poll.groups.values_list("pk", flat=True)),
                exact=True,
            ):
                self.permission_denied(request)

            try:
                self.add_user_to_voted_array(vote_user, poll)
                inform_changed_data(poll)
            except IntegrityError:
                raise ValidationError({"detail": "You have already voted."})
Exemplo n.º 36
0
    def sort_related_users(self, request, pk=None):
        """
        Special view endpoint to sort the assignment related users.

        Expects a list of IDs of the related users (pk of AssignmentRelatedUser model).
        """
        assignment = self.get_object()

        # Check data
        related_user_ids = request.data.get("related_users")
        if not isinstance(related_user_ids, list):
            raise ValidationError({"detail": "users has to be a list of IDs."})

        # Get all related users from AssignmentRelatedUser.
        related_users = {}
        for related_user in AssignmentRelatedUser.objects.filter(
            assignment__id=assignment.id
        ):
            related_users[related_user.pk] = related_user

        # Check all given candidates from the request
        valid_related_users = []
        for related_user_id in related_user_ids:
            if (
                not isinstance(related_user_id, int)
                or related_users.get(related_user_id) is None
            ):
                raise ValidationError({"detail": "Invalid data."})
            valid_related_users.append(related_users[related_user_id])

        # Sort the related users
        weight = 1
        with transaction.atomic():
            for valid_related_user in valid_related_users:
                valid_related_user.weight = weight
                valid_related_user.save(skip_autoupdate=True)
                weight += 1

        # send autoupdate
        inform_changed_data(assignment)

        # Initiate response.
        return Response({"detail": "Assignment related users successfully sorted."})
Exemplo n.º 37
0
    def sort_related_users(self, request, pk=None):
        """
        Special view endpoint to sort the assignment related users.

        Expects a list of IDs of the related users (pk of AssignmentRelatedUser model).
        """
        assignment = self.get_object()

        # Check data
        related_user_ids = request.data.get("related_users")
        if not isinstance(related_user_ids, list):
            raise ValidationError({"detail": "users has to be a list of IDs."})

        # Get all related users from AssignmentRelatedUser.
        related_users = {}
        for related_user in AssignmentRelatedUser.objects.filter(
            assignment__id=assignment.id
        ):
            related_users[related_user.pk] = related_user

        # Check all given candidates from the request
        valid_related_users = []
        for related_user_id in related_user_ids:
            if (
                not isinstance(related_user_id, int)
                or related_users.get(related_user_id) is None
            ):
                raise ValidationError({"detail": "Invalid data."})
            valid_related_users.append(related_users[related_user_id])

        # Sort the related users
        weight = 1
        with transaction.atomic():
            for valid_related_user in valid_related_users:
                valid_related_user.weight = weight
                valid_related_user.save(skip_autoupdate=True)
                weight += 1

        # send autoupdate
        inform_changed_data(assignment)

        # Initiate response.
        return Response({"detail": "Assignment related users successfully sorted."})
Exemplo n.º 38
0
    def handle_analog_vote(self, data, poll, user):
        for field in ["votesvalid", "votesinvalid", "votescast"]:
            setattr(poll, field, data[field])

        global_no_enabled = (poll.global_no and poll.pollmethod
                             == AssignmentPoll.POLLMETHOD_VOTES)
        if global_no_enabled:
            poll.amount_global_no = data.get("amount_global_no", Decimal(0))
        global_abstain_enabled = (poll.global_abstain and poll.pollmethod
                                  == AssignmentPoll.POLLMETHOD_VOTES)
        if global_abstain_enabled:
            poll.amount_global_abstain = data.get("amount_global_abstain",
                                                  Decimal(0))

        options = poll.get_options()
        options_data = data.get("options")

        with transaction.atomic():
            for option_id, vote in options_data.items():
                option = options.get(pk=int(option_id))
                vote_obj, _ = AssignmentVote.objects.get_or_create(
                    option=option, value="Y")
                vote_obj.weight = vote["Y"]
                vote_obj.save()

                if poll.pollmethod in (
                        AssignmentPoll.POLLMETHOD_YN,
                        AssignmentPoll.POLLMETHOD_YNA,
                ):
                    vote_obj, _ = AssignmentVote.objects.get_or_create(
                        option=option, value="N")
                    vote_obj.weight = vote["N"]
                    vote_obj.save()

                if poll.pollmethod == AssignmentPoll.POLLMETHOD_YNA:
                    vote_obj, _ = AssignmentVote.objects.get_or_create(
                        option=option, value="A")
                    vote_obj.weight = vote["A"]
                    vote_obj.save()
                inform_changed_data(option)

            poll.save()
Exemplo n.º 39
0
    def begin_speech(self):
        """
        Let the user speak.

        Set the weight to None and the time to now. If anyone is still
        speaking, end his speech.
        """
        try:
            current_speaker = (
                Speaker.objects.filter(
                    list_of_speakers=self.list_of_speakers, end_time=None
                )
                .exclude(begin_time=None)
                .get()
            )
        except Speaker.DoesNotExist:
            pass
        else:
            # Do not send an autoupdate for the countdown and the list_of_speakers. This is done
            # by saving the list_of_speakers and countdown later.
            current_speaker.end_speech(skip_autoupdate=True)
        self.weight = None
        self.begin_time = timezone.now()
        self.save()  # Here, the list_of_speakers is saved and causes an autoupdate.
        if config["agenda_couple_countdown_and_speakers"]:
            countdown, created = Countdown.objects.get_or_create(
                pk=1,
                defaults={
                    "default_time": config["projector_default_countdown"],
                    "title": "Default countdown",
                    "countdown_time": config["projector_default_countdown"],
                },
            )
            if created:
                restart_id_sequence("core_countdown")
            else:
                countdown.control(action="reset", skip_autoupdate=True)
            countdown.control(action="start", skip_autoupdate=True)

            inform_changed_data(
                countdown
            )  # Here, the autoupdate for the countdown is triggered.
Exemplo n.º 40
0
    def save(self, skip_autoupdate=False, *args, **kwargs):
        """
        Save the motion.

        1. Set the state of a new motion to the default state.
        2. Ensure that the identifier is not an empty string.
        3. Save the motion object.
        """
        if not self.state:
            self.reset_state()

        # Solves the problem, that there can only be one motion with an empty
        # string as identifier.
        if not self.identifier and isinstance(self.identifier, str):
            self.identifier = None

        # Try to save the motion until it succeeds with a correct identifier.
        while True:
            try:
                # Always skip autoupdate. Maybe we run it later in this method.
                with transaction.atomic():
                    super(Motion, self).save(skip_autoupdate=True,
                                             *args,
                                             **kwargs)  # type: ignore
            except IntegrityError:
                # Identifier is already used.
                if hasattr(self, '_identifier_prefix'):
                    # Calculate a new one and try again.
                    self.identifier_number, self.identifier = self.increment_identifier_number(
                        self.identifier_number,
                        self._identifier_prefix,
                    )
                else:
                    # Do not calculate a new one but reraise the IntegrityError.
                    # The error is caught in the category sort view.
                    raise
            else:
                # Save was successful. End loop.
                break

        if not skip_autoupdate:
            inform_changed_data(self)
Exemplo n.º 41
0
    def test_retrieve_non_manager_with_read_permission(self):
        """
        Checks, if the sections can be seen by a non manager, but he is in
        one of the read_groups.
        """
        self.admin.groups.remove(
            self.group_in)  # group_in has motions.can_manage permission
        self.admin.groups.add(self.group_out)  # group_out does not.
        inform_changed_data(self.admin)

        section = MotionCommentSection(name="test_name_f3mMD28LMcm29Coelwcm")
        section.save()
        section.read_groups.add(self.group_out, self.group_in)
        inform_changed_data(section)

        response = self.client.get(reverse("motioncommentsection-list"))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data), 1)
        self.assertEqual(response.data[0]["name"],
                         "test_name_f3mMD28LMcm29Coelwcm")
Exemplo n.º 42
0
    def test_patch_amendment_paragraphs_no_manage_perms(self):
        admin = get_user_model().objects.get(username="******")
        admin.groups.remove(GROUP_ADMIN_PK)
        admin.groups.add(GROUP_DELEGATE_PK)
        Submitter.objects.add(admin, self.motion)
        self.motion.state.allow_submitter_edit = True
        self.motion.state.save()
        inform_changed_data(admin)

        response = self.client.patch(
            reverse("motion-detail", args=[self.motion.pk]),
            {"amendment_paragraphs": ["test_paragraph_39fo8qcpcaFMmjfaD2Lb"]},
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        motion = Motion.objects.get()
        self.assertTrue(isinstance(motion.amendment_paragraphs, list))
        self.assertEqual(len(motion.amendment_paragraphs), 1)
        self.assertEqual(motion.amendment_paragraphs[0],
                         "test_paragraph_39fo8qcpcaFMmjfaD2Lb")
        self.assertEqual(motion.text, "")
Exemplo n.º 43
0
    def save(self, skip_autoupdate=False, *args, **kwargs):
        """
        Save the motion.

        1. Set the state of a new motion to the default state.
        2. Ensure that the identifier is not an empty string.
        3. Save the motion object.
        """
        if not self.state:
            self.reset_state()

        # Solves the problem, that there can only be one motion with an empty
        # string as identifier.
        if not self.identifier and isinstance(self.identifier, str):
            self.identifier = None

        # Try to save the motion until it succeeds with a correct identifier.
        while True:
            try:
                # Always skip autoupdate. Maybe we run it later in this method.
                with transaction.atomic():
                    super(Motion, self).save(  # type: ignore
                        skip_autoupdate=True, *args, **kwargs
                    )
            except IntegrityError:
                # Identifier is already used.
                if hasattr(self, "_identifier_prefix"):
                    # Calculate a new one and try again.
                    self.identifier_number, self.identifier = self.increment_identifier_number(
                        self.identifier_number, self._identifier_prefix
                    )
                else:
                    # Do not calculate a new one but reraise the IntegrityError.
                    # The error is caught in the category sort view.
                    raise
            else:
                # Save was successful. End loop.
                break

        if not skip_autoupdate:
            inform_changed_data(self)
Exemplo n.º 44
0
    def test_removal_of_supporters(self):
        admin = get_user_model().objects.get(username='******')
        group_staff = admin.groups.get(name='Staff')
        admin.groups.remove(group_staff)
        inform_changed_data(admin)
        self.motion.submitters.add(admin)
        supporter = get_user_model().objects.create_user(
            username='******',
            password='******')
        self.motion.supporters.add(supporter)
        config['motions_remove_supporters'] = True
        self.assertEqual(self.motion.supporters.count(), 1)

        response = self.client.patch(
            reverse('motion-detail', args=[self.motion.pk]),
            {'title': 'new_title_ohph1aedie5Du8sai2ye'})

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        motion = Motion.objects.get()
        self.assertEqual(motion.title, 'new_title_ohph1aedie5Du8sai2ye')
        self.assertEqual(motion.supporters.count(), 0)
Exemplo n.º 45
0
 def create(self, validated_data):
     """
     Customized create method. Set information about related agenda item
     into agenda_item_update_information container.
     """
     agenda_type = validated_data.pop("agenda_type", None)
     agenda_parent_id = validated_data.pop("agenda_parent_id", None)
     agenda_comment = validated_data.pop("agenda_comment", None)
     agenda_duration = validated_data.pop("agenda_duration", None)
     agenda_weight = validated_data.pop("agenda_weight", None)
     attachments = validated_data.pop("attachments", [])
     topic = Topic(**validated_data)
     topic.agenda_item_update_information["type"] = agenda_type
     topic.agenda_item_update_information["parent_id"] = agenda_parent_id
     topic.agenda_item_update_information["comment"] = agenda_comment
     topic.agenda_item_update_information["duration"] = agenda_duration
     topic.agenda_item_update_information["weight"] = agenda_weight
     topic.save(skip_autoupdate=True)
     topic.attachments.add(*attachments)
     inform_changed_data(topic)
     return topic
Exemplo n.º 46
0
 def nominate_other(self, request, user, assignment):
     if assignment.is_elected(user):
         raise ValidationError(
             {'detail': _('User %s is already elected.') % user})
     if assignment.phase == assignment.PHASE_FINISHED:
         detail = _(
             'You can not nominate someone to this election because it is finished.'
         )
         raise ValidationError({'detail': detail})
     if assignment.phase == assignment.PHASE_VOTING and not has_perm(
             request.user, 'assignments.can_manage'):
         # To nominate another user during voting you have to be a manager.
         self.permission_denied(request)
     if assignment.is_candidate(user):
         raise ValidationError(
             {'detail': _('User %s is already nominated.') % user})
     assignment.set_candidate(user)
     # Send new candidate via autoupdate because users without permission
     # to see users may not have it but can get it now.
     inform_changed_data(user)
     return _('User %s was nominated successfully.') % user
Exemplo n.º 47
0
    def test_set_no_can_change_password(self):
        admin = User.objects.get()
        admin.groups.add(GROUP_DELEGATE_PK)
        admin.groups.remove(GROUP_ADMIN_PK)
        can_change_password_permission = Permission.objects.get(
            content_type__app_label="users", codename="can_change_password")
        delegate_group = Group.objects.get(pk=GROUP_DELEGATE_PK)
        delegate_group.permissions.remove(can_change_password_permission)
        inform_changed_data(delegate_group)
        inform_changed_data(admin)

        response = self.admin_client.post(
            reverse("user_setpassword"),
            {
                "old_password": "******",
                "new_password": "******",
            },
        )
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        admin = User.objects.get()
        self.assertTrue(admin.check_password("admin"))
Exemplo n.º 48
0
    def post(self, request):
        # Load json list from request body.
        votes = json.loads(request.body.decode('utf-8'))
        keypads = []
        for vote in votes:
            keypad_id = vote['id']
            try:
                keypad = Keypad.objects.get(keypad_id=keypad_id)
            except Keypad.DoesNotExist:
                continue

            # Mark keypad as in range and update battery level.
            keypad.in_range = True
            keypad.battery_level = vote['bl']
            keypad.save(skip_autoupdate=True)
            keypads.append(keypad)

        # Trigger auto-update.
        inform_changed_data(keypads)

        return HttpResponse()
Exemplo n.º 49
0
 def create(self, validated_data):
     """
     Customized create method. Set information about related agenda item
     into agenda_item_update_information container.
     """
     agenda_type = validated_data.pop("agenda_type", None)
     agenda_parent_id = validated_data.pop("agenda_parent_id", None)
     agenda_comment = validated_data.pop("agenda_comment", None)
     agenda_duration = validated_data.pop("agenda_duration", None)
     agenda_weight = validated_data.pop("agenda_weight", None)
     attachments = validated_data.pop("attachments", [])
     topic = Topic(**validated_data)
     topic.agenda_item_update_information["type"] = agenda_type
     topic.agenda_item_update_information["parent_id"] = agenda_parent_id
     topic.agenda_item_update_information["comment"] = agenda_comment
     topic.agenda_item_update_information["duration"] = agenda_duration
     topic.agenda_item_update_information["weight"] = agenda_weight
     topic.save(skip_autoupdate=True)
     topic.attachments.add(*attachments)
     inform_changed_data(topic)
     return topic
Exemplo n.º 50
0
    def post(self, request):
        # Load json list from request body.
        votes = json.loads(request.body.decode('utf-8'))
        keypads = []
        for vote in votes:
            keypad_id = vote['id']
            try:
                keypad = Keypad.objects.get(keypad_id=keypad_id)
            except Keypad.DoesNotExist:
                continue

            # Mark keypad as in range and update battery level.
            keypad.in_range = True
            keypad.battery_level = vote['bl']
            keypad.save(skip_autoupdate=True)
            keypads.append(keypad)

        # Trigger auto-update.
        inform_changed_data(keypads)

        return HttpResponse()
Exemplo n.º 51
0
 def degrade_admin(self, can_manage_config=False, can_manage_logos_and_fonts=False):
     admin = get_user_model().objects.get(username="******")
     admin.groups.remove(GROUP_ADMIN_PK)
     admin.groups.add(GROUP_DELEGATE_PK)
     if can_manage_config or can_manage_logos_and_fonts:
         delegate_group = get_group_model().objects.get(pk=GROUP_DELEGATE_PK)
         if can_manage_config:
             delegate_group.permissions.add(
                 Permission.objects.get(
                     content_type__app_label="core", codename="can_manage_config"
                 )
             )
         if can_manage_logos_and_fonts:
             delegate_group.permissions.add(
                 Permission.objects.get(
                     content_type__app_label="core",
                     codename="can_manage_logos_and_fonts",
                 )
             )
         inform_changed_data(delegate_group)
     inform_changed_data(admin)
Exemplo n.º 52
0
    def setUp(self):
        self.client = APIClient()
        self.client.login(username="******", password="******")

        self.admin = get_user_model().objects.get()
        self.group_out = get_group_model().objects.get(
            pk=GROUP_DELEGATE_PK
        )  # The admin should not be in this group

        # Put the admin into the staff group, becaust in the admin group, he has all permissions for
        # every single comment section.
        self.admin.groups.add(GROUP_STAFF_PK)
        self.admin.groups.remove(GROUP_ADMIN_PK)
        inform_changed_data(self.admin)
        self.group_in = get_group_model().objects.get(pk=GROUP_STAFF_PK)

        self.motion = Motion(
            title="test_title_SlqfMw(waso0saWMPqcZ",
            text="test_text_f30skclqS9wWF=xdfaSL",
        )
        self.motion.save()

        self.section_no_groups = MotionCommentSection(
            name='test_name_gj4F§(fj"(edm"§F3f3fs'
        )
        self.section_no_groups.save()

        self.section_read = MotionCommentSection(name="test_name_2wv30(d2S&kvelkakl39")
        self.section_read.save()
        self.section_read.read_groups.add(
            self.group_in, self.group_out
        )  # Group out for testing multiple groups
        self.section_read.write_groups.add(self.group_out)

        self.section_read_write = MotionCommentSection(
            name="test_name_a3m9sd0(Mw2%slkrv30,"
        )
        self.section_read_write.save()
        self.section_read_write.read_groups.add(self.group_in)
        self.section_read_write.write_groups.add(self.group_in)
Exemplo n.º 53
0
    def create(self, request, *args, **kwargs):
        """
        Creates an agenda item and adds the content object to the agenda.
        Request args should specify the content object:
        {
            "collection": <The collection string>,
            "id": <The content object id>
        }
        """
        collection = request.data.get("collection")
        id = request.data.get("id")

        if not isinstance(collection, str):
            raise ValidationError(
                {"detail": "The collection needs to be a string"})
        if not isinstance(id, int):
            raise ValidationError({"detail": "The id needs to be an int"})

        try:
            model = get_model_from_collection_string(collection)
        except ValueError:
            raise ValidationError({"detail": "Invalid collection"})

        try:
            content_object = model.objects.get(pk=id)
        except model.DoesNotExist:
            raise ValidationError({"detail": "The id is invalid"})

        if not hasattr(content_object, "get_agenda_title_information"):
            raise ValidationError(
                {"detail": "The collection does not have agenda items"})

        try:
            item = Item.objects.create(content_object=content_object)
        except IntegrityError:
            raise ValidationError(
                {"detail": "The item is already in the agenda"})

        inform_changed_data(content_object)
        return Response({id: item.id})
Exemplo n.º 54
0
 def test_internal_by_anonymous_without_perm_to_see_internal_items(self):
     group = Group.objects.get(pk=1)  # Group with pk 1 is for anonymous users.
     permission_string = "agenda.can_see_internal_items"
     app_label, codename = permission_string.split(".")
     permission = group.permissions.get(
         content_type__app_label=app_label, codename=codename
     )
     group.permissions.remove(permission)
     inform_changed_data(group)
     self.item.type = Item.INTERNAL_ITEM
     self.item.save()
     response = self.client.get(reverse("item-detail", args=[self.item.pk]))
     self.assertEqual(response.status_code, status.HTTP_200_OK)
     self.assertEqual(
         sorted(response.data.keys()),
         sorted(
             (
                 "id",
                 "title_information",
                 "speakers",
                 "speaker_list_closed",
                 "content_object",
             )
         ),
     )
     forbidden_keys = (
         "item_number",
         "title_with_type",
         "comment",
         "closed",
         "type",
         "is_internal",
         "is_hidden",
         "duration",
         "weight",
         "parent",
     )
     for key in forbidden_keys:
         self.assertFalse(key in response.data.keys())
Exemplo n.º 55
0
 def create_votes_types_yn_yna(
     self, data, poll, vote_weight, vote_user, request_user
 ):
     """
     Helper function for handle_(named|pseudoanonymous)_vote
     Assumes data is already validated
     vote_user is the user whose vote is given
     request_user is the user who gives the vote, may be a delegate
     """
     options = poll.get_options()
     weight = vote_weight if config["users_activate_vote_weight"] else Decimal(1)
     for option_id, result in data.items():
         option = options.get(pk=option_id)
         vote = AssignmentVote.objects.create(
             option=option,
             user=vote_user,
             delegated_user=request_user,
             value=result,
             weight=weight,
         )
         inform_changed_data(vote, no_delete_on_restriction=True)
         inform_changed_data(option, no_delete_on_restriction=True)
Exemplo n.º 56
0
    def sort_speakers(self, request, pk=None):
        """
        Special view endpoint to sort the list of speakers.

        Expects a list of IDs of the speakers.
        """
        # Retrieve item.
        item = self.get_object()

        # Check data
        speaker_ids = request.data.get('speakers')
        if not isinstance(speaker_ids, list):
            raise ValidationError(
                {'detail': _('Invalid data.')})

        # Get all speakers
        speakers = {}
        for speaker in item.speakers.filter(begin_time=None):
            speakers[speaker.pk] = speaker

        # Check and sort speakers
        valid_speakers = []
        for speaker_id in speaker_ids:
            if not isinstance(speaker_id, int) or speakers.get(speaker_id) is None:
                raise ValidationError(
                    {'detail': _('Invalid data.')})
            valid_speakers.append(speakers[speaker_id])
        weight = 0
        with transaction.atomic():
            for speaker in valid_speakers:
                speaker.weight = weight
                speaker.save(skip_autoupdate=True)
                weight += 1

        # send autoupdate
        inform_changed_data(item)

        # Initiate response.
        return Response({'detail': _('List of speakers successfully sorted.')})
Exemplo n.º 57
0
    def update(self, *args, **kwargs):
        """
        Customized view endpoint to update all children if the item type has changed.
        """
        old_type = self.get_object().type

        response = super().update(*args, **kwargs)

        # Update all children if the item type has changed.
        item = self.get_object()

        if old_type != item.type:
            items_to_update = []

            # Recursively add children to items_to_update.
            def add_item(item):
                items_to_update.append(item)
                for child in item.children.all():
                    add_item(child)

            add_item(item)
            inform_changed_data(items_to_update)

        return response
Exemplo n.º 58
0
 def post(self, request, poll_id=0, keypad_id=0):
     keypad = super(KeypadCallback, self).post(request, poll_id, keypad_id)
     if keypad:
         inform_changed_data(keypad)
     return HttpResponse()
Exemplo n.º 59
0
    def post(self, request, poll_id):
        # Get assignment poll.
        try:
            poll = AssignmentPoll.objects.get(id=poll_id)
        except AssignmentPoll.DoesNotExist:
            return HttpResponse('')

        # Load json list from request body.
        votes = json.loads(request.body.decode('utf-8'))
        candidate_count = poll.assignment.related_users.all().count()
        keypad_set = set()
        connections = []
        for vote in votes:
            keypad_id = vote['id']
            try:
                keypad = Keypad.objects.get(keypad_id=keypad_id)
            except Keypad.DoesNotExist:
                continue

            # Mark keypad as in range and update battery level.
            keypad.in_range = True
            keypad.battery_level = vote['bl']
            keypad.save(skip_autoupdate=True)

            # Validate vote value.
            try:
                value = int(vote['value'])
            except ValueError:
                continue
            if value < 0 or value > 9:
                # Invalid candidate number.
                continue

            # Get the selected candidate.
            candidate_id = None
            if 0 < value <= candidate_count:
                candidate_id = AssignmentOption.objects.filter(poll=poll_id).order_by('weight').all()[value - 1].candidate_id

            # Write poll connection.
            try:
                conn = AssignmentPollKeypadConnection.objects.get(poll=poll, keypad=keypad)
            except AssignmentPollKeypadConnection.DoesNotExist:
                conn = AssignmentPollKeypadConnection(poll=poll, keypad=keypad)
            conn.serial_number = vote['sn']
            conn.value = str(value)
            conn.candidate_id = candidate_id
            if conn.pk:
                # Save updated connection.
                conn.save(skip_autoupdate=True)
            else:
                # Add new connection to bulk create list.
                connections.append(conn)
            keypad_set.add(keypad.id)

        # Bulk create connections.
        AssignmentPollKeypadConnection.objects.bulk_create(connections)

        # Trigger auto update.
        connections = AssignmentPollKeypadConnection.objects.filter(poll=poll, keypad_id__in=keypad_set)
        inform_changed_data(connections)

        return HttpResponse()