diff --git a/client/src/app/core/repositories/assignments/assignment-poll-repository.service.ts b/client/src/app/core/repositories/assignments/assignment-poll-repository.service.ts index f23f591eb..78257cb08 100644 --- a/client/src/app/core/repositories/assignments/assignment-poll-repository.service.ts +++ b/client/src/app/core/repositories/assignments/assignment-poll-repository.service.ts @@ -5,12 +5,61 @@ import { TranslateService } from '@ngx-translate/core'; import { DataSendService } from 'app/core/core-services/data-send.service'; import { RelationManagerService } from 'app/core/core-services/relation-manager.service'; import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; +import { RelationDefinition } from 'app/core/definitions/relations'; +import { AssignmentOption } from 'app/shared/models/assignments/assignment-option'; import { AssignmentPoll } from 'app/shared/models/assignments/assignment-poll'; +import { ViewAssignmentOption } from 'app/site/assignments/models/view-assignment-option'; import { AssignmentPollTitleInformation, ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll'; -import { BaseRepository } from '../base-repository'; +import { ViewAssignmentVote } from 'app/site/assignments/models/view-assignment-vote'; +import { ViewGroup } from 'app/site/users/models/view-group'; +import { ViewUser } from 'app/site/users/models/view-user'; +import { BaseRepository, NestedModelDescriptors } from '../base-repository'; import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service'; import { DataStoreService } from '../../core-services/data-store.service'; +const AssignmentPollRelations: RelationDefinition[] = [ + { + type: 'M2M', + ownIdKey: 'groups_id', + ownKey: 'groups', + foreignViewModel: ViewGroup + }, + { + type: 'M2M', + ownIdKey: 'voted_id', + ownKey: 'voted', + foreignViewModel: ViewUser + } +]; + +const AssignmentPollNestedModelDescriptors: NestedModelDescriptors = { + 'assignments/assignment-poll': [ + { + ownKey: 'options', + foreignViewModel: ViewAssignmentOption, + foreignModel: AssignmentOption, + order: 'weight', + relationDefinitionsByKey: { + user: { + type: 'M2O', + ownIdKey: 'user_id', + ownKey: 'user', + foreignViewModel: ViewUser + }, + votes: { + type: 'O2M', + foreignIdKey: 'option_id', + ownKey: 'votes', + foreignViewModel: ViewAssignmentVote + } + }, + titles: { + getTitle: (viewOption: ViewAssignmentOption) => (viewOption.user ? viewOption.user.getTitle() : '') + } + } + ] +}; + /** * Repository Service for Assignments. * @@ -49,8 +98,9 @@ export class AssignmentPollRepositoryService extends BaseRepository< viewModelStoreService, translate, relationManager, - AssignmentPoll - // TODO: relations + AssignmentPoll, + AssignmentPollRelations, + AssignmentPollNestedModelDescriptors ); } diff --git a/client/src/app/core/repositories/assignments/assignment-repository.service.ts b/client/src/app/core/repositories/assignments/assignment-repository.service.ts index fde6641a5..ecd6775fa 100644 --- a/client/src/app/core/repositories/assignments/assignment-repository.service.ts +++ b/client/src/app/core/repositories/assignments/assignment-repository.service.ts @@ -8,11 +8,8 @@ import { RelationManagerService } from 'app/core/core-services/relation-manager. import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; import { RelationDefinition } from 'app/core/definitions/relations'; import { Assignment } from 'app/shared/models/assignments/assignment'; -import { AssignmentOption } from 'app/shared/models/assignments/assignment-option'; -import { AssignmentPoll } from 'app/shared/models/assignments/assignment-poll'; import { AssignmentRelatedUser } from 'app/shared/models/assignments/assignment-related-user'; import { AssignmentTitleInformation, ViewAssignment } from 'app/site/assignments/models/view-assignment'; -import { ViewAssignmentOption } from 'app/site/assignments/models/view-assignment-option'; import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll'; import { ViewAssignmentRelatedUser } from 'app/site/assignments/models/view-assignment-related-user'; import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile'; @@ -35,6 +32,12 @@ const AssignmentRelations: RelationDefinition[] = [ ownIdKey: 'attachments_id', ownKey: 'attachments', foreignViewModel: ViewMediafile + }, + { + type: 'O2M', + ownKey: 'polls', + foreignIdKey: 'assignment_id', + foreignViewModel: ViewAssignmentPoll } ]; @@ -57,28 +60,6 @@ const AssignmentNestedModelDescriptors: NestedModelDescriptors = { getTitle: (viewAssignmentRelatedUser: ViewAssignmentRelatedUser) => viewAssignmentRelatedUser.user ? viewAssignmentRelatedUser.user.getFullName() : '' } - }, - { - ownKey: 'polls', - foreignViewModel: ViewAssignmentPoll, - foreignModel: AssignmentPoll, - relationDefinitionsByKey: {} - } - ], - 'assignments/assignment-poll': [ - { - ownKey: 'options', - foreignViewModel: ViewAssignmentOption, - foreignModel: AssignmentOption, - order: 'weight', - relationDefinitionsByKey: { - user: { - type: 'M2O', - ownIdKey: 'candidate_id', - ownKey: 'user', - foreignViewModel: ViewUser - } - } } ] }; diff --git a/client/src/app/core/repositories/assignments/assignment-vote-repository.service.ts b/client/src/app/core/repositories/assignments/assignment-vote-repository.service.ts index 555d258f3..3e16559f3 100644 --- a/client/src/app/core/repositories/assignments/assignment-vote-repository.service.ts +++ b/client/src/app/core/repositories/assignments/assignment-vote-repository.service.ts @@ -5,12 +5,23 @@ import { TranslateService } from '@ngx-translate/core'; import { DataSendService } from 'app/core/core-services/data-send.service'; import { RelationManagerService } from 'app/core/core-services/relation-manager.service'; import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; +import { RelationDefinition } from 'app/core/definitions/relations'; import { AssignmentVote } from 'app/shared/models/assignments/assignment-vote'; import { ViewAssignmentVote } from 'app/site/assignments/models/view-assignment-vote'; +import { ViewUser } from 'app/site/users/models/view-user'; import { BaseRepository } from '../base-repository'; import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service'; import { DataStoreService } from '../../core-services/data-store.service'; +const AssignmentVoteRelations: RelationDefinition[] = [ + { + type: 'M2O', + ownIdKey: 'user_id', + ownKey: 'user', + foreignViewModel: ViewUser + } +]; + /** * Repository Service for Assignments. * @@ -43,8 +54,8 @@ export class AssignmentVoteRepositoryService extends BaseRepository '' + } + } + ] +}; + /** * Repository Service for Assignments. * @@ -39,8 +81,9 @@ export class MotionPollRepositoryService extends BaseRepository< viewModelStoreService, translate, relationManager, - MotionPoll - // TODO: relations + MotionPoll, + MotionPollRelations, + MotionPollNestedModelDescriptors ); } diff --git a/client/src/app/core/repositories/motions/motion-repository.service.ts b/client/src/app/core/repositories/motions/motion-repository.service.ts index 3626cd7a0..b9f4af858 100644 --- a/client/src/app/core/repositories/motions/motion-repository.service.ts +++ b/client/src/app/core/repositories/motions/motion-repository.service.ts @@ -23,6 +23,7 @@ import { MotionTitleInformation, ViewMotion } from 'app/site/motions/models/view import { ViewMotionAmendedParagraph } from 'app/site/motions/models/view-motion-amended-paragraph'; import { ViewMotionBlock } from 'app/site/motions/models/view-motion-block'; import { ViewMotionChangeRecommendation } from 'app/site/motions/models/view-motion-change-recommendation'; +import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll'; import { ViewState } from 'app/site/motions/models/view-state'; import { ViewStatuteParagraph } from 'app/site/motions/models/view-statute-paragraph'; import { ViewSubmitter } from 'app/site/motions/models/view-submitter'; @@ -125,12 +126,17 @@ const MotionRelations: RelationDefinition[] = [ ownKey: 'amendments', foreignViewModel: ViewMotion }, - // TMP: { type: 'M2O', ownIdKey: 'parent_id', ownKey: 'parent', foreignViewModel: ViewMotion + }, + { + type: 'O2M', + foreignIdKey: 'motion_id', + ownKey: 'polls', + foreignViewModel: ViewMotionPoll } // Personal notes are dynamically added in the repo. ]; diff --git a/client/src/app/core/repositories/motions/motion-vote-repository.service.ts b/client/src/app/core/repositories/motions/motion-vote-repository.service.ts index ff95c237f..801659fa0 100644 --- a/client/src/app/core/repositories/motions/motion-vote-repository.service.ts +++ b/client/src/app/core/repositories/motions/motion-vote-repository.service.ts @@ -5,12 +5,23 @@ import { TranslateService } from '@ngx-translate/core'; import { DataSendService } from 'app/core/core-services/data-send.service'; import { RelationManagerService } from 'app/core/core-services/relation-manager.service'; import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; +import { RelationDefinition } from 'app/core/definitions/relations'; import { MotionVote } from 'app/shared/models/motions/motion-vote'; import { ViewMotionVote } from 'app/site/motions/models/view-motion-vote'; +import { ViewUser } from 'app/site/users/models/view-user'; import { BaseRepository } from '../base-repository'; import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service'; import { DataStoreService } from '../../core-services/data-store.service'; +const MotionVoteRelations: RelationDefinition[] = [ + { + type: 'M2O', + ownIdKey: 'user_id', + ownKey: 'user', + foreignViewModel: ViewUser + } +]; + /** * Repository Service for Assignments. * @@ -43,8 +54,8 @@ export class MotionVoteRepositoryService extends BaseRepository { public static COLLECTIONSTRING = 'assignments/assignment-option'; public user_id: number; + public weight: number; public constructor(input?: any) { super(AssignmentOption.COLLECTIONSTRING, input); diff --git a/client/src/app/shared/models/assignments/assignment.ts b/client/src/app/shared/models/assignments/assignment.ts index d05a90f4a..7e763f88a 100644 --- a/client/src/app/shared/models/assignments/assignment.ts +++ b/client/src/app/shared/models/assignments/assignment.ts @@ -1,4 +1,3 @@ -import { AssignmentPoll } from './assignment-poll'; import { AssignmentRelatedUser } from './assignment-related-user'; import { BaseModelWithAgendaItemAndListOfSpeakers } from '../base/base-model-with-agenda-item-and-list-of-speakers'; @@ -22,18 +21,9 @@ export class Assignment extends BaseModelWithAgendaItemAndListOfSpeakers { - return a.weight - b.weight; - }) - .map((candidate: AssignmentRelatedUser) => candidate.user_id); - } } export interface Assignment extends AssignmentWithoutNestedModels {} diff --git a/client/src/app/shared/models/base/base-decimal-model.ts b/client/src/app/shared/models/base/base-decimal-model.ts index f7bedde9b..1f40f5cb9 100644 --- a/client/src/app/shared/models/base/base-decimal-model.ts +++ b/client/src/app/shared/models/base/base-decimal-model.ts @@ -1,11 +1,11 @@ import { BaseModel } from './base-model'; export abstract class BaseDecimalModel extends BaseModel { - protected abstract decimalFields: (keyof this)[]; + protected abstract getDecimalFields(): (keyof this)[]; public deserialize(input: any): void { if (input && typeof input === 'object') { - this.decimalFields.forEach(field => (input[field] = parseInt(input[field], 10))); + this.getDecimalFields().forEach(field => (input[field] = parseInt(input[field], 10))); } super.deserialize(input); } diff --git a/client/src/app/shared/models/motions/motion.ts b/client/src/app/shared/models/motions/motion.ts index 6aa35620e..2d3a7c9f2 100644 --- a/client/src/app/shared/models/motions/motion.ts +++ b/client/src/app/shared/models/motions/motion.ts @@ -1,5 +1,4 @@ import { BaseModelWithAgendaItemAndListOfSpeakers } from '../base/base-model-with-agenda-item-and-list-of-speakers'; -import { MotionPoll } from './motion-poll'; import { Submitter } from './submitter'; export interface MotionComment { @@ -33,7 +32,6 @@ export interface MotionWithoutNestedModels extends BaseModelWithAgendaItemAndLis recommendation_extension: string; tags_id: number[]; attachments_id: number[]; - polls: MotionPoll[]; weight: number; sort_parent_id: number; created: string; diff --git a/client/src/app/shared/models/poll/base-option.ts b/client/src/app/shared/models/poll/base-option.ts index cc0df616a..53da72281 100644 --- a/client/src/app/shared/models/poll/base-option.ts +++ b/client/src/app/shared/models/poll/base-option.ts @@ -5,7 +5,8 @@ export abstract class BaseOption extends BaseDecimalModel { public yes: number; public no: number; public abstain: number; - public votes_id: number[]; - protected decimalFields: (keyof BaseOption)[] = ['yes', 'no', 'abstain']; + protected getDecimalFields(): (keyof BaseOption)[] { + return ['yes', 'no', 'abstain']; + } } diff --git a/client/src/app/shared/models/poll/base-poll.ts b/client/src/app/shared/models/poll/base-poll.ts index e8c4a1f5b..1fc1fe6fe 100644 --- a/client/src/app/shared/models/poll/base-poll.ts +++ b/client/src/app/shared/models/poll/base-poll.ts @@ -28,6 +28,8 @@ export interface BasePollWithoutNestedModels { export abstract class BasePoll> extends BaseDecimalModel { public options: O[]; - protected decimalFields: (keyof BasePoll)[] = ['votesvalid', 'votesinvalid', 'votescast']; + protected getDecimalFields(): (keyof BasePoll)[] { + return ['votesvalid', 'votesinvalid', 'votescast']; + } } export interface BasePoll> extends BasePollWithoutNestedModels {} diff --git a/client/src/app/shared/models/poll/base-vote.ts b/client/src/app/shared/models/poll/base-vote.ts index 0a3f36dfe..849b7d0a5 100644 --- a/client/src/app/shared/models/poll/base-vote.ts +++ b/client/src/app/shared/models/poll/base-vote.ts @@ -3,7 +3,10 @@ import { BaseDecimalModel } from '../base/base-decimal-model'; export abstract class BaseVote extends BaseDecimalModel { public weight: number; public value: 'Y' | 'N' | 'A'; + public option_id: number; public user_id?: number; - protected decimalFields: (keyof BaseVote)[] = ['weight']; + protected getDecimalFields(): (keyof BaseVote)[] { + return ['weight']; + } } diff --git a/client/src/app/site/assignments/models/view-assignment-option.ts b/client/src/app/site/assignments/models/view-assignment-option.ts index a1324ee4f..d364cbfbe 100644 --- a/client/src/app/site/assignments/models/view-assignment-option.ts +++ b/client/src/app/site/assignments/models/view-assignment-option.ts @@ -1,5 +1,7 @@ import { AssignmentOption } from 'app/shared/models/assignments/assignment-option'; +import { ViewUser } from 'app/site/users/models/view-user'; import { BaseViewModel } from '../../base/base-view-model'; +import { ViewAssignmentVote } from './view-assignment-vote'; export class ViewAssignmentOption extends BaseViewModel { public get option(): AssignmentOption { @@ -9,4 +11,9 @@ export class ViewAssignmentOption extends BaseViewModel { protected _collectionString = AssignmentOption.COLLECTIONSTRING; } -export interface ViewAssignmentOption extends AssignmentOption {} +interface TIMotionOptionRelations { + votes: ViewAssignmentVote[]; + user: ViewUser; +} + +export interface ViewAssignmentOption extends AssignmentOption, TIMotionOptionRelations {} diff --git a/client/src/app/site/assignments/models/view-assignment.ts b/client/src/app/site/assignments/models/view-assignment.ts index abe7291b2..75aaaddcf 100644 --- a/client/src/app/site/assignments/models/view-assignment.ts +++ b/client/src/app/site/assignments/models/view-assignment.ts @@ -104,7 +104,7 @@ export class ViewAssignment extends BaseViewModelWithAgendaItemAndListOfSpeakers } interface IAssignmentRelations { assignment_related_users: ViewAssignmentRelatedUser[]; - polls?: ViewAssignmentPoll[]; + polls: ViewAssignmentPoll[]; tags?: ViewTag[]; attachments?: ViewMediafile[]; } diff --git a/client/src/app/site/motions/models/view-motion-option.ts b/client/src/app/site/motions/models/view-motion-option.ts index 18517d950..f5e1f7165 100644 --- a/client/src/app/site/motions/models/view-motion-option.ts +++ b/client/src/app/site/motions/models/view-motion-option.ts @@ -1,5 +1,6 @@ import { MotionOption } from 'app/shared/models/motions/motion-option'; import { BaseViewModel } from '../../base/base-view-model'; +import { ViewMotionVote } from './view-motion-vote'; export class ViewMotionOption extends BaseViewModel { public get option(): MotionOption { @@ -9,4 +10,8 @@ export class ViewMotionOption extends BaseViewModel { protected _collectionString = MotionOption.COLLECTIONSTRING; } -export interface ViewMotionPoll extends MotionOption {} +interface TIMotionOptionRelations { + votes: ViewMotionVote[]; +} + +export interface ViewMotionOption extends MotionOption, TIMotionOptionRelations {} diff --git a/client/src/app/site/motions/models/view-motion.ts b/client/src/app/site/motions/models/view-motion.ts index fc66c3f20..4e71068ad 100644 --- a/client/src/app/site/motions/models/view-motion.ts +++ b/client/src/app/site/motions/models/view-motion.ts @@ -16,6 +16,7 @@ import { ViewCategory } from './view-category'; import { ViewMotionBlock } from './view-motion-block'; import { ViewMotionChangeRecommendation } from './view-motion-change-recommendation'; import { ViewMotionCommentSection } from './view-motion-comment-section'; +import { ViewMotionPoll } from './view-motion-poll'; import { ViewState } from './view-state'; import { ViewSubmitter } from './view-submitter'; import { ViewWorkflow } from './view-workflow'; @@ -359,6 +360,7 @@ interface TIMotionRelations { amendments?: ViewMotion[]; changeRecommendations?: ViewMotionChangeRecommendation[]; diffLines?: DiffLinesInParagraph[]; + polls: ViewMotionPoll[]; } export interface ViewMotion extends MotionWithoutNestedModels, TIMotionRelations {} diff --git a/client/src/app/site/motions/motions.config.ts b/client/src/app/site/motions/motions.config.ts index 554aaf19d..89cde7823 100644 --- a/client/src/app/site/motions/motions.config.ts +++ b/client/src/app/site/motions/motions.config.ts @@ -3,10 +3,14 @@ import { CategoryRepositoryService } from 'app/core/repositories/motions/categor import { ChangeRecommendationRepositoryService } from 'app/core/repositories/motions/change-recommendation-repository.service'; import { MotionBlockRepositoryService } from 'app/core/repositories/motions/motion-block-repository.service'; import { MotionCommentSectionRepositoryService } from 'app/core/repositories/motions/motion-comment-section-repository.service'; +import { MotionPollRepositoryService } from 'app/core/repositories/motions/motion-poll-repository.service'; import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service'; +import { MotionVoteRepositoryService } from 'app/core/repositories/motions/motion-vote-repository.service'; import { StateRepositoryService } from 'app/core/repositories/motions/state-repository.service'; import { StatuteParagraphRepositoryService } from 'app/core/repositories/motions/statute-paragraph-repository.service'; import { WorkflowRepositoryService } from 'app/core/repositories/motions/workflow-repository.service'; +import { MotionPoll } from 'app/shared/models/motions/motion-poll'; +import { MotionVote } from 'app/shared/models/motions/motion-vote'; import { State } from 'app/shared/models/motions/state'; import { Category } from '../../shared/models/motions/category'; import { Motion } from '../../shared/models/motions/motion'; @@ -19,6 +23,8 @@ import { ViewMotion } from './models/view-motion'; import { ViewMotionBlock } from './models/view-motion-block'; import { ViewMotionChangeRecommendation } from './models/view-motion-change-recommendation'; import { ViewMotionCommentSection } from './models/view-motion-comment-section'; +import { ViewMotionPoll } from './models/view-motion-poll'; +import { ViewMotionVote } from './models/view-motion-vote'; import { ViewState } from './models/view-state'; import { ViewStatuteParagraph } from './models/view-statute-paragraph'; import { ViewWorkflow } from './models/view-workflow'; @@ -70,7 +76,9 @@ export const MotionsAppConfig: AppConfig = { viewModel: ViewStatuteParagraph, searchOrder: 9, repository: StatuteParagraphRepositoryService - } + }, + { model: MotionPoll, viewModel: ViewMotionPoll, repository: MotionPollRepositoryService }, + { model: MotionVote, viewModel: ViewMotionVote, repository: MotionVoteRepositoryService } ], mainMenuEntries: [ { diff --git a/client/src/app/site/motions/services/motion-pdf.service.ts b/client/src/app/site/motions/services/motion-pdf.service.ts index 6cda57f65..cbea9c8e5 100644 --- a/client/src/app/site/motions/services/motion-pdf.service.ts +++ b/client/src/app/site/motions/services/motion-pdf.service.ts @@ -358,11 +358,11 @@ export class MotionPdfService { } // voting results - if (motion.motion.polls.length && (!infoToExport || infoToExport.includes('polls'))) { + if (motion.polls.length && (!infoToExport || infoToExport.includes('polls'))) { const column1 = []; const column2 = []; const column3 = []; - motion.motion.polls.map((poll, index) => { + motion.polls.map((poll, index) => { /*if (poll.has_votes) { if (motion.motion.polls.length > 1) { column1.push(index + 1 + '. ' + this.translate.instant('Vote')); diff --git a/client/src/app/site/motions/services/motion-poll-pdf.service.ts b/client/src/app/site/motions/services/motion-poll-pdf.service.ts index a67998eda..168ed88f4 100644 --- a/client/src/app/site/motions/services/motion-poll-pdf.service.ts +++ b/client/src/app/site/motions/services/motion-poll-pdf.service.ts @@ -71,8 +71,8 @@ export class MotionPollPdfService extends PollPdfService { )}`; if (!title) { title = `${this.translate.instant('Motion')} - ${motion.identifier}`; - if (motion.motion.polls.length > 1) { - title += ` (${this.translate.instant('Vote')} ${motion.motion.polls.length})`; + if (motion.polls.length > 1) { + title += ` (${this.translate.instant('Vote')} ${motion.polls.length})`; } } if (!subtitle) { diff --git a/openslides/assignments/access_permissions.py b/openslides/assignments/access_permissions.py index 2afba46f1..3fb5ed90a 100644 --- a/openslides/assignments/access_permissions.py +++ b/openslides/assignments/access_permissions.py @@ -51,3 +51,12 @@ class AssignmentPollAccessPermissions(BaseAccessPermissions): self, full_data: List[Dict[str, Any]], user_id: int ) -> List[Dict[str, Any]]: return full_data + + +class AssignmentVoteAccessPermissions(BaseAccessPermissions): + base_permission = "assignments.can_see" + + async def get_restricted_data( + self, full_data: List[Dict[str, Any]], user_id: int + ) -> List[Dict[str, Any]]: + return full_data diff --git a/openslides/assignments/models.py b/openslides/assignments/models.py index 1446c07c8..8211abc32 100644 --- a/openslides/assignments/models.py +++ b/openslides/assignments/models.py @@ -19,6 +19,7 @@ from ..utils.models import CASCADE_AND_AUTOUPDATE, SET_NULL_AND_AUTOUPDATE from .access_permissions import ( AssignmentAccessPermissions, AssignmentPollAccessPermissions, + AssignmentVoteAccessPermissions, ) @@ -268,6 +269,7 @@ class Assignment(RESTModelMixin, AgendaItemWithListOfSpeakersMixin, models.Model class AssignmentVote(RESTModelMixin, BaseVote): + access_permissions = AssignmentVoteAccessPermissions() option = models.ForeignKey( "AssignmentOption", on_delete=models.CASCADE, related_name="votes" ) diff --git a/openslides/assignments/serializers.py b/openslides/assignments/serializers.py index b9f7a8ebb..9ef737cb8 100644 --- a/openslides/assignments/serializers.py +++ b/openslides/assignments/serializers.py @@ -74,12 +74,10 @@ class AssignmentOptionSerializer(ModelSerializer): max_digits=15, decimal_places=6, min_value=-2, read_only=True ) - votes = IdPrimaryKeyRelatedField(many=True, read_only=True) - class Meta: model = AssignmentOption - fields = ("user",) + BASE_OPTION_FIELDS - read_only_fields = ("user",) + BASE_OPTION_FIELDS + fields = ("user", "weight") + BASE_OPTION_FIELDS + read_only_fields = ("user", "weight") + BASE_OPTION_FIELDS class AssignmentPollSerializer(ModelSerializer): @@ -133,7 +131,6 @@ class AssignmentSerializer(ModelSerializer): assignment_related_users = AssignmentRelatedUserSerializer( many=True, read_only=True ) - polls = IdPrimaryKeyRelatedField(many=True, read_only=True) agenda_create = BooleanField(write_only=True, required=False, allow_null=True) agenda_type = IntegerField( write_only=True, required=False, min_value=1, max_value=3, allow_null=True @@ -150,7 +147,6 @@ class AssignmentSerializer(ModelSerializer): "phase", "assignment_related_users", "poll_description_default", - "polls", "agenda_item_id", "list_of_speakers_id", "agenda_create", diff --git a/openslides/assignments/views.py b/openslides/assignments/views.py index 656294e1e..51069d693 100644 --- a/openslides/assignments/views.py +++ b/openslides/assignments/views.py @@ -246,22 +246,6 @@ class AssignmentViewSet(ModelViewSet): message = "User {0} was successfully unelected." return Response({"detail": message, "args": [str(user)]}) - @detail_route(methods=["post"]) - def create_poll(self, request, pk=None): - """ - View to create a poll. It is a POST request without any data. - """ - assignment = self.get_object() - if not assignment.candidates.exists(): - raise ValidationError( - {"detail": "Can not create ballot because there are no candidates."} - ) - with transaction.atomic(): - poll = assignment.create_poll() - return Response( - {"detail": "Ballot created successfully.", "createdPollId": poll.pk} - ) - @detail_route(methods=["post"]) def sort_related_users(self, request, pk=None): """ diff --git a/openslides/motions/serializers.py b/openslides/motions/serializers.py index 24a48f00c..dfcee8b25 100644 --- a/openslides/motions/serializers.py +++ b/openslides/motions/serializers.py @@ -243,8 +243,6 @@ class MotionOptionSerializer(ModelSerializer): max_digits=15, decimal_places=6, min_value=-2, read_only=True ) - votes = IdPrimaryKeyRelatedField(many=True, read_only=True) - class Meta: model = MotionOption fields = BASE_OPTION_FIELDS @@ -371,7 +369,6 @@ class MotionSerializer(ModelSerializer): """ comments = MotionCommentSerializer(many=True, read_only=True) - polls = IdPrimaryKeyRelatedField(many=True, read_only=True) modified_final_version = CharField(allow_blank=True, required=False) reason = CharField(allow_blank=True, required=False) state_restriction = SerializerMethodField() @@ -419,7 +416,6 @@ class MotionSerializer(ModelSerializer): "recommendation_extension", "tags", "attachments", - "polls", "agenda_item_id", "list_of_speakers_id", "agenda_create", diff --git a/openslides/poll/serializers.py b/openslides/poll/serializers.py index ebf5409f8..9bc312623 100644 --- a/openslides/poll/serializers.py +++ b/openslides/poll/serializers.py @@ -11,6 +11,6 @@ BASE_POLL_FIELDS = ( "id", ) -BASE_OPTION_FIELDS = ("id", "yes", "no", "abstain", "votes") +BASE_OPTION_FIELDS = ("id", "yes", "no", "abstain") -BASE_VOTE_FIELDS = ("id", "weight", "value", "user") +BASE_VOTE_FIELDS = ("id", "weight", "value", "user", "option") diff --git a/tests/integration/motions/test_motions.py b/tests/integration/motions/test_motions.py index dcb3d31c3..026353ae9 100644 --- a/tests/integration/motions/test_motions.py +++ b/tests/integration/motions/test_motions.py @@ -419,7 +419,7 @@ class RetrieveMotion(TestCase): self.assertEqual(response.status_code, status.HTTP_200_OK) def test_user_without_can_see_user_permission_to_see_motion_and_submitter_data( - self + self, ): admin = get_user_model().objects.get(username="admin") Submitter.objects.add(admin, self.motion) diff --git a/tests/integration/motions/test_polls.py b/tests/integration/motions/test_polls.py index afd96cb14..1aa528090 100644 --- a/tests/integration/motions/test_polls.py +++ b/tests/integration/motions/test_polls.py @@ -582,7 +582,6 @@ class VoteMotionPollNamedAutoupdates(TestCase): "yes": "0.000000", "no": "0.000000", "abstain": "1.000000", - "votes_id": [vote.id], } ], "voted_id": [self.user1.id], @@ -594,6 +593,7 @@ class VoteMotionPollNamedAutoupdates(TestCase): "weight": "1.000000", "value": "A", "user_id": self.user1.id, + "option_id": 1, }, }, ) @@ -605,6 +605,7 @@ class VoteMotionPollNamedAutoupdates(TestCase): autoupdate[0]["motions/motion-vote:1"], { "pollstate": 2, + "option_id": 1, "id": 1, "weight": "1.000000", "value": "A", @@ -626,7 +627,7 @@ class VoteMotionPollNamedAutoupdates(TestCase): "type": "named", "title": "test_title_tho8PhiePh8upaex6phi", "groups_id": [GROUP_DELEGATE_PK], - "options": [{"id": 1, "votes_id": [vote.id]}], + "options": [{"id": 1}], "id": 1, }, ) @@ -653,12 +654,12 @@ class VoteMotionPollPseudoanonymousAutoupdates(TestCase): self.other_user, _ = self.create_user() inform_changed_data(self.other_user) - self.user, user1_password = self.create_user() + self.user, user_password = self.create_user() self.user.groups.add(self.delegate_group) self.user.is_present = True self.user.save() self.user_client = APIClient() - self.user_client.login(username=self.user.username, password=user1_password) + self.user_client.login(username=self.user.username, password=user_password) self.poll = MotionPoll.objects.create( motion=self.motion, @@ -699,7 +700,6 @@ class VoteMotionPollPseudoanonymousAutoupdates(TestCase): "yes": "0.000000", "no": "0.000000", "abstain": "1.000000", - "votes_id": [vote.id], } ], "voted_id": [self.user.id], @@ -707,6 +707,7 @@ class VoteMotionPollPseudoanonymousAutoupdates(TestCase): }, "motions/motion-vote:1": { "pollstate": 2, + "option_id": 1, "id": 1, "weight": "1.000000", "value": "A", @@ -730,7 +731,7 @@ class VoteMotionPollPseudoanonymousAutoupdates(TestCase): "type": "pseudoanonymous", "title": "test_title_cahP1umooteehah2jeey", "groups_id": [GROUP_DELEGATE_PK], - "options": [{"id": 1, "votes_id": [vote.id]}], + "options": [{"id": 1}], "id": 1, }, ) @@ -953,7 +954,6 @@ class PublishMotionPoll(TestCase): "yes": "0.000000", "no": "2.000000", "abstain": "0.000000", - "votes_id": [1], } ], "voted_id": [], @@ -961,6 +961,7 @@ class PublishMotionPoll(TestCase): }, "motions/motion-vote:1": { "pollstate": 4, + "option_id": 1, "id": 1, "weight": "2.000000", "value": "N",