Relations in the client

This commit is contained in:
FinnStutzenstein 2019-10-29 09:00:11 +01:00
parent ced40cab74
commit ce171980e8
28 changed files with 205 additions and 98 deletions

View File

@ -5,12 +5,61 @@ import { TranslateService } from '@ngx-translate/core';
import { DataSendService } from 'app/core/core-services/data-send.service'; import { DataSendService } from 'app/core/core-services/data-send.service';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service'; import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.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 { 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 { 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 { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
import { DataStoreService } from '../../core-services/data-store.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. * Repository Service for Assignments.
* *
@ -49,8 +98,9 @@ export class AssignmentPollRepositoryService extends BaseRepository<
viewModelStoreService, viewModelStoreService,
translate, translate,
relationManager, relationManager,
AssignmentPoll AssignmentPoll,
// TODO: relations AssignmentPollRelations,
AssignmentPollNestedModelDescriptors
); );
} }

View File

@ -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 { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { RelationDefinition } from 'app/core/definitions/relations'; import { RelationDefinition } from 'app/core/definitions/relations';
import { Assignment } from 'app/shared/models/assignments/assignment'; 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 { AssignmentRelatedUser } from 'app/shared/models/assignments/assignment-related-user';
import { AssignmentTitleInformation, ViewAssignment } from 'app/site/assignments/models/view-assignment'; 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 { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll';
import { ViewAssignmentRelatedUser } from 'app/site/assignments/models/view-assignment-related-user'; import { ViewAssignmentRelatedUser } from 'app/site/assignments/models/view-assignment-related-user';
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile'; import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
@ -35,6 +32,12 @@ const AssignmentRelations: RelationDefinition[] = [
ownIdKey: 'attachments_id', ownIdKey: 'attachments_id',
ownKey: 'attachments', ownKey: 'attachments',
foreignViewModel: ViewMediafile foreignViewModel: ViewMediafile
},
{
type: 'O2M',
ownKey: 'polls',
foreignIdKey: 'assignment_id',
foreignViewModel: ViewAssignmentPoll
} }
]; ];
@ -57,28 +60,6 @@ const AssignmentNestedModelDescriptors: NestedModelDescriptors = {
getTitle: (viewAssignmentRelatedUser: ViewAssignmentRelatedUser) => getTitle: (viewAssignmentRelatedUser: ViewAssignmentRelatedUser) =>
viewAssignmentRelatedUser.user ? viewAssignmentRelatedUser.user.getFullName() : '' 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
}
}
} }
] ]
}; };

View File

@ -5,12 +5,23 @@ import { TranslateService } from '@ngx-translate/core';
import { DataSendService } from 'app/core/core-services/data-send.service'; import { DataSendService } from 'app/core/core-services/data-send.service';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service'; import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.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 { AssignmentVote } from 'app/shared/models/assignments/assignment-vote';
import { ViewAssignmentVote } from 'app/site/assignments/models/view-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 { BaseRepository } from '../base-repository';
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service'; import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
import { DataStoreService } from '../../core-services/data-store.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. * Repository Service for Assignments.
* *
@ -43,8 +54,8 @@ export class AssignmentVoteRepositoryService extends BaseRepository<ViewAssignme
viewModelStoreService, viewModelStoreService,
translate, translate,
relationManager, relationManager,
AssignmentVote AssignmentVote,
// TODO: relations AssignmentVoteRelations
); );
} }

View File

@ -5,12 +5,54 @@ import { TranslateService } from '@ngx-translate/core';
import { DataSendService } from 'app/core/core-services/data-send.service'; import { DataSendService } from 'app/core/core-services/data-send.service';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service'; import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { RelationDefinition } from 'app/core/definitions/relations';
import { MotionOption } from 'app/shared/models/motions/motion-option';
import { MotionPoll } from 'app/shared/models/motions/motion-poll'; import { MotionPoll } from 'app/shared/models/motions/motion-poll';
import { ViewMotionOption } from 'app/site/motions/models/view-motion-option';
import { MotionPollTitleInformation, ViewMotionPoll } from 'app/site/motions/models/view-motion-poll'; import { MotionPollTitleInformation, ViewMotionPoll } from 'app/site/motions/models/view-motion-poll';
import { BaseRepository } from '../base-repository'; import { ViewMotionVote } from 'app/site/motions/models/view-motion-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 { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
import { DataStoreService } from '../../core-services/data-store.service'; import { DataStoreService } from '../../core-services/data-store.service';
const MotionPollRelations: RelationDefinition[] = [
{
type: 'M2M',
ownIdKey: 'groups_id',
ownKey: 'groups',
foreignViewModel: ViewGroup
},
{
type: 'M2M',
ownIdKey: 'voted_id',
ownKey: 'voted',
foreignViewModel: ViewUser
}
];
const MotionPollNestedModelDescriptors: NestedModelDescriptors = {
'motions/motion-poll': [
{
ownKey: 'options',
foreignViewModel: ViewMotionOption,
foreignModel: MotionOption,
relationDefinitionsByKey: {
votes: {
type: 'O2M',
foreignIdKey: 'option_id',
ownKey: 'votes',
foreignViewModel: ViewMotionVote
}
},
titles: {
getTitle: (viewOption: ViewMotionOption) => ''
}
}
]
};
/** /**
* Repository Service for Assignments. * Repository Service for Assignments.
* *
@ -39,8 +81,9 @@ export class MotionPollRepositoryService extends BaseRepository<
viewModelStoreService, viewModelStoreService,
translate, translate,
relationManager, relationManager,
MotionPoll MotionPoll,
// TODO: relations MotionPollRelations,
MotionPollNestedModelDescriptors
); );
} }

View File

@ -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 { ViewMotionAmendedParagraph } from 'app/site/motions/models/view-motion-amended-paragraph';
import { ViewMotionBlock } from 'app/site/motions/models/view-motion-block'; import { ViewMotionBlock } from 'app/site/motions/models/view-motion-block';
import { ViewMotionChangeRecommendation } from 'app/site/motions/models/view-motion-change-recommendation'; 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 { ViewState } from 'app/site/motions/models/view-state';
import { ViewStatuteParagraph } from 'app/site/motions/models/view-statute-paragraph'; import { ViewStatuteParagraph } from 'app/site/motions/models/view-statute-paragraph';
import { ViewSubmitter } from 'app/site/motions/models/view-submitter'; import { ViewSubmitter } from 'app/site/motions/models/view-submitter';
@ -125,12 +126,17 @@ const MotionRelations: RelationDefinition[] = [
ownKey: 'amendments', ownKey: 'amendments',
foreignViewModel: ViewMotion foreignViewModel: ViewMotion
}, },
// TMP:
{ {
type: 'M2O', type: 'M2O',
ownIdKey: 'parent_id', ownIdKey: 'parent_id',
ownKey: 'parent', ownKey: 'parent',
foreignViewModel: ViewMotion foreignViewModel: ViewMotion
},
{
type: 'O2M',
foreignIdKey: 'motion_id',
ownKey: 'polls',
foreignViewModel: ViewMotionPoll
} }
// Personal notes are dynamically added in the repo. // Personal notes are dynamically added in the repo.
]; ];

View File

@ -5,12 +5,23 @@ import { TranslateService } from '@ngx-translate/core';
import { DataSendService } from 'app/core/core-services/data-send.service'; import { DataSendService } from 'app/core/core-services/data-send.service';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service'; import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.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 { MotionVote } from 'app/shared/models/motions/motion-vote';
import { ViewMotionVote } from 'app/site/motions/models/view-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 { BaseRepository } from '../base-repository';
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service'; import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
import { DataStoreService } from '../../core-services/data-store.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. * Repository Service for Assignments.
* *
@ -43,8 +54,8 @@ export class MotionVoteRepositoryService extends BaseRepository<ViewMotionVote,
viewModelStoreService, viewModelStoreService,
translate, translate,
relationManager, relationManager,
MotionVote MotionVote,
// TODO: relations MotionVoteRelations
); );
} }

View File

@ -4,6 +4,7 @@ export class AssignmentOption extends BaseOption<AssignmentOption> {
public static COLLECTIONSTRING = 'assignments/assignment-option'; public static COLLECTIONSTRING = 'assignments/assignment-option';
public user_id: number; public user_id: number;
public weight: number;
public constructor(input?: any) { public constructor(input?: any) {
super(AssignmentOption.COLLECTIONSTRING, input); super(AssignmentOption.COLLECTIONSTRING, input);

View File

@ -1,4 +1,3 @@
import { AssignmentPoll } from './assignment-poll';
import { AssignmentRelatedUser } from './assignment-related-user'; import { AssignmentRelatedUser } from './assignment-related-user';
import { BaseModelWithAgendaItemAndListOfSpeakers } from '../base/base-model-with-agenda-item-and-list-of-speakers'; import { BaseModelWithAgendaItemAndListOfSpeakers } from '../base/base-model-with-agenda-item-and-list-of-speakers';
@ -22,18 +21,9 @@ export class Assignment extends BaseModelWithAgendaItemAndListOfSpeakers<Assignm
public id: number; public id: number;
public assignment_related_users: AssignmentRelatedUser[]; public assignment_related_users: AssignmentRelatedUser[];
public polls: AssignmentPoll[];
public constructor(input?: any) { public constructor(input?: any) {
super(Assignment.COLLECTIONSTRING, input); super(Assignment.COLLECTIONSTRING, input);
} }
public get candidates_id(): number[] {
return this.assignment_related_users
.sort((a: AssignmentRelatedUser, b: AssignmentRelatedUser) => {
return a.weight - b.weight;
})
.map((candidate: AssignmentRelatedUser) => candidate.user_id);
}
} }
export interface Assignment extends AssignmentWithoutNestedModels {} export interface Assignment extends AssignmentWithoutNestedModels {}

View File

@ -1,11 +1,11 @@
import { BaseModel } from './base-model'; import { BaseModel } from './base-model';
export abstract class BaseDecimalModel<T = any> extends BaseModel<T> { export abstract class BaseDecimalModel<T = any> extends BaseModel<T> {
protected abstract decimalFields: (keyof this)[]; protected abstract getDecimalFields(): (keyof this)[];
public deserialize(input: any): void { public deserialize(input: any): void {
if (input && typeof input === 'object') { 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); super.deserialize(input);
} }

View File

@ -1,5 +1,4 @@
import { BaseModelWithAgendaItemAndListOfSpeakers } from '../base/base-model-with-agenda-item-and-list-of-speakers'; import { BaseModelWithAgendaItemAndListOfSpeakers } from '../base/base-model-with-agenda-item-and-list-of-speakers';
import { MotionPoll } from './motion-poll';
import { Submitter } from './submitter'; import { Submitter } from './submitter';
export interface MotionComment { export interface MotionComment {
@ -33,7 +32,6 @@ export interface MotionWithoutNestedModels extends BaseModelWithAgendaItemAndLis
recommendation_extension: string; recommendation_extension: string;
tags_id: number[]; tags_id: number[];
attachments_id: number[]; attachments_id: number[];
polls: MotionPoll[];
weight: number; weight: number;
sort_parent_id: number; sort_parent_id: number;
created: string; created: string;

View File

@ -5,7 +5,8 @@ export abstract class BaseOption<T> extends BaseDecimalModel<T> {
public yes: number; public yes: number;
public no: number; public no: number;
public abstain: number; public abstain: number;
public votes_id: number[];
protected decimalFields: (keyof BaseOption<T>)[] = ['yes', 'no', 'abstain']; protected getDecimalFields(): (keyof BaseOption<T>)[] {
return ['yes', 'no', 'abstain'];
}
} }

View File

@ -28,6 +28,8 @@ export interface BasePollWithoutNestedModels {
export abstract class BasePoll<T, O extends BaseOption<any>> extends BaseDecimalModel<T> { export abstract class BasePoll<T, O extends BaseOption<any>> extends BaseDecimalModel<T> {
public options: O[]; public options: O[];
protected decimalFields: (keyof BasePoll<T, O>)[] = ['votesvalid', 'votesinvalid', 'votescast']; protected getDecimalFields(): (keyof BasePoll<T, O>)[] {
return ['votesvalid', 'votesinvalid', 'votescast'];
}
} }
export interface BasePoll<T, O extends BaseOption<any>> extends BasePollWithoutNestedModels {} export interface BasePoll<T, O extends BaseOption<any>> extends BasePollWithoutNestedModels {}

View File

@ -3,7 +3,10 @@ import { BaseDecimalModel } from '../base/base-decimal-model';
export abstract class BaseVote<T> extends BaseDecimalModel<T> { export abstract class BaseVote<T> extends BaseDecimalModel<T> {
public weight: number; public weight: number;
public value: 'Y' | 'N' | 'A'; public value: 'Y' | 'N' | 'A';
public option_id: number;
public user_id?: number; public user_id?: number;
protected decimalFields: (keyof BaseVote<T>)[] = ['weight']; protected getDecimalFields(): (keyof BaseVote<T>)[] {
return ['weight'];
}
} }

View File

@ -1,5 +1,7 @@
import { AssignmentOption } from 'app/shared/models/assignments/assignment-option'; 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 { BaseViewModel } from '../../base/base-view-model';
import { ViewAssignmentVote } from './view-assignment-vote';
export class ViewAssignmentOption extends BaseViewModel<AssignmentOption> { export class ViewAssignmentOption extends BaseViewModel<AssignmentOption> {
public get option(): AssignmentOption { public get option(): AssignmentOption {
@ -9,4 +11,9 @@ export class ViewAssignmentOption extends BaseViewModel<AssignmentOption> {
protected _collectionString = AssignmentOption.COLLECTIONSTRING; protected _collectionString = AssignmentOption.COLLECTIONSTRING;
} }
export interface ViewAssignmentOption extends AssignmentOption {} interface TIMotionOptionRelations {
votes: ViewAssignmentVote[];
user: ViewUser;
}
export interface ViewAssignmentOption extends AssignmentOption, TIMotionOptionRelations {}

View File

@ -104,7 +104,7 @@ export class ViewAssignment extends BaseViewModelWithAgendaItemAndListOfSpeakers
} }
interface IAssignmentRelations { interface IAssignmentRelations {
assignment_related_users: ViewAssignmentRelatedUser[]; assignment_related_users: ViewAssignmentRelatedUser[];
polls?: ViewAssignmentPoll[]; polls: ViewAssignmentPoll[];
tags?: ViewTag[]; tags?: ViewTag[];
attachments?: ViewMediafile[]; attachments?: ViewMediafile[];
} }

View File

@ -1,5 +1,6 @@
import { MotionOption } from 'app/shared/models/motions/motion-option'; import { MotionOption } from 'app/shared/models/motions/motion-option';
import { BaseViewModel } from '../../base/base-view-model'; import { BaseViewModel } from '../../base/base-view-model';
import { ViewMotionVote } from './view-motion-vote';
export class ViewMotionOption extends BaseViewModel<MotionOption> { export class ViewMotionOption extends BaseViewModel<MotionOption> {
public get option(): MotionOption { public get option(): MotionOption {
@ -9,4 +10,8 @@ export class ViewMotionOption extends BaseViewModel<MotionOption> {
protected _collectionString = MotionOption.COLLECTIONSTRING; protected _collectionString = MotionOption.COLLECTIONSTRING;
} }
export interface ViewMotionPoll extends MotionOption {} interface TIMotionOptionRelations {
votes: ViewMotionVote[];
}
export interface ViewMotionOption extends MotionOption, TIMotionOptionRelations {}

View File

@ -16,6 +16,7 @@ import { ViewCategory } from './view-category';
import { ViewMotionBlock } from './view-motion-block'; import { ViewMotionBlock } from './view-motion-block';
import { ViewMotionChangeRecommendation } from './view-motion-change-recommendation'; import { ViewMotionChangeRecommendation } from './view-motion-change-recommendation';
import { ViewMotionCommentSection } from './view-motion-comment-section'; import { ViewMotionCommentSection } from './view-motion-comment-section';
import { ViewMotionPoll } from './view-motion-poll';
import { ViewState } from './view-state'; import { ViewState } from './view-state';
import { ViewSubmitter } from './view-submitter'; import { ViewSubmitter } from './view-submitter';
import { ViewWorkflow } from './view-workflow'; import { ViewWorkflow } from './view-workflow';
@ -359,6 +360,7 @@ interface TIMotionRelations {
amendments?: ViewMotion[]; amendments?: ViewMotion[];
changeRecommendations?: ViewMotionChangeRecommendation[]; changeRecommendations?: ViewMotionChangeRecommendation[];
diffLines?: DiffLinesInParagraph[]; diffLines?: DiffLinesInParagraph[];
polls: ViewMotionPoll[];
} }
export interface ViewMotion extends MotionWithoutNestedModels, TIMotionRelations {} export interface ViewMotion extends MotionWithoutNestedModels, TIMotionRelations {}

View File

@ -3,10 +3,14 @@ import { CategoryRepositoryService } from 'app/core/repositories/motions/categor
import { ChangeRecommendationRepositoryService } from 'app/core/repositories/motions/change-recommendation-repository.service'; import { ChangeRecommendationRepositoryService } from 'app/core/repositories/motions/change-recommendation-repository.service';
import { MotionBlockRepositoryService } from 'app/core/repositories/motions/motion-block-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 { 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 { 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 { StateRepositoryService } from 'app/core/repositories/motions/state-repository.service';
import { StatuteParagraphRepositoryService } from 'app/core/repositories/motions/statute-paragraph-repository.service'; import { StatuteParagraphRepositoryService } from 'app/core/repositories/motions/statute-paragraph-repository.service';
import { WorkflowRepositoryService } from 'app/core/repositories/motions/workflow-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 { State } from 'app/shared/models/motions/state';
import { Category } from '../../shared/models/motions/category'; import { Category } from '../../shared/models/motions/category';
import { Motion } from '../../shared/models/motions/motion'; 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 { ViewMotionBlock } from './models/view-motion-block';
import { ViewMotionChangeRecommendation } from './models/view-motion-change-recommendation'; import { ViewMotionChangeRecommendation } from './models/view-motion-change-recommendation';
import { ViewMotionCommentSection } from './models/view-motion-comment-section'; 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 { ViewState } from './models/view-state';
import { ViewStatuteParagraph } from './models/view-statute-paragraph'; import { ViewStatuteParagraph } from './models/view-statute-paragraph';
import { ViewWorkflow } from './models/view-workflow'; import { ViewWorkflow } from './models/view-workflow';
@ -70,7 +76,9 @@ export const MotionsAppConfig: AppConfig = {
viewModel: ViewStatuteParagraph, viewModel: ViewStatuteParagraph,
searchOrder: 9, searchOrder: 9,
repository: StatuteParagraphRepositoryService repository: StatuteParagraphRepositoryService
} },
{ model: MotionPoll, viewModel: ViewMotionPoll, repository: MotionPollRepositoryService },
{ model: MotionVote, viewModel: ViewMotionVote, repository: MotionVoteRepositoryService }
], ],
mainMenuEntries: [ mainMenuEntries: [
{ {

View File

@ -358,11 +358,11 @@ export class MotionPdfService {
} }
// voting results // voting results
if (motion.motion.polls.length && (!infoToExport || infoToExport.includes('polls'))) { if (motion.polls.length && (!infoToExport || infoToExport.includes('polls'))) {
const column1 = []; const column1 = [];
const column2 = []; const column2 = [];
const column3 = []; const column3 = [];
motion.motion.polls.map((poll, index) => { motion.polls.map((poll, index) => {
/*if (poll.has_votes) { /*if (poll.has_votes) {
if (motion.motion.polls.length > 1) { if (motion.motion.polls.length > 1) {
column1.push(index + 1 + '. ' + this.translate.instant('Vote')); column1.push(index + 1 + '. ' + this.translate.instant('Vote'));

View File

@ -71,8 +71,8 @@ export class MotionPollPdfService extends PollPdfService {
)}`; )}`;
if (!title) { if (!title) {
title = `${this.translate.instant('Motion')} - ${motion.identifier}`; title = `${this.translate.instant('Motion')} - ${motion.identifier}`;
if (motion.motion.polls.length > 1) { if (motion.polls.length > 1) {
title += ` (${this.translate.instant('Vote')} ${motion.motion.polls.length})`; title += ` (${this.translate.instant('Vote')} ${motion.polls.length})`;
} }
} }
if (!subtitle) { if (!subtitle) {

View File

@ -51,3 +51,12 @@ class AssignmentPollAccessPermissions(BaseAccessPermissions):
self, full_data: List[Dict[str, Any]], user_id: int self, full_data: List[Dict[str, Any]], user_id: int
) -> List[Dict[str, Any]]: ) -> List[Dict[str, Any]]:
return full_data 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

View File

@ -19,6 +19,7 @@ from ..utils.models import CASCADE_AND_AUTOUPDATE, SET_NULL_AND_AUTOUPDATE
from .access_permissions import ( from .access_permissions import (
AssignmentAccessPermissions, AssignmentAccessPermissions,
AssignmentPollAccessPermissions, AssignmentPollAccessPermissions,
AssignmentVoteAccessPermissions,
) )
@ -268,6 +269,7 @@ class Assignment(RESTModelMixin, AgendaItemWithListOfSpeakersMixin, models.Model
class AssignmentVote(RESTModelMixin, BaseVote): class AssignmentVote(RESTModelMixin, BaseVote):
access_permissions = AssignmentVoteAccessPermissions()
option = models.ForeignKey( option = models.ForeignKey(
"AssignmentOption", on_delete=models.CASCADE, related_name="votes" "AssignmentOption", on_delete=models.CASCADE, related_name="votes"
) )

View File

@ -74,12 +74,10 @@ class AssignmentOptionSerializer(ModelSerializer):
max_digits=15, decimal_places=6, min_value=-2, read_only=True max_digits=15, decimal_places=6, min_value=-2, read_only=True
) )
votes = IdPrimaryKeyRelatedField(many=True, read_only=True)
class Meta: class Meta:
model = AssignmentOption model = AssignmentOption
fields = ("user",) + BASE_OPTION_FIELDS fields = ("user", "weight") + BASE_OPTION_FIELDS
read_only_fields = ("user",) + BASE_OPTION_FIELDS read_only_fields = ("user", "weight") + BASE_OPTION_FIELDS
class AssignmentPollSerializer(ModelSerializer): class AssignmentPollSerializer(ModelSerializer):
@ -133,7 +131,6 @@ class AssignmentSerializer(ModelSerializer):
assignment_related_users = AssignmentRelatedUserSerializer( assignment_related_users = AssignmentRelatedUserSerializer(
many=True, read_only=True many=True, read_only=True
) )
polls = IdPrimaryKeyRelatedField(many=True, read_only=True)
agenda_create = BooleanField(write_only=True, required=False, allow_null=True) agenda_create = BooleanField(write_only=True, required=False, allow_null=True)
agenda_type = IntegerField( agenda_type = IntegerField(
write_only=True, required=False, min_value=1, max_value=3, allow_null=True write_only=True, required=False, min_value=1, max_value=3, allow_null=True
@ -150,7 +147,6 @@ class AssignmentSerializer(ModelSerializer):
"phase", "phase",
"assignment_related_users", "assignment_related_users",
"poll_description_default", "poll_description_default",
"polls",
"agenda_item_id", "agenda_item_id",
"list_of_speakers_id", "list_of_speakers_id",
"agenda_create", "agenda_create",

View File

@ -246,22 +246,6 @@ class AssignmentViewSet(ModelViewSet):
message = "User {0} was successfully unelected." message = "User {0} was successfully unelected."
return Response({"detail": message, "args": [str(user)]}) 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"]) @detail_route(methods=["post"])
def sort_related_users(self, request, pk=None): def sort_related_users(self, request, pk=None):
""" """

View File

@ -243,8 +243,6 @@ class MotionOptionSerializer(ModelSerializer):
max_digits=15, decimal_places=6, min_value=-2, read_only=True max_digits=15, decimal_places=6, min_value=-2, read_only=True
) )
votes = IdPrimaryKeyRelatedField(many=True, read_only=True)
class Meta: class Meta:
model = MotionOption model = MotionOption
fields = BASE_OPTION_FIELDS fields = BASE_OPTION_FIELDS
@ -371,7 +369,6 @@ class MotionSerializer(ModelSerializer):
""" """
comments = MotionCommentSerializer(many=True, read_only=True) comments = MotionCommentSerializer(many=True, read_only=True)
polls = IdPrimaryKeyRelatedField(many=True, read_only=True)
modified_final_version = CharField(allow_blank=True, required=False) modified_final_version = CharField(allow_blank=True, required=False)
reason = CharField(allow_blank=True, required=False) reason = CharField(allow_blank=True, required=False)
state_restriction = SerializerMethodField() state_restriction = SerializerMethodField()
@ -419,7 +416,6 @@ class MotionSerializer(ModelSerializer):
"recommendation_extension", "recommendation_extension",
"tags", "tags",
"attachments", "attachments",
"polls",
"agenda_item_id", "agenda_item_id",
"list_of_speakers_id", "list_of_speakers_id",
"agenda_create", "agenda_create",

View File

@ -11,6 +11,6 @@ BASE_POLL_FIELDS = (
"id", "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")

View File

@ -419,7 +419,7 @@ class RetrieveMotion(TestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_user_without_can_see_user_permission_to_see_motion_and_submitter_data( def test_user_without_can_see_user_permission_to_see_motion_and_submitter_data(
self self,
): ):
admin = get_user_model().objects.get(username="admin") admin = get_user_model().objects.get(username="admin")
Submitter.objects.add(admin, self.motion) Submitter.objects.add(admin, self.motion)

View File

@ -582,7 +582,6 @@ class VoteMotionPollNamedAutoupdates(TestCase):
"yes": "0.000000", "yes": "0.000000",
"no": "0.000000", "no": "0.000000",
"abstain": "1.000000", "abstain": "1.000000",
"votes_id": [vote.id],
} }
], ],
"voted_id": [self.user1.id], "voted_id": [self.user1.id],
@ -594,6 +593,7 @@ class VoteMotionPollNamedAutoupdates(TestCase):
"weight": "1.000000", "weight": "1.000000",
"value": "A", "value": "A",
"user_id": self.user1.id, "user_id": self.user1.id,
"option_id": 1,
}, },
}, },
) )
@ -605,6 +605,7 @@ class VoteMotionPollNamedAutoupdates(TestCase):
autoupdate[0]["motions/motion-vote:1"], autoupdate[0]["motions/motion-vote:1"],
{ {
"pollstate": 2, "pollstate": 2,
"option_id": 1,
"id": 1, "id": 1,
"weight": "1.000000", "weight": "1.000000",
"value": "A", "value": "A",
@ -626,7 +627,7 @@ class VoteMotionPollNamedAutoupdates(TestCase):
"type": "named", "type": "named",
"title": "test_title_tho8PhiePh8upaex6phi", "title": "test_title_tho8PhiePh8upaex6phi",
"groups_id": [GROUP_DELEGATE_PK], "groups_id": [GROUP_DELEGATE_PK],
"options": [{"id": 1, "votes_id": [vote.id]}], "options": [{"id": 1}],
"id": 1, "id": 1,
}, },
) )
@ -653,12 +654,12 @@ class VoteMotionPollPseudoanonymousAutoupdates(TestCase):
self.other_user, _ = self.create_user() self.other_user, _ = self.create_user()
inform_changed_data(self.other_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.groups.add(self.delegate_group)
self.user.is_present = True self.user.is_present = True
self.user.save() self.user.save()
self.user_client = APIClient() 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( self.poll = MotionPoll.objects.create(
motion=self.motion, motion=self.motion,
@ -699,7 +700,6 @@ class VoteMotionPollPseudoanonymousAutoupdates(TestCase):
"yes": "0.000000", "yes": "0.000000",
"no": "0.000000", "no": "0.000000",
"abstain": "1.000000", "abstain": "1.000000",
"votes_id": [vote.id],
} }
], ],
"voted_id": [self.user.id], "voted_id": [self.user.id],
@ -707,6 +707,7 @@ class VoteMotionPollPseudoanonymousAutoupdates(TestCase):
}, },
"motions/motion-vote:1": { "motions/motion-vote:1": {
"pollstate": 2, "pollstate": 2,
"option_id": 1,
"id": 1, "id": 1,
"weight": "1.000000", "weight": "1.000000",
"value": "A", "value": "A",
@ -730,7 +731,7 @@ class VoteMotionPollPseudoanonymousAutoupdates(TestCase):
"type": "pseudoanonymous", "type": "pseudoanonymous",
"title": "test_title_cahP1umooteehah2jeey", "title": "test_title_cahP1umooteehah2jeey",
"groups_id": [GROUP_DELEGATE_PK], "groups_id": [GROUP_DELEGATE_PK],
"options": [{"id": 1, "votes_id": [vote.id]}], "options": [{"id": 1}],
"id": 1, "id": 1,
}, },
) )
@ -953,7 +954,6 @@ class PublishMotionPoll(TestCase):
"yes": "0.000000", "yes": "0.000000",
"no": "2.000000", "no": "2.000000",
"abstain": "0.000000", "abstain": "0.000000",
"votes_id": [1],
} }
], ],
"voted_id": [], "voted_id": [],
@ -961,6 +961,7 @@ class PublishMotionPoll(TestCase):
}, },
"motions/motion-vote:1": { "motions/motion-vote:1": {
"pollstate": 4, "pollstate": 4,
"option_id": 1,
"id": 1, "id": 1,
"weight": "2.000000", "weight": "2.000000",
"value": "N", "value": "N",