sorting of motion comment sections

This commit is contained in:
FinnStutzenstein 2019-07-19 13:43:33 +02:00
parent 8cb9892426
commit 74647dc75d
8 changed files with 151 additions and 2 deletions

View File

@ -54,6 +54,14 @@ export class MotionCommentSectionRepositoryService extends BaseRepository<
private http: HttpService private http: HttpService
) { ) {
super(DS, dataSend, mapperService, viewModelStoreService, translate, MotionCommentSection, [Group]); super(DS, dataSend, mapperService, viewModelStoreService, translate, MotionCommentSection, [Group]);
this.viewModelSortFn = (a: ViewMotionCommentSection, b: ViewMotionCommentSection) => {
if (a.weight === b.weight) {
return a.id - b.id;
} else {
return a.weight - b.weight;
}
};
} }
public getTitle = (titleInformation: MotionCommentSectionTitleInformation) => { public getTitle = (titleInformation: MotionCommentSectionTitleInformation) => {
@ -109,4 +117,13 @@ export class MotionCommentSectionRepositoryService extends BaseRepository<
private async deleteComment(motion: ViewMotion, section: ViewMotionCommentSection): Promise<void> { private async deleteComment(motion: ViewMotion, section: ViewMotionCommentSection): Promise<void> {
return await this.http.delete(`/rest/motions/motion/${motion.id}/manage_comments/`, { section_id: section.id }); return await this.http.delete(`/rest/motions/motion/${motion.id}/manage_comments/`, { section_id: section.id });
} }
/**
* Sort all comment sections. All sections must be given excatly once.
*/
public async sortCommentSections(sections: ViewMotionCommentSection[]): Promise<void> {
return await this.http.post('/rest/motions/motion-comment-section', {
ids: sections.map(section => section.id)
});
}
} }

View File

@ -11,6 +11,7 @@ export class MotionCommentSection extends BaseModel<MotionCommentSection> {
public name: string; public name: string;
public read_groups_id: number[]; public read_groups_id: number[];
public write_groups_id: number[]; public write_groups_id: number[];
public weight: number;
public constructor(input?: any) { public constructor(input?: any) {
super(MotionCommentSection.COLLECTIONSTRING, input); super(MotionCommentSection.COLLECTIONSTRING, input);

View File

@ -48,6 +48,10 @@ export class ViewMotionCommentSection extends BaseViewModel<MotionCommentSection
return this._writeGroups; return this._writeGroups;
} }
public get weight(): number {
return this.section.weight;
}
/** /**
* TODO: Where is this needed? Try to avoid this. * TODO: Where is this needed? Try to avoid this.
*/ */

View File

@ -0,0 +1,16 @@
# Generated by Django 2.2.3 on 2019-07-19 10:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("motions", "0028_subcategories")]
operations = [
migrations.AddField(
model_name="motioncommentsection",
name="weight",
field=models.IntegerField(default=10000),
)
]

View File

@ -583,6 +583,11 @@ class MotionCommentSection(RESTModelMixin, models.Model):
These groups have write-access to the section. These groups have write-access to the section.
""" """
weight = models.IntegerField(default=10000)
"""
To sort comment sections.
"""
class Meta: class Meta:
default_permissions = () default_permissions = ()

View File

@ -373,7 +373,8 @@ class MotionCommentSectionSerializer(ModelSerializer):
class Meta: class Meta:
model = MotionCommentSection model = MotionCommentSection
fields = ("id", "name", "read_groups", "write_groups") fields = ("id", "name", "read_groups", "write_groups", "weight")
read_only_fields = ("weight",)
def create(self, validated_data): def create(self, validated_data):
""" Call inform_changed_data on creation, so the cache includes the groups. """ """ Call inform_changed_data on creation, so the cache includes the groups. """

View File

@ -4,6 +4,7 @@ import jsonschema
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError as DjangoValidationError from django.core.exceptions import ValidationError as DjangoValidationError
from django.db import transaction from django.db import transaction
from django.db.models import Case, When
from django.db.models.deletion import ProtectedError from django.db.models.deletion import ProtectedError
from django.http.request import QueryDict from django.http.request import QueryDict
from rest_framework import status from rest_framework import status
@ -1228,7 +1229,7 @@ class MotionCommentSectionViewSet(ModelViewSet):
""" """
if self.action in ("list", "retrieve"): if self.action in ("list", "retrieve"):
result = self.get_access_permissions().check_permissions(self.request.user) result = self.get_access_permissions().check_permissions(self.request.user)
elif self.action in ("create", "destroy", "update", "partial_update"): elif self.action in ("create", "destroy", "update", "partial_update", "sort"):
result = has_perm(self.request.user, "motions.can_see") and has_perm( result = has_perm(self.request.user, "motions.can_see") and has_perm(
self.request.user, "motions.can_manage" self.request.user, "motions.can_manage"
) )
@ -1268,6 +1269,39 @@ class MotionCommentSectionViewSet(ModelViewSet):
inform_changed_data(MotionComment.objects.filter(section=section)) inform_changed_data(MotionComment.objects.filter(section=section))
return response return response
@list_route(methods=["post"])
def sort(self, request, *args, **kwargs):
"""
Changes the sorting of comment sections. Every id must be given exactly once.
Expected data: { ids: [<id>, <id>, ...] }
"""
# Check request data format
ids = request.data.get("ids")
if not isinstance(ids, list):
raise ValidationError({"detail": "ids must be a list"})
for id in ids:
if not isinstance(id, int):
raise ValidationError({"detail": "every id must be an int"})
# Validate, that every id is given exactly once.
ids_set = set(ids)
if len(ids_set) != len(ids):
raise ValidationError({"detail": "only unique ids are expected"})
db_ids_set = set(
list(MotionCommentSection.objects.all().values_list(flat=True))
)
if ids_set != db_ids_set:
raise ValidationError({"detail": "every id must be given"})
# Ids are ok.
preserved = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(ids)])
queryset = MotionCommentSection.objects.filter(pk__in=ids).order_by(preserved)
for index, section in enumerate(queryset):
section.weight = index + 1
section.save()
return Response()
class StatuteParagraphViewSet(ModelViewSet): class StatuteParagraphViewSet(ModelViewSet):
""" """

View File

@ -1331,6 +1331,77 @@ class TestMotionCommentSection(TestCase):
self.assertEqual(MotionCommentSection.objects.count(), 1) self.assertEqual(MotionCommentSection.objects.count(), 1)
class TestMotionCommentSectionSorting(TestCase):
"""
Tests sorting of comment sections.
"""
def setUp(self):
self.client = APIClient()
self.client.login(username="admin", password="admin")
self.section1 = MotionCommentSection(name="test_name_hponzp<zp7NUJKLAykbX")
self.section1.save()
self.section2 = MotionCommentSection(name="test_name_eix,b<bojbP'JO;<kVKL")
self.section2.save()
self.section3 = MotionCommentSection(name="test_name_ojMOeigSIOfhmpouweqc")
self.section3.save()
def test_simple(self):
response = self.client.post(
reverse("motioncommentsection-sort"), {"ids": [3, 2, 1]}, format="json"
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
section1 = MotionCommentSection.objects.get(pk=1)
self.assertEqual(section1.weight, 3)
section2 = MotionCommentSection.objects.get(pk=2)
self.assertEqual(section2.weight, 2)
section3 = MotionCommentSection.objects.get(pk=3)
self.assertEqual(section3.weight, 1)
def test_wrong_data(self):
response = self.client.post(
reverse("motioncommentsection-sort"), {"ids": "some_string"}, format="json"
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assert_not_changed()
def test_wrong_id_type(self):
response = self.client.post(
reverse("motioncommentsection-sort"),
{"ids": [1, 2, "some_string"]},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assert_not_changed()
def test_missing_id(self):
response = self.client.post(
reverse("motioncommentsection-sort"), {"ids": [3, 1]}, format="json"
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assert_not_changed()
def test_duplicate_id(self):
response = self.client.post(
reverse("motioncommentsection-sort"), {"ids": [3, 2, 1, 1]}, format="json"
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assert_not_changed()
def test_wrong_id(self):
response = self.client.post(
reverse("motioncommentsection-sort"), {"ids": [3, 4, 1]}, format="json"
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assert_not_changed()
def assert_not_changed(self):
""" Asserts, that every comment section has the default weight of 10000. """
for section in MotionCommentSection.objects.all():
self.assertEqual(section.weight, 10000)
class RetrieveMotionChangeRecommendation(TestCase): class RetrieveMotionChangeRecommendation(TestCase):
""" """
Tests retrieving motion change recommendations. Tests retrieving motion change recommendations.