table update on pseudoanonymize, view base classes for votes and
options, renaming for assignment percent bases
This commit is contained in:
parent
82c8ade0ba
commit
72678770bb
@ -2,24 +2,36 @@ import { CalculablePollKey } from 'app/site/polls/services/poll.service';
|
|||||||
import { AssignmentOption } from './assignment-option';
|
import { AssignmentOption } from './assignment-option';
|
||||||
import { BasePoll } from '../poll/base-poll';
|
import { BasePoll } from '../poll/base-poll';
|
||||||
|
|
||||||
export enum AssignmentPollMethods {
|
export enum AssignmentPollMethod {
|
||||||
YN = 'YN',
|
YN = 'YN',
|
||||||
YNA = 'YNA',
|
YNA = 'YNA',
|
||||||
Votes = 'votes'
|
Votes = 'votes'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum AssignmentPollPercentBase {
|
||||||
|
YN = 'YN',
|
||||||
|
YNA = 'YNA',
|
||||||
|
Votes = 'votes',
|
||||||
|
Valid = 'valid',
|
||||||
|
Cast = 'cast',
|
||||||
|
Disabled = 'disabled'
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Content of the 'polls' property of assignments
|
* Class representing a poll for an assignment.
|
||||||
* @ignore
|
|
||||||
*/
|
*/
|
||||||
export class AssignmentPoll extends BasePoll<AssignmentPoll, AssignmentOption> {
|
export class AssignmentPoll extends BasePoll<
|
||||||
|
AssignmentPoll,
|
||||||
|
AssignmentOption,
|
||||||
|
AssignmentPollMethod,
|
||||||
|
AssignmentPollPercentBase
|
||||||
|
> {
|
||||||
public static COLLECTIONSTRING = 'assignments/assignment-poll';
|
public static COLLECTIONSTRING = 'assignments/assignment-poll';
|
||||||
public static defaultGroupsConfig = 'assignment_poll_default_groups';
|
public static defaultGroupsConfig = 'assignment_poll_default_groups';
|
||||||
public static defaultPollMethodConfig = 'assignment_poll_method';
|
public static defaultPollMethodConfig = 'assignment_poll_method';
|
||||||
|
|
||||||
public id: number;
|
public id: number;
|
||||||
public assignment_id: number;
|
public assignment_id: number;
|
||||||
public pollmethod: AssignmentPollMethods;
|
|
||||||
public votes_amount: number;
|
public votes_amount: number;
|
||||||
public allow_multiple_votes_per_candidate: boolean;
|
public allow_multiple_votes_per_candidate: boolean;
|
||||||
public global_no: boolean;
|
public global_no: boolean;
|
||||||
@ -27,11 +39,11 @@ export class AssignmentPoll extends BasePoll<AssignmentPoll, AssignmentOption> {
|
|||||||
public description: string;
|
public description: string;
|
||||||
|
|
||||||
public get pollmethodFields(): CalculablePollKey[] {
|
public get pollmethodFields(): CalculablePollKey[] {
|
||||||
if (this.pollmethod === AssignmentPollMethods.YN) {
|
if (this.pollmethod === AssignmentPollMethod.YN) {
|
||||||
return ['yes', 'no'];
|
return ['yes', 'no'];
|
||||||
} else if (this.pollmethod === AssignmentPollMethods.YNA) {
|
} else if (this.pollmethod === AssignmentPollMethod.YNA) {
|
||||||
return ['yes', 'no', 'abstain'];
|
return ['yes', 'no', 'abstain'];
|
||||||
} else if (this.pollmethod === AssignmentPollMethods.Votes) {
|
} else if (this.pollmethod === AssignmentPollMethod.Votes) {
|
||||||
return ['yes'];
|
return ['yes'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { CalculablePollKey } from 'app/site/polls/services/poll.service';
|
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';
|
import { MotionOption } from './motion-option';
|
||||||
|
|
||||||
export enum MotionPollMethods {
|
export enum MotionPollMethod {
|
||||||
YN = 'YN',
|
YN = 'YN',
|
||||||
YNA = 'YNA'
|
YNA = 'YNA'
|
||||||
}
|
}
|
||||||
@ -10,19 +10,18 @@ export enum MotionPollMethods {
|
|||||||
/**
|
/**
|
||||||
* Class representing a poll for a motion.
|
* Class representing a poll for a motion.
|
||||||
*/
|
*/
|
||||||
export class MotionPoll extends BasePoll<MotionPoll, MotionOption> {
|
export class MotionPoll extends BasePoll<MotionPoll, MotionOption, MotionPollMethod, PercentBase> {
|
||||||
public static COLLECTIONSTRING = 'motions/motion-poll';
|
public static COLLECTIONSTRING = 'motions/motion-poll';
|
||||||
public static defaultGroupsConfig = 'motion_poll_default_groups';
|
public static defaultGroupsConfig = 'motion_poll_default_groups';
|
||||||
|
|
||||||
public id: number;
|
public id: number;
|
||||||
public motion_id: number;
|
public motion_id: number;
|
||||||
public pollmethod: MotionPollMethods;
|
|
||||||
|
|
||||||
public get pollmethodFields(): CalculablePollKey[] {
|
public get pollmethodFields(): CalculablePollKey[] {
|
||||||
const ynField: CalculablePollKey[] = ['yes', 'no'];
|
const ynField: CalculablePollKey[] = ['yes', 'no'];
|
||||||
if (this.pollmethod === MotionPollMethods.YN) {
|
if (this.pollmethod === MotionPollMethod.YN) {
|
||||||
return ynField;
|
return ynField;
|
||||||
} else if (this.pollmethod === MotionPollMethods.YNA) {
|
} else if (this.pollmethod === MotionPollMethod.YNA) {
|
||||||
return ynField.concat(['abstain']);
|
return ynField.concat(['abstain']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,13 @@ export enum PollType {
|
|||||||
Pseudoanonymous = 'pseudoanonymous'
|
Pseudoanonymous = 'pseudoanonymous'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum MajorityMethod {
|
||||||
|
Simple = 'simple',
|
||||||
|
TwoThirds = 'two_thirds',
|
||||||
|
ThreeQuarters = 'three_quarters',
|
||||||
|
Disabled = 'disabled'
|
||||||
|
}
|
||||||
|
|
||||||
export enum PercentBase {
|
export enum PercentBase {
|
||||||
YN = 'YN',
|
YN = 'YN',
|
||||||
YNA = 'YNA',
|
YNA = 'YNA',
|
||||||
@ -31,14 +38,12 @@ export enum PercentBase {
|
|||||||
Disabled = 'disabled'
|
Disabled = 'disabled'
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum MajorityMethod {
|
export abstract class BasePoll<
|
||||||
Simple = 'simple',
|
T = any,
|
||||||
TwoThirds = 'two_thirds',
|
O extends BaseOption<any> = any,
|
||||||
ThreeQuarters = 'three_quarters',
|
PM extends string = string,
|
||||||
Disabled = 'disabled'
|
PB extends string = string
|
||||||
}
|
> extends BaseDecimalModel<T> {
|
||||||
|
|
||||||
export abstract class BasePoll<T = any, O extends BaseOption<any> = any> extends BaseDecimalModel<T> {
|
|
||||||
public state: PollState;
|
public state: PollState;
|
||||||
public type: PollType;
|
public type: PollType;
|
||||||
public title: string;
|
public title: string;
|
||||||
@ -47,7 +52,9 @@ export abstract class BasePoll<T = any, O extends BaseOption<any> = any> extends
|
|||||||
public votescast: number;
|
public votescast: number;
|
||||||
public groups_id: number[];
|
public groups_id: number[];
|
||||||
public majority_method: MajorityMethod;
|
public majority_method: MajorityMethod;
|
||||||
public onehundred_percent_base: PercentBase;
|
|
||||||
|
public pollmethod: PM;
|
||||||
|
public onehundred_percent_base: PB;
|
||||||
|
|
||||||
public get isCreated(): boolean {
|
public get isCreated(): boolean {
|
||||||
return this.state === PollState.Created;
|
return this.state === PollState.Created;
|
||||||
|
@ -16,7 +16,7 @@ export const GeneralValueVerbose = {
|
|||||||
votesabstain: 'Votes abstain'
|
votesabstain: 'Votes abstain'
|
||||||
};
|
};
|
||||||
|
|
||||||
export abstract class BaseVote<T> extends BaseDecimalModel<T> {
|
export abstract class BaseVote<T = any> extends BaseDecimalModel<T> {
|
||||||
public weight: number;
|
public weight: number;
|
||||||
public value: VoteValue;
|
public value: VoteValue;
|
||||||
public option_id: number;
|
public option_id: number;
|
||||||
|
@ -103,7 +103,7 @@
|
|||||||
<b *ngIf="!vote.user">{{ 'Anonymous' | translate }}</b>
|
<b *ngIf="!vote.user">{{ 'Anonymous' | translate }}</b>
|
||||||
</div>
|
</div>
|
||||||
<!-- Y/N/(A) -->
|
<!-- Y/N/(A) -->
|
||||||
<ng-container *ngIf="poll.pollmethod !== AssignmentPollMethods.Votes">
|
<ng-container *ngIf="poll.pollmethod !== AssignmentPollMethod.Votes">
|
||||||
<ng-container *ngFor="let option of poll.options">
|
<ng-container *ngFor="let option of poll.options">
|
||||||
<div
|
<div
|
||||||
*pblNgridCellDef="'votes-' + option.user_id; row as vote"
|
*pblNgridCellDef="'votes-' + option.user_id; row as vote"
|
||||||
@ -116,7 +116,7 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<!-- Votes method -->
|
<!-- Votes method -->
|
||||||
<ng-container *ngIf="poll.pollmethod === AssignmentPollMethods.Votes">
|
<ng-container *ngIf="poll.pollmethod === AssignmentPollMethod.Votes">
|
||||||
<div *pblNgridCellDef="'votes'; row as vote">
|
<div *pblNgridCellDef="'votes'; row as vote">
|
||||||
<div *ngFor="let candidate of vote.votes">{{ candidate }}</div>
|
<div *ngFor="let candidate of vote.votes">{{ candidate }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,11 +8,12 @@ import { PblColumnDefinition } from '@pebula/ngrid';
|
|||||||
|
|
||||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||||
import { AssignmentPollRepositoryService } from 'app/core/repositories/assignments/assignment-poll-repository.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 { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service';
|
||||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
import { ViewportService } from 'app/core/ui-services/viewport.service';
|
import { ViewportService } from 'app/core/ui-services/viewport.service';
|
||||||
import { ChartType } from 'app/shared/components/charts/charts.component';
|
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 { BasePollDetailComponent } from 'app/site/polls/components/base-poll-detail.component';
|
||||||
import { VotingResult } from 'app/site/polls/models/view-base-poll';
|
import { VotingResult } from 'app/site/polls/models/view-base-poll';
|
||||||
import { PollService } from 'app/site/polls/services/poll.service';
|
import { PollService } from 'app/site/polls/services/poll.service';
|
||||||
@ -26,7 +27,7 @@ import { ViewAssignmentPoll } from '../../models/view-assignment-poll';
|
|||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class AssignmentPollDetailComponent extends BasePollDetailComponent<ViewAssignmentPoll> {
|
export class AssignmentPollDetailComponent extends BasePollDetailComponent<ViewAssignmentPoll> {
|
||||||
public AssignmentPollMethods = AssignmentPollMethods;
|
public AssignmentPollMethod = AssignmentPollMethod;
|
||||||
|
|
||||||
public columnDefinitionSingleVotes: PblColumnDefinition[];
|
public columnDefinitionSingleVotes: PblColumnDefinition[];
|
||||||
|
|
||||||
@ -41,7 +42,7 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponent<ViewA
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get isVotedPoll(): boolean {
|
public get isVotedPoll(): boolean {
|
||||||
return this.poll.pollmethod === AssignmentPollMethods.Votes;
|
return this.poll.pollmethod === AssignmentPollMethod.Votes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _chartType: ChartType = 'horizontalBar';
|
private _chartType: ChartType = 'horizontalBar';
|
||||||
@ -56,17 +57,18 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponent<ViewA
|
|||||||
prompt: PromptService,
|
prompt: PromptService,
|
||||||
pollDialog: AssignmentPollDialogService,
|
pollDialog: AssignmentPollDialogService,
|
||||||
pollService: PollService,
|
pollService: PollService,
|
||||||
|
votesRepo: AssignmentVoteRepositoryService,
|
||||||
private operator: OperatorService,
|
private operator: OperatorService,
|
||||||
private viewport: ViewportService
|
private viewport: ViewportService
|
||||||
) {
|
) {
|
||||||
super(title, translate, matSnackbar, repo, route, groupRepo, prompt, pollDialog, pollService);
|
super(title, translate, matSnackbar, repo, route, groupRepo, prompt, pollDialog, pollService, votesRepo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onPollWithOptionsLoaded(): void {
|
protected createVotesData(): void {
|
||||||
const votes = {};
|
const votes = {};
|
||||||
let i = -1;
|
let i = -1;
|
||||||
|
|
||||||
this.columnDefinitionSingleVotes = [
|
const definitions: PblColumnDefinition[] = [
|
||||||
{
|
{
|
||||||
prop: 'user',
|
prop: 'user',
|
||||||
label: 'Participant',
|
label: 'Participant',
|
||||||
@ -75,7 +77,7 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponent<ViewA
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
if (this.isVotedPoll) {
|
if (this.isVotedPoll) {
|
||||||
this.columnDefinitionSingleVotes.push(this.getVoteColumnDefinition('votes', 'Votes'));
|
definitions.push(this.getVoteColumnDefinition('votes', 'Votes'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,9 +92,7 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponent<ViewA
|
|||||||
*/
|
*/
|
||||||
for (const option of this.poll.options) {
|
for (const option of this.poll.options) {
|
||||||
if (!this.isVotedPoll) {
|
if (!this.isVotedPoll) {
|
||||||
this.columnDefinitionSingleVotes.push(
|
definitions.push(this.getVoteColumnDefinition('votes-' + option.user_id, option.user.getFullName()));
|
||||||
this.getVoteColumnDefinition('votes-' + option.user_id, option.user.getFullName())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const vote of option.votes) {
|
for (const vote of option.votes) {
|
||||||
@ -125,6 +125,7 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponent<ViewA
|
|||||||
|
|
||||||
this.setVotesData(Object.values(votes));
|
this.setVotesData(Object.values(votes));
|
||||||
this.candidatesLabels = this.pollService.getChartLabels(this.poll);
|
this.candidatesLabels = this.pollService.getChartLabels(this.poll);
|
||||||
|
this.columnDefinitionSingleVotes = definitions;
|
||||||
this.isReady = true;
|
this.isReady = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,11 +152,11 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponent<ViewA
|
|||||||
}
|
}
|
||||||
|
|
||||||
public voteFitsMethod(result: VotingResult): boolean {
|
public voteFitsMethod(result: VotingResult): boolean {
|
||||||
if (this.poll.pollmethod === AssignmentPollMethods.Votes) {
|
if (this.poll.pollmethod === AssignmentPollMethod.Votes) {
|
||||||
if (result.vote === 'abstain' || result.vote === 'no') {
|
if (result.vote === 'abstain' || result.vote === 'no') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (this.poll.pollmethod === AssignmentPollMethods.YN) {
|
} else if (this.poll.pollmethod === AssignmentPollMethod.YN) {
|
||||||
if (result.vote === 'abstain') {
|
if (result.vote === 'abstain') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<os-poll-form [data]="pollData" [pollMethods]="assignmentPollMethods" #pollForm></os-poll-form>
|
<os-poll-form [data]="pollData" [pollMethods]="AssignmentPollMethodVerbose" [percentBases]="AssignmentPollPercentBaseVerbose" #pollForm></os-poll-form>
|
||||||
|
|
||||||
<!-- Analog voting -->
|
<!-- Analog voting -->
|
||||||
<ng-container *ngIf="pollForm.contentForm.get('type').value === 'analog'">
|
<ng-container *ngIf="pollForm.contentForm.get('type').value === 'analog'">
|
||||||
|
@ -6,9 +6,12 @@ import { Title } from '@angular/platform-browser';
|
|||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
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 { 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 { BasePollDialogComponent } from 'app/site/polls/components/base-poll-dialog.component';
|
||||||
import { PollFormComponent } from 'app/site/polls/components/poll-form/poll-form.component';
|
import { PollFormComponent } from 'app/site/polls/components/poll-form/poll-form.component';
|
||||||
import { ViewUser } from 'app/site/users/models/view-user';
|
import { ViewUser } from 'app/site/users/models/view-user';
|
||||||
@ -50,7 +53,8 @@ export class AssignmentPollDialogComponent extends BasePollDialogComponent<ViewA
|
|||||||
public voteValueVerbose = VoteValueVerbose;
|
public voteValueVerbose = VoteValueVerbose;
|
||||||
public generalValueVerbose = GeneralValueVerbose;
|
public generalValueVerbose = GeneralValueVerbose;
|
||||||
|
|
||||||
public assignmentPollMethods = AssignmentPollMethodsVerbose;
|
public AssignmentPollMethodVerbose = AssignmentPollMethodVerbose;
|
||||||
|
public AssignmentPollPercentBaseVerbose = AssignmentPollPercentBaseVerbose;
|
||||||
|
|
||||||
public options: OptionsObject;
|
public options: OptionsObject;
|
||||||
|
|
||||||
@ -92,10 +96,10 @@ export class AssignmentPollDialogComponent extends BasePollDialogComponent<ViewA
|
|||||||
private setAnalogPollValues(): void {
|
private setAnalogPollValues(): void {
|
||||||
const pollmethod = this.pollForm.contentForm.get('pollmethod').value;
|
const pollmethod = this.pollForm.contentForm.get('pollmethod').value;
|
||||||
this.analogPollValues = ['Y'];
|
this.analogPollValues = ['Y'];
|
||||||
if (pollmethod !== AssignmentPollMethods.Votes) {
|
if (pollmethod !== AssignmentPollMethod.Votes) {
|
||||||
this.analogPollValues.push('N');
|
this.analogPollValues.push('N');
|
||||||
}
|
}
|
||||||
if (pollmethod === AssignmentPollMethods.YNA) {
|
if (pollmethod === AssignmentPollMethod.YNA) {
|
||||||
this.analogPollValues.push('A');
|
this.analogPollValues.push('A');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,10 +114,10 @@ export class AssignmentPollDialogComponent extends BasePollDialogComponent<ViewA
|
|||||||
for (const option of data.options) {
|
for (const option of data.options) {
|
||||||
const votes: any = {};
|
const votes: any = {};
|
||||||
votes.Y = option.yes;
|
votes.Y = option.yes;
|
||||||
if (data.pollmethod !== AssignmentPollMethods.Votes) {
|
if (data.pollmethod !== AssignmentPollMethod.Votes) {
|
||||||
votes.N = option.no;
|
votes.N = option.no;
|
||||||
}
|
}
|
||||||
if (data.pollmethod === AssignmentPollMethods.YNA) {
|
if (data.pollmethod === AssignmentPollMethod.YNA) {
|
||||||
votes.A = option.abstain;
|
votes.A = option.abstain;
|
||||||
}
|
}
|
||||||
update.options[option.user_id] = votes;
|
update.options[option.user_id] = votes;
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<span *ngIf="poll.user_has_not_voted">You have not give any voting here!</span>
|
<span *ngIf="poll.user_has_not_voted">You have not give any voting here!</span>
|
||||||
|
|
||||||
<!-- Leftover votes -->
|
<!-- Leftover votes -->
|
||||||
<h4 *ngIf="poll.pollmethod === pollMethods.Votes && poll.votes_amount > 1 && !currentVotes.global">
|
<h4 *ngIf="poll.pollmethod === AssignmentPollMethod.Votes && poll.votes_amount > 1 && !currentVotes.global">
|
||||||
{{ 'Votes for this poll' | translate }}: {{ getVotesCount() }}/{{ poll.votes_amount }}
|
{{ 'Votes for this poll' | translate }}: {{ getVotesCount() }}/{{ poll.votes_amount }}
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
@ -16,9 +16,9 @@
|
|||||||
<div *ngIf="poll.type !== PollType.Pseudoanonymous || !option.user_has_voted">
|
<div *ngIf="poll.type !== PollType.Pseudoanonymous || !option.user_has_voted">
|
||||||
<div
|
<div
|
||||||
[ngClass]="{
|
[ngClass]="{
|
||||||
'yna-grid': poll.pollmethod === pollMethods.YNA,
|
'yna-grid': poll.pollmethod === AssignmentPollMethod.YNA,
|
||||||
'yn-grid': poll.pollmethod === pollMethods.YN,
|
'yn-grid': poll.pollmethod === AssignmentPollMethod.YN,
|
||||||
'single-vote-grid': poll.pollmethod === pollMethods.Votes
|
'single-vote-grid': poll.pollmethod === AssignmentPollMethod.Votes
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div class="vote-candidate-name">
|
<div class="vote-candidate-name">
|
||||||
@ -34,7 +34,7 @@
|
|||||||
>
|
>
|
||||||
<mat-icon> {{ action.icon }}</mat-icon>
|
<mat-icon> {{ action.icon }}</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<span *ngIf="poll.pollmethod !== pollMethods.Votes" class="vote-label">
|
<span *ngIf="poll.pollmethod !== AssignmentPollMethod.Votes" class="vote-label">
|
||||||
{{ action.label | translate }}
|
{{ action.label | translate }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -44,7 +44,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- global no/abstain -->
|
<!-- global no/abstain -->
|
||||||
<ng-container *ngIf="poll.pollmethod === pollMethods.Votes && (poll.global_no || poll.global_abstain)">
|
<ng-container *ngIf="poll.pollmethod === AssignmentPollMethod.Votes && (poll.global_no || poll.global_abstain)">
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
<div class="global-option-grid">
|
<div class="global-option-grid">
|
||||||
<div *ngIf="poll.global_no">
|
<div *ngIf="poll.global_no">
|
||||||
|
@ -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 { AssignmentPollRepositoryService } from 'app/core/repositories/assignments/assignment-poll-repository.service';
|
||||||
import { AssignmentVoteRepositoryService } from 'app/core/repositories/assignments/assignment-vote-repository.service';
|
import { AssignmentVoteRepositoryService } from 'app/core/repositories/assignments/assignment-vote-repository.service';
|
||||||
import { VotingService } from 'app/core/ui-services/voting.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 { PollType } from 'app/shared/models/poll/base-poll';
|
||||||
import { BasePollVoteComponent } from 'app/site/polls/components/base-poll-vote.component';
|
import { BasePollVoteComponent } from 'app/site/polls/components/base-poll-vote.component';
|
||||||
import { ViewAssignmentPoll } from '../../models/view-assignment-poll';
|
import { ViewAssignmentPoll } from '../../models/view-assignment-poll';
|
||||||
@ -28,7 +28,7 @@ interface VoteActions {
|
|||||||
styleUrls: ['./assignment-poll-vote.component.scss']
|
styleUrls: ['./assignment-poll-vote.component.scss']
|
||||||
})
|
})
|
||||||
export class AssignmentPollVoteComponent extends BasePollVoteComponent<ViewAssignmentPoll> implements OnInit {
|
export class AssignmentPollVoteComponent extends BasePollVoteComponent<ViewAssignmentPoll> implements OnInit {
|
||||||
public pollMethods = AssignmentPollMethods;
|
public AssignmentPollMethod = AssignmentPollMethod;
|
||||||
public PollType = PollType;
|
public PollType = PollType;
|
||||||
public voteActions: VoteActions[] = [];
|
public voteActions: VoteActions[] = [];
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ export class AssignmentPollVoteComponent extends BasePollVoteComponent<ViewAssig
|
|||||||
label: 'Yes'
|
label: 'Yes'
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.poll.pollmethod !== AssignmentPollMethods.Votes) {
|
if (this.poll.pollmethod !== AssignmentPollMethod.Votes) {
|
||||||
this.voteActions.push({
|
this.voteActions.push({
|
||||||
vote: 'N',
|
vote: 'N',
|
||||||
css: 'voted-no',
|
css: 'voted-no',
|
||||||
@ -79,7 +79,7 @@ export class AssignmentPollVoteComponent extends BasePollVoteComponent<ViewAssig
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.poll.pollmethod === AssignmentPollMethods.YNA) {
|
if (this.poll.pollmethod === AssignmentPollMethod.YNA) {
|
||||||
this.voteActions.push({
|
this.voteActions.push({
|
||||||
vote: 'A',
|
vote: 'A',
|
||||||
css: 'voted-abstain',
|
css: 'voted-abstain',
|
||||||
@ -101,7 +101,7 @@ export class AssignmentPollVoteComponent extends BasePollVoteComponent<ViewAssig
|
|||||||
|
|
||||||
for (const option of this.poll.options) {
|
for (const option of this.poll.options) {
|
||||||
let curr_vote = filtered.find(vote => vote.option.id === option.id);
|
let curr_vote = filtered.find(vote => 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') {
|
if (curr_vote.value !== 'Y') {
|
||||||
this.currentVotes.global = curr_vote.valueVerbose;
|
this.currentVotes.global = curr_vote.valueVerbose;
|
||||||
curr_vote = null;
|
curr_vote = null;
|
||||||
@ -120,7 +120,7 @@ export class AssignmentPollVoteComponent extends BasePollVoteComponent<ViewAssig
|
|||||||
|
|
||||||
public saveSingleVote(optionId: number, vote: 'Y' | 'N' | 'A'): void {
|
public saveSingleVote(optionId: number, vote: 'Y' | 'N' | 'A'): void {
|
||||||
let requestData;
|
let requestData;
|
||||||
if (this.poll.pollmethod === AssignmentPollMethods.Votes) {
|
if (this.poll.pollmethod === AssignmentPollMethod.Votes) {
|
||||||
const pollOptionIds = this.getPollOptionIds();
|
const pollOptionIds = this.getPollOptionIds();
|
||||||
requestData = pollOptionIds.reduce((o, n) => {
|
requestData = pollOptionIds.reduce((o, n) => {
|
||||||
if ((n === optionId && vote === 'Y') !== (this.currentVotes[n] === 'Yes')) {
|
if ((n === optionId && vote === 'Y') !== (this.currentVotes[n] === 'Yes')) {
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="hasVotes">
|
<div *ngIf="canSeeVotes">
|
||||||
<os-charts
|
<os-charts
|
||||||
[class]="chartType === 'doughnut' ? 'doughnut-chart' : 'bar-chart'"
|
[class]="chartType === 'doughnut' ? 'doughnut-chart' : 'bar-chart'"
|
||||||
[type]="chartType"
|
[type]="chartType"
|
||||||
|
@ -10,7 +10,6 @@ import { OperatorService } from 'app/core/core-services/operator.service';
|
|||||||
import { AssignmentPollRepositoryService } from 'app/core/repositories/assignments/assignment-poll-repository.service';
|
import { AssignmentPollRepositoryService } from 'app/core/repositories/assignments/assignment-poll-repository.service';
|
||||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
import { ChartType } from 'app/shared/components/charts/charts.component';
|
import { ChartType } from 'app/shared/components/charts/charts.component';
|
||||||
import { PollState } from 'app/shared/models/poll/base-poll';
|
|
||||||
import { BasePollComponent } from 'app/site/polls/components/base-poll.component';
|
import { BasePollComponent } from 'app/site/polls/components/base-poll.component';
|
||||||
import { PollService } from 'app/site/polls/services/poll.service';
|
import { PollService } from 'app/site/polls/services/poll.service';
|
||||||
import { AssignmentPollDialogService } from '../../services/assignment-poll-dialog.service';
|
import { AssignmentPollDialogService } from '../../services/assignment-poll-dialog.service';
|
||||||
@ -61,8 +60,8 @@ export class AssignmentPollComponent extends BasePollComponent<ViewAssignmentPol
|
|||||||
return this.operator.hasPerms('assignments.can_see');
|
return this.operator.hasPerms('assignments.can_see');
|
||||||
}
|
}
|
||||||
|
|
||||||
public get hasVotes(): boolean {
|
public get canSeeVotes(): boolean {
|
||||||
return (this.canManage && this.poll.state === PollState.Finished) || this.poll.state === PollState.Published;
|
return (this.canManage && this.poll.isFinished) || this.poll.isPublished;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,21 +1,12 @@
|
|||||||
import { AssignmentOption } from 'app/shared/models/assignments/assignment-option';
|
import { AssignmentOption } from 'app/shared/models/assignments/assignment-option';
|
||||||
|
import { ViewBaseOption } from 'app/site/polls/models/view-base-option';
|
||||||
import { ViewUser } from 'app/site/users/models/view-user';
|
import { ViewUser } from 'app/site/users/models/view-user';
|
||||||
import { BaseViewModel } from '../../base/base-view-model';
|
|
||||||
import { ViewAssignmentPoll } from './view-assignment-poll';
|
|
||||||
import { ViewAssignmentVote } from './view-assignment-vote';
|
|
||||||
|
|
||||||
export class ViewAssignmentOption extends BaseViewModel<AssignmentOption> {
|
export class ViewAssignmentOption extends ViewBaseOption<AssignmentOption> {
|
||||||
public get option(): AssignmentOption {
|
|
||||||
return this._model;
|
|
||||||
}
|
|
||||||
public static COLLECTIONSTRING = AssignmentOption.COLLECTIONSTRING;
|
public static COLLECTIONSTRING = AssignmentOption.COLLECTIONSTRING;
|
||||||
protected _collectionString = AssignmentOption.COLLECTIONSTRING;
|
protected _collectionString = AssignmentOption.COLLECTIONSTRING;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TIAssignmentOptionRelations {
|
export interface ViewAssignmentOption extends AssignmentOption {
|
||||||
votes: ViewAssignmentVote[];
|
|
||||||
user: ViewUser;
|
user: ViewUser;
|
||||||
poll: ViewAssignmentPoll;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ViewAssignmentOption extends AssignmentOption, TIAssignmentOptionRelations {}
|
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
import { BehaviorSubject } from 'rxjs';
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
|
||||||
import { ChartData } from 'app/shared/components/charts/charts.component';
|
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 { BaseViewModel } from 'app/site/base/base-view-model';
|
||||||
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
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 { ViewAssignment } from './view-assignment';
|
||||||
import { ViewAssignmentOption } from './view-assignment-option';
|
import { ViewAssignmentOption } from './view-assignment-option';
|
||||||
|
|
||||||
@ -12,21 +16,35 @@ export interface AssignmentPollTitleInformation {
|
|||||||
title: string;
|
title: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AssignmentPollMethodsVerbose = {
|
export const AssignmentPollMethodVerbose = {
|
||||||
votes: 'Yes per candidate',
|
votes: 'Yes per candidate',
|
||||||
YN: 'Yes/No per candidate',
|
YN: 'Yes/No per candidate',
|
||||||
YNA: 'Yes/No/Abstain per candidate'
|
YNA: 'Yes/No/Abstain per candidate'
|
||||||
};
|
};
|
||||||
|
|
||||||
export class ViewAssignmentPoll extends ViewBasePoll<AssignmentPoll> 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<AssignmentPoll, AssignmentPollMethod, AssignmentPollPercentBase>
|
||||||
|
implements AssignmentPollTitleInformation {
|
||||||
public static COLLECTIONSTRING = AssignmentPoll.COLLECTIONSTRING;
|
public static COLLECTIONSTRING = AssignmentPoll.COLLECTIONSTRING;
|
||||||
protected _collectionString = AssignmentPoll.COLLECTIONSTRING;
|
protected _collectionString = AssignmentPoll.COLLECTIONSTRING;
|
||||||
|
|
||||||
public readonly tableChartData: Map<string, BehaviorSubject<ChartData>> = new Map();
|
public readonly tableChartData: Map<string, BehaviorSubject<ChartData>> = new Map();
|
||||||
public readonly pollClassType: 'assignment' | 'motion' = 'assignment';
|
public readonly pollClassType = PollClassType.Assignment;
|
||||||
|
|
||||||
public get pollmethodVerbose(): string {
|
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 {
|
public getContentObject(): BaseViewModel {
|
||||||
|
@ -1,19 +1,9 @@
|
|||||||
import { AssignmentVote } from 'app/shared/models/assignments/assignment-vote';
|
import { AssignmentVote } from 'app/shared/models/assignments/assignment-vote';
|
||||||
import { ViewUser } from 'app/site/users/models/view-user';
|
import { ViewBaseVote } from 'app/site/polls/models/view-base-vote';
|
||||||
import { BaseViewModel } from '../../base/base-view-model';
|
|
||||||
import { ViewAssignmentOption } from './view-assignment-option';
|
|
||||||
|
|
||||||
export class ViewAssignmentVote extends BaseViewModel<AssignmentVote> {
|
export class ViewAssignmentVote extends ViewBaseVote<AssignmentVote> {
|
||||||
public get vote(): AssignmentVote {
|
|
||||||
return this._model;
|
|
||||||
}
|
|
||||||
public static COLLECTIONSTRING = AssignmentVote.COLLECTIONSTRING;
|
public static COLLECTIONSTRING = AssignmentVote.COLLECTIONSTRING;
|
||||||
protected _collectionString = AssignmentVote.COLLECTIONSTRING;
|
protected _collectionString = AssignmentVote.COLLECTIONSTRING;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TIAssignmentVoteRelations {
|
export interface ViewAssignmentVote extends AssignmentVote {}
|
||||||
user: ViewUser;
|
|
||||||
option: ViewAssignmentOption;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ViewAssignmentVote extends AssignmentVote, TIAssignmentVoteRelations {}
|
|
||||||
|
@ -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 { AssignmentRepositoryService } from 'app/core/repositories/assignments/assignment-repository.service';
|
||||||
import { UserRepositoryService } from 'app/core/repositories/users/user-repository.service';
|
import { UserRepositoryService } from 'app/core/repositories/users/user-repository.service';
|
||||||
import { ConfigService } from 'app/core/ui-services/config.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';
|
import { ViewAssignmentPoll } from '../models/view-assignment-poll';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -162,7 +162,7 @@ export class AssignmentPollPdfService extends PollPdfService {
|
|||||||
return resultObject;
|
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 choices = method === 'YNA' ? ['Yes', 'No', 'Abstain'] : ['Yes', 'No'];
|
||||||
const columnstack = choices.map(choice => {
|
const columnstack = choices.map(choice => {
|
||||||
return {
|
return {
|
||||||
|
@ -5,8 +5,12 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { ConstantsService } from 'app/core/core-services/constants.service';
|
import { ConstantsService } from 'app/core/core-services/constants.service';
|
||||||
import { AssignmentPollRepositoryService } from 'app/core/repositories/assignments/assignment-poll-repository.service';
|
import { AssignmentPollRepositoryService } from 'app/core/repositories/assignments/assignment-poll-repository.service';
|
||||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
import { AssignmentPoll, AssignmentPollMethods } from 'app/shared/models/assignments/assignment-poll';
|
import {
|
||||||
import { MajorityMethod, PercentBase } from 'app/shared/models/poll/base-poll';
|
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';
|
import { PollData, PollService } from 'app/site/polls/services/poll.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
@ -16,7 +20,7 @@ export class AssignmentPollService extends PollService {
|
|||||||
/**
|
/**
|
||||||
* The default percentage base
|
* The default percentage base
|
||||||
*/
|
*/
|
||||||
public defaultPercentBase: PercentBase;
|
public defaultPercentBase: AssignmentPollPercentBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default majority method
|
* The default majority method
|
||||||
@ -25,7 +29,7 @@ export class AssignmentPollService extends PollService {
|
|||||||
|
|
||||||
public defaultGroupIds: number[];
|
public defaultGroupIds: number[];
|
||||||
|
|
||||||
public defaultPollMethod: AssignmentPollMethods;
|
public defaultPollMethod: AssignmentPollMethod;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor. Subscribes to the configuration values needed
|
* Constructor. Subscribes to the configuration values needed
|
||||||
@ -39,14 +43,14 @@ export class AssignmentPollService extends PollService {
|
|||||||
) {
|
) {
|
||||||
super(constants);
|
super(constants);
|
||||||
config
|
config
|
||||||
.get<PercentBase>('motion_poll_default_100_percent_base')
|
.get<AssignmentPollPercentBase>('assignment_poll_default_100_percent_base')
|
||||||
.subscribe(base => (this.defaultPercentBase = base));
|
.subscribe(base => (this.defaultPercentBase = base));
|
||||||
config
|
config
|
||||||
.get<MajorityMethod>('motion_poll_default_majority_method')
|
.get<MajorityMethod>('assignment_poll_default_majority_method')
|
||||||
.subscribe(method => (this.defaultMajorityMethod = method));
|
.subscribe(method => (this.defaultMajorityMethod = method));
|
||||||
config.get<number[]>(AssignmentPoll.defaultGroupsConfig).subscribe(ids => (this.defaultGroupIds = ids));
|
config.get<number[]>(AssignmentPoll.defaultGroupsConfig).subscribe(ids => (this.defaultGroupIds = ids));
|
||||||
config
|
config
|
||||||
.get<AssignmentPollMethods>(AssignmentPoll.defaultPollMethodConfig)
|
.get<AssignmentPollMethod>(AssignmentPoll.defaultPollMethodConfig)
|
||||||
.subscribe(method => (this.defaultPollMethod = method));
|
.subscribe(method => (this.defaultPollMethod = method));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,19 +81,22 @@ export class AssignmentPollService extends PollService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getPercentBase(poll: PollData): number {
|
public getPercentBase(poll: PollData): number {
|
||||||
const base: PercentBase = poll.onehundred_percent_base;
|
const base: AssignmentPollPercentBase = poll.onehundred_percent_base as AssignmentPollPercentBase;
|
||||||
let totalByBase: number;
|
let totalByBase: number;
|
||||||
switch (base) {
|
switch (base) {
|
||||||
case PercentBase.YN:
|
case AssignmentPollPercentBase.YN:
|
||||||
totalByBase = this.sumOptionsYN(poll);
|
totalByBase = this.sumOptionsYN(poll);
|
||||||
break;
|
break;
|
||||||
case PercentBase.YNA:
|
case AssignmentPollPercentBase.YNA:
|
||||||
totalByBase = this.sumOptionsYNA(poll);
|
totalByBase = this.sumOptionsYNA(poll);
|
||||||
break;
|
break;
|
||||||
case PercentBase.Valid:
|
case AssignmentPollPercentBase.Votes:
|
||||||
|
totalByBase = this.sumOptionsYNA(poll);
|
||||||
|
break;
|
||||||
|
case AssignmentPollPercentBase.Valid:
|
||||||
totalByBase = poll.votesvalid;
|
totalByBase = poll.votesvalid;
|
||||||
break;
|
break;
|
||||||
case PercentBase.Cast:
|
case AssignmentPollPercentBase.Cast:
|
||||||
totalByBase = poll.votescast;
|
totalByBase = poll.votescast;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -1,19 +1,8 @@
|
|||||||
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 { ViewBaseOption } from 'app/site/polls/models/view-base-option';
|
||||||
import { ViewMotionPoll } from './view-motion-poll';
|
|
||||||
import { ViewMotionVote } from './view-motion-vote';
|
|
||||||
|
|
||||||
export class ViewMotionOption extends BaseViewModel<MotionOption> {
|
export class ViewMotionOption extends ViewBaseOption<MotionOption> {
|
||||||
public get option(): MotionOption {
|
|
||||||
return this._model;
|
|
||||||
}
|
|
||||||
public static COLLECTIONSTRING = MotionOption.COLLECTIONSTRING;
|
public static COLLECTIONSTRING = MotionOption.COLLECTIONSTRING;
|
||||||
protected _collectionString = MotionOption.COLLECTIONSTRING;
|
protected _collectionString = MotionOption.COLLECTIONSTRING;
|
||||||
}
|
}
|
||||||
|
export interface ViewMotionOption extends MotionOption {}
|
||||||
interface TIMotionOptionRelations {
|
|
||||||
votes: ViewMotionVote[];
|
|
||||||
poll: ViewMotionPoll;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ViewMotionOption extends MotionOption, TIMotionOptionRelations {}
|
|
||||||
|
@ -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 { BaseViewModel } from 'app/site/base/base-view-model';
|
||||||
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
||||||
import { ViewMotionOption } from 'app/site/motions/models/view-motion-option';
|
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';
|
import { ViewMotion } from './view-motion';
|
||||||
|
|
||||||
export interface MotionPollTitleInformation {
|
export interface MotionPollTitleInformation {
|
||||||
title: string;
|
title: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MotionPollMethodsVerbose = {
|
export const MotionPollMethodVerbose = {
|
||||||
YN: 'Yes/No',
|
YN: 'Yes/No',
|
||||||
YNA: 'Yes/No/Abstain'
|
YNA: 'Yes/No/Abstain'
|
||||||
};
|
};
|
||||||
|
|
||||||
export class ViewMotionPoll extends ViewBasePoll<MotionPoll> 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<MotionPoll, MotionPollMethod, PercentBase>
|
||||||
|
implements MotionPollTitleInformation {
|
||||||
public static COLLECTIONSTRING = MotionPoll.COLLECTIONSTRING;
|
public static COLLECTIONSTRING = MotionPoll.COLLECTIONSTRING;
|
||||||
protected _collectionString = MotionPoll.COLLECTIONSTRING;
|
protected _collectionString = MotionPoll.COLLECTIONSTRING;
|
||||||
|
|
||||||
public readonly pollClassType: 'assignment' | 'motion' = 'motion';
|
public readonly pollClassType = PollClassType.Motion;
|
||||||
|
|
||||||
public get result(): ViewMotionOption {
|
public get result(): ViewMotionOption {
|
||||||
return this.options[0];
|
return this.options[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
public get hasVotes(): boolean {
|
public get hasVotes(): boolean {
|
||||||
return !!this.result.votes.length;
|
return this.result && !!this.result.votes.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getContentObject(): BaseViewModel {
|
public getContentObject(): BaseViewModel {
|
||||||
@ -71,7 +81,11 @@ export class ViewMotionPoll extends ViewBasePoll<MotionPoll> implements MotionPo
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get pollmethodVerbose(): string {
|
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 {
|
public anySpecialVotes(): boolean {
|
||||||
|
@ -1,19 +1,9 @@
|
|||||||
import { MotionVote } from 'app/shared/models/motions/motion-vote';
|
import { MotionVote } from 'app/shared/models/motions/motion-vote';
|
||||||
import { ViewUser } from 'app/site/users/models/view-user';
|
import { ViewBaseVote } from 'app/site/polls/models/view-base-vote';
|
||||||
import { BaseViewModel } from '../../base/base-view-model';
|
|
||||||
import { ViewMotionOption } from './view-motion-option';
|
|
||||||
|
|
||||||
export class ViewMotionVote extends BaseViewModel<MotionVote> {
|
export class ViewMotionVote extends ViewBaseVote<MotionVote> {
|
||||||
public get vote(): MotionVote {
|
|
||||||
return this._model;
|
|
||||||
}
|
|
||||||
public static COLLECTIONSTRING = MotionVote.COLLECTIONSTRING;
|
public static COLLECTIONSTRING = MotionVote.COLLECTIONSTRING;
|
||||||
protected _collectionString = MotionVote.COLLECTIONSTRING;
|
protected _collectionString = MotionVote.COLLECTIONSTRING;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TIMotionVoteRelations {
|
export interface ViewMotionVote extends MotionVote {}
|
||||||
user?: ViewUser;
|
|
||||||
option: ViewMotionOption;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ViewMotionVote extends MotionVote, TIMotionVoteRelations {}
|
|
||||||
|
@ -82,6 +82,7 @@
|
|||||||
<div class="named-result-table" *ngIf="poll.type === 'named'">
|
<div class="named-result-table" *ngIf="poll.type === 'named'">
|
||||||
<h2>{{ 'Single votes' | translate }}</h2>
|
<h2>{{ 'Single votes' | translate }}</h2>
|
||||||
<os-list-view-table
|
<os-list-view-table
|
||||||
|
*ngIf="votesDataObservable"
|
||||||
[listObservable]="votesDataObservable"
|
[listObservable]="votesDataObservable"
|
||||||
[columns]="columnDefinition"
|
[columns]="columnDefinition"
|
||||||
[filterProps]="filterProps"
|
[filterProps]="filterProps"
|
||||||
@ -107,12 +108,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>{{ vote.valueVerbose | translate }}</div>
|
<div>{{ vote.valueVerbose | translate }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- No Results -->
|
|
||||||
<div *pblNgridNoDataRef class="pbl-ngrid-no-data">
|
|
||||||
{{ 'The individual votes were made anonymous.' | translate }}
|
|
||||||
</div>
|
|
||||||
</os-list-view-table>
|
</os-list-view-table>
|
||||||
|
<div *ngIf="!votesDataObservable">
|
||||||
|
{{ 'The individual votes were anonymized.' | translate }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import { PblColumnDefinition } from '@pebula/ngrid';
|
|||||||
|
|
||||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||||
import { MotionPollRepositoryService } from 'app/core/repositories/motions/motion-poll-repository.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 { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service';
|
||||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
import { ChartType } from 'app/shared/components/charts/charts.component';
|
import { ChartType } from 'app/shared/components/charts/charts.component';
|
||||||
@ -60,12 +61,13 @@ export class MotionPollDetailComponent extends BasePollDetailComponent<ViewMotio
|
|||||||
pollDialog: MotionPollDialogService,
|
pollDialog: MotionPollDialogService,
|
||||||
pollService: PollService,
|
pollService: PollService,
|
||||||
private operator: OperatorService,
|
private operator: OperatorService,
|
||||||
private router: Router
|
private router: Router,
|
||||||
|
votesRepo: MotionVoteRepositoryService
|
||||||
) {
|
) {
|
||||||
super(title, translate, matSnackbar, repo, route, groupRepo, prompt, pollDialog, pollService);
|
super(title, translate, matSnackbar, repo, route, groupRepo, prompt, pollDialog, pollService, votesRepo);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected onPollWithOptionsLoaded(): void {
|
protected createVotesData(): void {
|
||||||
this.setVotesData(this.poll.options[0].votes);
|
this.setVotesData(this.poll.options[0].votes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<os-poll-form [data]="pollData" #pollForm></os-poll-form>
|
<os-poll-form [data]="pollData" [percentBases]="PercentBaseVerbose" #pollForm></os-poll-form>
|
||||||
<ng-container *ngIf="pollForm.contentForm.get('type').value === 'analog'">
|
<ng-container *ngIf="pollForm.contentForm.get('type').value === 'analog'">
|
||||||
<div class="os-form-card-mobile" mat-dialog-content>
|
<div class="os-form-card-mobile" mat-dialog-content>
|
||||||
<form [formGroup]="dialogVoteForm">
|
<form [formGroup]="dialogVoteForm">
|
||||||
|
@ -6,9 +6,9 @@ import { Title } from '@angular/platform-browser';
|
|||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll';
|
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 { BasePollDialogComponent } from 'app/site/polls/components/base-poll-dialog.component';
|
||||||
import { PollFormComponent } from 'app/site/polls/components/poll-form/poll-form.component';
|
import { PollFormComponent } from 'app/site/polls/components/poll-form/poll-form.component';
|
||||||
|
import { PercentBaseVerbose } from 'app/site/polls/models/view-base-poll';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'os-motion-poll-dialog',
|
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']
|
styleUrls: ['./motion-poll-dialog.component.scss']
|
||||||
})
|
})
|
||||||
export class MotionPollDialogComponent extends BasePollDialogComponent<ViewMotionPoll> {
|
export class MotionPollDialogComponent extends BasePollDialogComponent<ViewMotionPoll> {
|
||||||
public motionPollMethods = { YNA: MotionPollMethodsVerbose.YNA };
|
public PercentBaseVerbose = PercentBaseVerbose;
|
||||||
|
|
||||||
@ViewChild('pollForm', { static: false })
|
@ViewChild('pollForm', { static: false })
|
||||||
protected pollForm: PollFormComponent<ViewMotionPoll>;
|
protected pollForm: PollFormComponent<ViewMotionPoll>;
|
||||||
|
@ -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 { MotionPollRepositoryService } from 'app/core/repositories/motions/motion-poll-repository.service';
|
||||||
import { MotionVoteRepositoryService } from 'app/core/repositories/motions/motion-vote-repository.service';
|
import { MotionVoteRepositoryService } from 'app/core/repositories/motions/motion-vote-repository.service';
|
||||||
import { VotingService } from 'app/core/ui-services/voting.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 { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll';
|
||||||
import { ViewMotionVote } from 'app/site/motions/models/view-motion-vote';
|
import { ViewMotionVote } from 'app/site/motions/models/view-motion-vote';
|
||||||
import { BasePollVoteComponent } from 'app/site/polls/components/base-poll-vote.component';
|
import { BasePollVoteComponent } from 'app/site/polls/components/base-poll-vote.component';
|
||||||
@ -34,7 +34,7 @@ export class MotionPollVoteComponent extends BasePollVoteComponent<ViewMotionPol
|
|||||||
*/
|
*/
|
||||||
public currentVote: ViewMotionVote;
|
public currentVote: ViewMotionVote;
|
||||||
|
|
||||||
public pollMethods = MotionPollMethods;
|
public MotionPollMethod = MotionPollMethod;
|
||||||
|
|
||||||
private votes: ViewMotionVote[];
|
private votes: ViewMotionVote[];
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { ConstantsService } from 'app/core/core-services/constants.service';
|
import { ConstantsService } from 'app/core/core-services/constants.service';
|
||||||
import { MotionPollRepositoryService } from 'app/core/repositories/motions/motion-poll-repository.service';
|
import { MotionPollRepositoryService } from 'app/core/repositories/motions/motion-poll-repository.service';
|
||||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
import { MotionPoll, MotionPollMethods } from 'app/shared/models/motions/motion-poll';
|
import { MotionPoll, MotionPollMethod } from 'app/shared/models/motions/motion-poll';
|
||||||
import { MajorityMethod, PercentBase } from 'app/shared/models/poll/base-poll';
|
import { MajorityMethod, PercentBase } from 'app/shared/models/poll/base-poll';
|
||||||
import { PollData, PollService } from 'app/site/polls/services/poll.service';
|
import { PollData, PollService } from 'app/site/polls/services/poll.service';
|
||||||
|
|
||||||
@ -60,13 +60,13 @@ export class MotionPollService extends PollService {
|
|||||||
const length = this.pollRepo.getViewModelList().filter(item => item.motion_id === poll.motion_id).length;
|
const length = this.pollRepo.getViewModelList().filter(item => item.motion_id === poll.motion_id).length;
|
||||||
|
|
||||||
poll.title = !length ? this.translate.instant('Vote') : `${this.translate.instant('Vote')} (${length + 1})`;
|
poll.title = !length ? this.translate.instant('Vote') : `${this.translate.instant('Vote')} (${length + 1})`;
|
||||||
poll.pollmethod = MotionPollMethods.YNA;
|
poll.pollmethod = MotionPollMethod.YNA;
|
||||||
|
|
||||||
return poll;
|
return poll;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPercentBase(poll: PollData): number {
|
public getPercentBase(poll: PollData): number {
|
||||||
const base: PercentBase = poll.onehundred_percent_base;
|
const base: PercentBase = poll.onehundred_percent_base as PercentBase;
|
||||||
|
|
||||||
let totalByBase: number;
|
let totalByBase: number;
|
||||||
const result = poll.options[0];
|
const result = poll.options[0];
|
||||||
|
@ -6,17 +6,21 @@ import { ActivatedRoute } from '@angular/router';
|
|||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { Label } from 'ng2-charts';
|
import { Label } from 'ng2-charts';
|
||||||
import { BehaviorSubject, from, Observable } from 'rxjs';
|
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 { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service';
|
||||||
import { BasePollDialogService } from 'app/core/ui-services/base-poll-dialog.service';
|
import { BasePollDialogService } from 'app/core/ui-services/base-poll-dialog.service';
|
||||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
import { ChartData, ChartType } from 'app/shared/components/charts/charts.component';
|
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 { BaseViewComponent } from 'app/site/base/base-view';
|
||||||
import { ViewGroup } from 'app/site/users/models/view-group';
|
import { ViewGroup } from 'app/site/users/models/view-group';
|
||||||
import { ViewUser } from 'app/site/users/models/view-user';
|
import { ViewUser } from 'app/site/users/models/view-user';
|
||||||
import { BasePollRepositoryService } from '../services/base-poll-repository.service';
|
import { BasePollRepositoryService } from '../services/base-poll-repository.service';
|
||||||
import { PollService } from '../services/poll.service';
|
import { PollService } from '../services/poll.service';
|
||||||
import { ViewBasePoll } from '../models/view-base-poll';
|
import { ViewBasePoll } from '../models/view-base-poll';
|
||||||
|
import { ViewBaseVote } from '../models/view-base-vote';
|
||||||
|
|
||||||
export interface BaseVoteData {
|
export interface BaseVoteData {
|
||||||
user?: ViewUser;
|
user?: ViewUser;
|
||||||
@ -74,6 +78,8 @@ export abstract class BasePollDetailComponent<V extends ViewBasePoll> extends Ba
|
|||||||
// The observable for the votes-per-user table
|
// The observable for the votes-per-user table
|
||||||
public votesDataObservable: Observable<BaseVoteData[]>;
|
public votesDataObservable: Observable<BaseVoteData[]>;
|
||||||
|
|
||||||
|
protected optionsLoaded = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
@ -98,9 +104,23 @@ export abstract class BasePollDetailComponent<V extends ViewBasePoll> extends Ba
|
|||||||
protected groupRepo: GroupRepositoryService,
|
protected groupRepo: GroupRepositoryService,
|
||||||
protected promptService: PromptService,
|
protected promptService: PromptService,
|
||||||
protected pollDialog: BasePollDialogService<V>,
|
protected pollDialog: BasePollDialogService<V>,
|
||||||
protected pollService: PollService
|
protected pollService: PollService,
|
||||||
|
protected votesRepo: BaseRepository<ViewBaseVote, BaseVote, object>
|
||||||
) {
|
) {
|
||||||
super(title, translate, matSnackbar);
|
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<V extends ViewBasePoll> extends Ba
|
|||||||
*/
|
*/
|
||||||
protected onPollLoaded(): void {}
|
protected onPollLoaded(): void {}
|
||||||
|
|
||||||
protected onPollWithOptionsLoaded(): void {}
|
protected onPollWithOptionsLoaded(): void {
|
||||||
|
this.createVotesData();
|
||||||
|
}
|
||||||
|
|
||||||
protected onStateChanged(): void {}
|
protected onStateChanged(): void {}
|
||||||
|
|
||||||
protected abstract hasPerms(): boolean;
|
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
|
* sets the votes data only if the poll wasn't pseudoanonymized
|
||||||
*/
|
*/
|
||||||
@ -160,6 +186,11 @@ export abstract class BasePollDetailComponent<V extends ViewBasePoll> 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.
|
* Initializes data for the shown chart.
|
||||||
* Could be overwritten to implement custom chart data.
|
* Could be overwritten to implement custom chart data.
|
||||||
@ -169,10 +200,10 @@ export abstract class BasePollDetailComponent<V extends ViewBasePoll> extends Ba
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This checks, if the poll has votes.
|
* This checks if the poll has votes.
|
||||||
*/
|
*/
|
||||||
private checkData(): void {
|
private checkData(): void {
|
||||||
if (this.poll.state === 3 || this.poll.state === 4) {
|
if (this.poll.stateHasVotes) {
|
||||||
setTimeout(() => this.initChartData());
|
setTimeout(() => this.initChartData());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,6 +234,7 @@ export abstract class BasePollDetailComponent<V extends ViewBasePoll> extends Ba
|
|||||||
if (!this.poll.options || !this.poll.options.length) {
|
if (!this.poll.options || !this.poll.options.length) {
|
||||||
setTimeout(() => this.waitForOptions(), 1);
|
setTimeout(() => this.waitForOptions(), 1);
|
||||||
} else {
|
} else {
|
||||||
|
this.optionsLoaded = true;
|
||||||
this.onPollWithOptionsLoaded();
|
this.onPollWithOptionsLoaded();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@
|
|||||||
formControlName="onehundred_percent_base"
|
formControlName="onehundred_percent_base"
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
<ng-container *ngFor="let option of percentBases | keyvalue">
|
<ng-container *ngFor="let option of percentBases | keyvalue: keepEntryOrder">
|
||||||
<mat-option [value]="option.key">{{ option.value | translate }}</mat-option>
|
<mat-option [value]="option.key">{{ option.value | translate }}</mat-option>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
|
@ -44,6 +44,12 @@ export class PollFormComponent<T extends ViewBasePoll> extends BaseViewComponent
|
|||||||
@Input()
|
@Input()
|
||||||
public pollMethods: { [key: string]: string };
|
public pollMethods: { [key: string]: string };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The different percent bases for this poll.
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
public percentBases: { [key: string]: string };
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
public data: Partial<T>;
|
public data: Partial<T>;
|
||||||
|
|
||||||
@ -52,11 +58,6 @@ export class PollFormComponent<T extends ViewBasePoll> extends BaseViewComponent
|
|||||||
*/
|
*/
|
||||||
public pollTypes = PollTypeVerbose;
|
public pollTypes = PollTypeVerbose;
|
||||||
|
|
||||||
/**
|
|
||||||
* The percent base for the poll.
|
|
||||||
*/
|
|
||||||
public percentBases: { [key: string]: string } = PercentBaseVerbose;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The majority methods for the poll.
|
* The majority methods for the poll.
|
||||||
*/
|
*/
|
||||||
@ -233,4 +234,11 @@ export class PollFormComponent<T extends ViewBasePoll> extends BaseViewComponent
|
|||||||
global_abstain: [false]
|
global_abstain: [false]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* compare function used with the KeyValuePipe to display the percent bases in original order
|
||||||
|
*/
|
||||||
|
public keepEntryOrder(): number {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
15
client/src/app/site/polls/models/view-base-option.ts
Normal file
15
client/src/app/site/polls/models/view-base-option.ts
Normal file
@ -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<M extends BaseOption<M> = any> extends BaseViewModel<M> {
|
||||||
|
public get option(): M {
|
||||||
|
return this._model;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ViewBaseOption<M extends BaseOption<M> = any> extends BaseOption<M> {
|
||||||
|
votes: ViewBaseVote[];
|
||||||
|
poll: ViewBasePoll;
|
||||||
|
}
|
@ -1,11 +1,10 @@
|
|||||||
import { BasePoll, PercentBase, PollType } from 'app/shared/models/poll/base-poll';
|
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 { BaseProjectableViewModel } from 'app/site/base/base-projectable-view-model';
|
||||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||||
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
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 { ViewGroup } from 'app/site/users/models/view-group';
|
||||||
import { ViewUser } from 'app/site/users/models/view-user';
|
import { ViewUser } from 'app/site/users/models/view-user';
|
||||||
|
import { ViewBaseOption } from './view-base-option';
|
||||||
|
|
||||||
export enum PollClassType {
|
export enum PollClassType {
|
||||||
Motion = 'motion',
|
Motion = 'motion',
|
||||||
@ -73,9 +72,6 @@ export const MajorityMethodVerbose = {
|
|||||||
disabled: 'Disabled'
|
disabled: 'Disabled'
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: These need to be in order
|
|
||||||
*/
|
|
||||||
export const PercentBaseVerbose = {
|
export const PercentBaseVerbose = {
|
||||||
YN: 'Yes/No',
|
YN: 'Yes/No',
|
||||||
YNA: 'Yes/No/Abstain',
|
YNA: 'Yes/No/Abstain',
|
||||||
@ -84,7 +80,11 @@ export const PercentBaseVerbose = {
|
|||||||
disabled: 'Disabled'
|
disabled: 'Disabled'
|
||||||
};
|
};
|
||||||
|
|
||||||
export abstract class ViewBasePoll<M extends BasePoll<M, any> = any> extends BaseProjectableViewModel<M> {
|
export abstract class ViewBasePoll<
|
||||||
|
M extends BasePoll<M, any, PM, PB> = any,
|
||||||
|
PM extends string = string,
|
||||||
|
PB extends string = string
|
||||||
|
> extends BaseProjectableViewModel<M> {
|
||||||
private _tableData: PollTableData[] = [];
|
private _tableData: PollTableData[] = [];
|
||||||
|
|
||||||
protected voteTableKeys: VotingResult[] = [
|
protected voteTableKeys: VotingResult[] = [
|
||||||
@ -157,15 +157,15 @@ export abstract class ViewBasePoll<M extends BasePoll<M, any> = any> extends Bas
|
|||||||
return MajorityMethodVerbose[this.majority_method];
|
return MajorityMethodVerbose[this.majority_method];
|
||||||
}
|
}
|
||||||
|
|
||||||
public get percentBaseVerbose(): string {
|
public abstract get pollmethodVerbose(): string;
|
||||||
return PercentBaseVerbose[this.onehundred_percent_base];
|
|
||||||
}
|
public abstract get percentBaseVerbose(): string;
|
||||||
|
|
||||||
public get showAbstainPercent(): boolean {
|
public get showAbstainPercent(): boolean {
|
||||||
return this.onehundred_percent_base === PercentBase.YNA;
|
return this.onehundred_percent_base === PercentBase.YNA;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract readonly pollClassType: 'motion' | 'assignment';
|
public abstract readonly pollClassType: PollClassType;
|
||||||
|
|
||||||
public canBeVotedFor: () => boolean;
|
public canBeVotedFor: () => boolean;
|
||||||
|
|
||||||
@ -188,8 +188,12 @@ export abstract class ViewBasePoll<M extends BasePoll<M, any> = any> extends Bas
|
|||||||
public abstract generateTableData(): PollTableData[];
|
public abstract generateTableData(): PollTableData[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ViewBasePoll<M extends BasePoll<M, any> = any> extends BasePoll<M, any> {
|
export interface ViewBasePoll<
|
||||||
|
M extends BasePoll<M, any, PM, PB> = any,
|
||||||
|
PM extends string = string,
|
||||||
|
PB extends string = string
|
||||||
|
> extends BasePoll<M, any, PM, PB> {
|
||||||
voted: ViewUser[];
|
voted: ViewUser[];
|
||||||
groups: ViewGroup[];
|
groups: ViewGroup[];
|
||||||
options: (ViewMotionOption | ViewAssignmentOption)[]; // TODO find a better solution. but works for the moment
|
options: ViewBaseOption[];
|
||||||
}
|
}
|
||||||
|
15
client/src/app/site/polls/models/view-base-vote.ts
Normal file
15
client/src/app/site/polls/models/view-base-vote.ts
Normal file
@ -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<M extends BaseVote<M> = any> extends BaseViewModel<M> {
|
||||||
|
public get vote(): M {
|
||||||
|
return this._model;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ViewBaseVote<M extends BaseVote<M> = any> extends BaseVote<M> {
|
||||||
|
user?: ViewUser;
|
||||||
|
option: ViewBaseOption;
|
||||||
|
}
|
@ -9,7 +9,7 @@ import { MotionPollRepositoryService } from 'app/core/repositories/motions/motio
|
|||||||
import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll';
|
import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll';
|
||||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||||
import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll';
|
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({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -28,13 +28,13 @@ export class PollListObservableService implements HasViewModelListObservable<Vie
|
|||||||
) {
|
) {
|
||||||
motionPollRepo
|
motionPollRepo
|
||||||
.getViewModelListObservable()
|
.getViewModelListObservable()
|
||||||
.subscribe(polls => this.adjustViewModelListObservable(polls, 'motion'));
|
.subscribe(polls => this.adjustViewModelListObservable(polls, PollClassType.Motion));
|
||||||
assignmentPollRepo
|
assignmentPollRepo
|
||||||
.getViewModelListObservable()
|
.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;
|
this[mode + 'Polls'] = polls;
|
||||||
|
|
||||||
const allPolls = (this.motionPolls as ViewBasePoll[]).concat(this.assignmentPolls);
|
const allPolls = (this.motionPolls as ViewBasePoll[]).concat(this.assignmentPolls);
|
||||||
|
@ -2,10 +2,10 @@ import { Injectable } from '@angular/core';
|
|||||||
|
|
||||||
import { _ } from 'app/core/translate/translation-marker';
|
import { _ } from 'app/core/translate/translation-marker';
|
||||||
import { ChartData, ChartType } from 'app/shared/components/charts/charts.component';
|
import { ChartData, 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 { MotionPollMethods } from 'app/shared/models/motions/motion-poll';
|
import { MotionPollMethod } from 'app/shared/models/motions/motion-poll';
|
||||||
import { BasePoll, MajorityMethod, PercentBase, PollColor, PollType } from 'app/shared/models/poll/base-poll';
|
import { BasePoll, MajorityMethod, PollColor, PollType } from 'app/shared/models/poll/base-poll';
|
||||||
import { AssignmentPollMethodsVerbose } from 'app/site/assignments/models/view-assignment-poll';
|
import { AssignmentPollMethodVerbose } from 'app/site/assignments/models/view-assignment-poll';
|
||||||
import {
|
import {
|
||||||
MajorityMethodVerbose,
|
MajorityMethodVerbose,
|
||||||
PercentBaseVerbose,
|
PercentBaseVerbose,
|
||||||
@ -90,8 +90,8 @@ export const PollMajorityMethod: CalculableMajorityMethod[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export interface PollData {
|
export interface PollData {
|
||||||
pollmethod?: string;
|
pollmethod: string;
|
||||||
onehundred_percent_base: PercentBase;
|
onehundred_percent_base: string;
|
||||||
options: {
|
options: {
|
||||||
user?: {
|
user?: {
|
||||||
full_name: string;
|
full_name: string;
|
||||||
@ -120,7 +120,7 @@ export abstract class PollService {
|
|||||||
/**
|
/**
|
||||||
* The default percentage base
|
* The default percentage base
|
||||||
*/
|
*/
|
||||||
public abstract defaultPercentBase: PercentBase;
|
public abstract defaultPercentBase: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default majority method
|
* The default majority method
|
||||||
@ -170,7 +170,7 @@ export abstract class PollService {
|
|||||||
case 'onehundred_percent_base':
|
case 'onehundred_percent_base':
|
||||||
return PercentBaseVerbose[value];
|
return PercentBaseVerbose[value];
|
||||||
case 'pollmethod':
|
case 'pollmethod':
|
||||||
return AssignmentPollMethodsVerbose[value];
|
return AssignmentPollMethodVerbose[value];
|
||||||
case 'type':
|
case 'type':
|
||||||
return PollTypeVerbose[value];
|
return PollTypeVerbose[value];
|
||||||
}
|
}
|
||||||
@ -181,7 +181,7 @@ export abstract class PollService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public generateChartData(poll: PollData): ChartData {
|
public generateChartData(poll: PollData): ChartData {
|
||||||
if (poll.pollmethod === AssignmentPollMethods.Votes) {
|
if (poll.pollmethod === AssignmentPollMethod.Votes) {
|
||||||
return this.generateCircleChartData(poll);
|
return this.generateCircleChartData(poll);
|
||||||
} else {
|
} else {
|
||||||
return this.generateBarChartData(poll);
|
return this.generateBarChartData(poll);
|
||||||
@ -191,7 +191,7 @@ export abstract class PollService {
|
|||||||
public generateBarChartData(poll: PollData): ChartData {
|
public generateBarChartData(poll: PollData): ChartData {
|
||||||
const fields = ['yes', 'no'];
|
const fields = ['yes', 'no'];
|
||||||
// cast is needed because ViewBasePoll doesn't have the field `pollmethod`, no easy fix :(
|
// cast is needed because ViewBasePoll doesn't have the field `pollmethod`, no easy fix :(
|
||||||
if ((<any>poll).pollmethod === MotionPollMethods.YNA) {
|
if ((<any>poll).pollmethod === MotionPollMethod.YNA) {
|
||||||
fields.push('abstain');
|
fields.push('abstain');
|
||||||
}
|
}
|
||||||
const data: ChartData = fields.map(key => ({
|
const data: ChartData = fields.map(key => ({
|
||||||
@ -213,7 +213,7 @@ export abstract class PollService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getChartType(poll: PollData): ChartType {
|
public getChartType(poll: PollData): ChartType {
|
||||||
if ((<any>poll).pollmethod === AssignmentPollMethods.Votes) {
|
if ((<any>poll).pollmethod === AssignmentPollMethod.Votes) {
|
||||||
return 'doughnut';
|
return 'doughnut';
|
||||||
} else {
|
} else {
|
||||||
return 'horizontalBar';
|
return 'horizontalBar';
|
||||||
|
@ -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 { MajorityMethod, PercentBase, PollState, PollType } from 'app/shared/models/poll/base-poll';
|
||||||
import { AssignmentTitleInformation } from 'app/site/assignments/models/view-assignment';
|
import { AssignmentTitleInformation } from 'app/site/assignments/models/view-assignment';
|
||||||
import { BasePollSlideData } from 'app/slides/polls/base-poll-slide-data';
|
import { BasePollSlideData } from 'app/slides/polls/base-poll-slide-data';
|
||||||
@ -8,7 +8,7 @@ export interface AssignmentPollSlideData extends BasePollSlideData {
|
|||||||
poll: {
|
poll: {
|
||||||
title: string;
|
title: string;
|
||||||
type: PollType;
|
type: PollType;
|
||||||
pollmethod: AssignmentPollMethods;
|
pollmethod: AssignmentPollMethod;
|
||||||
votes_amount: number;
|
votes_amount: number;
|
||||||
description: string;
|
description: string;
|
||||||
state: PollState;
|
state: PollState;
|
||||||
|
@ -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 { MajorityMethod, PercentBase, PollState, PollType } from 'app/shared/models/poll/base-poll';
|
||||||
import { MotionTitleInformation } from 'app/site/motions/models/view-motion';
|
import { MotionTitleInformation } from 'app/site/motions/models/view-motion';
|
||||||
import { BasePollSlideData } from 'app/slides/polls/base-poll-slide-data';
|
import { BasePollSlideData } from 'app/slides/polls/base-poll-slide-data';
|
||||||
@ -8,7 +8,7 @@ export interface MotionPollSlideData extends BasePollSlideData {
|
|||||||
poll: {
|
poll: {
|
||||||
title: string;
|
title: string;
|
||||||
type: PollType;
|
type: PollType;
|
||||||
pollmethod: MotionPollMethods;
|
pollmethod: MotionPollMethod;
|
||||||
state: PollState;
|
state: PollState;
|
||||||
onehundred_percent_base: PercentBase;
|
onehundred_percent_base: PercentBase;
|
||||||
majority_method: MajorityMethod;
|
majority_method: MajorityMethod;
|
||||||
|
@ -96,8 +96,8 @@ class Migration(migrations.Migration):
|
|||||||
name="onehundred_percent_base",
|
name="onehundred_percent_base",
|
||||||
field=models.CharField(
|
field=models.CharField(
|
||||||
choices=[
|
choices=[
|
||||||
("YN", "Yes/No per candidate"),
|
("YN", "Yes/No"),
|
||||||
("YNA", "Yes/No/Abstain per candidate"),
|
("YNA", "Yes/No/Abstain"),
|
||||||
("valid", "All valid ballots"),
|
("valid", "All valid ballots"),
|
||||||
("cast", "All casted ballots"),
|
("cast", "All casted ballots"),
|
||||||
("disabled", "Disabled (no percents)"),
|
("disabled", "Disabled (no percents)"),
|
||||||
|
@ -155,8 +155,8 @@ class BasePoll(models.Model):
|
|||||||
PERCENT_BASE_CAST = "cast"
|
PERCENT_BASE_CAST = "cast"
|
||||||
PERCENT_BASE_DISABLED = "disabled"
|
PERCENT_BASE_DISABLED = "disabled"
|
||||||
PERCENT_BASES: Iterable[Tuple[str, str]] = (
|
PERCENT_BASES: Iterable[Tuple[str, str]] = (
|
||||||
(PERCENT_BASE_YN, "Yes/No per candidate"),
|
(PERCENT_BASE_YN, "Yes/No"),
|
||||||
(PERCENT_BASE_YNA, "Yes/No/Abstain per candidate"),
|
(PERCENT_BASE_YNA, "Yes/No/Abstain"),
|
||||||
(PERCENT_BASE_VALID, "All valid ballots"),
|
(PERCENT_BASE_VALID, "All valid ballots"),
|
||||||
(PERCENT_BASE_CAST, "All casted ballots"),
|
(PERCENT_BASE_CAST, "All casted ballots"),
|
||||||
(PERCENT_BASE_DISABLED, "Disabled (no percents)"),
|
(PERCENT_BASE_DISABLED, "Disabled (no percents)"),
|
||||||
|
Loading…
Reference in New Issue
Block a user