Added api for assignments.

Also small changes in agenda REST api.
This commit is contained in:
Norman Jäckel 2015-01-17 14:25:05 +01:00
parent 413abf2b9a
commit 4821a3b59b
12 changed files with 242 additions and 16 deletions

View File

@ -29,5 +29,5 @@ class AgendaAppConfig(AppConfig):
Item = self.get_model('Item')
register_slide('agenda', agenda_slide, Item)
# Register viewset.
# Register viewsets.
router.register('agenda/item', ItemViewSet)

View File

@ -437,6 +437,6 @@ class Speaker(RESTModelMixin, AbsoluteUrlMixin, models.Model):
def get_root_rest_element(self):
"""
Returns the item to this instance, which is the root rest element.
Returns the item to this instance which is the root rest element.
"""
return self.item

View File

@ -1,4 +1,5 @@
from openslides.utils import rest_api
from rest_framework.reverse import reverse
from .models import Item, Speaker
@ -17,16 +18,33 @@ class SpeakerSerializer(rest_api.serializers.HyperlinkedModelSerializer):
'weight')
class RelatedItemRelatedField(rest_api.serializers.RelatedField):
"""
A custom field to use for the `content_object` generic relationship.
"""
def to_representation(self, value):
"""
Returns the url to the related object.
"""
request = self.context.get('request', None)
assert request is not None, (
"`%s` requires the request in the serializer"
" context. Add `context={'request': request}` when instantiating "
"the serializer." % self.__class__.__name__)
view_name = '%s-detail' % type(value)._meta.object_name.lower()
return reverse(view_name, kwargs={'pk': value.pk}, request=request)
class ItemSerializer(rest_api.serializers.HyperlinkedModelSerializer):
"""
Serializer for a agenda.models.Item objects.
Serializer for agenda.models.Item objects.
"""
get_title = rest_api.serializers.CharField(read_only=True)
get_title_supplement = rest_api.serializers.CharField(read_only=True)
item_no = rest_api.serializers.CharField(read_only=True)
speaker_set = SpeakerSerializer(many=True, read_only=True)
tags = rest_api.serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='tag-detail')
# content_object = serializers.PrimaryKeyRelatedField(read_only=True)
content_object = RelatedItemRelatedField(read_only=True)
class Meta:
model = Item

View File

@ -774,7 +774,7 @@ class ItemCSVImportView(CSVImportView):
class ItemViewSet(rest_api.viewsets.ModelViewSet):
"""
API endpoint to view, edit and delete agenda items.
API endpoint to retrieve, create, edit and delete agenda items.
"""
model = Item
serializer_class = ItemSerializer

View File

@ -13,9 +13,11 @@ class AssignmentAppConfig(AppConfig):
# Import all required stuff.
from openslides.config.signals import config_signal
from openslides.projector.api import register_slide_model
from openslides.utils.rest_api import router
from openslides.utils.signals import template_manipulation
from .signals import setup_assignment_config
from .template import add_assignment_stylesheets
from .views import AssignmentViewSet
# Connect signals.
config_signal.connect(setup_assignment_config, dispatch_uid='setup_assignment_config')
@ -28,3 +30,6 @@ class AssignmentAppConfig(AppConfig):
AssignmentPoll = self.get_model('AssignmentPoll')
register_slide_model(Assignment, 'assignment/slide.html')
register_slide_model(AssignmentPoll, 'assignment/assignmentpoll_slide.html')
# Register viewsets.
router.register('assignment/assignment', AssignmentViewSet)

View File

@ -14,11 +14,12 @@ from openslides.poll.models import (BaseOption, BasePoll, BaseVote,
from openslides.projector.models import SlideMixin
from openslides.utils.exceptions import OpenSlidesError
from openslides.utils.models import AbsoluteUrlMixin
from openslides.utils.rest_api import RESTModelMixin
from openslides.utils.utils import html_strong
from openslides.users.models import User
class AssignmentCandidate(models.Model):
class AssignmentCandidate(RESTModelMixin, models.Model):
"""
Many2Many table between an assignment and the candidates.
"""
@ -33,8 +34,14 @@ class AssignmentCandidate(models.Model):
def __str__(self):
return str(self.person)
def get_root_rest_element(self):
"""
Returns the assignment to this instance which is the root rest element.
"""
return self.assignment
class Assignment(SlideMixin, AbsoluteUrlMixin, models.Model):
class Assignment(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, models.Model):
slide_callback_name = 'assignment'
STATUS = (
@ -262,11 +269,17 @@ class Assignment(SlideMixin, AbsoluteUrlMixin, models.Model):
return '(%s)' % _('Assignment')
class AssignmentVote(BaseVote):
class AssignmentVote(RESTModelMixin, BaseVote):
option = models.ForeignKey('AssignmentOption')
def get_root_rest_element(self):
"""
Returns the assignment to this instance which is the root rest element.
"""
return self.option.poll.assignment
class AssignmentOption(BaseOption):
class AssignmentOption(RESTModelMixin, BaseOption):
poll = models.ForeignKey('AssignmentPoll')
candidate = models.ForeignKey(User)
vote_class = AssignmentVote
@ -274,8 +287,14 @@ class AssignmentOption(BaseOption):
def __str__(self):
return str(self.candidate)
def get_root_rest_element(self):
"""
Returns the assignment to this instance which is the root rest element.
"""
return self.poll.assignment
class AssignmentPoll(SlideMixin, CollectDefaultVotesMixin,
class AssignmentPoll(RESTModelMixin, SlideMixin, CollectDefaultVotesMixin,
PublishPollMixin, AbsoluteUrlMixin, BasePoll):
slide_callback_name = 'assignmentpoll'
@ -326,3 +345,9 @@ class AssignmentPoll(SlideMixin, CollectDefaultVotesMixin,
def get_slide_context(self, **context):
return super(AssignmentPoll, self).get_slide_context(poll=self)
def get_root_rest_element(self):
"""
Returns the assignment to this instance which is the root rest element.
"""
return self.assignment

View File

@ -0,0 +1,143 @@
from openslides.utils import rest_api
from .models import (
models,
Assignment,
AssignmentCandidate,
AssignmentOption,
AssignmentPoll,
AssignmentVote)
class AssignmentCandidateSerializer(rest_api.serializers.HyperlinkedModelSerializer):
"""
Serializer for assignment.models.AssignmentCandidate objects.
"""
class Meta:
model = AssignmentCandidate
fields = (
'id',
'person',
'elected',
'blocked')
class AssignmentVoteSerializer(rest_api.serializers.HyperlinkedModelSerializer):
"""
Serializer for assignment.models.AssignmentVote objects.
"""
class Meta:
model = AssignmentVote
fields = (
'weight',
'value')
class AssignmentOptionSerializer(rest_api.serializers.HyperlinkedModelSerializer):
"""
Serializer for assignment.models.AssignmentOption objects.
"""
assignmentvote_set = AssignmentVoteSerializer(many=True, read_only=True)
class Meta:
model = AssignmentOption
fields = (
'candidate',
'assignmentvote_set')
class FilterPollListSerializer(rest_api.serializers.ListSerializer):
"""
Customized serilizer to filter polls and exclude unpublished ones.
"""
def to_representation(self, data):
"""
List of object instances -> List of dicts of primitive datatypes.
This method is adapted to filter the data and exclude unpublished polls.
"""
# Dealing with nested relationships, data can be a Manager,
# so, first get a queryset from the Manager if needed
iterable = data.filter(published=True) if isinstance(data, models.Manager) else data
return [self.child.to_representation(item) for item in iterable]
class AssignmentAllPollSerializer(rest_api.serializers.HyperlinkedModelSerializer):
"""
Serializer for assignment.models.AssignmentPoll objects.
Serializes all polls.
"""
assignmentoption_set = AssignmentOptionSerializer(many=True, read_only=True)
class Meta:
model = AssignmentPoll
fields = (
'id',
'yesnoabstain',
'description',
'published',
'assignmentoption_set',
'votesvalid',
'votesinvalid',
'votescast')
class AssignmentShortPollSerializer(AssignmentAllPollSerializer):
"""
Serializer for assignment.models.AssignmentPoll objects.
Serializes only short polls.
"""
class Meta:
list_serializer_class = FilterPollListSerializer
model = AssignmentPoll
fields = (
'id',
'yesnoabstain',
'description',
'published',
'assignmentoption_set',
'votesvalid',
'votesinvalid',
'votescast')
class AssignmentFullSerializer(rest_api.serializers.HyperlinkedModelSerializer):
"""
Serializer for assignment.models.Assignment objects. With all polls.
"""
assignmentcandidate_set = AssignmentCandidateSerializer(many=True, read_only=True)
poll_set = AssignmentAllPollSerializer(many=True, read_only=True)
tags = rest_api.serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='tag-detail')
class Meta:
model = Assignment
fields = (
'name',
'description',
'posts',
'poll_description_default',
'status',
'assignmentcandidate_set',
'poll_set',
'tags')
class AssignmentShortSerializer(AssignmentFullSerializer):
"""
Serializer for assignment.models.Assignment objects. Without unpublished poll.
"""
poll_set = AssignmentShortPollSerializer(many=True, read_only=True)
class Meta:
model = Assignment
fields = (
'name',
'description',
'posts',
'poll_description_default',
'status',
'assignmentcandidate_set',
'poll_set',
'tags')

View File

@ -12,6 +12,7 @@ from openslides.agenda.views import CreateRelatedAgendaItemView as _CreateRelate
from openslides.config.api import config
from openslides.users.models import Group, User # TODO: remove this
from openslides.poll.views import PollFormView
from openslides.utils import rest_api
from openslides.utils.pdf import stylesheet
from openslides.utils.utils import html_strong
from openslides.utils.views import (CreateView, DeleteView, DetailView,
@ -21,6 +22,7 @@ from openslides.utils.views import (CreateView, DeleteView, DetailView,
from .forms import AssignmentForm, AssignmentRunForm
from .models import Assignment, AssignmentPoll
from .serializers import AssignmentFullSerializer, AssignmentShortSerializer
class AssignmentListView(ListView):
@ -186,6 +188,35 @@ class AssignmentRunOtherDeleteView(SingleObjectMixin, QuestionView):
self.is_blocked = self.get_object().is_blocked(self.person)
class AssignmentViewSet(rest_api.viewsets.ModelViewSet):
"""
API endpoint to retrieve, create, edit and delete assignments.
"""
model = Assignment
queryset = Assignment.objects.all()
def check_permissions(self, request):
"""
Calls self.permission_denied() if the requesting user has not the
permission to see and in case of create, update or destroy requests
the permission to manage and to see organizational items.
"""
if (not request.user.has_perm('assignment.can_see_assignment') or
(self.action in ('create', 'update', 'destroy') and not
request.user.has_perm('assignment.can_manage_assignment'))):
self.permission_denied(request)
def get_serializer_class(self):
"""
Returns different serializer classes with respect to users permissions.
"""
if self.request.user.has_perm('assignment.can_manage_assignment'):
serializer_class = AssignmentFullSerializer
else:
serializer_class = AssignmentShortSerializer
return serializer_class
class PollCreateView(SingleObjectMixin, RedirectView):
model = Assignment
required_permission = 'assignment.can_manage_assignment'

View File

@ -26,7 +26,7 @@ class CoreAppConfig(AppConfig):
CustomSlide = self.get_model('CustomSlide')
register_slide_model(CustomSlide, 'core/customslide_slide.html')
# Register viewset.
# Register viewsets.
router.register('core/customslide', CustomSlideViewSet)
router.register('core/tag', TagViewSet)

View File

@ -219,7 +219,7 @@ class CustomSlideDeleteView(CustomSlideViewMixin, utils_views.DeleteView):
class CustomSlideViewSet(rest_api.viewsets.ModelViewSet):
"""
API endpoint to view, edit and delete custom slides.
API endpoint to retrieve, create, update and delete custom slides.
"""
model = CustomSlide
queryset = CustomSlide.objects.all()
@ -314,7 +314,7 @@ class TagListView(utils_views.AjaxMixin, utils_views.ListView):
class TagViewSet(rest_api.viewsets.ModelViewSet):
"""
API endpoint to view, edit and delete tags.
API endpoint to retrieve, create, edit and delete tags.
"""
model = Tag
queryset = Tag.objects.all()

View File

@ -30,5 +30,5 @@ class UsersAppConfig(AppConfig):
# Register slides.
register_slide_model(User, 'participant/user_slide.html')
# Register viewset.
# Register viewsets.
router.register('users/user', UserViewSet)

View File

@ -5,7 +5,9 @@ from .models import User
class UserShortSerializer(rest_api.serializers.ModelSerializer):
"""
Serializer for a users.models.User objects.
Serializer for users.models.User objects.
Serializes only name fields.
"""
class Meta:
model = User
@ -19,7 +21,9 @@ class UserShortSerializer(rest_api.serializers.ModelSerializer):
class UserFullSerializer(rest_api.serializers.ModelSerializer):
"""
Serializer for a users.models.User objects.
Serializer for users.models.User objects.
Serializes all relevant fields.
"""
class Meta:
model = User