text:
import json

import pytest
from django.contrib.auth import get_user_model
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APIClient

from openslides.core.config import config
from openslides.core.models import Tag
from openslides.motions.models import (
    Category,
    Motion,
    MotionBlock,
    MotionChangeRecommendation,
    MotionComment,
    MotionCommentSection,
    MotionLog,
    State,
    StatuteParagraph,
    Submitter,
    Workflow,
)
from openslides.utils.auth import get_group_model
from openslides.utils.autoupdate import inform_changed_data
from openslides.utils.test import TestCase

from ..helpers import count_queries

GROUP_DEFAULT_PK = 1
GROUP_ADMIN_PK = 2
GROUP_DELEGATE_PK = 3
GROUP_STAFF_PK = 4

@pytest.mark.django_db(transaction=False)
def test_motion_db_queries():
    """
    Tests that only the following db queries are done:
    * 1 requests to get the list of all motions,
    * 1 request to get the associated workflow
    * 1 request for all motion comments
    * 1 request for all motion comment sections required for the comments
    * 1 request for all users required for the read_groups of the sections
    * 1 request to get the agenda item,
    * 1 request to get the motion log,
    * 1 request to get the polls,
    * 1 request to get the attachments,
    * 1 request to get the tags,
    * 2 requests to get the submitters and supporters.

    Two comment sections are created and for each motions two comments.
    """
    section1 = MotionCommentSection.objects.create(name="test_section")
    section2 = MotionCommentSection.objects.create(name="test_section")

    for index in range(10):
        motion = Motion.objects.create(title=f"motion{index}")

        MotionComment.objects.create(
            comment="test_comment", motion=motion, section=section1
        )
        MotionComment.objects.create(
            comment="test_comment2", motion=motion, section=section2
        )

        get_user_model().objects.create_user(
            username=f"user_{index}", password="password"
        )
    # TODO: Create some polls etc.

    assert count_queries(Motion.get_elements) == 12

@pytest.mark.django_db(transaction=False)
def test_category_db_queries():
    """
    Tests that only the following db queries are done:
    * 1 requests to get the list of all categories.
    """
    for index in range(10):
        Category.objects.create(name=f"category{index}")

    assert count_queries(Category.get_elements) == 1

@pytest.mark.django_db(transaction=False)
def test_statute_paragraph_db_queries():
    """
    Tests that only the following db queries are done:
    * 1 requests to get the list of all statute paragraphs.
    """
    for index in range(10):
        StatuteParagraph.objects.create(
            title=f"statute_paragraph{index}", text=f"text{index}"
        )

    assert count_queries(StatuteParagraph.get_elements) == 1

@pytest.mark.django_db(transaction=False)
def test_workflow_db_queries():
    """
    Tests that only the following db queries are done:
    * 1 requests to get the list of all workflows,
    * 1 request to get all states and
    * 1 request to get the next states of all states.
    """

    assert count_queries(Workflow.get_elements) == 3

class TestStatuteParagraphs(TestCase):
    """
    Tests all CRUD operations of statute paragraphs.
    """

    def setUp(self):
        self.client = APIClient()
        self.client.login(username="admin", password="admin")

    def create_statute_paragraph(self):
        self.title = "test_title_fiWs82D0D)2kje3KDm2s"
        self.text = "test_text_3jfjoDqm,S;cmor3DJwk"
        self.cp = StatuteParagraph.objects.create(title=self.title, text=self.text)

    def test_create_simple(self):
        response = self.client.post(
            reverse("statuteparagraph-list"),
            {
                "title": "test_title_f3FM328cq)tzdU238df2",
                "text": "test_text_2fb)BEjwdI38=kfemiRkcOW",
            },
        )
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        cp = StatuteParagraph.objects.get()
        self.assertEqual(cp.title, "test_title_f3FM328cq)tzdU238df2")
        self.assertEqual(cp.text, "test_text_2fb)BEjwdI38=kfemiRkcOW")

    def test_create_without_data(self):
        response = self.client.post(reverse("statuteparagraph-list"), {})
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(
            response.data,
            {"title": ["This field is required."], "text": ["This field is required."]},
        )

    def test_create_non_admin(self):
        self.admin = get_user_model().objects.get(username="admin")
        self.admin.groups.add(GROUP_DELEGATE_PK)
        self.admin.groups.remove(GROUP_ADMIN_PK)
        inform_changed_data(self.admin)

        response = self.client.post(
            reverse("statuteparagraph-list"),
            {
                "title": "test_title_f3(Dj2jdP39fjW2kdcwe",
                "text": "test_text_vlC)=fwWmcwcpWMvnuw(",
            },
        )

        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_retrieve_simple(self):
        self.create_statute_paragraph()
        response = self.client.get(
            reverse("statuteparagraph-detail", args=[self.cp.pk])
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(
            sorted(response.data.keys()), sorted(("id", "title", "text", "weight"))
        )

    def test_update_simple(self):
        self.create_statute_paragraph()
        response = self.client.patch(
            reverse("statuteparagraph-detail", args=[self.cp.pk]),
            {"text": "test_text_ke(czr/cwk1Sl2seeFwE"},
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        cp = StatuteParagraph.objects.get()
        self.assertEqual(cp.title, self.title)
        self.assertEqual(cp.text, "test_text_ke(czr/cwk1Sl2seeFwE")

    def test_update_non_admin(self):
        self.admin = get_user_model().objects.get(username="admin")
        self.admin.groups.add(GROUP_DELEGATE_PK)
        self.admin.groups.remove(GROUP_ADMIN_PK)
        inform_changed_data(self.admin)

        self.create_statute_paragraph()
        response = self.client.patch(
            reverse("statuteparagraph-detail", args=[self.cp.pk]),
            {"text": "test_text_ke(czr/cwk1Sl2seeFwE"},
        )
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        cp = StatuteParagraph.objects.get()
        self.assertEqual(cp.text, self.text)

    def test_delete_simple(self):
        self.create_statute_paragraph()
        response = self.client.delete(
            reverse("statuteparagraph-detail", args=[self.cp.pk])
        )
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
        self.assertEqual(StatuteParagraph.objects.count(), 0)

    def test_delete_non_admin(self):
        self.admin = get_user_model().objects.get(username="admin")
        self.admin.groups.add(GROUP_DELEGATE_PK)
        self.admin.groups.remove(GROUP_ADMIN_PK)
        inform_changed_data(self.admin)

        self.create_statute_paragraph()
        response = self.client.delete(
            reverse("statuteparagraph-detail", args=[self.cp.pk])
        )
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        self.assertEqual(StatuteParagraph.objects.count(), 1)

class CreateMotion(TestCase):
    """
    Tests motion creation.
    """

    def setUp(self):
        self.client = APIClient()
        self.client.login(username="admin", password="admin")

    def test_simple(self):
        """
        Tests that a motion is created with a specific title and text.

        The created motion should have an identifier and the admin user should
        be the submitter.
        """
        response = self.client.post(
            reverse("motion-list"),
            {
                "title": "test_title_OoCoo3MeiT9li5Iengu9",
                "text": "test_text_thuoz0iecheiheereiCi",
            },
        )
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        motion = Motion.objects.get()
        self.assertEqual(motion.title, "test_title_OoCoo3MeiT9li5Iengu9")
        self.assertEqual(motion.identifier, "1")
        self.assertTrue(motion.submitters.exists())
        self.assertEqual(motion.submitters.get().user.username, "admin")

    def test_with_reason(self):
        response = self.client.post(
            reverse("motion-list"),
            {
                "title": "test_title_saib4hiHaifo9ohp9yie",
                "text": "test_text_shahhie8Ej4mohvoorie",
                "reason": "test_reason_Ou8GivahYivoh3phoh9c",
            },
        )
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(
            Motion.objects.get().reason, "test_reason_Ou8GivahYivoh3phoh9c"
        )

    def test_without_data(self):
        response = self.client.post(reverse("motion-list"), {})
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(
            response.data,
            {"title": ["This field is required."], "text": ["This field is required."]},
        )

    def test_with_category(self):
        category = Category.objects.create(
            name="test_category_name_CiengahzooH4ohxietha",
            prefix="TEST_PREFIX_la0eadaewuec3seoxeiN",
        )
        response = self.client.post(
            reverse("motion-list"),
            {
                "title": "test_title_Air0bahchaiph1ietoo2",
                "text": "test_text_chaeF9wosh8OowazaiVu",
                "category_id": category.pk,
            },
        )
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        motion = Motion.objects.get()
        self.assertEqual(motion.category, category)
        self.assertEqual(motion.identifier, "TEST_PREFIX_la0eadaewuec3seoxeiN 1")

    def test_with_submitters(self):
        submitter_1 = get_user_model().objects.create_user(
            username="test_username_ooFe6aebei9ieQui2poo",
            password="test_password_vie9saiQu5Aengoo9ku0",
        )
        submitter_2 = get_user_model().objects.create_user(
            username="test_username_eeciengoc4aihie5eeSh",
            password="test_password_peik2Eihu5oTh7siequi",
        )
        response = self.client.post(
            reverse("motion-list"),
            {
                "title": "test_title_pha7moPh7quoth4paina",
                "text": "test_text_YooGhae6tiangung5Rie",
                "submitters_id": [submitter_1.pk, submitter_2.pk],
            },
        )
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        motion = Motion.objects.get()
        self.assertEqual(motion.submitters.count(), 2)

    def test_with_one_supporter(self):
        supporter = get_user_model().objects.create_user(
            username="test_username_ahGhi4Quohyee7ohngie",
            password="test_password_Nei6aeh8OhY8Aegh1ohX",
        )
        response = self.client.post(
            reverse("motion-list"),
            {
                "title": "test_title_Oecee4Da2Mu9EY6Ui4mu",
                "text": "test_text_FbhgnTFgkbjdmvcjbffg",
                "supporters_id": [supporter.pk],
            },
        )
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        motion = Motion.objects.get()
        self.assertEqual(
            motion.supporters.get().username, "test_username_ahGhi4Quohyee7ohngie"
        )

    def test_with_tag(self):
        tag = Tag.objects.create(name="test_tag_iRee3kiecoos4rorohth")
        response = self.client.post(
            reverse("motion-list"),
            {
                "title": "test_title_Hahke4loos4eiduNiid9",
                "text": "test_text_johcho0Ucaibiehieghe",
                "tags_id": [tag.pk],
            },
        )
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        motion = Motion.objects.get()
        self.assertEqual(motion.tags.get().name, "test_tag_iRee3kiecoos4rorohth")

    def test_with_workflow(self):
        """
        Test to create a motion with a specific workflow.
        """
        response = self.client.post(
            reverse("motion-list"),
            {
                "title": "test_title_eemuR5hoo4ru2ahgh5EJ",
                "text": "test_text_ohviePopahPhoili7yee",
                "workflow_id": "2",
            },
        )

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(Motion.objects.get().state.workflow_id, 2)

    def test_non_admin(self):
        """
        Test to create a motion by a delegate, non staff user.
        """
        self.admin = get_user_model().objects.get(username="admin")
        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)

    def test_amendment_motion(self):
        """
        Test to create a motion with a parent motion as staff user.
        """
        parent_motion = self.create_parent_motion()
        response = self.client.post(
            reverse("motion-list"),
            {
                "title": "test_title_doe93Jsjd2sW20dkSl20",
                "text": "test_text_feS20SksD8D25skmwD25",
                "parent_id": parent_motion.id,
            },
        )
        created_motion = Motion.objects.get(pk=int(response.data["id"]))

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(created_motion.parent, parent_motion)

    def test_amendment_motion_parent_not_exist(self):
        """
        Test to create an amendment motion with a non existing parent.
        """
        response = self.client.post(
            reverse("motion-list"),
            {
                "title": "test_title_gEjdkW93Wj23KS2s8dSe",
                "text": "test_text_lfwLIC&AjfsaoijOEusa",
                "parent_id": 100,
            },
        )

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(response.data, {"detail": "The parent motion does not exist."})

    def test_amendment_motion_non_admin(self):
        """
        Test to create an amendment motion by a delegate. The parents
        category should be also set on the new motion.
        """
        parent_motion = self.create_parent_motion()
        category = Category.objects.create(
            name="test_category_name_Dslk3Fj8s8Ps36S3Kskw",
            prefix="TEST_PREFIX_L23skfmlq3kslamslS39",
        )
        parent_motion.category = category
        parent_motion.save()

        self.admin = get_user_model().objects.get(username="admin")
        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_fk3a0slalms47KSewnWG",
                "text": "test_text_al3FMwSCNM31WOmw9ezx",
                "parent_id": parent_motion.id,
            },
        )
        created_motion = Motion.objects.get(pk=int(response.data["id"]))

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(created_motion.parent, parent_motion)
        self.assertEqual(created_motion.category, category)

    def create_parent_motion(self):
        """
        Returns a new created motion used for testing amendments.
        """
        response = self.client.post(
            reverse("motion-list"),
            {
                "title": "test_title_3leoeo2qac7830c92j9s",
                "text": "test_text_9dm3ks9gDuW20Al38L9w",
            },
        )
        return Motion.objects.get(pk=int(response.data["id"]))

class RetrieveMotion(TestCase):
    """
    Tests retrieving a motion (with poll results).
    """

    def setUp(self):
        self.client = APIClient()
        self.client.login(username="admin", password="admin")
        self.motion = Motion(
            title="test_title_uj5eeSiedohSh3ohyaaj",
            text="test_text_ithohchaeThohmae5aug",
        )
        self.motion.save()
        self.motion.create_poll()
        for index in range(10):
            get_user_model().objects.create_user(
                username=f"user_{index}", password="password"
            )

    def test_guest_state_with_required_permission_to_see(self):
        config["general_system_enable_anonymous"] = True
        guest_client = APIClient()
        state = self.motion.state
        state.required_permission_to_see = (
            "permission_that_the_user_does_not_have_leeceiz9hi7iuta4ahY2"
        )
        state.save()
        # The cache has to be cleared, see:
        # https://github.com/OpenSlides/OpenSlides/issues/3396
        inform_changed_data(self.motion)

        response = guest_client.get(reverse("motion-detail", args=[self.motion.pk]))
        self.assertEqual(response.status_code, 404)

    def test_admin_state_with_required_permission_to_see(self):
        state = self.motion.state
        state.required_permission_to_see = (
            "permission_that_the_user_does_not_have_coo1Iewu8Eing2xahfoo"
        )
        state.save()
        response = self.client.get(reverse("motion-detail", args=[self.motion.pk]))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_submitter_state_with_required_permission_to_see(self):
        state = self.motion.state
        state.required_permission_to_see = (
            "permission_that_the_user_does_not_have_eiW8af9caizoh1thaece"
        )
        state.save()
        user = get_user_model().objects.create_user(
            username="username_ohS2opheikaSa5theijo",
            password="password_kau4eequaisheeBateef",
        )
        Submitter.objects.add(user, self.motion)
        submitter_client = APIClient()
        submitter_client.force_login(user)
        response = submitter_client.get(reverse("motion-detail", args=[self.motion.pk]))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_user_without_can_see_user_permission_to_see_motion_and_submitter_data(
        self
    ):
        admin = get_user_model().objects.get(username="admin")
        Submitter.objects.add(admin, self.motion)
        group = get_group_model().objects.get(
            pk=GROUP_DEFAULT_PK
        )  # Group with pk 1 is for anonymous and default users.
        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)
        config["general_system_enable_anonymous"] = True
        guest_client = APIClient()
        inform_changed_data(group)
        inform_changed_data(self.motion)

        response_1 = guest_client.get(reverse("motion-detail", args=[self.motion.pk]))
        self.assertEqual(response_1.status_code, status.HTTP_200_OK)
        submitter_id = response_1.data["submitters"][0]["user_id"]
        response_2 = guest_client.get(reverse("user-detail", args=[submitter_id]))
        self.assertEqual(response_2.status_code, status.HTTP_200_OK)

        extra_user = get_user_model().objects.create_user(
            username="username_wequePhieFoom0hai3wa",
            password="password_ooth7taechai5Oocieya",
        )

        response_3 = guest_client.get(reverse("user-detail", args=[extra_user.pk]))
        self.assertEqual(response_3.status_code, 404)

class UpdateMotion(TestCase):
    """
    Tests updating motions.
    """

    def setUp(self):
        self.client = APIClient()
        self.client.login(username="admin", password="admin")
        self.motion = Motion(
            title="test_title_aeng7ahChie3waiR8xoh",
            text="test_text_xeigheeha7thopubeu4U",
        )
        self.motion.save()

    def test_simple_patch(self):
        response = self.client.patch(
            reverse("motion-detail", args=[self.motion.pk]),
            {"identifier": "test_identifier_jieseghohj7OoSah1Ko9"},
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        motion = Motion.objects.get()
        self.assertEqual(motion.title, "test_title_aeng7ahChie3waiR8xoh")
        self.assertEqual(motion.identifier, "test_identifier_jieseghohj7OoSah1Ko9")

    def test_patch_workflow(self):
        """
        Tests to only update the workflow of a motion.
        """
        response = self.client.patch(
            reverse("motion-detail", args=[self.motion.pk]), {"workflow_id": "2"}
        )

        motion = Motion.objects.get()
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(motion.title, "test_title_aeng7ahChie3waiR8xoh")
        self.assertEqual(motion.workflow_id, 2)

    def test_patch_supporters(self):
        supporter = get_user_model().objects.create_user(
            username="test_username_ieB9eicah0uqu6Phoovo",
            password="test_password_XaeTe3aesh8ohg6Cohwo",
        )
        response = self.client.patch(
            reverse("motion-detail", args=[self.motion.pk]),
            json.dumps({"supporters_id": [supporter.pk]}),
            content_type="application/json",
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        motion = Motion.objects.get()
        self.assertEqual(motion.title, "test_title_aeng7ahChie3waiR8xoh")
        self.assertEqual(
            motion.supporters.get().username, "test_username_ieB9eicah0uqu6Phoovo"
        )

    def test_patch_supporters_non_manager(self):
        non_admin = get_user_model().objects.create_user(
            username="test_username_uqu6PhoovieB9eicah0o",
            password="test_password_Xaesh8ohg6CoheTe3awo",
        )
        self.client.login(
            username="test_username_uqu6PhoovieB9eicah0o",
            password="test_password_Xaesh8ohg6CoheTe3awo",
        )
        motion = Motion.objects.get()
        Submitter.objects.add(non_admin, self.motion)
        motion.supporters.clear()
        response = self.client.patch(
            reverse("motion-detail", args=[self.motion.pk]),
            json.dumps({"supporters_id": [1]}),
            content_type="application/json",
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertFalse(motion.supporters.exists())

    def test_removal_of_supporters(self):
        # No cache used here.
        admin = get_user_model().objects.get(username="admin")
        group_admin = admin.groups.get(name="Admin")
        admin.groups.remove(group_admin)
        Submitter.objects.add(admin, self.motion)
        supporter = get_user_model().objects.create_user(
            username="test_username_ahshi4oZin0OoSh9chee",
            password="test_password_Sia8ahgeenixu5cei2Ib",
        )
        self.motion.supporters.add(supporter)
        config["motions_remove_supporters"] = True
        self.assertEqual(self.motion.supporters.count(), 1)
        inform_changed_data((admin, self.motion))

        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)

class DeleteMotion(TestCase):
    """
    Tests deleting motions.
    """

    def setUp(self):
        self.client = APIClient()
        self.client.login(username="admin", password="admin")
        self.admin = get_user_model().objects.get(username="admin")
        self.motion = Motion(
            title="test_title_acle3fa93l11lwlkcc31",
            text="test_text_f390sjfyycj29ss56sro",
        )
        self.motion.save()

    def test_simple_delete(self):
        response = self.client.delete(reverse("motion-detail", args=[self.motion.pk]))
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
        motions = Motion.objects.count()
        self.assertEqual(motions, 0)

    def make_admin_delegate(self):
        self.admin.groups.remove(GROUP_ADMIN_PK)
        self.admin.groups.add(GROUP_DELEGATE_PK)
        inform_changed_data(self.admin)

    def put_motion_in_complex_workflow(self):
        workflow = Workflow.objects.get(name="Complex Workflow")
        self.motion.reset_state(workflow=workflow)
        self.motion.save()

    def test_delete_foreign_motion_as_delegate(self):
        self.make_admin_delegate()
        self.put_motion_in_complex_workflow()

        response = self.client.delete(reverse("motion-detail", args=[self.motion.pk]))
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_delete_own_motion_as_delegate(self):
        self.make_admin_delegate()
        self.put_motion_in_complex_workflow()
        Submitter.objects.add(self.admin, self.motion)

        response = self.client.delete(reverse("motion-detail", args=[self.motion.pk]))
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
        motions = Motion.objects.count()
        self.assertEqual(motions, 0)

class ManageMultipleSubmitters(TestCase):
    """
    Tests adding and removing of submitters.
    """

    def setUp(self):
        self.client = APIClient()
        self.client.login(username="admin", password="admin")

        self.admin = get_user_model().objects.get()
        self.motion1 = Motion(
            title="test_title_SlqfMw(waso0saWMPqcZ",
            text="test_text_f30skclqS9wWF=xdfaSL",
        )
        self.motion1.save()
        self.motion2 = Motion(
            title="test_title_f>FLEim38MC2m9PFp2jG",
            text="test_text_kg39KFGm,ao)22FK9lLu",
        )
        self.motion2.save()

    def test_set_submitters(self):
        response = self.client.post(
            reverse("motion-manage-multiple-submitters"),
            json.dumps(
                {
                    "motions": [
                        {"id": self.motion1.id, "submitters": [self.admin.pk]},
                        {"id": self.motion2.id, "submitters": [self.admin.pk]},
                    ]
                }
            ),
            content_type="application/json",
        )
        self.assertEqual(response.status_code, 200)
        self.assertEqual(self.motion1.submitters.count(), 1)
        self.assertEqual(self.motion2.submitters.count(), 1)
        self.assertEqual(
            self.motion1.submitters.get().user.pk, self.motion2.submitters.get().user.pk
        )

    def test_non_existing_user(self):
        response = self.client.post(
            reverse("motion-manage-multiple-submitters"),
            {"motions": [{"id": self.motion1.id, "submitters": [1337]}]},
        )
        self.assertEqual(response.status_code, 400)
        self.assertEqual(self.motion1.submitters.count(), 0)

    def test_add_user_no_data(self):
        response = self.client.post(reverse("motion-manage-multiple-submitters"))
        self.assertEqual(response.status_code, 400)
        self.assertEqual(self.motion1.submitters.count(), 0)
        self.assertEqual(self.motion2.submitters.count(), 0)

    def test_add_user_invalid_data(self):
        response = self.client.post(
            reverse("motion-manage-multiple-submitters"), {"motions": ["invalid_str"]}
        )
        self.assertEqual(response.status_code, 400)
        self.assertEqual(self.motion1.submitters.count(), 0)
        self.assertEqual(self.motion2.submitters.count(), 0)

    def test_add_without_permission(self):
        admin = get_user_model().objects.get(username="admin")
        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)

class ManageComments(TestCase):
    """
    Tests the manage_comment view.

    Tests creation/updating and deletion of motion comments.
    """

    def setUp(self):
        self.client = APIClient()
        self.client.login(username="admin", password="admin")

        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)

    def test_retrieve_comment(self):
        comment = MotionComment(
            motion=self.motion,
            section=self.section_read_write,
            comment="test_comment_gwic37Csc&3lf3eo2",
        )
        comment.save()

        response = self.client.get(reverse("motion-detail", args=[self.motion.pk]))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertTrue("comments" in response.data)
        comments = response.data["comments"]
        self.assertTrue(isinstance(comments, list))
        self.assertEqual(len(comments), 1)
        self.assertEqual(comments[0]["comment"], "test_comment_gwic37Csc&3lf3eo2")

    def test_retrieve_comment_no_read_permission(self):
        comment = MotionComment(
            motion=self.motion,
            section=self.section_no_groups,
            comment="test_comment_fgkj3C7veo3ijWE(j2DJ",
        )
        comment.save()

        response = self.client.get(reverse("motion-detail", args=[self.motion.pk]))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertTrue("comments" in response.data)
        comments = response.data["comments"]
        self.assertTrue(isinstance(comments, list))
        self.assertEqual(len(comments), 0)

    def test_wrong_data_type(self):
        response = self.client.post(
            reverse("motion-manage-comments", args=[self.motion.pk]),
            None,
            format="json",
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(
            response.data["detail"], "You have to provide a section_id of type int."
        )

    def test_wrong_comment_data_type(self):
        response = self.client.post(
            reverse("motion-manage-comments", args=[self.motion.pk]),
            {
                "section_id": self.section_read_write.id,
                "comment": [32, "no_correct_data"],
            },
            format="json",
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(response.data["detail"], "The comment should be a string.")

    def test_non_existing_section(self):
        response = self.client.post(
            reverse("motion-manage-comments", args=[self.motion.pk]),
            {"section_id": 42},
            format="json",
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(
            response.data["detail"], "A comment section with id 42 does not exist."
        )

    def test_create_comment(self):
        response = self.client.post(
            reverse("motion-manage-comments", args=[self.motion.pk]),
            {
                "section_id": self.section_read_write.pk,
                "comment": "test_comment_fk3jrnfwsdg%fj=feijf",
            },
            format="json",
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(MotionComment.objects.count(), 1)
        comment = MotionComment.objects.get()
        self.assertEqual(comment.comment, "test_comment_fk3jrnfwsdg%fj=feijf")

        # Check for a log entry
        motion_logs = MotionLog.objects.filter(motion=self.motion)
        self.assertEqual(motion_logs.count(), 1)
        comment_log = motion_logs.get()
        self.assertTrue(self.section_read_write.name in comment_log.message_list[0])

    def test_update_comment(self):
        comment = MotionComment(
            motion=self.motion,
            section=self.section_read_write,
            comment="test_comment_fji387fqwdf&ff=)Fe3j",
        )
        comment.save()

        response = self.client.post(
            reverse("motion-manage-comments", args=[self.motion.pk]),
            {
                "section_id": self.section_read_write.pk,
                "comment": "test_comment_fk3jrnfwsdg%fj=feijf",
            },
            format="json",
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        comment = MotionComment.objects.get()
        self.assertEqual(comment.comment, "test_comment_fk3jrnfwsdg%fj=feijf")

        # Check for a log entry
        motion_logs = MotionLog.objects.filter(motion=self.motion)
        self.assertEqual(motion_logs.count(), 1)
        comment_log = motion_logs.get()
        self.assertTrue(self.section_read_write.name in comment_log.message_list[0])

    def test_delete_comment(self):
        comment = MotionComment(
            motion=self.motion,
            section=self.section_read_write,
            comment='test_comment_5CJ"8f23jd3j2,r93keZ',
        )
        comment.save()

        response = self.client.delete(
            reverse("motion-manage-comments", args=[self.motion.pk]),
            {"section_id": self.section_read_write.pk},
            format="json",
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(MotionComment.objects.count(), 0)

        # Check for a log entry
        motion_logs = MotionLog.objects.filter(motion=self.motion)
        self.assertEqual(motion_logs.count(), 1)
        comment_log = motion_logs.get()
        self.assertTrue(self.section_read_write.name in comment_log.message_list[0])

    def test_delete_not_existing_comment(self):
        """
        This should fail silently; no error, if the user wants to delete
        a not existing comment.
        """
        response = self.client.delete(
            reverse("motion-manage-comments", args=[self.motion.pk]),
            {"section_id": self.section_read_write.pk},
            format="json",
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(MotionComment.objects.count(), 0)

        # Check that no log entry was created
        motion_logs = MotionLog.objects.filter(motion=self.motion)
        self.assertEqual(motion_logs.count(), 0)

    def test_create_comment_no_write_permission(self):
        response = self.client.post(
            reverse("motion-manage-comments", args=[self.motion.pk]),
            {
                "section_id": self.section_read.pk,
                "comment": "test_comment_f38jfwqfj830fj4j(FU3",
            },
            format="json",
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(MotionComment.objects.count(), 0)
        self.assertEqual(
            response.data["detail"],
            "You are not allowed to see or write to the comment section.",
        )

    def test_update_comment_no_write_permission(self):
        comment = MotionComment(
            motion=self.motion,
            section=self.section_read,
            comment="test_comment_jg38dwiej2D832(D§dk)",
        )
        comment.save()

        response = self.client.post(
            reverse("motion-manage-comments", args=[self.motion.pk]),
            {
                "section_id": self.section_read.pk,
                "comment": "test_comment_fk3jrnfwsdg%fj=feijf",
            },
            format="json",
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        comment = MotionComment.objects.get()
        self.assertEqual(comment.comment, "test_comment_jg38dwiej2D832(D§dk)")

    def test_delete_comment_no_write_permission(self):
        comment = MotionComment(
            motion=self.motion,
            section=self.section_read,
            comment="test_comment_fej(NF§kfePOF383o8DN",
        )
        comment.save()

        response = self.client.delete(
            reverse("motion-manage-comments", args=[self.motion.pk]),
            {"section_id": self.section_read.pk},
            format="json",
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(MotionComment.objects.count(), 1)
        comment = MotionComment.objects.get()
        self.assertEqual(comment.comment, "test_comment_fej(NF§kfePOF383o8DN")

class TestMotionCommentSection(TestCase):
    """
    Tests creating, updating and deletion of comment sections.
    """

    def setUp(self):
        self.client = APIClient()
        self.client.login(username="admin", password="admin")

        self.admin = get_user_model().objects.get()
        self.admin.groups.add(
            GROUP_STAFF_PK
        )  # Put the admin in a groiup with limited permissions for testing.
        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.group_out = get_group_model().objects.get(
            pk=GROUP_DELEGATE_PK
        )  # The admin should not be in this group

    def test_retrieve(self):
        """
        Checks, if the sections can be seen by a manager.
        """
        section = MotionCommentSection(name="test_name_f3jOF3m8fp.<qiqmf32=")
        section.save()

        response = self.client.get(reverse("motioncommentsection-list"))

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertTrue(isinstance(response.data, list))
        self.assertEqual(len(response.data), 1)
        self.assertEqual(response.data[0]["name"], "test_name_f3jOF3m8fp.<qiqmf32=")

    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")

    def test_retrieve_non_manager_no_read_permission(self):
        """
        Checks, if sections are removed, if the user is a non manager and is in
        any of the read_groups.
        """
        self.admin.groups.remove(self.group_in)
        inform_changed_data(self.admin)

        section = MotionCommentSection(name="test_name_f3jOF3m8fp.<qiqmf32=")
        section.save()
        section.read_groups.add(self.group_out)

        response = self.client.get(reverse("motioncommentsection-list"))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertTrue(isinstance(response.data, list))
        self.assertEqual(len(response.data), 0)

    def test_create(self):
        """
        Create a section just with a name.
        """
        response = self.client.post(
            reverse("motioncommentsection-list"),
            {"name": "test_name_ekjfen3n)F§zn83f§Fge"},
        )
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(MotionCommentSection.objects.count(), 1)
        self.assertEqual(
            MotionCommentSection.objects.get().name, "test_name_ekjfen3n)F§zn83f§Fge"
        )

    def test_create_no_permission(self):
        """
        Try to create a section without can_manage permissions.
        """
        self.admin.groups.remove(self.group_in)
        inform_changed_data(self.admin)

        response = self.client.post(
            reverse("motioncommentsection-list"),
            {"name": "test_name_wfl3jlkcmlq23ucn7eiq"},
        )
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        self.assertEqual(MotionCommentSection.objects.count(), 0)

    def test_create_no_name(self):
        """
        Create a section without a name. This should fail, because a name is required.
        """
        response = self.client.post(reverse("motioncommentsection-list"), {})
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(MotionCommentSection.objects.count(), 0)
        self.assertEqual(response.data["name"][0], "This field is required.")

    def test_create_with_groups(self):
        """
        Create a section with name and both groups.
        """
        response = self.client.post(
            reverse("motioncommentsection-list"),
            {
                "name": "test_name_fg4kmFn73FhFk327f/3h",
                "read_groups_id": [2, 3],
                "write_groups_id": [3, 4],
            },
        )
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(MotionCommentSection.objects.count(), 1)
        comment = MotionCommentSection.objects.get()
        self.assertEqual(comment.name, "test_name_fg4kmFn73FhFk327f/3h")
        self.assertEqual(list(comment.read_groups.values_list("pk", flat=True)), [2, 3])
        self.assertEqual(
            list(comment.write_groups.values_list("pk", flat=True)), [3, 4]
        )

    def test_create_with_one_group(self):
        """
        Create a section with a name and write_groups.
        """
        response = self.client.post(
            reverse("motioncommentsection-list"),
            {"name": "test_name_ekjfen3n)F§zn83f§Fge", "write_groups_id": [1, 3]},
        )
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(MotionCommentSection.objects.count(), 1)
        comment = MotionCommentSection.objects.get()
        self.assertEqual(comment.name, "test_name_ekjfen3n)F§zn83f§Fge")
        self.assertEqual(comment.read_groups.count(), 0)
        self.assertEqual(
            list(comment.write_groups.values_list("pk", flat=True)), [1, 3]
        )

    def test_create_with_non_existing_group(self):
        """
        Create a section with some non existing groups. This should fail.
        """
        response = self.client.post(
            reverse("motioncommentsection-list"),
            {"name": "test_name_4gnUVnF§29FnH3287fhG", "write_groups_id": [42, 1, 8]},
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(MotionCommentSection.objects.count(), 0)
        self.assertEqual(
            response.data["write_groups_id"][0],
            'Invalid pk "42" - object does not exist.',
        )

    def test_update(self):
        """
        Update a section name.
        """
        section = MotionCommentSection(name="test_name_dlfgNDf37ND(g3fNf43g")
        section.save()

        response = self.client.put(
            reverse("motioncommentsection-detail", args=[section.pk]),
            {"name": "test_name_ekjfen3n)F§zn83f§Fge"},
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(MotionCommentSection.objects.count(), 1)
        self.assertEqual(
            MotionCommentSection.objects.get().name, "test_name_ekjfen3n)F§zn83f§Fge"
        )

    def test_update_groups(self):
        """
        Update one of the groups.
        """
        section = MotionCommentSection(name="test_name_f3jFq3hShf/(fh2qlPOp")
        section.save()
        section.read_groups.add(2)
        section.write_groups.add(3)

        response = self.client.patch(
            reverse("motioncommentsection-detail", args=[section.pk]),
            {"name": "test_name_gkk3FhfhpmQMhC,Y378c", "read_groups_id": [2, 4]},
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(MotionCommentSection.objects.count(), 1)
        comment = MotionCommentSection.objects.get()
        self.assertEqual(comment.name, "test_name_gkk3FhfhpmQMhC,Y378c")
        self.assertEqual(list(comment.read_groups.values_list("pk", flat=True)), [2, 4])
        self.assertEqual(list(comment.write_groups.values_list("pk", flat=True)), [3])

    def test_update_no_permission(self):
        """
        Try to update a section without can_manage permissions.
        """
        self.admin.groups.remove(self.group_in)

        section = MotionCommentSection(name="test_name_wl2oxmmhe/2kd92lwPSi")
        section.save()

        response = self.client.patch(
            reverse("motioncommentsection-list"),
            {"name": "test_name_2slmDMwmqqcmC92mcklw"},
        )
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        self.assertEqual(MotionCommentSection.objects.count(), 1)
        self.assertEqual(
            MotionCommentSection.objects.get().name, "test_name_wl2oxmmhe/2kd92lwPSi"
        )

    def test_delete(self):
        """
        Delete a section.
        """
        section = MotionCommentSection(name="test_name_ecMCq;ymwuZZ723kD)2k")
        section.save()

        response = self.client.delete(
            reverse("motioncommentsection-detail", args=[section.pk])
        )
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
        self.assertEqual(MotionCommentSection.objects.count(), 0)

    def test_delete_non_existing_section(self):
        """
        Delete a non existing section.
        """
        response = self.client.delete(reverse("motioncommentsection-detail", args=[2]))
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
        self.assertEqual(MotionCommentSection.objects.count(), 0)

    def test_delete_with_existing_comments(self):
        """
        Delete a section with existing comments. This should fail, because sections
        are protected.
        """
        section = MotionCommentSection(name="test_name_ecMCq;ymwuZZ723kD)2k")
        section.save()

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

        comment = MotionComment(
            comment="test_comment_dlkMD23m)(D9020m0/Zd", motion=motion, section=section
        )
        comment.save()

        response = self.client.delete(
            reverse("motioncommentsection-detail", args=[section.pk])
        )
        self.assertTrue("test_title_SlqfMw(waso0saWMPqcZ" in response.data["detail"])
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(MotionCommentSection.objects.count(), 1)

    def test_delete_no_permission(self):
        """
        Try to delete a section without can_manage permissions
        """
        self.admin.groups.remove(self.group_in)
        inform_changed_data(self.admin)

        section = MotionCommentSection(name="test_name_wl2oxmmhe/2kd92lwPSi")
        section.save()

        response = self.client.delete(
            reverse("motioncommentsection-detail", args=[section.pk])
        )
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        self.assertEqual(MotionCommentSection.objects.count(), 1)

class RetrieveMotionChangeRecommendation(TestCase):
    """
    Tests retrieving motion change recommendations.
    """

    def setUp(self):
        self.client = APIClient()
        self.client.login(username="admin", password="admin")

        motion = Motion(
            title="test_title_3kd)K23,c9239mdj2wcG",
            text="test_text_f8FLP,gvprC;wovVEwlQ",
        )
        motion.save()

        self.public_cr = MotionChangeRecommendation(
            motion=motion, internal=False, line_from=1, line_to=1
        )
        self.public_cr.save()

        self.internal_cr = MotionChangeRecommendation(
            motion=motion, internal=True, line_from=2, line_to=2
        )
        self.internal_cr.save()

    def test_simple(self):
        """
        Test retrieving all change recommendations.
        """
        response = self.client.get(reverse("motionchangerecommendation-list"))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data), 2)

    def test_non_admin(self):
        """
        Test retrieving of all change recommendations that are public, if the user
        has no manage perms.
        """
        self.admin = get_user_model().objects.get(username="admin")
        self.admin.groups.add(GROUP_DELEGATE_PK)
        self.admin.groups.remove(GROUP_ADMIN_PK)
        inform_changed_data(self.admin)

        response = self.client.get(reverse("motionchangerecommendation-list"))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data), 1)
        self.assertEqual(response.data[0]["id"], self.public_cr.id)

class CreateMotionChangeRecommendation(TestCase):
    """
    Tests motion change recommendation creation.
    """

    def setUp(self):
        self.client = APIClient()
        self.client.login(username="admin", password="admin")

        self.client.post(
            reverse("motion-list"),
            {
                "title": "test_title_OoCoo3MeiT9li5Iengu9",
                "text": "test_text_thuoz0iecheiheereiCi",
            },
        )

    def test_simple(self):
        """
        Creating a change plain, simple change recommendation
        """
        response = self.client.post(
            reverse("motionchangerecommendation-list"),
            {
                "line_from": "5",
                "line_to": "7",
                "motion_id": "1",
                "text": "<p>New test</p>",
                "type": "0",
            },
        )
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

    def test_collission(self):
        """
        Two change recommendations with overlapping lines should lead to a Bad Request
        """
        response = self.client.post(
            reverse("motionchangerecommendation-list"),
            {
                "line_from": "5",
                "line_to": "7",
                "motion_id": "1",
                "text": "<p>New test</p>",
                "type": "0",
            },
        )
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

        response = self.client.post(
            reverse("motionchangerecommendation-list"),
            {
                "line_from": "3",
                "line_to": "6",
                "motion_id": "1",
                "text": "<p>New test</p>",
                "type": "0",
            },
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(
            response.data,
            {
                "detail": "The recommendation collides with an existing one (line 3 - 6)."
            },
        )

    def test_no_collission_different_motions(self):
        """
        Two change recommendations with overlapping lines, but affecting different motions, should not interfere
        """
        self.client.post(
            reverse("motion-list"),
            {
                "title": "test_title_OoCoo3MeiT9li5Iengu9",
                "text": "test_text_thuoz0iecheiheereiCi",
            },
        )

        response = self.client.post(
            reverse("motionchangerecommendation-list"),
            {
                "line_from": "5",
                "line_to": "7",
                "motion_id": "1",
                "text": "<p>New test</p>",
                "type": "0",
            },
        )
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

        response = self.client.post(
            reverse("motionchangerecommendation-list"),
            {
                "line_from": "3",
                "line_to": "6",
                "motion_id": "2",
                "text": "<p>New test</p>",
                "type": "0",
            },
        )
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

class SupportMotion(TestCase):
    """
    Tests supporting a motion.
    """

    def setUp(self):
        self.admin = get_user_model().objects.get(username="admin")
        self.admin.groups.add(GROUP_DELEGATE_PK)
        inform_changed_data(self.admin)
        self.client.login(username="admin", password="admin")
        self.motion = Motion(
            title="test_title_chee7ahCha6bingaew4e",
            text="test_text_birah1theL9ooseeFaip",
        )
        self.motion.save()

    def test_support(self):
        config["motions_min_supporters"] = 1

        response = self.client.post(reverse("motion-support", args=[self.motion.pk]))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(
            response.data, {"detail": "You have supported this motion successfully."}
        )

    def test_unsupport(self):
        config["motions_min_supporters"] = 1
        self.motion.supporters.add(self.admin)
        response = self.client.delete(reverse("motion-support", args=[self.motion.pk]))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(
            response.data, {"detail": "You have unsupported this motion successfully."}
        )

class SetState(TestCase):
    """
    Tests setting a state.
    """

    def setUp(self):
        self.client = APIClient()
        self.client.login(username="admin", password="admin")
        self.motion = Motion(
            title="test_title_iac4ohquie9Ku6othieC",
            text="test_text_Xohphei6Oobee0Evooyu",
        )
        self.motion.save()
        self.state_id_accepted = 2  # This should be the id of the state 'accepted'.

    def test_set_state(self):
        response = self.client.put(
            reverse("motion-set-state", args=[self.motion.pk]),
            {"state": self.state_id_accepted},
        )

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(
            response.data, {"detail": "The state of the motion was set to accepted."}
        )
        self.assertEqual(Motion.objects.get(pk=self.motion.pk).state.name, "accepted")

    def test_set_state_with_string(self):
        # Using a string is not allowed even if it is the correct name of the state.
        response = self.client.put(
            reverse("motion-set-state", args=[self.motion.pk]), {"state": "accepted"}
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(
            response.data, {"detail": "Invalid data. State must be an integer."}
        )

    def test_set_unknown_state(self):
        invalid_state_id = 0
        response = self.client.put(
            reverse("motion-set-state", args=[self.motion.pk]),
            {"state": invalid_state_id},
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(
            response.data,
            {"detail": "You can not set the state to %d." % invalid_state_id},
        )

    def test_reset(self):
        self.motion.set_state(self.state_id_accepted)
        self.motion.save()
        response = self.client.put(reverse("motion-set-state", args=[self.motion.pk]))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(
            response.data, {"detail": "The state of the motion was set to submitted."}
        )
        self.assertEqual(Motion.objects.get(pk=self.motion.pk).state.name, "submitted")

class SetRecommendation(TestCase):
    """
    Tests setting a recommendation.
    """

    def setUp(self):
        self.client = APIClient()
        self.client.login(username="admin", password="admin")
        self.motion = Motion(
            title="test_title_ahfooT5leilahcohJ2uz",
            text="test_text_enoogh7OhPoo6eohoCus",
        )
        self.motion.save()
        self.state_id_accepted = 2  # This should be the id of the state 'accepted'.

    def test_set_recommendation(self):
        response = self.client.put(
            reverse("motion-set-recommendation", args=[self.motion.pk]),
            {"recommendation": self.state_id_accepted},
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(
            response.data,
            {"detail": "The recommendation of the motion was set to Acceptance."},
        )
        self.assertEqual(
            Motion.objects.get(pk=self.motion.pk).recommendation.name, "accepted"
        )

    def test_set_state_with_string(self):
        # Using a string is not allowed even if it is the correct name of the state.
        response = self.client.put(
            reverse("motion-set-recommendation", args=[self.motion.pk]),
            {"recommendation": "accepted"},
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(
            response.data,
            {"detail": "Invalid data. Recommendation must be an integer."},
        )

    def test_set_unknown_recommendation(self):
        invalid_state_id = 0
        response = self.client.put(
            reverse("motion-set-recommendation", args=[self.motion.pk]),
            {"recommendation": invalid_state_id},
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(
            response.data,
            {"detail": "You can not set the recommendation to %d." % invalid_state_id},
        )

    def test_set_invalid_recommendation(self):
        # This is a valid state id, but this state is not recommendable because it belongs to a different workflow.
        invalid_state_id = 6  # State 'permitted'
        response = self.client.put(
            reverse("motion-set-recommendation", args=[self.motion.pk]),
            {"recommendation": invalid_state_id},
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(
            response.data,
            {"detail": "You can not set the recommendation to %d." % invalid_state_id},
        )

    def test_set_invalid_recommendation_2(self):
        # This is a valid state id, but this state is not recommendable because it has not recommendation label
        invalid_state_id = 1  # State 'submitted'
        self.motion.set_state(self.state_id_accepted)
        self.motion.save()
        response = self.client.put(
            reverse("motion-set-recommendation", args=[self.motion.pk]),
            {"recommendation": invalid_state_id},
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(
            response.data,
            {"detail": "You can not set the recommendation to %d." % invalid_state_id},
        )

    def test_reset(self):
        self.motion.set_recommendation(self.state_id_accepted)
        self.motion.save()
        response = self.client.put(
            reverse("motion-set-recommendation", args=[self.motion.pk])
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(
            response.data,
            {"detail": "The recommendation of the motion was set to None."},
        )
        self.assertTrue(Motion.objects.get(pk=self.motion.pk).recommendation is None)

    def test_set_recommendation_to_current_state(self):
        self.motion.set_state(self.state_id_accepted)
        self.motion.save()
        response = self.client.put(
            reverse("motion-set-recommendation", args=[self.motion.pk]),
            {"recommendation": self.state_id_accepted},
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(
            response.data,
            {"detail": "The recommendation of the motion was set to Acceptance."},
        )
        self.assertEqual(
            Motion.objects.get(pk=self.motion.pk).recommendation.name, "accepted"
        )

class CreateMotionPoll(TestCase):
    """
    Tests creating polls of motions.
    """

    def setUp(self):
        self.client = APIClient()
        self.client.login(username="admin", password="admin")
        self.motion = Motion(
            title="test_title_Aiqueigh2dae9phabiqu",
            text="test_text_Neekoh3zou6li5rue8iL",
        )
        self.motion.save()

    def test_create_first_poll_with_values_then_second_poll_without(self):
        self.poll = self.motion.create_poll()
        self.poll.set_vote_objects_with_values(
            self.poll.get_options().get(), {"Yes": 42, "No": 43, "Abstain": 44}
        )
        response = self.client.post(
            reverse("motion-create-poll", args=[self.motion.pk])
        )
        self.assertEqual(self.motion.polls.count(), 2)
        response = self.client.get(reverse("motion-detail", args=[self.motion.pk]))
        for key in ("yes", "no", "abstain"):
            self.assertTrue(
                response.data["polls"][1][key] is None,
                f"Vote value '{key}' should be None.",
            )

class UpdateMotionPoll(TestCase):
    """
    Tests updating polls of motions.
    """

    def setUp(self):
        self.client = APIClient()
        self.client.login(username="admin", password="admin")
        self.motion = Motion(
            title="test_title_Aiqueigh2dae9phabiqu",
            text="test_text_Neekoh3zou6li5rue8iL",
        )
        self.motion.save()
        self.poll = self.motion.create_poll()

    def test_invalid_votesvalid_value(self):
        response = self.client.put(
            reverse("motionpoll-detail", args=[self.poll.pk]),
            {"motion_id": self.motion.pk, "votesvalid": "-3"},
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_invalid_votesinvalid_value(self):
        response = self.client.put(
            reverse("motionpoll-detail", args=[self.poll.pk]),
            {"motion_id": self.motion.pk, "votesinvalid": "-3"},
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_invalid_votescast_value(self):
        response = self.client.put(
            reverse("motionpoll-detail", args=[self.poll.pk]),
            {"motion_id": self.motion.pk, "votescast": "-3"},
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_empty_value_for_votesvalid(self):
        response = self.client.put(
            reverse("motionpoll-detail", args=[self.poll.pk]),
            {"motion_id": self.motion.pk, "votesvalid": ""},
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)

class NumberMotionsInCategory(TestCase):
    """
    Tests numbering motions in a category.
    """

    def setUp(self):
        self.client = APIClient()
        self.client.login(username="admin", password="admin")
        self.category = Category.objects.create(
            name="test_cateogory_name_zah6Ahd4Ifofaeree6ai",
            prefix="test_prefix_ahz6tho2mooH8",
        )
        self.motion = Motion(
            title="test_title_Eeha8Haf6peulu8ooc0z",
            text="test_text_faghaZoov9ooV4Acaquk",
            category=self.category,
        )
        self.motion.save()
        self.motion.identifier = ""
        self.motion.save()
        self.motion_2 = Motion(
            title="test_title_kuheih2eja2Saeshusha",
            text="test_text_Ha5ShaeraeSuthooP2Bu",
            category=self.category,
        )
        self.motion_2.save()
        self.motion_2.identifier = ""
        self.motion_2.save()

    def test_numbering(self):
        response = self.client.post(
            reverse("category-numbering", args=[self.category.pk])
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(
            response.data,
            {
                "detail": "All motions in category test_cateogory_name_zah6Ahd4Ifofaeree6ai numbered successfully."
            },
        )
        self.assertEqual(
            Motion.objects.get(pk=self.motion.pk).identifier,
            "test_prefix_ahz6tho2mooH8 1",
        )
        self.assertEqual(
            Motion.objects.get(pk=self.motion_2.pk).identifier,
            "test_prefix_ahz6tho2mooH8 2",
        )

    def test_numbering_existing_identifier(self):
        self.motion_2.identifier = "test_prefix_ahz6tho2mooH8 1"
        self.motion_2.save()
        response = self.client.post(
            reverse("category-numbering", args=[self.category.pk])
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(
            response.data,
            {
                "detail": "All motions in category test_cateogory_name_zah6Ahd4Ifofaeree6ai numbered successfully."
            },
        )
        self.assertEqual(
            Motion.objects.get(pk=self.motion.pk).identifier,
            "test_prefix_ahz6tho2mooH8 1",
        )
        self.assertEqual(
            Motion.objects.get(pk=self.motion_2.pk).identifier,
            "test_prefix_ahz6tho2mooH8 2",
        )

    def test_numbering_with_given_order(self):
        self.motion_3 = Motion(
            title="test_title_eeb0kua5ciike4su2auJ",
            text="test_text_ahshuGhaew3eim8yoht7",
            category=self.category,
        )
        self.motion_3.save()
        self.motion_3.identifier = ""
        self.motion_3.save()
        response = self.client.post(
            reverse("category-numbering", args=[self.category.pk]),
            {"motions": [3, 2]},
            format="json",
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(
            response.data,
            {
                "detail": "All motions in category test_cateogory_name_zah6Ahd4Ifofaeree6ai numbered successfully."
            },
        )
        self.assertEqual(Motion.objects.get(pk=self.motion.pk).identifier, None)
        self.assertEqual(
            Motion.objects.get(pk=self.motion_2.pk).identifier,
            "test_prefix_ahz6tho2mooH8 2",
        )
        self.assertEqual(
            Motion.objects.get(pk=self.motion_3.pk).identifier,
            "test_prefix_ahz6tho2mooH8 1",
        )

class FollowRecommendationsForMotionBlock(TestCase):
    """
    Tests following the recommendations of motions in an motion block.
    """

    def setUp(self):
        self.state_id_accepted = 2  # This should be the id of the state 'accepted'.
        self.state_id_rejected = 3  # This should be the id of the state 'rejected'.

        self.client = APIClient()
        self.client.login(username="admin", password="admin")

        self.motion_block = MotionBlock.objects.create(
            title="test_motion_block_name_Ufoopiub7quaezaepeic"
        )

        self.motion = Motion(
            title="test_title_yo8ohy5eifeiyied2AeD",
            text="test_text_chi1aeth5faPhueQu8oh",
            motion_block=self.motion_block,
        )
        self.motion.save()
        self.motion.set_recommendation(self.state_id_accepted)
        self.motion.save()

        self.motion_2 = Motion(
            title="test_title_eith0EemaW8ahZa9Piej",
            text="test_text_haeho1ohk3ou7pau2Jee",
            motion_block=self.motion_block,
        )
        self.motion_2.save()
        self.motion_2.set_recommendation(self.state_id_rejected)
        self.motion_2.save()

    def test_follow_recommendations_for_motion_block(self):
        response = self.client.post(
            reverse("motionblock-follow-recommendations", args=[self.motion_block.pk])
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(
            Motion.objects.get(pk=self.motion.pk).state.id, self.state_id_accepted
        )
        self.assertEqual(
            Motion.objects.get(pk=self.motion_2.pk).state.id, self.state_id_rejected
        )

class CreateWorkflow(TestCase):
    """
    Tests the creating of workflows.
    """

    def setUp(self):
        self.client = APIClient()
        self.client.login(username="admin", password="admin")

    def test_creation(self):
        Workflow.objects.all().delete()
        response = self.client.post(
            reverse("workflow-list"), {"name": "test_name_OoCoo3MeiT9li5Iengu9"}
        )
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        workflow = Workflow.objects.get()
        self.assertEqual(workflow.name, "test_name_OoCoo3MeiT9li5Iengu9")
        first_state = workflow.first_state
        self.assertEqual(type(first_state), State)

class UpdateWorkflow(TestCase):
    """
    Tests the updating of workflows.
    """

    def setUp(self):
        self.client = APIClient()
        self.client.login(username="admin", password="admin")
        self.workflow = Workflow.objects.first()

    def test_rename_workflow(self):
        response = self.client.patch(
            reverse("workflow-detail", args=[self.workflow.pk]),
            {"name": 'test_name_wofi38DiWLT"8d3lwfo3'},
        )

        workflow = Workflow.objects.get(pk=self.workflow.id)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(workflow.name, 'test_name_wofi38DiWLT"8d3lwfo3')

class DeleteWorkflow(TestCase):
    """
    Tests the deletion of workflows.
    """

    def setUp(self):
        self.client = APIClient()
        self.client.login(username="admin", password="admin")
        self.workflow = Workflow.objects.first()

    def test_simple_delete(self):
        response = self.client.delete(
            reverse("workflow-detail", args=[self.workflow.pk])
        )
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
        self.assertEqual(Workflow.objects.count(), 1)  # Just the other default one

    def test_delete_with_assigned_motions(self):
        self.motion = Motion(
            title="test_title_chee7ahCha6bingaew4e",
            text="test_text_birah1theL9ooseeFaip",
        )
        self.motion.reset_state(self.workflow)
        self.motion.save()

        response = self.client.delete(
            reverse("workflow-detail", args=[self.workflow.pk])
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(Workflow.objects.count(), 2)


text:
<!DOCTYPE html>
<html lang="en" dir="ltr">

<head>
  <meta charset="UTF-8">
  <title>Ionic App</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <meta name="format-detection" content="telephone=no">
  <meta name="msapplication-tap-highlight" content="no">

  <link rel="icon" type="image/x-icon" href="assets/icon/favicon.ico">
  <link rel="manifest" href="manifest.json">
  <meta name="theme-color" content="#4e8ef7">

  <style>.item-avatar > img:first-child, .item-avatar .item-image, .item-avatar .item-content > img:first-child, .item-avatar .item-content .item-image, .item-avatar-left > img:first-child, .item-avatar-left .item-image, .item-avatar-left .item-content > img:first-child, .item-avatar-left .item-content .item-image {
    position: absolute;
    top: 16px;
    left: 16px;
    max-width: 40px;
    max-height: 40px;
    width: 100%;
    height: 100%;
    border-radius: 50%;
}</style>
  <!-- cordova.js required for cordova apps -->
  <script src="cordova.js"></script>

  <!-- un-comment this code to enable service worker
  <script>
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('service-worker.js')
        .then(() => console.log('service worker installed'))
        .catch(err => console.log('Error', err));
    }
  </script>-->

  <link href="build/main.css" rel="stylesheet">

</head>

<body>

  <!-- Ionic's root component and where the app will load -->
  <ion-app></ion-app>

  <!-- The polyfills js is generated during the build process -->
  <script src="build/polyfills.js"></script>

  <!-- The bundle js is generated during the build process -->
  <script src="build/main.js"></script>

</body>

</html>

redpajama_set_name: RedPajamaGithub


text: dynamixel motor control pack for ROS


text:
<!DOCTYPE html>

 	<title>Flip Stitch Pillow: Customise</title>
    <link rel="stylesheet" href="cust.css">

       	 	<button id="select"></button>
        	<button></button>
  			<button></button>
  			<button></button>

Pillow type

  				What kind of pillow are you looking for?

  			<button onclick="ss()"></button>
  			<button onclick="bb()"></button>
  			<button onclick="rr()"></button>
  			<button onclick="ff()"></button>

Sofa pillow                  Bed pillow                   Round pillow             Floor pouf pillow

Next


text:
User Guide
==========

1. [Annotating assets](#annotating_assets)
2. [Switching assets and data source](#switching_assets)
3. [Annotating](#annotating)
4. [View options](#view_options)
5. [Landmark sidebar](#landmark_sidebar)
6. [Autosave behaviour](#autosave)
7. [Keyboard shortcuts](#shortcuts)
8. [Data sources](#data_sources)
9. [The landmarker server](#server)
10. [Dropbox](#dropbox)
11. [Note on security](#security)

---------------------------------------

### 1. Annotating assets {#annotating_assets}
The landmarker can be used to annotate either 2D assets (images) or 3D assets (meshes).

However, in its current state, it can currently only work in one 2 modes image or mesh and annotate the corresponding type of assets at any one time. (This is a legacy concern we plan to adress).

The annotations are saved as landmark groups compliant with a given template.

### 2. Switching assets and data source {#switching_assets}
Regardless of the data source, the assets are identifiable by their name (id) or by their index. They are organized into a collection in which you can navigate with the arrow buttons at the bottom of the screen or the `j` and `k` keys.

You can quickly identify this data in the top left of the screen and click on any of these elements to switch asset:

![](http://i.imgur.com/eERY2pm.png)

+ Data source (click to logout)
+ Collection name and current mode
+ Asset name
+ Asset index

If they appear greyed out, there is no possibility to change them (for example a one item collection will not allow changing the asset)

### 3. Annotating {#annotating}
_Video demonstrating everything to come soon_

Once logged in and working on an asset, landmarking is pretty straightforward:

+ Right click to insert a landmark if there exists an unset landmark
+ Left click to move the closest landmark (hold ctrl to lock to selected landmark)
+ Click and drag to move a landmark
+ Shift click + drag allows you to select a group of landmark that you can move by dragging them or using the arrow keys
+ Hold left click to rotate a mesh
+ Hold right click to translate the mesh or the image (left click as well for images)

### 4. View options {#view_options}
![](http://i.imgur.com/VVFVYd0.png)

Some view options are available in the bottom left corner of the screen to hide/show the texture of the mesh and the links between connected landmarks.

The `snap` setting will disable the auto selection of closest landmark as well as the visual feedback. This is useful in cases with densely packed landmarks where click and drag can be more precise.

The slider controls the size of the landmarks relative to the asset's dimensions. You can also update this in increments by hitting `+` or `-`.

### 5. Landmark sidebar {#landmark_sidebar}
The sidebar in the bottom right of the screen mirrors what is happening on the screen. Landmarks in blue are existing landmarks, they are highlighted in purple once selected (by hovering near them or clicking them with snap disabled). Empty landmarks or in clear grey and the next one to be inserted is highlighted in orange. You can click on any empty landmark to set it to be inserted next, otherwise the default logic is to take the first available one from the top.

The top bar has undo / redo buttons on the left and the current template name on the right. You can click on the template name to change the template if applicable.

The save button, gives you the ability to save landmarks **remotely** and displays a red dot if there are any unsaved changes.

The download button will generate a json file of the current landmarks and initiate a download (on older browsers and Safari this will open a new tab/window with the json content).

If the current asset is not the first one in the collection, a `LOAD PREVIOUS` button is available, this will fetch the landmarks from the previous asset to replace the current set with. This is a destructive operation and can be undone, however for added security it will not save afterwards.

### 6. Autosave behaviour {#autosave}
If the autosave switch is on (it is by default), anytime you leave an asset, the changes will be saved (you should see a notification in the center of the screen). This currently applies to navigating with `j/k`, the pager at the bottom or changing asset manually by clicking on the index or name.

If it is off, it will not be possible to leave an asset with unsaved changes, a confirmation modal will be displayed allowing to leave or go back and save. This adds security if you want to ensure that saving changes remotely is a conscious operation.

### 7. Keyboard shortcuts {#shortcuts}
A list of keyboard shortcuts is available by clicking the help button in the bottom right corner of the screen or hitting `?`. The main ones to be aware of are:

| Key | Action |
| --- | ------ |
| j / k | go to the next / previous asset |
| a | select all landmarks |
| q / ESC | deselect landmarks if applicable |
| g | select all landmarks in a group with already selected landmarks |
| d | delete all selected landmarks |
| ctrl + s | save current landmark |
| z / y | undo / redo last operation |
| r | reset camera to it's default position |

### 8. Data sources {#data_sources}
The landmarker currently supports 2 data sources: Dropbox and the original custom landmarker server. On first visit, you'll see this landing page:

![](http://i.imgur.com/FhRlSOH.png)

### 9. The landmarker server {#server}
See the [landmarker-server](https://github.com/menpo/landmarkerio-server) repo for info about running your own server.

You can access a server by clicking on the second button and entering a url in the subsequent prompt, or directly using the url query string:

`https://www.landmarker.io?server=SERVERURL&t=TEMPLATENAME&c=COLLECTIONNAME&i=ASSETINDEX`

Apart the `server` parameter which needs to be a valid and reachable url, you can specify the template name with `t`, collection name with `c` and asset index with `i` on load. If they are not specified or invalid (as in not present in the server's data), the landmarker will pick the first available item.

The mode is fetched automatically from the server.

The server will load templates by itself by generating empty landmark groups compliant with the selected template if none is present. As such it is not possible to specify custom template when working from the server.

### 10. Dropbox {#dropbox}
If you selected dropbox from the landing page, you'll be presented with a filepicker like this one:

![](http://i.imgur.com/VAbnSYh.png)

Select the type of assets you want to load (images / meshes), as well as the folder - you can can navigate down the true to check the content of the folder. Note that all assets of the selected types will be loaded.

Once this is done, one of the default templates will have been selected for you, you can change this template and / or import one from dropbox by clicking on the template name in the right sidebar and then on the `+` button. To avoid collisions, importing a template with the same name as one which already exits will import it with as `[name]-1` and so on, so you can work with different versions of a template.

Landmarks will be saved in the same folder under a `landmarks` folder with the scheme `[asset-name]_[template-name].ljson`

### 11. Note on security {#security}
The main application runs on a secure connection at [https://www.landmarker.io](https://www.landmarker.io), on this version we disallow using a non secure server in this mode due to the mixed content security concerns (see [this article](https://developer.mozilla.org/en/docs/Security/MixedContent) from Mozilla or [this one](http://www.howtogeek.com/181911/htg-explains-what-exactly-is-a-mixed-content-warning/) from How To Geek for more information).

As a result it is not possible to use a local server from this connection, however another version is deployed on a regular http connection at [http://insecure.landmarker.io/](http://insecure.landmarker.io/) which will allow it. This version will however not allow you to connect to dropbox which is only possible on the https version. You can use [ngrok](https://ngrok.com/) in order to expose a local server behind https.


text:
 Gets a Publishing Page from any folder in the Pages library.

**Namespace:** [Microsoft.SharePoint.Client](Microsoft.SharePoint.Client.md)
**Assembly:** OfficeDevPnP.Core.dll
## Syntax
```C#
public static PublishingPage GetPublishingPage(this Web web, String fileLeafRef, Folder folder)
```
### Parameters
#### web
  Type: Microsoft.SharePoint.Client.Web
  The web.

#### fileLeafRef
  Type: System.String
  The file leaf reference.

#### folder
  Type: Microsoft.SharePoint.Client.Folder
  The folder where to search the page.

### Return Value
Type: PublishingPage
The PublishingPage object, if any. Otherwise null.

## See also
- [PublishingExtensions](Microsoft.SharePoint.Client.PublishingExtensions.md)
- System.ArgumentNullException
- System.ArgumentException
- [Microsoft.SharePoint.Client](Microsoft.SharePoint.Client.md)


text:
using UnityEngine;
using System.Collections;
using Leap;

public class LeapMouse : MonoBehaviour {
	public static Vector2 position {
		get {
			if (Instance != null) {
				if (Instance.UseMouseClone) {
					return Input.mousePosition;
				} else {
					return Instance.mousePosition;
				}
			}
			return Vector2.one * float.MaxValue;
		}

		set {
			if (Instance != null)
				Instance.mousePosition = value;
		}
	}
	Vector2 mousePosition = Vector2.one * -99999f;

	public static Vector3 value {
		get {
			if (Instance != null) {
				return Instance.leapValue;
			}

			return Vector3.zero;
		}
	}
	Vector3 leapValue = Vector3.zero;

	public static Vector3 rotValue {
		get {
			if (Instance != null) {
				return Instance.leapRotValue;
			}

			return Vector3.zero;
		}
	}
	Vector3 leapRotValue = Vector3.zero;

	public static LeapMouse Instance {
		get {
			if (instance == null) {
				instance = FindObjectOfType<LeapMouse> ();
			}

			return instance;
		}
	}

	static LeapMouse instance = null;

	static Rect Bound = new Rect (-98, 135, 148, 143);
	static float Near = -150f;
	static float Far = 150f;
	public static void SetBound (float left, float top, float width, float height) {
		PlayerPrefs.SetFloat ("left", left);
		PlayerPrefs.SetFloat ("top", top);
		PlayerPrefs.SetFloat ("width", width);
		PlayerPrefs.SetFloat ("height", height);

		Bound.Set (left, top, width, height);
	}

	public Transform BottomLeft;
	public Transform TopRight;
	public UISprite widget;
	bool useMouseClone = true;
	public bool UseMouseClone {
		get {
			return useMouseClone && !connected;
		}

		set {
			useMouseClone = value;
		}
	}

	void Awake () {
		UnityVectorExtension.InputScale = Vector3.one;

		float left = PlayerPrefs.GetFloat ("left", -100f);
		float top = PlayerPrefs.GetFloat ("top", 135f);
		float width = PlayerPrefs.GetFloat ("width", 150f);
		float height = PlayerPrefs.GetFloat ("height", 150f);

		SetBound (left, top, width, height);

		LeapInterface.HandEvent += HandEvent;
		LeapInterface.LostEvent += HandLost;
	}

	void OnDestroy () {
		instance = null;
		LeapInterface.HandEvent -= HandEvent;
		LeapInterface.LostEvent -= HandLost;
	}

	public void SetFillByHover (float fillPercent) {
		widget.fillAmount = fillPercent;
	}

	bool connected = false;
	void HandEvent (Hand h) {
		connected = true;

		Vector3 pos = UnityVectorExtension.ToUnity (h.StabilizedPalmPosition);
		pos.x = Mathf.Clamp (pos.x, Bound.xMin, Bound.xMax);
		pos.y = Mathf.Clamp (pos.y, Bound.yMin, Bound.yMax);
		pos.z = Mathf.Clamp (pos.z, Near, Far);

		pos -= new Vector3 (Bound.xMin, Bound.yMin, Near);

		pos.x = pos.x / Bound.width;
		pos.y = pos.y / Bound.height;
		pos.z = pos.z / (Far - Near);

		Set (pos);

		float pitch = h.Direction.Pitch * Mathf.Rad2Deg;
		float yaw = h.Direction.Yaw * Mathf.Rad2Deg;
		float roll = h.PalmNormal.Roll * Mathf.Rad2Deg;

		if (pitch > 180f) pitch -= 360f;
		if (yaw > 180f) yaw -= 360f;
		if (roll > 180f) roll -= 360f;

		Set (pitch, yaw, roll);
	}

	void HandLost () {
		connected = false;
	}

	void Set (Vector3 pos) {

		Vector3 screenSize = new Vector3 (UnityEngine.Screen.width, UnityEngine.Screen.height);
		mousePosition = Vector3.Scale (pos, screenSize);

		pos -= Vector3.one * 0.5f;
		leapValue = pos;

		Vector3 ratioNGUIScreen = TopRight.localPosition - BottomLeft.localPosition;
		transform.localPosition = Vector3.Scale (ratioNGUIScreen, pos);
	}

	void Set (float pitch, float yaw, float roll) {
		leapRotValue = new Vector3 (pitch, yaw, roll);
	}

	void Update () {
		if (Input.GetKeyDown (KeyCode.P)) {
			useMouseClone = !useMouseClone;
		}

		UnityEngine.Screen.showCursor = !UseMouseClone;
		if (UseMouseClone) {
			var _leapvalue = Vector3.Scale (Input.mousePosition, new Vector3(1f/UnityEngine.Screen.width, 1f/UnityEngine.Screen.height));

			if (Input.GetKey (KeyCode.Space)) {
				_leapvalue.z = 1f;
			} else if (Input.GetKey (KeyCode.LeftShift)) {
				_leapvalue.z = 0f;
			}else {
				_leapvalue.z = 0.5f;
			}

			Set (_leapvalue);

			var _leapRotValue = leapRotValue;
			if (Input.GetKey (KeyCode.A)) {
				_leapRotValue.z = -30f;
			}
			if (Input.GetKey (KeyCode.D)) {
				_leapRotValue.z = 30f;
			}

			Set (_leapRotValue.x, _leapRotValue.y, _leapRotValue.z);
		}

		LeapInput.Update ();
	}
}


text:
        Training - Search /

Employee

Invalid Employee

{{employee.firstName}} {{employee.lastName}}

        Competency
        Training
        Trained By

        Certification
        Certified By

                Loading training records. Please wait.

            <button class="btn" ng-show="canEditTrainingRecord" ng-click="onAddTrainingRecord()"> Add Training Record</button>

            <button class="btn" ng-hide="showAvailableCompetencies" ng-click="onShowAvailableCompetencies()"> Show Available Competencies</button>

            <button class="btn" ng-show="showAvailableCompetencies" ng-click="onHideAvailableCompetencies()"> Hide Available Competencies</button>


text:
package io.noties.markwon.html.tag;

import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import java.util.Map;

import io.noties.markwon.html.CssInlineStyleParser;
import io.noties.markwon.html.CssProperty;
import io.noties.markwon.image.ImageSize;

class ImageSizeParserImpl implements ImageHandler.ImageSizeParser {

    private final CssInlineStyleParser inlineStyleParser;

    ImageSizeParserImpl(@NonNull CssInlineStyleParser inlineStyleParser) {
        this.inlineStyleParser = inlineStyleParser;
    }

    @Override
    public ImageSize parse(@NonNull Map<String, String> attributes) {

        // strictly speaking percents when specified directly on an attribute
        // are not part of the HTML spec (I couldn't find any reference)

        ImageSize.Dimension width = null;
        ImageSize.Dimension height = null;

        // okay, let's first check styles
        final String style = attributes.get("style");

        if (!TextUtils.isEmpty(style)) {

            String key;

            for (CssProperty cssProperty : inlineStyleParser.parse(style)) {

                key = cssProperty.key();

                if ("width".equals(key)) {
                    width = dimension(cssProperty.value());
                } else if ("height".equals(key)) {
                    height = dimension(cssProperty.value());
                }

                if (width != null
                        && height != null) {
                    break;
                }
            }
        }

        if (width != null
                && height != null) {
            return new ImageSize(width, height);
        }

        // check tag attributes
        if (width == null) {
            width = dimension(attributes.get("width"));
        }

        if (height == null) {
            height = dimension(attributes.get("height"));
        }

        if (width == null
                && height == null) {
            return null;
        }

        return new ImageSize(width, height);
    }

    @Nullable
    @VisibleForTesting
    ImageSize.Dimension dimension(@Nullable String value) {

        if (TextUtils.isEmpty(value)) {
            return null;
        }

        final int length = value.length();

        for (int i = length - 1; i > -1; i--) {

            if (Character.isDigit(value.charAt(i))) {

                try {
                    final float val = Float.parseFloat(value.substring(0, i + 1));
                    final String unit;
                    if (i == length - 1) {
                        // no unit info
                        unit = null;
                    } else {
                        unit = value.substring(i + 1, length);
                    }
                    return new ImageSize.Dimension(val, unit);
                } catch (NumberFormatException e) {
                    // value cannot not be represented as a float
                    return null;
                }
            }
        }

        return null;
    }
}


text:
<!DOCTYPE HTML>

<title>ParallelEnvironmentInfo (xenon 3.0.4 API for Xenon developers)</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="../../../../../../stylesheet.css" title="Style">
<link rel="stylesheet" type="text/css" href="../../../../../../jquery/jquery-ui.css" title="Style">

JavaScript is disabled on your browser.

Skip navigation links

Overview

Package

Class

Tree

Deprecated

Index

Help

All Classes

SEARCH:
<input type="text" id="search" value="search" disabled="disabled">
<input type="reset" id="reset" value="reset" disabled="disabled">

JavaScript is disabled on your browser.

Summary:

Nested |

Field |

Constr |

Method

Detail:

Field |

Constr |

Method

Package nl.esciencecenter.xenon.adaptors.schedulers.gridengine

Class ParallelEnvironmentInfo

java.lang.Object

nl.esciencecenter.xenon.adaptors.schedulers.gridengine.ParallelEnvironmentInfo

class ParallelEnvironmentInfo
extends java.lang.Object

Class that holds some info on parallel environments used in Grid Engine.

Nested Class Summary

Nested Classes

Modifier and Type
Class
Description

static class
ParallelEnvironmentInfo.AllocationRule

Field Summary

Fields

Modifier and Type
Field
Description

private static java.lang.String
ALLOCATION_FILL_UP

private static java.lang.String
ALLOCATION_PE_SLOTS

private static java.lang.String
ALLOCATION_ROUND_ROBIN

private ParallelEnvironmentInfo.AllocationRule
allocationRule

private java.lang.String
name

private int
ppn

private int
slots

Constructor Summary

Constructors

Constructor
Description

ParallelEnvironmentInfo(java.lang.String name,
                       int slots,
                       ParallelEnvironmentInfo.AllocationRule allocationRule,
                       int ppn)

ParallelEnvironmentInfo(java.util.Map<java.lang.String,java.lang.String> info)

Method Summary

All Methods Instance Methods Concrete Methods

Modifier and Type
Method
Description

(package private) boolean
canAllocateMultiNode(int coresPerNode,
                    int nodes)

Check if pe can allocate X number of cores per node on Y number of nodes

(package private) boolean
canAllocateSingleNode(int coresPerNode)

Check if pe can allocate all slots on the same node

ParallelEnvironmentInfo.AllocationRule
getAllocationRule()

java.lang.String
getName()

int
getPpn()

int
getSlots()

java.lang.String
toString()

Methods inherited from class java.lang.Object

clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait

Field Detail

ALLOCATION_PE_SLOTS

private static final java.lang.String ALLOCATION_PE_SLOTS

See Also:

Constant Field Values

ALLOCATION_ROUND_ROBIN

private static final java.lang.String ALLOCATION_ROUND_ROBIN

See Also:

Constant Field Values

ALLOCATION_FILL_UP

private static final java.lang.String ALLOCATION_FILL_UP

See Also:

Constant Field Values

name

private final java.lang.String name

slots

private final int slots

allocationRule

private final ParallelEnvironmentInfo.AllocationRule allocationRule

ppn

private final int ppn

Constructor Detail

ParallelEnvironmentInfo

ParallelEnvironmentInfo(java.util.Map<java.lang.String,java.lang.String> info)
                 throws XenonException

Throws:

XenonException

ParallelEnvironmentInfo

ParallelEnvironmentInfo(java.lang.String name,
                        int slots,
                        ParallelEnvironmentInfo.AllocationRule allocationRule,
                        int ppn)

Method Detail

getName

public java.lang.String getName()

getSlots

public int getSlots()

getAllocationRule

public ParallelEnvironmentInfo.AllocationRule getAllocationRule()

getPpn

public int getPpn()

toString

public java.lang.String toString()

Overrides:

toString in class java.lang.Object

canAllocateSingleNode

boolean canAllocateSingleNode(int coresPerNode)

Check if pe can allocate all slots on the same node

Parameters:

coresPerNode - number of cores to reserve on a node

Returns:

True if pe can

canAllocateMultiNode

boolean canAllocateMultiNode(int coresPerNode,
                             int nodes)

Check if pe can allocate X number of cores per node on Y number of nodes

Parameters:

coresPerNode - number of cores to reserve on each node

nodes - number of nodes to reserve

Returns:

True if pe can

Skip navigation links

Overview

Package

Class

Tree

Deprecated

Index

Help

All Classes

JavaScript is disabled on your browser.

Summary:

Nested |

Field |

Constr |

Method

Detail:

Field |

Constr |

Method