diff --git a/client/src/app/shared/models/assignments/assignment-poll.ts b/client/src/app/shared/models/assignments/assignment-poll.ts index 65b17d59c..a272d6eec 100644 --- a/client/src/app/shared/models/assignments/assignment-poll.ts +++ b/client/src/app/shared/models/assignments/assignment-poll.ts @@ -2,24 +2,36 @@ import { CalculablePollKey } from 'app/site/polls/services/poll.service'; import { AssignmentOption } from './assignment-option'; import { BasePoll } from '../poll/base-poll'; -export enum AssignmentPollMethods { +export enum AssignmentPollMethod { YN = 'YN', YNA = 'YNA', Votes = 'votes' } +export enum AssignmentPollPercentBase { + YN = 'YN', + YNA = 'YNA', + Votes = 'votes', + Valid = 'valid', + Cast = 'cast', + Disabled = 'disabled' +} + /** - * Content of the 'polls' property of assignments - * @ignore + * Class representing a poll for an assignment. */ -export class AssignmentPoll extends BasePoll { +export class AssignmentPoll extends BasePoll< + AssignmentPoll, + AssignmentOption, + AssignmentPollMethod, + AssignmentPollPercentBase +> { public static COLLECTIONSTRING = 'assignments/assignment-poll'; public static defaultGroupsConfig = 'assignment_poll_default_groups'; public static defaultPollMethodConfig = 'assignment_poll_method'; public id: number; public assignment_id: number; - public pollmethod: AssignmentPollMethods; public votes_amount: number; public allow_multiple_votes_per_candidate: boolean; public global_no: boolean; @@ -27,11 +39,11 @@ export class AssignmentPoll extends BasePoll { public description: string; public get pollmethodFields(): CalculablePollKey[] { - if (this.pollmethod === AssignmentPollMethods.YN) { + if (this.pollmethod === AssignmentPollMethod.YN) { return ['yes', 'no']; - } else if (this.pollmethod === AssignmentPollMethods.YNA) { + } else if (this.pollmethod === AssignmentPollMethod.YNA) { return ['yes', 'no', 'abstain']; - } else if (this.pollmethod === AssignmentPollMethods.Votes) { + } else if (this.pollmethod === AssignmentPollMethod.Votes) { return ['yes']; } } diff --git a/client/src/app/shared/models/motions/motion-poll.ts b/client/src/app/shared/models/motions/motion-poll.ts index 061cce803..0e551bdbd 100644 --- a/client/src/app/shared/models/motions/motion-poll.ts +++ b/client/src/app/shared/models/motions/motion-poll.ts @@ -1,8 +1,8 @@ import { CalculablePollKey } from 'app/site/polls/services/poll.service'; -import { BasePoll } from '../poll/base-poll'; +import { BasePoll, PercentBase } from '../poll/base-poll'; import { MotionOption } from './motion-option'; -export enum MotionPollMethods { +export enum MotionPollMethod { YN = 'YN', YNA = 'YNA' } @@ -10,19 +10,18 @@ export enum MotionPollMethods { /** * Class representing a poll for a motion. */ -export class MotionPoll extends BasePoll { +export class MotionPoll extends BasePoll { public static COLLECTIONSTRING = 'motions/motion-poll'; public static defaultGroupsConfig = 'motion_poll_default_groups'; public id: number; public motion_id: number; - public pollmethod: MotionPollMethods; public get pollmethodFields(): CalculablePollKey[] { const ynField: CalculablePollKey[] = ['yes', 'no']; - if (this.pollmethod === MotionPollMethods.YN) { + if (this.pollmethod === MotionPollMethod.YN) { return ynField; - } else if (this.pollmethod === MotionPollMethods.YNA) { + } else if (this.pollmethod === MotionPollMethod.YNA) { return ynField.concat(['abstain']); } } diff --git a/client/src/app/shared/models/poll/base-poll.ts b/client/src/app/shared/models/poll/base-poll.ts index 275339b8b..a78f1eb04 100644 --- a/client/src/app/shared/models/poll/base-poll.ts +++ b/client/src/app/shared/models/poll/base-poll.ts @@ -23,6 +23,13 @@ export enum PollType { Pseudoanonymous = 'pseudoanonymous' } +export enum MajorityMethod { + Simple = 'simple', + TwoThirds = 'two_thirds', + ThreeQuarters = 'three_quarters', + Disabled = 'disabled' +} + export enum PercentBase { YN = 'YN', YNA = 'YNA', @@ -31,14 +38,12 @@ export enum PercentBase { Disabled = 'disabled' } -export enum MajorityMethod { - Simple = 'simple', - TwoThirds = 'two_thirds', - ThreeQuarters = 'three_quarters', - Disabled = 'disabled' -} - -export abstract class BasePoll = any> extends BaseDecimalModel { +export abstract class BasePoll< + T = any, + O extends BaseOption = any, + PM extends string = string, + PB extends string = string +> extends BaseDecimalModel { public state: PollState; public type: PollType; public title: string; @@ -47,7 +52,9 @@ export abstract class BasePoll = any> extends public votescast: number; public groups_id: number[]; public majority_method: MajorityMethod; - public onehundred_percent_base: PercentBase; + + public pollmethod: PM; + public onehundred_percent_base: PB; public get isCreated(): boolean { return this.state === PollState.Created; diff --git a/client/src/app/shared/models/poll/base-vote.ts b/client/src/app/shared/models/poll/base-vote.ts index f1aee007b..4a5f0a7ba 100644 --- a/client/src/app/shared/models/poll/base-vote.ts +++ b/client/src/app/shared/models/poll/base-vote.ts @@ -16,7 +16,7 @@ export const GeneralValueVerbose = { votesabstain: 'Votes abstain' }; -export abstract class BaseVote extends BaseDecimalModel { +export abstract class BaseVote extends BaseDecimalModel { public weight: number; public value: VoteValue; public option_id: number; diff --git a/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.html b/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.html index 0c534ba76..8fcdde2fa 100644 --- a/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.html +++ b/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.html @@ -103,7 +103,7 @@ {{ 'Anonymous' | translate }} - +
- +
{{ candidate }}
diff --git a/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.ts b/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.ts index 11fde092b..24973bf2d 100644 --- a/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.ts +++ b/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.ts @@ -8,11 +8,12 @@ import { PblColumnDefinition } from '@pebula/ngrid'; import { OperatorService } from 'app/core/core-services/operator.service'; import { AssignmentPollRepositoryService } from 'app/core/repositories/assignments/assignment-poll-repository.service'; +import { AssignmentVoteRepositoryService } from 'app/core/repositories/assignments/assignment-vote-repository.service'; import { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service'; import { PromptService } from 'app/core/ui-services/prompt.service'; import { ViewportService } from 'app/core/ui-services/viewport.service'; import { ChartType } from 'app/shared/components/charts/charts.component'; -import { AssignmentPollMethods } from 'app/shared/models/assignments/assignment-poll'; +import { AssignmentPollMethod } from 'app/shared/models/assignments/assignment-poll'; import { BasePollDetailComponent } from 'app/site/polls/components/base-poll-detail.component'; import { VotingResult } from 'app/site/polls/models/view-base-poll'; import { PollService } from 'app/site/polls/services/poll.service'; @@ -26,7 +27,7 @@ import { ViewAssignmentPoll } from '../../models/view-assignment-poll'; encapsulation: ViewEncapsulation.None }) export class AssignmentPollDetailComponent extends BasePollDetailComponent { - public AssignmentPollMethods = AssignmentPollMethods; + public AssignmentPollMethod = AssignmentPollMethod; public columnDefinitionSingleVotes: PblColumnDefinition[]; @@ -41,7 +42,7 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponent + diff --git a/client/src/app/site/assignments/components/assignment-poll-dialog/assignment-poll-dialog.component.ts b/client/src/app/site/assignments/components/assignment-poll-dialog/assignment-poll-dialog.component.ts index d2b5ac789..5fb3877ed 100644 --- a/client/src/app/site/assignments/components/assignment-poll-dialog/assignment-poll-dialog.component.ts +++ b/client/src/app/site/assignments/components/assignment-poll-dialog/assignment-poll-dialog.component.ts @@ -6,9 +6,12 @@ import { Title } from '@angular/platform-browser'; import { TranslateService } from '@ngx-translate/core'; -import { AssignmentPollMethods } from 'app/shared/models/assignments/assignment-poll'; +import { AssignmentPollMethod } from 'app/shared/models/assignments/assignment-poll'; import { GeneralValueVerbose, VoteValue, VoteValueVerbose } from 'app/shared/models/poll/base-vote'; -import { AssignmentPollMethodsVerbose } from 'app/site/assignments/models/view-assignment-poll'; +import { + AssignmentPollMethodVerbose, + AssignmentPollPercentBaseVerbose +} from 'app/site/assignments/models/view-assignment-poll'; import { BasePollDialogComponent } from 'app/site/polls/components/base-poll-dialog.component'; import { PollFormComponent } from 'app/site/polls/components/poll-form/poll-form.component'; import { ViewUser } from 'app/site/users/models/view-user'; @@ -50,7 +53,8 @@ export class AssignmentPollDialogComponent extends BasePollDialogComponentYou have not give any voting here! -

+

{{ 'Votes for this poll' | translate }}: {{ getVotesCount() }}/{{ poll.votes_amount }}

@@ -16,9 +16,9 @@
@@ -34,7 +34,7 @@ > {{ action.icon }} - + {{ action.label | translate }}
@@ -44,7 +44,7 @@
- +
diff --git a/client/src/app/site/assignments/components/assignment-poll-vote/assignment-poll-vote.component.ts b/client/src/app/site/assignments/components/assignment-poll-vote/assignment-poll-vote.component.ts index 4262be382..8f4dce333 100644 --- a/client/src/app/site/assignments/components/assignment-poll-vote/assignment-poll-vote.component.ts +++ b/client/src/app/site/assignments/components/assignment-poll-vote/assignment-poll-vote.component.ts @@ -8,7 +8,7 @@ import { OperatorService } from 'app/core/core-services/operator.service'; import { AssignmentPollRepositoryService } from 'app/core/repositories/assignments/assignment-poll-repository.service'; import { AssignmentVoteRepositoryService } from 'app/core/repositories/assignments/assignment-vote-repository.service'; import { VotingService } from 'app/core/ui-services/voting.service'; -import { AssignmentPollMethods } from 'app/shared/models/assignments/assignment-poll'; +import { AssignmentPollMethod } from 'app/shared/models/assignments/assignment-poll'; import { PollType } from 'app/shared/models/poll/base-poll'; import { BasePollVoteComponent } from 'app/site/polls/components/base-poll-vote.component'; import { ViewAssignmentPoll } from '../../models/view-assignment-poll'; @@ -28,7 +28,7 @@ interface VoteActions { styleUrls: ['./assignment-poll-vote.component.scss'] }) export class AssignmentPollVoteComponent extends BasePollVoteComponent implements OnInit { - public pollMethods = AssignmentPollMethods; + public AssignmentPollMethod = AssignmentPollMethod; public PollType = PollType; public voteActions: VoteActions[] = []; @@ -70,7 +70,7 @@ export class AssignmentPollVoteComponent extends BasePollVoteComponent vote.option.id === option.id); - if (this.poll.pollmethod === AssignmentPollMethods.Votes && curr_vote) { + if (this.poll.pollmethod === AssignmentPollMethod.Votes && curr_vote) { if (curr_vote.value !== 'Y') { this.currentVotes.global = curr_vote.valueVerbose; curr_vote = null; @@ -120,7 +120,7 @@ export class AssignmentPollVoteComponent extends BasePollVoteComponent { if ((n === optionId && vote === 'Y') !== (this.currentVotes[n] === 'Yes')) { diff --git a/client/src/app/site/assignments/components/assignment-poll/assignment-poll.component.html b/client/src/app/site/assignments/components/assignment-poll/assignment-poll.component.html index 8d69ac5ee..a919febf2 100644 --- a/client/src/app/site/assignments/components/assignment-poll/assignment-poll.component.html +++ b/client/src/app/site/assignments/components/assignment-poll/assignment-poll.component.html @@ -45,7 +45,7 @@
-
+
{ - public get option(): AssignmentOption { - return this._model; - } +export class ViewAssignmentOption extends ViewBaseOption { public static COLLECTIONSTRING = AssignmentOption.COLLECTIONSTRING; protected _collectionString = AssignmentOption.COLLECTIONSTRING; } -interface TIAssignmentOptionRelations { - votes: ViewAssignmentVote[]; +export interface ViewAssignmentOption extends AssignmentOption { user: ViewUser; - poll: ViewAssignmentPoll; } - -export interface ViewAssignmentOption extends AssignmentOption, TIAssignmentOptionRelations {} diff --git a/client/src/app/site/assignments/models/view-assignment-poll.ts b/client/src/app/site/assignments/models/view-assignment-poll.ts index ba1d90f41..ecc32df65 100644 --- a/client/src/app/site/assignments/models/view-assignment-poll.ts +++ b/client/src/app/site/assignments/models/view-assignment-poll.ts @@ -1,10 +1,14 @@ import { BehaviorSubject } from 'rxjs'; import { ChartData } from 'app/shared/components/charts/charts.component'; -import { AssignmentPoll } from 'app/shared/models/assignments/assignment-poll'; +import { + AssignmentPoll, + AssignmentPollMethod, + AssignmentPollPercentBase +} from 'app/shared/models/assignments/assignment-poll'; import { BaseViewModel } from 'app/site/base/base-view-model'; import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable'; -import { PollTableData, ViewBasePoll, VotingResult } from 'app/site/polls/models/view-base-poll'; +import { PollClassType, PollTableData, ViewBasePoll, VotingResult } from 'app/site/polls/models/view-base-poll'; import { ViewAssignment } from './view-assignment'; import { ViewAssignmentOption } from './view-assignment-option'; @@ -12,21 +16,35 @@ export interface AssignmentPollTitleInformation { title: string; } -export const AssignmentPollMethodsVerbose = { +export const AssignmentPollMethodVerbose = { votes: 'Yes per candidate', YN: 'Yes/No per candidate', YNA: 'Yes/No/Abstain per candidate' }; -export class ViewAssignmentPoll extends ViewBasePoll implements AssignmentPollTitleInformation { +export const AssignmentPollPercentBaseVerbose = { + YN: 'Yes/No per candidate', + YNA: 'Yes/No/Abstain per candidate', + votes: 'Sum of votes inclusive global ones', + valid: 'All valid ballots', + cast: 'All casted ballots', + disabled: 'Disabled (no percents)' +}; + +export class ViewAssignmentPoll extends ViewBasePoll + implements AssignmentPollTitleInformation { public static COLLECTIONSTRING = AssignmentPoll.COLLECTIONSTRING; protected _collectionString = AssignmentPoll.COLLECTIONSTRING; public readonly tableChartData: Map> = new Map(); - public readonly pollClassType: 'assignment' | 'motion' = 'assignment'; + public readonly pollClassType = PollClassType.Assignment; public get pollmethodVerbose(): string { - return AssignmentPollMethodsVerbose[this.pollmethod]; + return AssignmentPollMethodVerbose[this.pollmethod]; + } + + public get percentBaseVerbose(): string { + return AssignmentPollPercentBaseVerbose[this.onehundred_percent_base]; } public getContentObject(): BaseViewModel { diff --git a/client/src/app/site/assignments/models/view-assignment-vote.ts b/client/src/app/site/assignments/models/view-assignment-vote.ts index dc8c6e75f..5bd40ae17 100644 --- a/client/src/app/site/assignments/models/view-assignment-vote.ts +++ b/client/src/app/site/assignments/models/view-assignment-vote.ts @@ -1,19 +1,9 @@ import { AssignmentVote } from 'app/shared/models/assignments/assignment-vote'; -import { ViewUser } from 'app/site/users/models/view-user'; -import { BaseViewModel } from '../../base/base-view-model'; -import { ViewAssignmentOption } from './view-assignment-option'; +import { ViewBaseVote } from 'app/site/polls/models/view-base-vote'; -export class ViewAssignmentVote extends BaseViewModel { - public get vote(): AssignmentVote { - return this._model; - } +export class ViewAssignmentVote extends ViewBaseVote { public static COLLECTIONSTRING = AssignmentVote.COLLECTIONSTRING; protected _collectionString = AssignmentVote.COLLECTIONSTRING; } -interface TIAssignmentVoteRelations { - user: ViewUser; - option: ViewAssignmentOption; -} - -export interface ViewAssignmentVote extends AssignmentVote, TIAssignmentVoteRelations {} +export interface ViewAssignmentVote extends AssignmentVote {} diff --git a/client/src/app/site/assignments/services/assignment-poll-pdf.service.ts b/client/src/app/site/assignments/services/assignment-poll-pdf.service.ts index bdbc578b4..89cca7cf4 100644 --- a/client/src/app/site/assignments/services/assignment-poll-pdf.service.ts +++ b/client/src/app/site/assignments/services/assignment-poll-pdf.service.ts @@ -7,7 +7,7 @@ import { PdfDocumentService } from 'app/core/pdf-services/pdf-document.service'; import { AssignmentRepositoryService } from 'app/core/repositories/assignments/assignment-repository.service'; import { UserRepositoryService } from 'app/core/repositories/users/user-repository.service'; import { ConfigService } from 'app/core/ui-services/config.service'; -import { AssignmentPollMethods } from 'app/shared/models/assignments/assignment-poll'; +import { AssignmentPollMethod } from 'app/shared/models/assignments/assignment-poll'; import { ViewAssignmentPoll } from '../models/view-assignment-poll'; /** @@ -162,7 +162,7 @@ export class AssignmentPollPdfService extends PollPdfService { return resultObject; } - private createYNBallotEntry(option: string, method: AssignmentPollMethods): object { + private createYNBallotEntry(option: string, method: AssignmentPollMethod): object { const choices = method === 'YNA' ? ['Yes', 'No', 'Abstain'] : ['Yes', 'No']; const columnstack = choices.map(choice => { return { diff --git a/client/src/app/site/assignments/services/assignment-poll.service.ts b/client/src/app/site/assignments/services/assignment-poll.service.ts index 99ebd9843..4dbbc79ec 100644 --- a/client/src/app/site/assignments/services/assignment-poll.service.ts +++ b/client/src/app/site/assignments/services/assignment-poll.service.ts @@ -5,8 +5,12 @@ import { TranslateService } from '@ngx-translate/core'; import { ConstantsService } from 'app/core/core-services/constants.service'; import { AssignmentPollRepositoryService } from 'app/core/repositories/assignments/assignment-poll-repository.service'; import { ConfigService } from 'app/core/ui-services/config.service'; -import { AssignmentPoll, AssignmentPollMethods } from 'app/shared/models/assignments/assignment-poll'; -import { MajorityMethod, PercentBase } from 'app/shared/models/poll/base-poll'; +import { + AssignmentPoll, + AssignmentPollMethod, + AssignmentPollPercentBase +} from 'app/shared/models/assignments/assignment-poll'; +import { MajorityMethod } from 'app/shared/models/poll/base-poll'; import { PollData, PollService } from 'app/site/polls/services/poll.service'; @Injectable({ @@ -16,7 +20,7 @@ export class AssignmentPollService extends PollService { /** * The default percentage base */ - public defaultPercentBase: PercentBase; + public defaultPercentBase: AssignmentPollPercentBase; /** * The default majority method @@ -25,7 +29,7 @@ export class AssignmentPollService extends PollService { public defaultGroupIds: number[]; - public defaultPollMethod: AssignmentPollMethods; + public defaultPollMethod: AssignmentPollMethod; /** * Constructor. Subscribes to the configuration values needed @@ -39,14 +43,14 @@ export class AssignmentPollService extends PollService { ) { super(constants); config - .get('motion_poll_default_100_percent_base') + .get('assignment_poll_default_100_percent_base') .subscribe(base => (this.defaultPercentBase = base)); config - .get('motion_poll_default_majority_method') + .get('assignment_poll_default_majority_method') .subscribe(method => (this.defaultMajorityMethod = method)); config.get(AssignmentPoll.defaultGroupsConfig).subscribe(ids => (this.defaultGroupIds = ids)); config - .get(AssignmentPoll.defaultPollMethodConfig) + .get(AssignmentPoll.defaultPollMethodConfig) .subscribe(method => (this.defaultPollMethod = method)); } @@ -77,19 +81,22 @@ export class AssignmentPollService extends PollService { } public getPercentBase(poll: PollData): number { - const base: PercentBase = poll.onehundred_percent_base; + const base: AssignmentPollPercentBase = poll.onehundred_percent_base as AssignmentPollPercentBase; let totalByBase: number; switch (base) { - case PercentBase.YN: + case AssignmentPollPercentBase.YN: totalByBase = this.sumOptionsYN(poll); break; - case PercentBase.YNA: + case AssignmentPollPercentBase.YNA: totalByBase = this.sumOptionsYNA(poll); break; - case PercentBase.Valid: + case AssignmentPollPercentBase.Votes: + totalByBase = this.sumOptionsYNA(poll); + break; + case AssignmentPollPercentBase.Valid: totalByBase = poll.votesvalid; break; - case PercentBase.Cast: + case AssignmentPollPercentBase.Cast: totalByBase = poll.votescast; break; default: 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 b380648cb..245148093 100644 --- a/client/src/app/site/motions/models/view-motion-option.ts +++ b/client/src/app/site/motions/models/view-motion-option.ts @@ -1,19 +1,8 @@ import { MotionOption } from 'app/shared/models/motions/motion-option'; -import { BaseViewModel } from '../../base/base-view-model'; -import { ViewMotionPoll } from './view-motion-poll'; -import { ViewMotionVote } from './view-motion-vote'; +import { ViewBaseOption } from 'app/site/polls/models/view-base-option'; -export class ViewMotionOption extends BaseViewModel { - public get option(): MotionOption { - return this._model; - } +export class ViewMotionOption extends ViewBaseOption { public static COLLECTIONSTRING = MotionOption.COLLECTIONSTRING; protected _collectionString = MotionOption.COLLECTIONSTRING; } - -interface TIMotionOptionRelations { - votes: ViewMotionVote[]; - poll: ViewMotionPoll; -} - -export interface ViewMotionOption extends MotionOption, TIMotionOptionRelations {} +export interface ViewMotionOption extends MotionOption {} diff --git a/client/src/app/site/motions/models/view-motion-poll.ts b/client/src/app/site/motions/models/view-motion-poll.ts index e0c7164af..9d4cc0ce7 100644 --- a/client/src/app/site/motions/models/view-motion-poll.ts +++ b/client/src/app/site/motions/models/view-motion-poll.ts @@ -1,31 +1,41 @@ -import { MotionPoll } from 'app/shared/models/motions/motion-poll'; +import { MotionPoll, MotionPollMethod } from 'app/shared/models/motions/motion-poll'; +import { PercentBase } from 'app/shared/models/poll/base-poll'; import { BaseViewModel } from 'app/site/base/base-view-model'; import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable'; import { ViewMotionOption } from 'app/site/motions/models/view-motion-option'; -import { PollTableData, ViewBasePoll, VotingResult } from 'app/site/polls/models/view-base-poll'; +import { PollClassType, PollTableData, ViewBasePoll, VotingResult } from 'app/site/polls/models/view-base-poll'; import { ViewMotion } from './view-motion'; export interface MotionPollTitleInformation { title: string; } -export const MotionPollMethodsVerbose = { +export const MotionPollMethodVerbose = { YN: 'Yes/No', YNA: 'Yes/No/Abstain' }; -export class ViewMotionPoll extends ViewBasePoll implements MotionPollTitleInformation { +export const MotionPollPercentBaseVerbose = { + YN: 'Yes/No', + YNA: 'Yes/No/Abstain', + valid: 'All valid ballots', + cast: 'All casted ballots', + disabled: 'Disabled (no percents)' +}; + +export class ViewMotionPoll extends ViewBasePoll + implements MotionPollTitleInformation { public static COLLECTIONSTRING = MotionPoll.COLLECTIONSTRING; protected _collectionString = MotionPoll.COLLECTIONSTRING; - public readonly pollClassType: 'assignment' | 'motion' = 'motion'; + public readonly pollClassType = PollClassType.Motion; public get result(): ViewMotionOption { return this.options[0]; } public get hasVotes(): boolean { - return !!this.result.votes.length; + return this.result && !!this.result.votes.length; } public getContentObject(): BaseViewModel { @@ -71,7 +81,11 @@ export class ViewMotionPoll extends ViewBasePoll implements MotionPo } public get pollmethodVerbose(): string { - return MotionPollMethodsVerbose[this.pollmethod]; + return MotionPollMethodVerbose[this.pollmethod]; + } + + public get percentBaseVerbose(): string { + return MotionPollPercentBaseVerbose[this.onehundred_percent_base]; } public anySpecialVotes(): boolean { diff --git a/client/src/app/site/motions/models/view-motion-vote.ts b/client/src/app/site/motions/models/view-motion-vote.ts index 164de6592..25a7c0e40 100644 --- a/client/src/app/site/motions/models/view-motion-vote.ts +++ b/client/src/app/site/motions/models/view-motion-vote.ts @@ -1,19 +1,9 @@ import { MotionVote } from 'app/shared/models/motions/motion-vote'; -import { ViewUser } from 'app/site/users/models/view-user'; -import { BaseViewModel } from '../../base/base-view-model'; -import { ViewMotionOption } from './view-motion-option'; +import { ViewBaseVote } from 'app/site/polls/models/view-base-vote'; -export class ViewMotionVote extends BaseViewModel { - public get vote(): MotionVote { - return this._model; - } +export class ViewMotionVote extends ViewBaseVote { public static COLLECTIONSTRING = MotionVote.COLLECTIONSTRING; protected _collectionString = MotionVote.COLLECTIONSTRING; } -interface TIMotionVoteRelations { - user?: ViewUser; - option: ViewMotionOption; -} - -export interface ViewMotionVote extends MotionVote, TIMotionVoteRelations {} +export interface ViewMotionVote extends MotionVote {} diff --git a/client/src/app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.html b/client/src/app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.html index 11321386d..0a12465e2 100644 --- a/client/src/app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.html +++ b/client/src/app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.html @@ -82,6 +82,7 @@

{{ 'Single votes' | translate }}

{{ vote.valueVerbose | translate }}
- - -
- {{ 'The individual votes were made anonymous.' | translate }} -
+
+ {{ 'The individual votes were anonymized.' | translate }} +
diff --git a/client/src/app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.ts b/client/src/app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.ts index 7677243c3..07337bf3e 100644 --- a/client/src/app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.ts +++ b/client/src/app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.ts @@ -8,6 +8,7 @@ import { PblColumnDefinition } from '@pebula/ngrid'; import { OperatorService } from 'app/core/core-services/operator.service'; import { MotionPollRepositoryService } from 'app/core/repositories/motions/motion-poll-repository.service'; +import { MotionVoteRepositoryService } from 'app/core/repositories/motions/motion-vote-repository.service'; import { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service'; import { PromptService } from 'app/core/ui-services/prompt.service'; import { ChartType } from 'app/shared/components/charts/charts.component'; @@ -60,12 +61,13 @@ export class MotionPollDetailComponent extends BasePollDetailComponent +
diff --git a/client/src/app/site/motions/modules/motion-poll/motion-poll-dialog/motion-poll-dialog.component.ts b/client/src/app/site/motions/modules/motion-poll/motion-poll-dialog/motion-poll-dialog.component.ts index 4112858f4..598b8cfc9 100644 --- a/client/src/app/site/motions/modules/motion-poll/motion-poll-dialog/motion-poll-dialog.component.ts +++ b/client/src/app/site/motions/modules/motion-poll/motion-poll-dialog/motion-poll-dialog.component.ts @@ -6,9 +6,9 @@ import { Title } from '@angular/platform-browser'; import { TranslateService } from '@ngx-translate/core'; import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll'; -import { MotionPollMethodsVerbose } from 'app/site/motions/models/view-motion-poll'; import { BasePollDialogComponent } from 'app/site/polls/components/base-poll-dialog.component'; import { PollFormComponent } from 'app/site/polls/components/poll-form/poll-form.component'; +import { PercentBaseVerbose } from 'app/site/polls/models/view-base-poll'; @Component({ selector: 'os-motion-poll-dialog', @@ -16,7 +16,7 @@ import { PollFormComponent } from 'app/site/polls/components/poll-form/poll-form styleUrls: ['./motion-poll-dialog.component.scss'] }) export class MotionPollDialogComponent extends BasePollDialogComponent { - public motionPollMethods = { YNA: MotionPollMethodsVerbose.YNA }; + public PercentBaseVerbose = PercentBaseVerbose; @ViewChild('pollForm', { static: false }) protected pollForm: PollFormComponent; diff --git a/client/src/app/site/motions/modules/motion-poll/motion-poll-vote/motion-poll-vote.component.ts b/client/src/app/site/motions/modules/motion-poll/motion-poll-vote/motion-poll-vote.component.ts index 7bf6f6a8c..7d260554f 100644 --- a/client/src/app/site/motions/modules/motion-poll/motion-poll-vote/motion-poll-vote.component.ts +++ b/client/src/app/site/motions/modules/motion-poll/motion-poll-vote/motion-poll-vote.component.ts @@ -8,7 +8,7 @@ import { OperatorService } from 'app/core/core-services/operator.service'; import { MotionPollRepositoryService } from 'app/core/repositories/motions/motion-poll-repository.service'; import { MotionVoteRepositoryService } from 'app/core/repositories/motions/motion-vote-repository.service'; import { VotingService } from 'app/core/ui-services/voting.service'; -import { MotionPollMethods } from 'app/shared/models/motions/motion-poll'; +import { MotionPollMethod } from 'app/shared/models/motions/motion-poll'; import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll'; import { ViewMotionVote } from 'app/site/motions/models/view-motion-vote'; import { BasePollVoteComponent } from 'app/site/polls/components/base-poll-vote.component'; @@ -34,7 +34,7 @@ export class MotionPollVoteComponent extends BasePollVoteComponent item.motion_id === poll.motion_id).length; poll.title = !length ? this.translate.instant('Vote') : `${this.translate.instant('Vote')} (${length + 1})`; - poll.pollmethod = MotionPollMethods.YNA; + poll.pollmethod = MotionPollMethod.YNA; return poll; } public getPercentBase(poll: PollData): number { - const base: PercentBase = poll.onehundred_percent_base; + const base: PercentBase = poll.onehundred_percent_base as PercentBase; let totalByBase: number; const result = poll.options[0]; diff --git a/client/src/app/site/polls/components/base-poll-detail.component.ts b/client/src/app/site/polls/components/base-poll-detail.component.ts index fb70853ae..4f9ae1dd5 100644 --- a/client/src/app/site/polls/components/base-poll-detail.component.ts +++ b/client/src/app/site/polls/components/base-poll-detail.component.ts @@ -6,17 +6,21 @@ import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { Label } from 'ng2-charts'; import { BehaviorSubject, from, Observable } from 'rxjs'; +import { filter, map } from 'rxjs/operators'; +import { BaseRepository } from 'app/core/repositories/base-repository'; import { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service'; import { BasePollDialogService } from 'app/core/ui-services/base-poll-dialog.service'; import { PromptService } from 'app/core/ui-services/prompt.service'; import { ChartData, ChartType } from 'app/shared/components/charts/charts.component'; +import { BaseVote } from 'app/shared/models/poll/base-vote'; import { BaseViewComponent } from 'app/site/base/base-view'; import { ViewGroup } from 'app/site/users/models/view-group'; import { ViewUser } from 'app/site/users/models/view-user'; import { BasePollRepositoryService } from '../services/base-poll-repository.service'; import { PollService } from '../services/poll.service'; import { ViewBasePoll } from '../models/view-base-poll'; +import { ViewBaseVote } from '../models/view-base-vote'; export interface BaseVoteData { user?: ViewUser; @@ -74,6 +78,8 @@ export abstract class BasePollDetailComponent extends Ba // The observable for the votes-per-user table public votesDataObservable: Observable; + protected optionsLoaded = false; + /** * Constructor * @@ -98,9 +104,23 @@ export abstract class BasePollDetailComponent extends Ba protected groupRepo: GroupRepositoryService, protected promptService: PromptService, protected pollDialog: BasePollDialogService, - protected pollService: PollService + protected pollService: PollService, + protected votesRepo: BaseRepository ) { super(title, translate, matSnackbar); + votesRepo + .getViewModelListObservable() + .pipe( + filter(() => this.poll && this.canSeeVotes), // filter first for valid poll state to avoid unneccessary iteration of potentially thousands of votes + map(votes => votes.filter(vote => vote.option.poll_id === this.poll.id)), + filter(votes => !!votes.length) + ) + .subscribe(() => { + // votes data can only be created when options are ready + if (this.optionsLoaded) { + this.createVotesData(); + } + }); } /** @@ -143,12 +163,18 @@ export abstract class BasePollDetailComponent extends Ba */ protected onPollLoaded(): void {} - protected onPollWithOptionsLoaded(): void {} + protected onPollWithOptionsLoaded(): void { + this.createVotesData(); + } protected onStateChanged(): void {} protected abstract hasPerms(): boolean; + protected get canSeeVotes(): boolean { + return (this.hasPerms && this.poll.isFinished) || this.poll.isPublished; + } + /** * sets the votes data only if the poll wasn't pseudoanonymized */ @@ -160,6 +186,11 @@ export abstract class BasePollDetailComponent extends Ba } } + /** + * Is called when the underlying vote data changes. Is supposed to call setVotesData + */ + protected abstract createVotesData(): void; + /** * Initializes data for the shown chart. * Could be overwritten to implement custom chart data. @@ -169,10 +200,10 @@ export abstract class BasePollDetailComponent extends Ba } /** - * This checks, if the poll has votes. + * This checks if the poll has votes. */ private checkData(): void { - if (this.poll.state === 3 || this.poll.state === 4) { + if (this.poll.stateHasVotes) { setTimeout(() => this.initChartData()); } } @@ -203,6 +234,7 @@ export abstract class BasePollDetailComponent extends Ba if (!this.poll.options || !this.poll.options.length) { setTimeout(() => this.waitForOptions(), 1); } else { + this.optionsLoaded = true; this.onPollWithOptionsLoaded(); } } diff --git a/client/src/app/site/polls/components/poll-form/poll-form.component.html b/client/src/app/site/polls/components/poll-form/poll-form.component.html index f34793838..a4b2bd919 100644 --- a/client/src/app/site/polls/components/poll-form/poll-form.component.html +++ b/client/src/app/site/polls/components/poll-form/poll-form.component.html @@ -65,7 +65,7 @@ formControlName="onehundred_percent_base" required > - + {{ option.value | translate }} diff --git a/client/src/app/site/polls/components/poll-form/poll-form.component.ts b/client/src/app/site/polls/components/poll-form/poll-form.component.ts index 7a6196357..8072e4f14 100644 --- a/client/src/app/site/polls/components/poll-form/poll-form.component.ts +++ b/client/src/app/site/polls/components/poll-form/poll-form.component.ts @@ -44,6 +44,12 @@ export class PollFormComponent extends BaseViewComponent @Input() public pollMethods: { [key: string]: string }; + /** + * The different percent bases for this poll. + */ + @Input() + public percentBases: { [key: string]: string }; + @Input() public data: Partial; @@ -52,11 +58,6 @@ export class PollFormComponent extends BaseViewComponent */ public pollTypes = PollTypeVerbose; - /** - * The percent base for the poll. - */ - public percentBases: { [key: string]: string } = PercentBaseVerbose; - /** * The majority methods for the poll. */ @@ -233,4 +234,11 @@ export class PollFormComponent extends BaseViewComponent global_abstain: [false] }); } + + /** + * compare function used with the KeyValuePipe to display the percent bases in original order + */ + public keepEntryOrder(): number { + return 0; + } } diff --git a/client/src/app/site/polls/models/view-base-option.ts b/client/src/app/site/polls/models/view-base-option.ts new file mode 100644 index 000000000..60981426b --- /dev/null +++ b/client/src/app/site/polls/models/view-base-option.ts @@ -0,0 +1,15 @@ +import { BaseOption } from 'app/shared/models/poll/base-option'; +import { BaseViewModel } from '../../base/base-view-model'; +import { ViewBasePoll } from './view-base-poll'; +import { ViewBaseVote } from './view-base-vote'; + +export class ViewBaseOption = any> extends BaseViewModel { + public get option(): M { + return this._model; + } +} + +export interface ViewBaseOption = any> extends BaseOption { + votes: ViewBaseVote[]; + poll: ViewBasePoll; +} diff --git a/client/src/app/site/polls/models/view-base-poll.ts b/client/src/app/site/polls/models/view-base-poll.ts index 3ec74629f..b4df3afa9 100644 --- a/client/src/app/site/polls/models/view-base-poll.ts +++ b/client/src/app/site/polls/models/view-base-poll.ts @@ -1,11 +1,10 @@ import { BasePoll, PercentBase, PollType } from 'app/shared/models/poll/base-poll'; -import { ViewAssignmentOption } from 'app/site/assignments/models/view-assignment-option'; import { BaseProjectableViewModel } from 'app/site/base/base-projectable-view-model'; import { BaseViewModel } from 'app/site/base/base-view-model'; import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable'; -import { ViewMotionOption } from 'app/site/motions/models/view-motion-option'; import { ViewGroup } from 'app/site/users/models/view-group'; import { ViewUser } from 'app/site/users/models/view-user'; +import { ViewBaseOption } from './view-base-option'; export enum PollClassType { Motion = 'motion', @@ -73,9 +72,6 @@ export const MajorityMethodVerbose = { disabled: 'Disabled' }; -/** - * TODO: These need to be in order - */ export const PercentBaseVerbose = { YN: 'Yes/No', YNA: 'Yes/No/Abstain', @@ -84,7 +80,11 @@ export const PercentBaseVerbose = { disabled: 'Disabled' }; -export abstract class ViewBasePoll = any> extends BaseProjectableViewModel { +export abstract class ViewBasePoll< + M extends BasePoll = any, + PM extends string = string, + PB extends string = string +> extends BaseProjectableViewModel { private _tableData: PollTableData[] = []; protected voteTableKeys: VotingResult[] = [ @@ -157,15 +157,15 @@ export abstract class ViewBasePoll = any> extends Bas return MajorityMethodVerbose[this.majority_method]; } - public get percentBaseVerbose(): string { - return PercentBaseVerbose[this.onehundred_percent_base]; - } + public abstract get pollmethodVerbose(): string; + + public abstract get percentBaseVerbose(): string; public get showAbstainPercent(): boolean { return this.onehundred_percent_base === PercentBase.YNA; } - public abstract readonly pollClassType: 'motion' | 'assignment'; + public abstract readonly pollClassType: PollClassType; public canBeVotedFor: () => boolean; @@ -188,8 +188,12 @@ export abstract class ViewBasePoll = any> extends Bas public abstract generateTableData(): PollTableData[]; } -export interface ViewBasePoll = any> extends BasePoll { +export interface ViewBasePoll< + M extends BasePoll = any, + PM extends string = string, + PB extends string = string +> extends BasePoll { voted: ViewUser[]; groups: ViewGroup[]; - options: (ViewMotionOption | ViewAssignmentOption)[]; // TODO find a better solution. but works for the moment + options: ViewBaseOption[]; } diff --git a/client/src/app/site/polls/models/view-base-vote.ts b/client/src/app/site/polls/models/view-base-vote.ts new file mode 100644 index 000000000..d508a9f47 --- /dev/null +++ b/client/src/app/site/polls/models/view-base-vote.ts @@ -0,0 +1,15 @@ +import { BaseVote } from 'app/shared/models/poll/base-vote'; +import { ViewUser } from 'app/site/users/models/view-user'; +import { BaseViewModel } from '../../base/base-view-model'; +import { ViewBaseOption } from './view-base-option'; + +export class ViewBaseVote = any> extends BaseViewModel { + public get vote(): M { + return this._model; + } +} + +export interface ViewBaseVote = any> extends BaseVote { + user?: ViewUser; + option: ViewBaseOption; +} diff --git a/client/src/app/site/polls/services/poll-list-observable.service.ts b/client/src/app/site/polls/services/poll-list-observable.service.ts index 184fc6d2e..e7577e89f 100644 --- a/client/src/app/site/polls/services/poll-list-observable.service.ts +++ b/client/src/app/site/polls/services/poll-list-observable.service.ts @@ -9,7 +9,7 @@ import { MotionPollRepositoryService } from 'app/core/repositories/motions/motio import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll'; import { BaseViewModel } from 'app/site/base/base-view-model'; import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll'; -import { ViewBasePoll } from '../models/view-base-poll'; +import { PollClassType, ViewBasePoll } from '../models/view-base-poll'; @Injectable({ providedIn: 'root' @@ -28,13 +28,13 @@ export class PollListObservableService implements HasViewModelListObservable this.adjustViewModelListObservable(polls, 'motion')); + .subscribe(polls => this.adjustViewModelListObservable(polls, PollClassType.Motion)); assignmentPollRepo .getViewModelListObservable() - .subscribe(polls => this.adjustViewModelListObservable(polls, 'assignment')); + .subscribe(polls => this.adjustViewModelListObservable(polls, PollClassType.Assignment)); } - private adjustViewModelListObservable(polls: ViewBasePoll[], mode: 'motion' | 'assignment'): void { + private adjustViewModelListObservable(polls: ViewBasePoll[], mode: PollClassType): void { this[mode + 'Polls'] = polls; const allPolls = (this.motionPolls as ViewBasePoll[]).concat(this.assignmentPolls); diff --git a/client/src/app/site/polls/services/poll.service.ts b/client/src/app/site/polls/services/poll.service.ts index 47ca68e41..ecd2839f6 100644 --- a/client/src/app/site/polls/services/poll.service.ts +++ b/client/src/app/site/polls/services/poll.service.ts @@ -2,10 +2,10 @@ import { Injectable } from '@angular/core'; import { _ } from 'app/core/translate/translation-marker'; import { ChartData, ChartType } from 'app/shared/components/charts/charts.component'; -import { AssignmentPollMethods } from 'app/shared/models/assignments/assignment-poll'; -import { MotionPollMethods } from 'app/shared/models/motions/motion-poll'; -import { BasePoll, MajorityMethod, PercentBase, PollColor, PollType } from 'app/shared/models/poll/base-poll'; -import { AssignmentPollMethodsVerbose } from 'app/site/assignments/models/view-assignment-poll'; +import { AssignmentPollMethod } from 'app/shared/models/assignments/assignment-poll'; +import { MotionPollMethod } from 'app/shared/models/motions/motion-poll'; +import { BasePoll, MajorityMethod, PollColor, PollType } from 'app/shared/models/poll/base-poll'; +import { AssignmentPollMethodVerbose } from 'app/site/assignments/models/view-assignment-poll'; import { MajorityMethodVerbose, PercentBaseVerbose, @@ -90,8 +90,8 @@ export const PollMajorityMethod: CalculableMajorityMethod[] = [ ]; export interface PollData { - pollmethod?: string; - onehundred_percent_base: PercentBase; + pollmethod: string; + onehundred_percent_base: string; options: { user?: { full_name: string; @@ -120,7 +120,7 @@ export abstract class PollService { /** * The default percentage base */ - public abstract defaultPercentBase: PercentBase; + public abstract defaultPercentBase: string; /** * The default majority method @@ -170,7 +170,7 @@ export abstract class PollService { case 'onehundred_percent_base': return PercentBaseVerbose[value]; case 'pollmethod': - return AssignmentPollMethodsVerbose[value]; + return AssignmentPollMethodVerbose[value]; case 'type': return PollTypeVerbose[value]; } @@ -181,7 +181,7 @@ export abstract class PollService { } public generateChartData(poll: PollData): ChartData { - if (poll.pollmethod === AssignmentPollMethods.Votes) { + if (poll.pollmethod === AssignmentPollMethod.Votes) { return this.generateCircleChartData(poll); } else { return this.generateBarChartData(poll); @@ -191,7 +191,7 @@ export abstract class PollService { public generateBarChartData(poll: PollData): ChartData { const fields = ['yes', 'no']; // cast is needed because ViewBasePoll doesn't have the field `pollmethod`, no easy fix :( - if ((poll).pollmethod === MotionPollMethods.YNA) { + if ((poll).pollmethod === MotionPollMethod.YNA) { fields.push('abstain'); } const data: ChartData = fields.map(key => ({ @@ -213,7 +213,7 @@ export abstract class PollService { } public getChartType(poll: PollData): ChartType { - if ((poll).pollmethod === AssignmentPollMethods.Votes) { + if ((poll).pollmethod === AssignmentPollMethod.Votes) { return 'doughnut'; } else { return 'horizontalBar'; diff --git a/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide-data.ts b/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide-data.ts index d510fcc5c..e36d22df4 100644 --- a/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide-data.ts +++ b/client/src/app/slides/assignments/assignment-poll/assignment-poll-slide-data.ts @@ -1,4 +1,4 @@ -import { AssignmentPollMethods } from 'app/shared/models/assignments/assignment-poll'; +import { AssignmentPollMethod } from 'app/shared/models/assignments/assignment-poll'; import { MajorityMethod, PercentBase, PollState, PollType } from 'app/shared/models/poll/base-poll'; import { AssignmentTitleInformation } from 'app/site/assignments/models/view-assignment'; import { BasePollSlideData } from 'app/slides/polls/base-poll-slide-data'; @@ -8,7 +8,7 @@ export interface AssignmentPollSlideData extends BasePollSlideData { poll: { title: string; type: PollType; - pollmethod: AssignmentPollMethods; + pollmethod: AssignmentPollMethod; votes_amount: number; description: string; state: PollState; diff --git a/client/src/app/slides/motions/motion-poll/motion-poll-slide-data.ts b/client/src/app/slides/motions/motion-poll/motion-poll-slide-data.ts index 435bbd42d..1ca3e479f 100644 --- a/client/src/app/slides/motions/motion-poll/motion-poll-slide-data.ts +++ b/client/src/app/slides/motions/motion-poll/motion-poll-slide-data.ts @@ -1,4 +1,4 @@ -import { MotionPollMethods } from 'app/shared/models/motions/motion-poll'; +import { MotionPollMethod } from 'app/shared/models/motions/motion-poll'; import { MajorityMethod, PercentBase, PollState, PollType } from 'app/shared/models/poll/base-poll'; import { MotionTitleInformation } from 'app/site/motions/models/view-motion'; import { BasePollSlideData } from 'app/slides/polls/base-poll-slide-data'; @@ -8,7 +8,7 @@ export interface MotionPollSlideData extends BasePollSlideData { poll: { title: string; type: PollType; - pollmethod: MotionPollMethods; + pollmethod: MotionPollMethod; state: PollState; onehundred_percent_base: PercentBase; majority_method: MajorityMethod; diff --git a/openslides/motions/migrations/0033_voting_1.py b/openslides/motions/migrations/0033_voting_1.py index 88c3cf38a..da78dd429 100644 --- a/openslides/motions/migrations/0033_voting_1.py +++ b/openslides/motions/migrations/0033_voting_1.py @@ -96,8 +96,8 @@ class Migration(migrations.Migration): name="onehundred_percent_base", field=models.CharField( choices=[ - ("YN", "Yes/No per candidate"), - ("YNA", "Yes/No/Abstain per candidate"), + ("YN", "Yes/No"), + ("YNA", "Yes/No/Abstain"), ("valid", "All valid ballots"), ("cast", "All casted ballots"), ("disabled", "Disabled (no percents)"), diff --git a/openslides/poll/models.py b/openslides/poll/models.py index 5b81a3337..23ab76f58 100644 --- a/openslides/poll/models.py +++ b/openslides/poll/models.py @@ -155,8 +155,8 @@ class BasePoll(models.Model): PERCENT_BASE_CAST = "cast" PERCENT_BASE_DISABLED = "disabled" PERCENT_BASES: Iterable[Tuple[str, str]] = ( - (PERCENT_BASE_YN, "Yes/No per candidate"), - (PERCENT_BASE_YNA, "Yes/No/Abstain per candidate"), + (PERCENT_BASE_YN, "Yes/No"), + (PERCENT_BASE_YNA, "Yes/No/Abstain"), (PERCENT_BASE_VALID, "All valid ballots"), (PERCENT_BASE_CAST, "All casted ballots"), (PERCENT_BASE_DISABLED, "Disabled (no percents)"),