Added view for bulk category set and bulk motion block set.

Due to rebasing this PR, this are the appropriate authorships:
Server: @normanjaeckel and @FinnStutzenstein
Client: @MaximilianKrambach
This commit is contained in:
Norman Jäckel 2019-05-11 22:43:32 +02:00 committed by FinnStutzenstein
parent 8983f6aef3
commit cf29f97613
4 changed files with 206 additions and 29 deletions

View File

@ -40,10 +40,10 @@ Motions:
[#4235, #4518, #4521].
- Allowed submitters to set state of new motions in complex and customized
workflow [#4236].
- Added multi select action to manage submitters, tags, states and
recommendations [#4037, #4132].
- Added multi select action to manage submitters, categories, motion blocks,
tags, states and recommendations [#4037, #4132, #4702].
- Added timestampes for motions [#4134].
- New config option to set reason as required field [#4232]
- New config option to set reason as required field [#4232].
User:
- Added new admin group which grants all permissions. Users of existing group

View File

@ -350,6 +350,34 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
await this.httpService.post(restPath, { motions: motionsIdMap });
}
/**
* Set the motion blocks of motions in bulk
*
* @param viewMotion target motion
* @param motionblockId the number that indicates the motion block
*/
public async setMultiMotionBlock(viewMotions: ViewMotion[], motionblockId: number): Promise<void> {
const restPath = `/rest/motions/motion/manage_multiple_motion_block/`;
const motionsIdMap: { id: number; motion_block: number }[] = viewMotions.map(motion => {
return { id: motion.id, motion_block: motionblockId };
});
await this.httpService.post(restPath, { motions: motionsIdMap });
}
/**
* Set the category of motions in bulk
*
* @param viewMotion target motion
* @param categoryId the number that indicates the category
*/
public async setMultiCategory(viewMotions: ViewMotion[], categoryId: number): Promise<void> {
const restPath = `/rest/motions/motion/manage_multiple_category/`;
const motionsIdMap: { id: number; category: number }[] = viewMotions.map(motion => {
return { id: motion.id, category: categoryId };
});
await this.httpService.post(restPath, { motions: motionsIdMap });
}
/**
* Set the recommenders state of a motion
*

View File

@ -179,20 +179,12 @@ export class MotionMultiselectService {
clearChoice
);
if (selectedChoice) {
let i = 0;
for (const motion of motions) {
++i;
const message =
this.translate.instant(this.messageForSpinner) +
`\n${i} ` +
this.translate.instant('of') +
` ${motions.length}`;
this.spinnerService.setVisibility(true, message);
await this.repo.update(
{ category_id: selectedChoice.action ? null : (selectedChoice.items as number) },
motion
);
}
const message = this.translate.instant(this.messageForSpinner);
this.spinnerService.setVisibility(true, message);
await this.repo.setMultiCategory(motions, selectedChoice.items as number).catch(error => {
this.spinnerService.setVisibility(false);
throw error;
});
this.spinnerService.setVisibility(false);
}
}
@ -315,18 +307,13 @@ export class MotionMultiselectService {
clearChoice
);
if (selectedChoice) {
let i = 0;
for (const motion of motions) {
++i;
const message =
this.translate.instant(this.messageForSpinner) +
`\n${i} ` +
this.translate.instant('of') +
` ${motions.length}`;
this.spinnerService.setVisibility(true, message);
const blockId = selectedChoice.action ? null : (selectedChoice.items as number);
await this.repo.update({ motion_block_id: blockId }, motion);
}
const message = this.translate.instant(this.messageForSpinner);
this.spinnerService.setVisibility(true, message);
const blockId = selectedChoice.action ? null : (selectedChoice.items as number);
await this.repo.setMultiMotionBlock(motions, blockId).catch(error => {
this.spinnerService.setVisibility(false);
throw error;
});
this.spinnerService.setVisibility(false);
}
}

View File

@ -80,6 +80,8 @@ class MotionViewSet(TreeSortMixin, ModelViewSet):
result = has_perm(self.request.user, "motions.can_see")
# The rest of the check is done in the respective method. See below.
elif self.action in (
"manage_multiple_category",
"manage_multiple_motion_block",
"manage_multiple_state",
"set_recommendation",
"manage_multiple_recommendation",
@ -523,6 +525,166 @@ class MotionViewSet(TreeSortMixin, ModelViewSet):
# Initiate response.
return Response({"detail": message})
@list_route(methods=["post"])
@transaction.atomic
def manage_multiple_category(self, request):
"""
Set categories of multiple motions.
Send POST {"motions": [... see schema ...]} to changed the categories.
"""
motions = request.data.get("motions")
schema = {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Motion manage multiple categories schema",
"description": "An array of motion ids with the respective category id that should be set as category.",
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {"description": "The id of the motion.", "type": "integer"},
"category": {
"description": "The id for the category that should become the new category.",
"anyOf": [{"type": "number", "minimum": 1}, {"type": "null"}],
},
},
"required": ["id", "category"],
},
"uniqueItems": True,
}
# Validate request data.
try:
jsonschema.validate(motions, schema)
except jsonschema.ValidationError as err:
raise ValidationError({"detail": str(err)})
motion_result = []
for item in motions:
# Get motion.
try:
motion = Motion.objects.get(pk=item["id"])
except Motion.DoesNotExist:
raise ValidationError({"detail": f"Motion {item['id']} does not exist"})
# Get category
category = None
if item["category"] is not None:
try:
category = Category.objects.get(pk=item["category"])
except Category.DoesNotExist:
raise ValidationError(
{"detail": f"Category {item['category']} does not exist"}
)
# Set category
motion.category = category
# Save motion.
motion.save(
update_fields=["category", "last_modified"], skip_autoupdate=True
)
# Fire autoupdate again to save information to OpenSlides history.
information = (
["Category removed"]
if category is None
else ["Category set to {arg1}", category.name]
)
inform_changed_data(
motion, information=information, user_id=request.user.pk
)
# Finish motion.
motion_result.append(motion)
# Send response.
return Response(
{"detail": f"Category of {len(motion_result)} motions successfully set."}
)
@list_route(methods=["post"])
@transaction.atomic
def manage_multiple_motion_block(self, request):
"""
Set motion blocks of multiple motions.
Send POST {"motions": [... see schema ...]} to changed the motion blocks.
"""
motions = request.data.get("motions")
schema = {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Motion manage multiple motion blocks schema",
"description": "An array of motion ids with the respective motion block id that should be set as motion block.",
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {"description": "The id of the motion.", "type": "integer"},
"motion_block": {
"description": "The id for the motion block that should become the new motion block.",
"anyOf": [{"type": "number", "minimum": 1}, {"type": "null"}],
},
},
"required": ["id", "motion_block"],
},
"uniqueItems": True,
}
# Validate request data.
try:
jsonschema.validate(motions, schema)
except jsonschema.ValidationError as err:
raise ValidationError({"detail": str(err)})
motion_result = []
for item in motions:
# Get motion.
try:
motion = Motion.objects.get(pk=item["id"])
except Motion.DoesNotExist:
raise ValidationError({"detail": f"Motion {item['id']} does not exist"})
# Get motion block
motion_block = None
if item["motion_block"] is not None:
try:
motion_block = MotionBlock.objects.get(pk=item["motion_block"])
except MotionBlock.DoesNotExist:
raise ValidationError(
{"detail": f"MotionBlock {item['motion_block']} does not exist"}
)
# Set motion bock
motion.motion_block = motion_block
# Save motion.
motion.save(
update_fields=["motion_block", "last_modified"], skip_autoupdate=True
)
# Fire autoupdate again to save information to OpenSlides history.
information = (
["Motion block removed"]
if motion_block is None
else ["Motion block set to {arg1}", motion_block.title]
)
inform_changed_data(
motion, information=information, user_id=request.user.pk
)
# Finish motion.
motion_result.append(motion)
# Send response.
return Response(
{
"detail": f"Motion block of {len(motion_result)} motions successfully set."
}
)
@detail_route(methods=["put"])
def set_state(self, request, pk=None):
"""