Merge pull request #1409 from normanjaeckel/DjangoRESTFramework-MoreWork
Added api for assignments.
This commit is contained in:
commit
98cb6503a5
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
143
openslides/assignment/serializers.py
Normal file
143
openslides/assignment/serializers.py
Normal 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')
|
@ -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'
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user