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 { 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<AssignmentPoll, AssignmentOption> {
|
||||
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<AssignmentPoll, AssignmentOption> {
|
||||
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'];
|
||||
}
|
||||
}
|
||||
|
@ -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<MotionPoll, MotionOption> {
|
||||
export class MotionPoll extends BasePoll<MotionPoll, MotionOption, MotionPollMethod, PercentBase> {
|
||||
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']);
|
||||
}
|
||||
}
|
||||
|
@ -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<T = any, O extends BaseOption<any> = any> extends BaseDecimalModel<T> {
|
||||
export abstract class BasePoll<
|
||||
T = any,
|
||||
O extends BaseOption<any> = any,
|
||||
PM extends string = string,
|
||||
PB extends string = string
|
||||
> extends BaseDecimalModel<T> {
|
||||
public state: PollState;
|
||||
public type: PollType;
|
||||
public title: string;
|
||||
@ -47,7 +52,9 @@ export abstract class BasePoll<T = any, O extends BaseOption<any> = 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;
|
||||
|
@ -16,7 +16,7 @@ export const GeneralValueVerbose = {
|
||||
votesabstain: 'Votes abstain'
|
||||
};
|
||||
|
||||
export abstract class BaseVote<T> extends BaseDecimalModel<T> {
|
||||
export abstract class BaseVote<T = any> extends BaseDecimalModel<T> {
|
||||
public weight: number;
|
||||
public value: VoteValue;
|
||||
public option_id: number;
|
||||
|
@ -103,7 +103,7 @@
|
||||
<b *ngIf="!vote.user">{{ 'Anonymous' | translate }}</b>
|
||||
</div>
|
||||
<!-- 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">
|
||||
<div
|
||||
*pblNgridCellDef="'votes-' + option.user_id; row as vote"
|
||||
@ -116,7 +116,7 @@
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<!-- Votes method -->
|
||||
<ng-container *ngIf="poll.pollmethod === AssignmentPollMethods.Votes">
|
||||
<ng-container *ngIf="poll.pollmethod === AssignmentPollMethod.Votes">
|
||||
<div *pblNgridCellDef="'votes'; row as vote">
|
||||
<div *ngFor="let candidate of vote.votes">{{ candidate }}</div>
|
||||
</div>
|
||||
|
@ -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<ViewAssignmentPoll> {
|
||||
public AssignmentPollMethods = AssignmentPollMethods;
|
||||
public AssignmentPollMethod = AssignmentPollMethod;
|
||||
|
||||
public columnDefinitionSingleVotes: PblColumnDefinition[];
|
||||
|
||||
@ -41,7 +42,7 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponent<ViewA
|
||||
}
|
||||
|
||||
public get isVotedPoll(): boolean {
|
||||
return this.poll.pollmethod === AssignmentPollMethods.Votes;
|
||||
return this.poll.pollmethod === AssignmentPollMethod.Votes;
|
||||
}
|
||||
|
||||
private _chartType: ChartType = 'horizontalBar';
|
||||
@ -56,17 +57,18 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponent<ViewA
|
||||
prompt: PromptService,
|
||||
pollDialog: AssignmentPollDialogService,
|
||||
pollService: PollService,
|
||||
votesRepo: AssignmentVoteRepositoryService,
|
||||
private operator: OperatorService,
|
||||
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 = {};
|
||||
let i = -1;
|
||||
|
||||
this.columnDefinitionSingleVotes = [
|
||||
const definitions: PblColumnDefinition[] = [
|
||||
{
|
||||
prop: 'user',
|
||||
label: 'Participant',
|
||||
@ -75,7 +77,7 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponent<ViewA
|
||||
}
|
||||
];
|
||||
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) {
|
||||
if (!this.isVotedPoll) {
|
||||
this.columnDefinitionSingleVotes.push(
|
||||
this.getVoteColumnDefinition('votes-' + option.user_id, option.user.getFullName())
|
||||
);
|
||||
definitions.push(this.getVoteColumnDefinition('votes-' + option.user_id, option.user.getFullName()));
|
||||
}
|
||||
|
||||
for (const vote of option.votes) {
|
||||
@ -125,6 +125,7 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponent<ViewA
|
||||
|
||||
this.setVotesData(Object.values(votes));
|
||||
this.candidatesLabels = this.pollService.getChartLabels(this.poll);
|
||||
this.columnDefinitionSingleVotes = definitions;
|
||||
this.isReady = true;
|
||||
}
|
||||
|
||||
@ -151,11 +152,11 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponent<ViewA
|
||||
}
|
||||
|
||||
public voteFitsMethod(result: VotingResult): boolean {
|
||||
if (this.poll.pollmethod === AssignmentPollMethods.Votes) {
|
||||
if (this.poll.pollmethod === AssignmentPollMethod.Votes) {
|
||||
if (result.vote === 'abstain' || result.vote === 'no') {
|
||||
return false;
|
||||
}
|
||||
} else if (this.poll.pollmethod === AssignmentPollMethods.YN) {
|
||||
} else if (this.poll.pollmethod === AssignmentPollMethod.YN) {
|
||||
if (result.vote === 'abstain') {
|
||||
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 -->
|
||||
<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 { 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 BasePollDialogComponent<ViewA
|
||||
public voteValueVerbose = VoteValueVerbose;
|
||||
public generalValueVerbose = GeneralValueVerbose;
|
||||
|
||||
public assignmentPollMethods = AssignmentPollMethodsVerbose;
|
||||
public AssignmentPollMethodVerbose = AssignmentPollMethodVerbose;
|
||||
public AssignmentPollPercentBaseVerbose = AssignmentPollPercentBaseVerbose;
|
||||
|
||||
public options: OptionsObject;
|
||||
|
||||
@ -92,10 +96,10 @@ export class AssignmentPollDialogComponent extends BasePollDialogComponent<ViewA
|
||||
private setAnalogPollValues(): void {
|
||||
const pollmethod = this.pollForm.contentForm.get('pollmethod').value;
|
||||
this.analogPollValues = ['Y'];
|
||||
if (pollmethod !== AssignmentPollMethods.Votes) {
|
||||
if (pollmethod !== AssignmentPollMethod.Votes) {
|
||||
this.analogPollValues.push('N');
|
||||
}
|
||||
if (pollmethod === AssignmentPollMethods.YNA) {
|
||||
if (pollmethod === AssignmentPollMethod.YNA) {
|
||||
this.analogPollValues.push('A');
|
||||
}
|
||||
}
|
||||
@ -110,10 +114,10 @@ export class AssignmentPollDialogComponent extends BasePollDialogComponent<ViewA
|
||||
for (const option of data.options) {
|
||||
const votes: any = {};
|
||||
votes.Y = option.yes;
|
||||
if (data.pollmethod !== AssignmentPollMethods.Votes) {
|
||||
if (data.pollmethod !== AssignmentPollMethod.Votes) {
|
||||
votes.N = option.no;
|
||||
}
|
||||
if (data.pollmethod === AssignmentPollMethods.YNA) {
|
||||
if (data.pollmethod === AssignmentPollMethod.YNA) {
|
||||
votes.A = option.abstain;
|
||||
}
|
||||
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>
|
||||
|
||||
<!-- 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 }}
|
||||
</h4>
|
||||
|
||||
@ -16,9 +16,9 @@
|
||||
<div *ngIf="poll.type !== PollType.Pseudoanonymous || !option.user_has_voted">
|
||||
<div
|
||||
[ngClass]="{
|
||||
'yna-grid': poll.pollmethod === pollMethods.YNA,
|
||||
'yn-grid': poll.pollmethod === pollMethods.YN,
|
||||
'single-vote-grid': poll.pollmethod === pollMethods.Votes
|
||||
'yna-grid': poll.pollmethod === AssignmentPollMethod.YNA,
|
||||
'yn-grid': poll.pollmethod === AssignmentPollMethod.YN,
|
||||
'single-vote-grid': poll.pollmethod === AssignmentPollMethod.Votes
|
||||
}"
|
||||
>
|
||||
<div class="vote-candidate-name">
|
||||
@ -34,7 +34,7 @@
|
||||
>
|
||||
<mat-icon> {{ action.icon }}</mat-icon>
|
||||
</button>
|
||||
<span *ngIf="poll.pollmethod !== pollMethods.Votes" class="vote-label">
|
||||
<span *ngIf="poll.pollmethod !== AssignmentPollMethod.Votes" class="vote-label">
|
||||
{{ action.label | translate }}
|
||||
</span>
|
||||
</div>
|
||||
@ -44,7 +44,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 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>
|
||||
<div class="global-option-grid">
|
||||
<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 { 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<ViewAssignmentPoll> implements OnInit {
|
||||
public pollMethods = AssignmentPollMethods;
|
||||
public AssignmentPollMethod = AssignmentPollMethod;
|
||||
public PollType = PollType;
|
||||
public voteActions: VoteActions[] = [];
|
||||
|
||||
@ -70,7 +70,7 @@ export class AssignmentPollVoteComponent extends BasePollVoteComponent<ViewAssig
|
||||
label: 'Yes'
|
||||
});
|
||||
|
||||
if (this.poll.pollmethod !== AssignmentPollMethods.Votes) {
|
||||
if (this.poll.pollmethod !== AssignmentPollMethod.Votes) {
|
||||
this.voteActions.push({
|
||||
vote: 'N',
|
||||
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({
|
||||
vote: 'A',
|
||||
css: 'voted-abstain',
|
||||
@ -101,7 +101,7 @@ export class AssignmentPollVoteComponent extends BasePollVoteComponent<ViewAssig
|
||||
|
||||
for (const option of this.poll.options) {
|
||||
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') {
|
||||
this.currentVotes.global = curr_vote.valueVerbose;
|
||||
curr_vote = null;
|
||||
@ -120,7 +120,7 @@ export class AssignmentPollVoteComponent extends BasePollVoteComponent<ViewAssig
|
||||
|
||||
public saveSingleVote(optionId: number, vote: 'Y' | 'N' | 'A'): void {
|
||||
let requestData;
|
||||
if (this.poll.pollmethod === AssignmentPollMethods.Votes) {
|
||||
if (this.poll.pollmethod === AssignmentPollMethod.Votes) {
|
||||
const pollOptionIds = this.getPollOptionIds();
|
||||
requestData = pollOptionIds.reduce((o, n) => {
|
||||
if ((n === optionId && vote === 'Y') !== (this.currentVotes[n] === 'Yes')) {
|
||||
|
@ -45,7 +45,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="hasVotes">
|
||||
<div *ngIf="canSeeVotes">
|
||||
<os-charts
|
||||
[class]="chartType === 'doughnut' ? 'doughnut-chart' : 'bar-chart'"
|
||||
[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 { PromptService } from 'app/core/ui-services/prompt.service';
|
||||
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 { PollService } from 'app/site/polls/services/poll.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');
|
||||
}
|
||||
|
||||
public get hasVotes(): boolean {
|
||||
return (this.canManage && this.poll.state === PollState.Finished) || this.poll.state === PollState.Published;
|
||||
public get canSeeVotes(): boolean {
|
||||
return (this.canManage && this.poll.isFinished) || this.poll.isPublished;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,21 +1,12 @@
|
||||
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 { BaseViewModel } from '../../base/base-view-model';
|
||||
import { ViewAssignmentPoll } from './view-assignment-poll';
|
||||
import { ViewAssignmentVote } from './view-assignment-vote';
|
||||
|
||||
export class ViewAssignmentOption extends BaseViewModel<AssignmentOption> {
|
||||
public get option(): AssignmentOption {
|
||||
return this._model;
|
||||
}
|
||||
export class ViewAssignmentOption extends ViewBaseOption<AssignmentOption> {
|
||||
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 {}
|
||||
|
@ -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<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;
|
||||
protected _collectionString = AssignmentPoll.COLLECTIONSTRING;
|
||||
|
||||
public readonly tableChartData: Map<string, BehaviorSubject<ChartData>> = 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 {
|
||||
|
@ -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<AssignmentVote> {
|
||||
public get vote(): AssignmentVote {
|
||||
return this._model;
|
||||
}
|
||||
export class ViewAssignmentVote extends ViewBaseVote<AssignmentVote> {
|
||||
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 {}
|
||||
|
@ -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 {
|
||||
|
@ -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<PercentBase>('motion_poll_default_100_percent_base')
|
||||
.get<AssignmentPollPercentBase>('assignment_poll_default_100_percent_base')
|
||||
.subscribe(base => (this.defaultPercentBase = base));
|
||||
config
|
||||
.get<MajorityMethod>('motion_poll_default_majority_method')
|
||||
.get<MajorityMethod>('assignment_poll_default_majority_method')
|
||||
.subscribe(method => (this.defaultMajorityMethod = method));
|
||||
config.get<number[]>(AssignmentPoll.defaultGroupsConfig).subscribe(ids => (this.defaultGroupIds = ids));
|
||||
config
|
||||
.get<AssignmentPollMethods>(AssignmentPoll.defaultPollMethodConfig)
|
||||
.get<AssignmentPollMethod>(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:
|
||||
|
@ -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<MotionOption> {
|
||||
public get option(): MotionOption {
|
||||
return this._model;
|
||||
}
|
||||
export class ViewMotionOption extends ViewBaseOption<MotionOption> {
|
||||
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 {}
|
||||
|
@ -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<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;
|
||||
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<MotionPoll> 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 {
|
||||
|
@ -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<MotionVote> {
|
||||
public get vote(): MotionVote {
|
||||
return this._model;
|
||||
}
|
||||
export class ViewMotionVote extends ViewBaseVote<MotionVote> {
|
||||
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 {}
|
||||
|
@ -82,6 +82,7 @@
|
||||
<div class="named-result-table" *ngIf="poll.type === 'named'">
|
||||
<h2>{{ 'Single votes' | translate }}</h2>
|
||||
<os-list-view-table
|
||||
*ngIf="votesDataObservable"
|
||||
[listObservable]="votesDataObservable"
|
||||
[columns]="columnDefinition"
|
||||
[filterProps]="filterProps"
|
||||
@ -107,12 +108,10 @@
|
||||
</div>
|
||||
<div>{{ vote.valueVerbose | translate }}</div>
|
||||
</div>
|
||||
|
||||
<!-- No Results -->
|
||||
<div *pblNgridNoDataRef class="pbl-ngrid-no-data">
|
||||
{{ 'The individual votes were made anonymous.' | translate }}
|
||||
</div>
|
||||
</os-list-view-table>
|
||||
<div *ngIf="!votesDataObservable">
|
||||
{{ 'The individual votes were anonymized.' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -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<ViewMotio
|
||||
pollDialog: MotionPollDialogService,
|
||||
pollService: PollService,
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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'">
|
||||
<div class="os-form-card-mobile" mat-dialog-content>
|
||||
<form [formGroup]="dialogVoteForm">
|
||||
|
@ -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<ViewMotionPoll> {
|
||||
public motionPollMethods = { YNA: MotionPollMethodsVerbose.YNA };
|
||||
public PercentBaseVerbose = PercentBaseVerbose;
|
||||
|
||||
@ViewChild('pollForm', { static: false })
|
||||
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 { 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<ViewMotionPol
|
||||
*/
|
||||
public currentVote: ViewMotionVote;
|
||||
|
||||
public pollMethods = MotionPollMethods;
|
||||
public MotionPollMethod = MotionPollMethod;
|
||||
|
||||
private votes: ViewMotionVote[];
|
||||
|
||||
|
@ -5,7 +5,7 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
import { ConstantsService } from 'app/core/core-services/constants.service';
|
||||
import { MotionPollRepositoryService } from 'app/core/repositories/motions/motion-poll-repository.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 { 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;
|
||||
|
||||
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];
|
||||
|
@ -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<V extends ViewBasePoll> extends Ba
|
||||
// The observable for the votes-per-user table
|
||||
public votesDataObservable: Observable<BaseVoteData[]>;
|
||||
|
||||
protected optionsLoaded = false;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@ -98,9 +104,23 @@ export abstract class BasePollDetailComponent<V extends ViewBasePoll> extends Ba
|
||||
protected groupRepo: GroupRepositoryService,
|
||||
protected promptService: PromptService,
|
||||
protected pollDialog: BasePollDialogService<V>,
|
||||
protected pollService: PollService
|
||||
protected pollService: PollService,
|
||||
protected votesRepo: BaseRepository<ViewBaseVote, BaseVote, object>
|
||||
) {
|
||||
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 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<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.
|
||||
* 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 {
|
||||
if (this.poll.state === 3 || this.poll.state === 4) {
|
||||
if (this.poll.stateHasVotes) {
|
||||
setTimeout(() => this.initChartData());
|
||||
}
|
||||
}
|
||||
@ -203,6 +234,7 @@ export abstract class BasePollDetailComponent<V extends ViewBasePoll> extends Ba
|
||||
if (!this.poll.options || !this.poll.options.length) {
|
||||
setTimeout(() => this.waitForOptions(), 1);
|
||||
} else {
|
||||
this.optionsLoaded = true;
|
||||
this.onPollWithOptionsLoaded();
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +65,7 @@
|
||||
formControlName="onehundred_percent_base"
|
||||
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>
|
||||
</ng-container>
|
||||
</mat-select>
|
||||
|
@ -44,6 +44,12 @@ export class PollFormComponent<T extends ViewBasePoll> 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<T>;
|
||||
|
||||
@ -52,11 +58,6 @@ export class PollFormComponent<T extends ViewBasePoll> 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<T extends ViewBasePoll> extends BaseViewComponent
|
||||
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 { 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<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[] = [];
|
||||
|
||||
protected voteTableKeys: VotingResult[] = [
|
||||
@ -157,15 +157,15 @@ export abstract class ViewBasePoll<M extends BasePoll<M, any> = 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<M extends BasePoll<M, any> = any> extends Bas
|
||||
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[];
|
||||
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 { 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<Vie
|
||||
) {
|
||||
motionPollRepo
|
||||
.getViewModelListObservable()
|
||||
.subscribe(polls => 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);
|
||||
|
@ -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 ((<any>poll).pollmethod === MotionPollMethods.YNA) {
|
||||
if ((<any>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 ((<any>poll).pollmethod === AssignmentPollMethods.Votes) {
|
||||
if ((<any>poll).pollmethod === AssignmentPollMethod.Votes) {
|
||||
return 'doughnut';
|
||||
} else {
|
||||
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 { 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;
|
||||
|
@ -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;
|
||||
|
@ -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)"),
|
||||
|
@ -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)"),
|
||||
|
Loading…
Reference in New Issue
Block a user