Last changes and cleanup some todos
This commit is contained in:
parent
d15c9892ed
commit
64f2720b1a
14
.travis.yml
14
.travis.yml
@ -85,11 +85,7 @@ matrix:
|
|||||||
- "3.6"
|
- "3.6"
|
||||||
script:
|
script:
|
||||||
- mypy openslides/ tests/
|
- mypy openslides/ tests/
|
||||||
<<<<<<< HEAD
|
- pytest --cov --cov-fail-under=75
|
||||||
- pytest --cov --cov-fail-under=72
|
|
||||||
=======
|
|
||||||
- pytest --cov --cov-fail-under=74
|
|
||||||
>>>>>>> Initial work for supporting voting
|
|
||||||
|
|
||||||
- name: "Server: Tests Python 3.7"
|
- name: "Server: Tests Python 3.7"
|
||||||
language: python
|
language: python
|
||||||
@ -100,11 +96,7 @@ matrix:
|
|||||||
- isort --check-only --diff --recursive openslides tests
|
- isort --check-only --diff --recursive openslides tests
|
||||||
- black --check --diff --target-version py36 openslides tests
|
- black --check --diff --target-version py36 openslides tests
|
||||||
- mypy openslides/ tests/
|
- mypy openslides/ tests/
|
||||||
<<<<<<< HEAD
|
- pytest --cov --cov-fail-under=75
|
||||||
- pytest --cov --cov-fail-under=72
|
|
||||||
=======
|
|
||||||
- pytest --cov --cov-fail-under=74
|
|
||||||
>>>>>>> Initial work for supporting voting
|
|
||||||
|
|
||||||
- name: "Server: Tests Python 3.8"
|
- name: "Server: Tests Python 3.8"
|
||||||
language: python
|
language: python
|
||||||
@ -115,7 +107,7 @@ matrix:
|
|||||||
- isort --check-only --diff --recursive openslides tests
|
- isort --check-only --diff --recursive openslides tests
|
||||||
- black --check --diff --target-version py36 openslides tests
|
- black --check --diff --target-version py36 openslides tests
|
||||||
- mypy openslides/ tests/
|
- mypy openslides/ tests/
|
||||||
- pytest --cov --cov-fail-under=72
|
- pytest --cov --cov-fail-under=75
|
||||||
|
|
||||||
- name: "Client: Linting"
|
- name: "Client: Linting"
|
||||||
language: node_js
|
language: node_js
|
||||||
|
@ -114,14 +114,6 @@ export class AppComponent {
|
|||||||
.subscribe(() => servertimeService.startScheduler());
|
.subscribe(() => servertimeService.startScheduler());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to alter the normal Array.toString - function
|
|
||||||
*
|
|
||||||
* Will add a whitespace after a comma and shorten the output to
|
|
||||||
* three strings.
|
|
||||||
*
|
|
||||||
* TODO: Should be renamed
|
|
||||||
*/
|
|
||||||
private overloadArrayFunctions(): void {
|
private overloadArrayFunctions(): void {
|
||||||
Object.defineProperty(Array.prototype, 'toString', {
|
Object.defineProperty(Array.prototype, 'toString', {
|
||||||
value: function(): string {
|
value: function(): string {
|
||||||
@ -153,7 +145,6 @@ export class AppComponent {
|
|||||||
enumerable: false
|
enumerable: false
|
||||||
});
|
});
|
||||||
|
|
||||||
// intersect
|
|
||||||
Object.defineProperty(Array.prototype, 'intersect', {
|
Object.defineProperty(Array.prototype, 'intersect', {
|
||||||
value: function<T>(other: T[]): T[] {
|
value: function<T>(other: T[]): T[] {
|
||||||
let a = this;
|
let a = this;
|
||||||
@ -167,7 +158,6 @@ export class AppComponent {
|
|||||||
enumerable: false
|
enumerable: false
|
||||||
});
|
});
|
||||||
|
|
||||||
// mapToObject
|
|
||||||
Object.defineProperty(Array.prototype, 'mapToObject', {
|
Object.defineProperty(Array.prototype, 'mapToObject', {
|
||||||
value: function<T>(f: (item: T) => { [key: string]: any }): { [key: string]: any } {
|
value: function<T>(f: (item: T) => { [key: string]: any }): { [key: string]: any } {
|
||||||
return this.reduce((aggr, item) => {
|
return this.reduce((aggr, item) => {
|
||||||
@ -188,18 +178,17 @@ export class AppComponent {
|
|||||||
* Adds some functions to Set.
|
* Adds some functions to Set.
|
||||||
*/
|
*/
|
||||||
private overloadSetFunctions(): void {
|
private overloadSetFunctions(): void {
|
||||||
// equals
|
|
||||||
Object.defineProperty(Set.prototype, 'equals', {
|
Object.defineProperty(Set.prototype, 'equals', {
|
||||||
value: function<T>(other: Set<T>): boolean {
|
value: function<T>(other: Set<T>): boolean {
|
||||||
const _difference = new Set(this);
|
const difference = new Set(this);
|
||||||
for (const elem of other) {
|
for (const elem of other) {
|
||||||
if (_difference.has(elem)) {
|
if (difference.has(elem)) {
|
||||||
_difference.delete(elem);
|
difference.delete(elem);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return !_difference.size;
|
return !difference.size;
|
||||||
},
|
},
|
||||||
enumerable: false
|
enumerable: false
|
||||||
});
|
});
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { _ } from 'app/core/translate/translation-marker';
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import { _ } from 'app/core/translate/translation-marker';
|
||||||
import { BannerDefinition, BannerService } from '../ui-services/banner.service';
|
import { BannerDefinition, BannerService } from '../ui-services/banner.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,7 +71,6 @@ export abstract class PollPdfService {
|
|||||||
* @returns the amount of ballots, depending on the config settings
|
* @returns the amount of ballots, depending on the config settings
|
||||||
*/
|
*/
|
||||||
protected getBallotCount(): number {
|
protected getBallotCount(): number {
|
||||||
// TODO: seems to be broken
|
|
||||||
switch (this.ballotCountSelection) {
|
switch (this.ballotCountSelection) {
|
||||||
case 'NUMBER_OF_ALL_PARTICIPANTS':
|
case 'NUMBER_OF_ALL_PARTICIPANTS':
|
||||||
return this.userRepo.getViewModelList().length;
|
return this.userRepo.getViewModelList().length;
|
||||||
|
@ -9,6 +9,7 @@ import { ViewModelStoreService } from 'app/core/core-services/view-model-store.s
|
|||||||
import { RelationDefinition } from 'app/core/definitions/relations';
|
import { RelationDefinition } from 'app/core/definitions/relations';
|
||||||
import { VotingService } from 'app/core/ui-services/voting.service';
|
import { VotingService } from 'app/core/ui-services/voting.service';
|
||||||
import { MotionPoll } from 'app/shared/models/motions/motion-poll';
|
import { MotionPoll } from 'app/shared/models/motions/motion-poll';
|
||||||
|
import { VoteValue } from 'app/shared/models/poll/base-vote';
|
||||||
import { ViewMotion } from 'app/site/motions/models/view-motion';
|
import { ViewMotion } from 'app/site/motions/models/view-motion';
|
||||||
import { ViewMotionOption } from 'app/site/motions/models/view-motion-option';
|
import { ViewMotionOption } from 'app/site/motions/models/view-motion-option';
|
||||||
import { MotionPollTitleInformation, ViewMotionPoll } from 'app/site/motions/models/view-motion-poll';
|
import { MotionPollTitleInformation, ViewMotionPoll } from 'app/site/motions/models/view-motion-poll';
|
||||||
@ -91,7 +92,7 @@ export class MotionPollRepositoryService extends BasePollRepositoryService<
|
|||||||
return this.translate.instant(plural ? 'Polls' : 'Poll');
|
return this.translate.instant(plural ? 'Polls' : 'Poll');
|
||||||
};
|
};
|
||||||
|
|
||||||
public vote(vote: 'Y' | 'N' | 'A', poll_id: number): Promise<void> {
|
public vote(vote: VoteValue, poll_id: number): Promise<void> {
|
||||||
return this.http.post(`/rest/motions/motion-poll/${poll_id}/vote/`, JSON.stringify(vote));
|
return this.http.post(`/rest/motions/motion-poll/${poll_id}/vote/`, JSON.stringify(vote));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,6 @@ _('Only main agenda items');
|
|||||||
_('Topics');
|
_('Topics');
|
||||||
_('Open requests to speak');
|
_('Open requests to speak');
|
||||||
|
|
||||||
|
|
||||||
// ** Motions **
|
// ** Motions **
|
||||||
// config strings
|
// config strings
|
||||||
// subgroup general
|
// subgroup general
|
||||||
@ -246,7 +245,6 @@ _('Motion block');
|
|||||||
_('The text field may not be blank.');
|
_('The text field may not be blank.');
|
||||||
_('The reason field may not be blank.');
|
_('The reason field may not be blank.');
|
||||||
|
|
||||||
|
|
||||||
// ** Assignments **
|
// ** Assignments **
|
||||||
// Assignment config strings
|
// Assignment config strings
|
||||||
_('Elections');
|
_('Elections');
|
||||||
@ -257,7 +255,7 @@ _('All valid ballots');
|
|||||||
_('All casted ballots');
|
_('All casted ballots');
|
||||||
_('Disabled (no percents)');
|
_('Disabled (no percents)');
|
||||||
_('Default groups with voting rights');
|
_('Default groups with voting rights');
|
||||||
_('Sort election results by amount of votes')
|
_('Sort election results by amount of votes');
|
||||||
_('Put all candidates on the list of speakers');
|
_('Put all candidates on the list of speakers');
|
||||||
// subgroup ballot papers
|
// subgroup ballot papers
|
||||||
_('Ballot papers');
|
_('Ballot papers');
|
||||||
@ -291,7 +289,6 @@ _('Entitled to vote');
|
|||||||
_('Voting method');
|
_('Voting method');
|
||||||
_('Amount of votes');
|
_('Amount of votes');
|
||||||
|
|
||||||
|
|
||||||
// ** Users **
|
// ** Users **
|
||||||
// permission strings (see models.py of each Django app)
|
// permission strings (see models.py of each Django app)
|
||||||
// agenda
|
// agenda
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { _ } from 'app/core/translate/translation-marker';
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { _ } from 'app/core/translate/translation-marker';
|
||||||
import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll';
|
import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll';
|
||||||
import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll';
|
import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll';
|
||||||
import { ViewBasePoll } from 'app/site/polls/models/view-base-poll';
|
import { ViewBasePoll } from 'app/site/polls/models/view-base-poll';
|
||||||
@ -44,10 +44,7 @@ export class VotingBannerService {
|
|||||||
const banner =
|
const banner =
|
||||||
pollsToVote.length === 1
|
pollsToVote.length === 1
|
||||||
? this.createBanner(this.getTextForPoll(pollsToVote[0]), pollsToVote[0].parentLink)
|
? this.createBanner(this.getTextForPoll(pollsToVote[0]), pollsToVote[0].parentLink)
|
||||||
: this.createBanner(
|
: this.createBanner(`${pollsToVote.length} ${this.translate.instant('open votes')}`, '/polls/');
|
||||||
`${pollsToVote.length} ${this.translate.instant('open votes')}`,
|
|
||||||
'/polls/'
|
|
||||||
);
|
|
||||||
this.sliceBanner(banner);
|
this.sliceBanner(banner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,21 +47,15 @@ export class AttachmentControlComponent extends BaseFormControlComponent<ViewMed
|
|||||||
return 'attachment-control';
|
return 'attachment-control';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Default constructor
|
|
||||||
*
|
|
||||||
* @param dialogService Reference to the `MatDialog`
|
|
||||||
* @param mediaService Reference for the `MediaFileRepositoryService`
|
|
||||||
*/
|
|
||||||
public constructor(
|
public constructor(
|
||||||
fb: FormBuilder,
|
formBuilder: FormBuilder,
|
||||||
fm: FocusMonitor,
|
focusMonitor: FocusMonitor,
|
||||||
element: ElementRef<HTMLElement>,
|
element: ElementRef<HTMLElement>,
|
||||||
@Optional() @Self() public ngControl: NgControl,
|
@Optional() @Self() public ngControl: NgControl,
|
||||||
private dialogService: MatDialog,
|
private dialogService: MatDialog,
|
||||||
private mediaService: MediafileRepositoryService
|
private mediaService: MediafileRepositoryService
|
||||||
) {
|
) {
|
||||||
super(fb, fm, element, ngControl);
|
super(formBuilder, focusMonitor, element, ngControl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -102,12 +96,15 @@ export class AttachmentControlComponent extends BaseFormControlComponent<ViewMed
|
|||||||
this.errorHandler.emit(error);
|
this.errorHandler.emit(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onContainerClick(event: MouseEvent): void {
|
/**
|
||||||
// TODO: implement
|
* Declared as abstract in MatFormFieldControl and not required for this component
|
||||||
}
|
*/
|
||||||
|
public onContainerClick(event: MouseEvent): void {}
|
||||||
|
|
||||||
protected initializeForm(): void {
|
protected initializeForm(): void {
|
||||||
this.contentForm = this.fb.control([]);
|
this.contentForm = this.fb.control([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updateForm(value: ViewMediafile[] | null): void {
|
protected updateForm(value: ViewMediafile[] | null): void {
|
||||||
this.contentForm.setValue(value || []);
|
this.contentForm.setValue(value || []);
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,8 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngSwitchDefault>
|
<ng-container *ngSwitchDefault>
|
||||||
<a class="banner-link" [routerLink]="banner.link" [style.cursor]="banner.link ? 'pointer' : 'default'">
|
<a class="banner-link" [routerLink]="banner.link" [style.cursor]="banner.link ? 'pointer' : 'default'">
|
||||||
<mat-icon>{{ banner.icon }}</mat-icon> <span>{{ banner.text }}</span>
|
<mat-icon>{{ banner.icon }}</mat-icon>
|
||||||
|
<span>{{ banner.text }}</span>
|
||||||
<div *ngIf="banner.subText">
|
<div *ngIf="banner.subText">
|
||||||
{{ banner.subText | translate }}
|
{{ banner.subText | translate }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -76,7 +76,7 @@ export class ChartsComponent extends BaseViewComponent {
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
this.circleColors = !!circleColors[0].backgroundColor.length ? circleColors : null;
|
this.circleColors = !!circleColors[0].backgroundColor.length ? circleColors : null;
|
||||||
this.checkChartType();
|
this.checkAndUpdateChartType();
|
||||||
this.cd.detectChanges();
|
this.cd.detectChanges();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -87,7 +87,8 @@ export class ChartsComponent extends BaseViewComponent {
|
|||||||
*/
|
*/
|
||||||
@Input()
|
@Input()
|
||||||
public set type(type: ChartType) {
|
public set type(type: ChartType) {
|
||||||
this.checkChartType(type);
|
this._type = type;
|
||||||
|
this.checkAndUpdateChartType();
|
||||||
this.cd.detectChanges();
|
this.cd.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,12 +291,10 @@ export class ChartsComponent extends BaseViewComponent {
|
|||||||
this.cd.detectChanges();
|
this.cd.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkChartType(chartType?: ChartType): void {
|
private checkAndUpdateChartType(): void {
|
||||||
let type = chartType || this._type;
|
if (this._type === 'stackedBar') {
|
||||||
if (type === 'stackedBar') {
|
|
||||||
this.setupBar();
|
this.setupBar();
|
||||||
type = 'horizontalBar';
|
this._type = 'horizontalBar';
|
||||||
}
|
}
|
||||||
this._type = type;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,6 @@
|
|||||||
'bar action';
|
'bar action';
|
||||||
grid-template-columns: auto min-content;
|
grid-template-columns: auto min-content;
|
||||||
|
|
||||||
.mat-progress-bar-buffer {
|
|
||||||
// TODO theme
|
|
||||||
// background-color: mat-color($background, card) !important;
|
|
||||||
background-color: white !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
.message {
|
||||||
grid-area: message;
|
grid-area: message;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
@import '~@angular/material/theming';
|
||||||
|
|
||||||
|
/** Custom component theme. Only lives in a specific scope */
|
||||||
|
@mixin os-progress-snack-bar-style($theme) {
|
||||||
|
$background: map-get($theme, background);
|
||||||
|
|
||||||
|
.mat-progress-bar-buffer {
|
||||||
|
background-color: mat-color($background, card) !important;
|
||||||
|
}
|
||||||
|
}
|
@ -9,8 +9,10 @@
|
|||||||
[removable]="true"
|
[removable]="true"
|
||||||
(removed)="removeItem(item.id)"
|
(removed)="removeItem(item.id)"
|
||||||
[disableRipple]="true"
|
[disableRipple]="true"
|
||||||
>{{ item.getTitle() }} <mat-icon matChipRemove>cancel</mat-icon></mat-chip
|
|
||||||
>
|
>
|
||||||
|
{{ item.getTitle() }}
|
||||||
|
<mat-icon matChipRemove>cancel</mat-icon>
|
||||||
|
</mat-chip>
|
||||||
</mat-chip-list>
|
</mat-chip-list>
|
||||||
</div>
|
</div>
|
||||||
<div class="os-search-value-selector-chip-placeholder"></div>
|
<div class="os-search-value-selector-chip-placeholder"></div>
|
||||||
|
@ -119,17 +119,14 @@ export class SearchValueSelectorComponent extends BaseFormControlComponent<Selec
|
|||||||
*/
|
*/
|
||||||
private selectableItems: Selectable[];
|
private selectableItems: Selectable[];
|
||||||
|
|
||||||
/**
|
|
||||||
* Empty constructor
|
|
||||||
*/
|
|
||||||
public constructor(
|
public constructor(
|
||||||
protected translate: TranslateService,
|
protected translate: TranslateService,
|
||||||
fb: FormBuilder,
|
formBuilder: FormBuilder,
|
||||||
@Optional() @Self() public ngControl: NgControl,
|
@Optional() @Self() public ngControl: NgControl,
|
||||||
fm: FocusMonitor,
|
focusMonitor: FocusMonitor,
|
||||||
element: ElementRef<HTMLElement>
|
element: ElementRef<HTMLElement>
|
||||||
) {
|
) {
|
||||||
super(fb, fm, element, ngControl);
|
super(formBuilder, focusMonitor, element, ngControl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { BaseModel } from './base-model';
|
import { BaseModel } from './base-model';
|
||||||
|
|
||||||
export abstract class BaseDecimalModel<T = any> extends BaseModel<T> {
|
export abstract class BaseDecimalModel<T = any> extends BaseModel<T> {
|
||||||
// TODO: no more elegant solution available in current Typescript
|
|
||||||
protected abstract getDecimalFields(): string[];
|
protected abstract getDecimalFields(): string[];
|
||||||
|
|
||||||
public deserialize(input: any): void {
|
public deserialize(input: any): void {
|
||||||
|
@ -38,6 +38,10 @@ export enum PercentBase {
|
|||||||
Disabled = 'disabled'
|
Disabled = 'disabled'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const VOTE_MAJORITY = -1;
|
||||||
|
export const VOTE_UNDOCUMENTED = -2;
|
||||||
|
export const LOWEST_VOTE_VALUE = VOTE_UNDOCUMENTED;
|
||||||
|
|
||||||
export abstract class BasePoll<
|
export abstract class BasePoll<
|
||||||
T = any,
|
T = any,
|
||||||
O extends BaseOption<any> = any,
|
O extends BaseOption<any> = any,
|
||||||
|
@ -2,6 +2,8 @@ import { Pipe, PipeTransform } from '@angular/core';
|
|||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { VOTE_MAJORITY, VOTE_UNDOCUMENTED } from '../models/poll/base-poll';
|
||||||
|
|
||||||
@Pipe({
|
@Pipe({
|
||||||
name: 'parsePollNumber'
|
name: 'parsePollNumber'
|
||||||
})
|
})
|
||||||
@ -11,9 +13,9 @@ export class ParsePollNumberPipe implements PipeTransform {
|
|||||||
public transform(value: number): number | string {
|
public transform(value: number): number | string {
|
||||||
const input = Math.trunc(value);
|
const input = Math.trunc(value);
|
||||||
switch (input) {
|
switch (input) {
|
||||||
case -1:
|
case VOTE_MAJORITY:
|
||||||
return this.translate.instant('majority');
|
return this.translate.instant('majority');
|
||||||
case -2:
|
case VOTE_UNDOCUMENTED:
|
||||||
return this.translate.instant('undocumented');
|
return this.translate.instant('undocumented');
|
||||||
default:
|
default:
|
||||||
return input;
|
return input;
|
||||||
|
@ -18,7 +18,7 @@ export const AssignmentsAppConfig: AppConfig = {
|
|||||||
{
|
{
|
||||||
model: Assignment,
|
model: Assignment,
|
||||||
viewModel: ViewAssignment,
|
viewModel: ViewAssignment,
|
||||||
// searchOrder: 3, // TODO: enable, if there is a detail page and so on.
|
searchOrder: 3,
|
||||||
repository: AssignmentRepositoryService
|
repository: AssignmentRepositoryService
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -308,7 +308,6 @@ export class AssignmentDetailComponent extends BaseViewComponent implements OnIn
|
|||||||
* Creates a new Poll
|
* Creates a new Poll
|
||||||
*/
|
*/
|
||||||
public openDialog(): void {
|
public openDialog(): void {
|
||||||
// TODO: That is not really a ViewObject
|
|
||||||
const dialogData = {
|
const dialogData = {
|
||||||
collectionString: ViewAssignmentPoll.COLLECTIONSTRING,
|
collectionString: ViewAssignmentPoll.COLLECTIONSTRING,
|
||||||
assignment_id: this.assignment.id,
|
assignment_id: this.assignment.id,
|
||||||
|
@ -12,6 +12,7 @@ import { AssignmentVoteRepositoryService } from 'app/core/repositories/assignmen
|
|||||||
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';
|
||||||
|
import { VoteValue } from 'app/shared/models/poll/base-vote';
|
||||||
import { BasePollDetailComponent } from 'app/site/polls/components/base-poll-detail.component';
|
import { BasePollDetailComponent } from 'app/site/polls/components/base-poll-detail.component';
|
||||||
import { PollService, PollTableData, VotingResult } from 'app/site/polls/services/poll.service';
|
import { PollService, PollTableData, VotingResult } from 'app/site/polls/services/poll.service';
|
||||||
import { AssignmentPollDialogService } from '../../services/assignment-poll-dialog.service';
|
import { AssignmentPollDialogService } from '../../services/assignment-poll-dialog.service';
|
||||||
@ -110,7 +111,7 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponent<ViewA
|
|||||||
this.isReady = true;
|
this.isReady = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private voteValueToLabel(vote: 'Y' | 'N' | 'A'): string {
|
private voteValueToLabel(vote: VoteValue): string {
|
||||||
if (vote === 'Y') {
|
if (vote === 'Y') {
|
||||||
return this.translate.instant('Yes');
|
return this.translate.instant('Yes');
|
||||||
} else if (vote === 'N') {
|
} else if (vote === 'N') {
|
||||||
|
@ -8,7 +8,7 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
|
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
|
||||||
|
|
||||||
import { AssignmentPollMethod } 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 { LOWEST_VOTE_VALUE, PollType } from 'app/shared/models/poll/base-poll';
|
||||||
import { GeneralValueVerbose, VoteValue, VoteValueVerbose } from 'app/shared/models/poll/base-vote';
|
import { GeneralValueVerbose, VoteValue, VoteValueVerbose } from 'app/shared/models/poll/base-vote';
|
||||||
import {
|
import {
|
||||||
AssignmentPollMethodVerbose,
|
AssignmentPollMethodVerbose,
|
||||||
@ -168,30 +168,20 @@ export class AssignmentPollDialogComponent extends BasePollDialogComponent<ViewA
|
|||||||
[option.user_id]: this.fb.group(
|
[option.user_id]: this.fb.group(
|
||||||
// for each user, create a form group with a control for each valid input (Y, N, A)
|
// for each user, create a form group with a control for each valid input (Y, N, A)
|
||||||
this.analogPollValues.mapToObject(value => ({
|
this.analogPollValues.mapToObject(value => ({
|
||||||
[value]: ['', [Validators.min(-2)]]
|
[value]: ['', [Validators.min(LOWEST_VOTE_VALUE)]]
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
),
|
),
|
||||||
amount_global_no: ['', [Validators.min(-2)]],
|
amount_global_no: ['', [Validators.min(LOWEST_VOTE_VALUE)]],
|
||||||
amount_global_abstain: ['', [Validators.min(-2)]],
|
amount_global_abstain: ['', [Validators.min(LOWEST_VOTE_VALUE)]],
|
||||||
// insert all used global fields
|
// insert all used global fields
|
||||||
...this.sumValues.mapToObject(sumValue => ({
|
...this.sumValues.mapToObject(sumValue => ({
|
||||||
[sumValue]: ['', [Validators.min(-2)]]
|
[sumValue]: ['', [Validators.min(LOWEST_VOTE_VALUE)]]
|
||||||
}))
|
}))
|
||||||
});
|
});
|
||||||
if (this.isAnalogPoll && this.pollData.poll) {
|
if (this.isAnalogPoll && this.pollData.poll) {
|
||||||
this.updateDialogVoteForm(this.pollData);
|
this.updateDialogVoteForm(this.pollData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a per-poll value
|
|
||||||
*
|
|
||||||
* @param value
|
|
||||||
* @param weight
|
|
||||||
*/
|
|
||||||
public setSumValue(value: any /*SummaryPollKey*/, weight: string): void {
|
|
||||||
this.pollData[value] = parseFloat(weight);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -14,19 +14,18 @@ import { PromptService } from 'app/core/ui-services/prompt.service';
|
|||||||
import { VotingService } from 'app/core/ui-services/voting.service';
|
import { VotingService } from 'app/core/ui-services/voting.service';
|
||||||
import { AssignmentPollMethod } 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 { VoteValue } from 'app/shared/models/poll/base-vote';
|
||||||
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';
|
||||||
|
|
||||||
// TODO: Duplicate
|
// TODO: Duplicate
|
||||||
interface VoteActions {
|
interface VoteActions {
|
||||||
vote: Vote;
|
vote: VoteValue;
|
||||||
css: string;
|
css: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
label: string;
|
label: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Vote = 'Y' | 'N' | 'A';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'os-assignment-poll-vote',
|
selector: 'os-assignment-poll-vote',
|
||||||
templateUrl: './assignment-poll-vote.component.html',
|
templateUrl: './assignment-poll-vote.component.html',
|
||||||
@ -45,12 +44,12 @@ export class AssignmentPollVoteComponent extends BasePollVoteComponent<ViewAssig
|
|||||||
title: Title,
|
title: Title,
|
||||||
protected translate: TranslateService,
|
protected translate: TranslateService,
|
||||||
matSnackbar: MatSnackBar,
|
matSnackbar: MatSnackBar,
|
||||||
vmanager: VotingService,
|
|
||||||
operator: OperatorService,
|
operator: OperatorService,
|
||||||
|
public vmanager: VotingService,
|
||||||
private pollRepo: AssignmentPollRepositoryService,
|
private pollRepo: AssignmentPollRepositoryService,
|
||||||
private promptService: PromptService
|
private promptService: PromptService
|
||||||
) {
|
) {
|
||||||
super(title, translate, matSnackbar, vmanager, operator);
|
super(title, translate, matSnackbar, operator);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
@ -116,7 +115,7 @@ export class AssignmentPollVoteComponent extends BasePollVoteComponent<ViewAssig
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public saveSingleVote(optionId: number, vote: Vote): void {
|
public saveSingleVote(optionId: number, vote: VoteValue): void {
|
||||||
if (this.isGlobalOptionSelected()) {
|
if (this.isGlobalOptionSelected()) {
|
||||||
delete this.voteRequestData.global;
|
delete this.voteRequestData.global;
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
AssignmentPollMethod,
|
AssignmentPollMethod,
|
||||||
AssignmentPollPercentBase
|
AssignmentPollPercentBase
|
||||||
} from 'app/shared/models/assignments/assignment-poll';
|
} from 'app/shared/models/assignments/assignment-poll';
|
||||||
import { MajorityMethod } from 'app/shared/models/poll/base-poll';
|
import { MajorityMethod, VOTE_UNDOCUMENTED } from 'app/shared/models/poll/base-poll';
|
||||||
import { PollData, PollService, PollTableData, VotingResult } from 'app/site/polls/services/poll.service';
|
import { PollData, PollService, PollTableData, VotingResult } from 'app/site/polls/services/poll.service';
|
||||||
import { ViewAssignmentPoll } from '../models/view-assignment-poll';
|
import { ViewAssignmentPoll } from '../models/view-assignment-poll';
|
||||||
|
|
||||||
@ -77,17 +77,16 @@ export class AssignmentPollService extends PollService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getGlobalVoteKeys(poll: ViewAssignmentPoll): VotingResult[] {
|
private getGlobalVoteKeys(poll: ViewAssignmentPoll): VotingResult[] {
|
||||||
// debugger;
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
vote: 'amount_global_no',
|
vote: 'amount_global_no',
|
||||||
showPercent: this.showPercentOfValidOrCast(poll),
|
showPercent: this.showPercentOfValidOrCast(poll),
|
||||||
hide: poll.amount_global_no === -2 || !poll.amount_global_no
|
hide: poll.amount_global_no === VOTE_UNDOCUMENTED || !poll.amount_global_no
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
vote: 'amount_global_abstain',
|
vote: 'amount_global_abstain',
|
||||||
showPercent: this.showPercentOfValidOrCast(poll),
|
showPercent: this.showPercentOfValidOrCast(poll),
|
||||||
hide: poll.amount_global_abstain === -2 || !poll.amount_global_abstain
|
hide: poll.amount_global_abstain === VOTE_UNDOCUMENTED || !poll.amount_global_abstain
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -1628,7 +1628,6 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public openDialog(): void {
|
public openDialog(): void {
|
||||||
// TODO: Could be simpler, requires a lot of refactoring
|
|
||||||
const dialogData = {
|
const dialogData = {
|
||||||
collectionString: ViewMotionPoll.COLLECTIONSTRING,
|
collectionString: ViewMotionPoll.COLLECTIONSTRING,
|
||||||
motion_id: this.motion.id,
|
motion_id: this.motion.id,
|
||||||
|
@ -5,6 +5,7 @@ import { Title } from '@angular/platform-browser';
|
|||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { LOWEST_VOTE_VALUE } from 'app/shared/models/poll/base-poll';
|
||||||
import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll';
|
import { ViewMotionPoll } 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';
|
||||||
@ -22,11 +23,11 @@ export class MotionPollDialogComponent extends BasePollDialogComponent<ViewMotio
|
|||||||
protected pollForm: PollFormComponent<ViewMotionPoll>;
|
protected pollForm: PollFormComponent<ViewMotionPoll>;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private fb: FormBuilder,
|
|
||||||
title: Title,
|
title: Title,
|
||||||
protected translate: TranslateService,
|
translate: TranslateService,
|
||||||
matSnackbar: MatSnackBar,
|
matSnackbar: MatSnackBar,
|
||||||
public dialogRef: MatDialogRef<BasePollDialogComponent<ViewMotionPoll>>,
|
public dialogRef: MatDialogRef<BasePollDialogComponent<ViewMotionPoll>>,
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
@Inject(MAT_DIALOG_DATA) public pollData: Partial<ViewMotionPoll>
|
@Inject(MAT_DIALOG_DATA) public pollData: Partial<ViewMotionPoll>
|
||||||
) {
|
) {
|
||||||
super(title, translate, matSnackbar, dialogRef);
|
super(title, translate, matSnackbar, dialogRef);
|
||||||
@ -56,13 +57,13 @@ export class MotionPollDialogComponent extends BasePollDialogComponent<ViewMotio
|
|||||||
* Pre-executed method to initialize the dialog-form depending on the poll-method.
|
* Pre-executed method to initialize the dialog-form depending on the poll-method.
|
||||||
*/
|
*/
|
||||||
private createDialog(): void {
|
private createDialog(): void {
|
||||||
this.dialogVoteForm = this.fb.group({
|
this.dialogVoteForm = this.formBuilder.group({
|
||||||
Y: ['', [Validators.min(-2)]],
|
Y: ['', [Validators.min(LOWEST_VOTE_VALUE)]],
|
||||||
N: ['', [Validators.min(-2)]],
|
N: ['', [Validators.min(LOWEST_VOTE_VALUE)]],
|
||||||
A: ['', [Validators.min(-2)]],
|
A: ['', [Validators.min(LOWEST_VOTE_VALUE)]],
|
||||||
votesvalid: ['', [Validators.min(-2)]],
|
votesvalid: ['', [Validators.min(LOWEST_VOTE_VALUE)]],
|
||||||
votesinvalid: ['', [Validators.min(-2)]],
|
votesinvalid: ['', [Validators.min(LOWEST_VOTE_VALUE)]],
|
||||||
votescast: ['', [Validators.min(-2)]]
|
votescast: ['', [Validators.min(LOWEST_VOTE_VALUE)]]
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.pollData.poll) {
|
if (this.pollData.poll) {
|
||||||
|
@ -8,11 +8,12 @@ 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 { PromptService } from 'app/core/ui-services/prompt.service';
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
import { VotingService } from 'app/core/ui-services/voting.service';
|
import { VotingService } from 'app/core/ui-services/voting.service';
|
||||||
|
import { VoteValue } from 'app/shared/models/poll/base-vote';
|
||||||
import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll';
|
import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll';
|
||||||
import { BasePollVoteComponent } from 'app/site/polls/components/base-poll-vote.component';
|
import { BasePollVoteComponent } from 'app/site/polls/components/base-poll-vote.component';
|
||||||
|
|
||||||
interface VoteOption {
|
interface VoteOption {
|
||||||
vote?: 'Y' | 'N' | 'A';
|
vote?: VoteValue;
|
||||||
css?: string;
|
css?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
@ -50,18 +51,15 @@ export class MotionPollVoteComponent extends BasePollVoteComponent<ViewMotionPol
|
|||||||
title: Title,
|
title: Title,
|
||||||
translate: TranslateService,
|
translate: TranslateService,
|
||||||
matSnackbar: MatSnackBar,
|
matSnackbar: MatSnackBar,
|
||||||
vmanager: VotingService,
|
|
||||||
operator: OperatorService,
|
operator: OperatorService,
|
||||||
|
public vmanager: VotingService,
|
||||||
private pollRepo: MotionPollRepositoryService,
|
private pollRepo: MotionPollRepositoryService,
|
||||||
private promptService: PromptService
|
private promptService: PromptService
|
||||||
) {
|
) {
|
||||||
super(title, translate, matSnackbar, vmanager, operator);
|
super(title, translate, matSnackbar, operator);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public saveVote(vote: VoteValue): void {
|
||||||
* TODO: 'Y' | 'N' | 'A' should refer to some ENUM
|
|
||||||
*/
|
|
||||||
public saveVote(vote: 'Y' | 'N' | 'A'): void {
|
|
||||||
this.currentVote.vote = vote;
|
this.currentVote.vote = vote;
|
||||||
const title = this.translate.instant('Submit selection now?');
|
const title = this.translate.instant('Submit selection now?');
|
||||||
const content = this.translate.instant('Your decision cannot be changed afterwards.');
|
const content = this.translate.instant('Your decision cannot be changed afterwards.');
|
||||||
|
@ -4,6 +4,7 @@ import { Title } from '@angular/platform-browser';
|
|||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
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 { PromptService } from 'app/core/ui-services/prompt.service';
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
import { VotingPrivacyWarningComponent } from 'app/shared/components/voting-privacy-warning/voting-privacy-warning.component';
|
import { VotingPrivacyWarningComponent } from 'app/shared/components/voting-privacy-warning/voting-privacy-warning.component';
|
||||||
@ -14,7 +15,6 @@ import { MotionPollPdfService } from 'app/site/motions/services/motion-poll-pdf.
|
|||||||
import { MotionPollService } from 'app/site/motions/services/motion-poll.service';
|
import { MotionPollService } from 'app/site/motions/services/motion-poll.service';
|
||||||
import { BasePollComponent } from 'app/site/polls/components/base-poll.component';
|
import { BasePollComponent } from 'app/site/polls/components/base-poll.component';
|
||||||
import { PollService, PollTableData } from 'app/site/polls/services/poll.service';
|
import { PollService, PollTableData } from 'app/site/polls/services/poll.service';
|
||||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component to show a motion-poll.
|
* Component to show a motion-poll.
|
||||||
@ -25,10 +25,6 @@ import { OperatorService } from 'app/core/core-services/operator.service';
|
|||||||
styleUrls: ['./motion-poll.component.scss']
|
styleUrls: ['./motion-poll.component.scss']
|
||||||
})
|
})
|
||||||
export class MotionPollComponent extends BasePollComponent<ViewMotionPoll> {
|
export class MotionPollComponent extends BasePollComponent<ViewMotionPoll> {
|
||||||
/**
|
|
||||||
* The dedicated `ViewMotionPoll`.
|
|
||||||
* TODO: shadows superclass `poll`. Maybe change when chart data is generated?
|
|
||||||
*/
|
|
||||||
@Input()
|
@Input()
|
||||||
public set poll(value: ViewMotionPoll) {
|
public set poll(value: ViewMotionPoll) {
|
||||||
this.initPoll(value);
|
this.initPoll(value);
|
||||||
|
@ -8,6 +8,7 @@ import { Label } from 'ng2-charts';
|
|||||||
import { BehaviorSubject, from, Observable } from 'rxjs';
|
import { BehaviorSubject, from, Observable } from 'rxjs';
|
||||||
import { filter, map } from 'rxjs/operators';
|
import { filter, map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { Deferred } from 'app/core/promises/deferred';
|
||||||
import { BaseRepository } from 'app/core/repositories/base-repository';
|
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';
|
||||||
@ -73,7 +74,7 @@ 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;
|
protected optionsLoaded = new Deferred();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
@ -103,7 +104,13 @@ export abstract class BasePollDetailComponent<V extends ViewBasePoll> extends Ba
|
|||||||
protected votesRepo: BaseRepository<ViewBaseVote, BaseVote, object>
|
protected votesRepo: BaseRepository<ViewBaseVote, BaseVote, object>
|
||||||
) {
|
) {
|
||||||
super(title, translate, matSnackbar);
|
super(title, translate, matSnackbar);
|
||||||
votesRepo
|
this.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async setup(): Promise<void> {
|
||||||
|
await this.optionsLoaded;
|
||||||
|
|
||||||
|
this.votesRepo
|
||||||
.getViewModelListObservable()
|
.getViewModelListObservable()
|
||||||
.pipe(
|
.pipe(
|
||||||
filter(() => this.poll && this.canSeeVotes), // filter first for valid poll state to avoid unneccessary iteration of potentially thousands of votes
|
filter(() => this.poll && this.canSeeVotes), // filter first for valid poll state to avoid unneccessary iteration of potentially thousands of votes
|
||||||
@ -111,10 +118,7 @@ export abstract class BasePollDetailComponent<V extends ViewBasePoll> extends Ba
|
|||||||
filter(votes => !!votes.length)
|
filter(votes => !!votes.length)
|
||||||
)
|
)
|
||||||
.subscribe(() => {
|
.subscribe(() => {
|
||||||
// votes data can only be created when options are ready
|
this.createVotesData();
|
||||||
if (this.optionsLoaded) {
|
|
||||||
this.createVotesData();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,10 +160,6 @@ export abstract class BasePollDetailComponent<V extends ViewBasePoll> extends Ba
|
|||||||
*/
|
*/
|
||||||
protected onPollLoaded(): void {}
|
protected onPollLoaded(): void {}
|
||||||
|
|
||||||
protected onPollWithOptionsLoaded(): void {
|
|
||||||
this.createVotesData();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected onStateChanged(): void {}
|
protected onStateChanged(): void {}
|
||||||
|
|
||||||
protected abstract hasPerms(): boolean;
|
protected abstract hasPerms(): boolean;
|
||||||
@ -194,15 +194,6 @@ export abstract class BasePollDetailComponent<V extends ViewBasePoll> extends Ba
|
|||||||
this.chartDataSubject.next(this.pollService.generateChartData(this.poll));
|
this.chartDataSubject.next(this.pollService.generateChartData(this.poll));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This checks if the poll has votes.
|
|
||||||
*/
|
|
||||||
private checkData(): void {
|
|
||||||
if (this.poll.stateHasVotes) {
|
|
||||||
setTimeout(() => this.initChartData());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper-function to search for this poll and display data or create a new one.
|
* Helper-function to search for this poll and display data or create a new one.
|
||||||
*/
|
*/
|
||||||
@ -214,23 +205,12 @@ export abstract class BasePollDetailComponent<V extends ViewBasePoll> extends Ba
|
|||||||
if (poll) {
|
if (poll) {
|
||||||
this.poll = poll;
|
this.poll = poll;
|
||||||
this.onPollLoaded();
|
this.onPollLoaded();
|
||||||
this.waitForOptions();
|
this.createVotesData();
|
||||||
this.checkData();
|
this.initChartData();
|
||||||
|
this.optionsLoaded.resolve();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Waits until poll's options are loaded.
|
|
||||||
*/
|
|
||||||
private waitForOptions(): void {
|
|
||||||
if (!this.poll.options || !this.poll.options.length) {
|
|
||||||
setTimeout(() => this.waitForOptions(), 1);
|
|
||||||
} else {
|
|
||||||
this.optionsLoaded = true;
|
|
||||||
this.onPollWithOptionsLoaded();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import { Title } from '@angular/platform-browser';
|
|||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { VOTE_UNDOCUMENTED } from 'app/shared/models/poll/base-poll';
|
||||||
import { OneOfValidator } from 'app/shared/validators/one-of-validator';
|
import { OneOfValidator } from 'app/shared/validators/one-of-validator';
|
||||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||||
import { PollFormComponent } from './poll-form/poll-form.component';
|
import { PollFormComponent } from './poll-form/poll-form.component';
|
||||||
@ -80,7 +81,9 @@ export abstract class BasePollDialogComponent<T extends ViewBasePoll> extends Ba
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* check recursively whether the given vote data object is empty, meaning all values would be '-2' when sent
|
* check recursively whether the given vote data object is empty, meaning all values would
|
||||||
|
* be VOTE_UNDOCUMENTED when sent
|
||||||
|
*
|
||||||
* @param voteData the (partial) vote data
|
* @param voteData the (partial) vote data
|
||||||
*/
|
*/
|
||||||
private isVoteDataEmpty(voteData: object): boolean {
|
private isVoteDataEmpty(voteData: object): boolean {
|
||||||
@ -91,7 +94,7 @@ export abstract class BasePollDialogComponent<T extends ViewBasePoll> extends Ba
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* iterates over the given data and returns a new object with all empty fields recursively
|
* iterates over the given data and returns a new object with all empty fields recursively
|
||||||
* replaced with '-2'
|
* replaced with VOTE_UNDOCUMENTED
|
||||||
* @param voteData the (partial) data
|
* @param voteData the (partial) data
|
||||||
*/
|
*/
|
||||||
private replaceEmptyValues(voteData: object, undo: boolean = false): object {
|
private replaceEmptyValues(voteData: object, undo: boolean = false): object {
|
||||||
@ -101,9 +104,9 @@ export abstract class BasePollDialogComponent<T extends ViewBasePoll> extends Ba
|
|||||||
result[key] = this.replaceEmptyValues(voteData[key], undo);
|
result[key] = this.replaceEmptyValues(voteData[key], undo);
|
||||||
} else {
|
} else {
|
||||||
if (undo) {
|
if (undo) {
|
||||||
result[key] = voteData[key] === -2 ? null : voteData[key];
|
result[key] = voteData[key] === VOTE_UNDOCUMENTED ? null : voteData[key];
|
||||||
} else {
|
} else {
|
||||||
result[key] = !!voteData[key] ? voteData[key] : -2;
|
result[key] = !!voteData[key] ? voteData[key] : VOTE_UNDOCUMENTED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,7 +114,9 @@ export abstract class BasePollDialogComponent<T extends ViewBasePoll> extends Ba
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* reverses the replacement of empty values by '-2'; replaces each '-2' with null
|
* reverses the replacement of empty values by VOTE_UNDOCUMENTED; replaces each
|
||||||
|
* VOTE_UNDOCUMENTED with null
|
||||||
|
*
|
||||||
* @param voteData the vote data
|
* @param voteData the vote data
|
||||||
*/
|
*/
|
||||||
protected undoReplaceEmptyValues(voteData: object): object {
|
protected undoReplaceEmptyValues(voteData: object): object {
|
||||||
|
@ -5,7 +5,7 @@ import { Title } from '@angular/platform-browser';
|
|||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||||
import { VotingError, VotingService } from 'app/core/ui-services/voting.service';
|
import { VotingError } from 'app/core/ui-services/voting.service';
|
||||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||||
import { ViewUser } from 'app/site/users/models/view-user';
|
import { ViewUser } from 'app/site/users/models/view-user';
|
||||||
import { ViewBasePoll } from '../models/view-base-poll';
|
import { ViewBasePoll } from '../models/view-base-poll';
|
||||||
@ -20,9 +20,8 @@ export abstract class BasePollVoteComponent<V extends ViewBasePoll> extends Base
|
|||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
title: Title,
|
title: Title,
|
||||||
protected translate: TranslateService,
|
translate: TranslateService,
|
||||||
matSnackbar: MatSnackBar,
|
matSnackbar: MatSnackBar,
|
||||||
public vmanager: VotingService,
|
|
||||||
protected operator: OperatorService
|
protected operator: OperatorService
|
||||||
) {
|
) {
|
||||||
super(title, translate, matSnackbar);
|
super(title, translate, matSnackbar);
|
||||||
@ -30,10 +29,7 @@ export abstract class BasePollVoteComponent<V extends ViewBasePoll> extends Base
|
|||||||
this.subscriptions.push(
|
this.subscriptions.push(
|
||||||
this.operator.getViewUserObservable().subscribe(user => {
|
this.operator.getViewUserObservable().subscribe(user => {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
// this.updateVotes();
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// protected abstract updateVotes(): void;
|
|
||||||
}
|
}
|
||||||
|
@ -205,7 +205,6 @@ export class PollFormComponent<T extends ViewBasePoll> extends BaseViewComponent
|
|||||||
/**
|
/**
|
||||||
* Disable votes_amount form control if the poll type is anonymous
|
* Disable votes_amount form control if the poll type is anonymous
|
||||||
* and the poll method is votes.
|
* and the poll method is votes.
|
||||||
* TODO: Enabling this requires at least another layout and some rework
|
|
||||||
*/
|
*/
|
||||||
private setVotesAmountCtrl(): void {
|
private setVotesAmountCtrl(): void {
|
||||||
if (this.contentForm.get('type').value === PollType.Pseudoanonymous) {
|
if (this.contentForm.get('type').value === PollType.Pseudoanonymous) {
|
||||||
|
@ -3,7 +3,14 @@ import { Injectable } from '@angular/core';
|
|||||||
import { _ } from 'app/core/translate/translation-marker';
|
import { _ } from 'app/core/translate/translation-marker';
|
||||||
import { ChartData, ChartDate } from 'app/shared/components/charts/charts.component';
|
import { ChartData, ChartDate } from 'app/shared/components/charts/charts.component';
|
||||||
import { AssignmentPollMethod } from 'app/shared/models/assignments/assignment-poll';
|
import { AssignmentPollMethod } from 'app/shared/models/assignments/assignment-poll';
|
||||||
import { BasePoll, MajorityMethod, PercentBase, PollColor, PollType } from 'app/shared/models/poll/base-poll';
|
import {
|
||||||
|
BasePoll,
|
||||||
|
MajorityMethod,
|
||||||
|
PercentBase,
|
||||||
|
PollColor,
|
||||||
|
PollType,
|
||||||
|
VOTE_UNDOCUMENTED
|
||||||
|
} from 'app/shared/models/poll/base-poll';
|
||||||
import { AssignmentPollMethodVerbose } from 'app/site/assignments/models/view-assignment-poll';
|
import { AssignmentPollMethodVerbose } from 'app/site/assignments/models/view-assignment-poll';
|
||||||
import {
|
import {
|
||||||
MajorityMethodVerbose,
|
MajorityMethodVerbose,
|
||||||
@ -70,17 +77,17 @@ export const PollMajorityMethod: CalculableMajorityMethod[] = [
|
|||||||
{
|
{
|
||||||
value: 'simple_majority',
|
value: 'simple_majority',
|
||||||
display_name: 'Simple majority',
|
display_name: 'Simple majority',
|
||||||
calc: base => calcMajority(base * 0.5, true)
|
calc: base => calcMajority(base / 2, true)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'two-thirds_majority',
|
value: 'two-thirds_majority',
|
||||||
display_name: 'Two-thirds majority',
|
display_name: 'Two-thirds majority',
|
||||||
calc: base => calcMajority((base / 3) * 2)
|
calc: base => calcMajority((base * 2) / 3)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'three-quarters_majority',
|
value: 'three-quarters_majority',
|
||||||
display_name: 'Three-quarters majority',
|
display_name: 'Three-quarters majority',
|
||||||
calc: base => calcMajority((base / 4) * 3)
|
calc: base => calcMajority((base * 3) / 4)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'disabled',
|
value: 'disabled',
|
||||||
@ -91,6 +98,7 @@ export const PollMajorityMethod: CalculableMajorityMethod[] = [
|
|||||||
|
|
||||||
export interface PollData {
|
export interface PollData {
|
||||||
pollmethod: string;
|
pollmethod: string;
|
||||||
|
type: string;
|
||||||
onehundred_percent_base: string;
|
onehundred_percent_base: string;
|
||||||
options: {
|
options: {
|
||||||
user?: {
|
user?: {
|
||||||
@ -242,20 +250,18 @@ export abstract class PollService {
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
vote: 'votesvalid',
|
vote: 'votesvalid',
|
||||||
hide: poll.votesvalid === -2,
|
hide: poll.votesvalid === VOTE_UNDOCUMENTED,
|
||||||
showPercent: this.showPercentOfValidOrCast(poll)
|
showPercent: this.showPercentOfValidOrCast(poll)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
vote: 'votesinvalid',
|
vote: 'votesinvalid',
|
||||||
icon: 'not_interested',
|
icon: 'not_interested',
|
||||||
// TODO || PollType === analog
|
hide: poll.votesinvalid === VOTE_UNDOCUMENTED || poll.type !== PollType.Analog,
|
||||||
hide: poll.votesinvalid === -2,
|
|
||||||
showPercent: poll.onehundred_percent_base === PercentBase.Cast
|
showPercent: poll.onehundred_percent_base === PercentBase.Cast
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
vote: 'votescast',
|
vote: 'votescast',
|
||||||
// TODO || PollType === analog
|
hide: poll.votescast === VOTE_UNDOCUMENTED || poll.type !== PollType.Analog,
|
||||||
hide: poll.votescast === -2,
|
|
||||||
showPercent: poll.onehundred_percent_base === PercentBase.Cast
|
showPercent: poll.onehundred_percent_base === PercentBase.Cast
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@ -312,6 +318,6 @@ export abstract class PollService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public isVoteDocumented(vote: number): boolean {
|
public isVoteDocumented(vote: number): boolean {
|
||||||
return vote !== null && vote !== undefined && vote !== -2;
|
return vote !== null && vote !== undefined && vote !== VOTE_UNDOCUMENTED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,9 +110,7 @@ export class UserDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
|
|
||||||
this.constantsService.get<UserBackends>('UserBackends').subscribe(backends => (this.userBackends = backends));
|
this.constantsService.get<UserBackends>('UserBackends').subscribe(backends => (this.userBackends = backends));
|
||||||
|
|
||||||
this.groupRepo
|
this.groupRepo.getViewModelListObservableWithoutDefaultGroup().subscribe(this.groups);
|
||||||
.getViewModelListObservable()
|
|
||||||
.subscribe(groups => this.groups.next(groups.filter(group => group.id !== 1)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
@import './app/site/motions/modules/motion-poll/motion-poll/motion-poll.component.scss-theme.scss';
|
@import './app/site/motions/modules/motion-poll/motion-poll/motion-poll.component.scss-theme.scss';
|
||||||
@import './app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.scss-theme.scss';
|
@import './app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.scss-theme.scss';
|
||||||
@import './app/site/assignments/components/assignment-poll-detail/assignment-poll-detail-component.scss-theme.scss';
|
@import './app/site/assignments/components/assignment-poll-detail/assignment-poll-detail-component.scss-theme.scss';
|
||||||
|
@import './app/shared/components/progress-snack-bar/progress-snack-bar.component.scss-theme.scss';
|
||||||
|
|
||||||
/** fonts */
|
/** fonts */
|
||||||
@import './assets/styles/fonts.scss';
|
@import './assets/styles/fonts.scss';
|
||||||
@ -62,6 +63,7 @@ $narrow-spacing: (
|
|||||||
@include os-motion-poll-style($theme);
|
@include os-motion-poll-style($theme);
|
||||||
@include os-motion-poll-detail-style($theme);
|
@include os-motion-poll-detail-style($theme);
|
||||||
@include os-assignment-poll-detail-style($theme);
|
@include os-assignment-poll-detail-style($theme);
|
||||||
|
@include os-progress-snack-bar-style($theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Load projector specific SCSS values */
|
/** Load projector specific SCSS values */
|
||||||
|
@ -7,7 +7,6 @@ from django.db import connections, models
|
|||||||
from jsonfield import JSONField
|
from jsonfield import JSONField
|
||||||
|
|
||||||
from openslides.utils import logging
|
from openslides.utils import logging
|
||||||
|
|
||||||
from openslides.utils.manager import BaseManager
|
from openslides.utils.manager import BaseManager
|
||||||
|
|
||||||
from ..agenda.mixins import ListOfSpeakersMixin
|
from ..agenda.mixins import ListOfSpeakersMixin
|
||||||
|
@ -184,8 +184,9 @@ class TestCreation(TestCase):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
sorted([group.id for group in mediafile.access_groups.all()]), [2, 4]
|
sorted([group.id for group in mediafile.access_groups.all()]), [2, 4]
|
||||||
)
|
)
|
||||||
self.assertTrue(mediafile.mediafile.name)
|
self.assertEqual(mediafile.mediafile.name, "")
|
||||||
self.assertEqual(mediafile.path, mediafile.original_filename)
|
self.assertEqual(mediafile.original_filename, "")
|
||||||
|
self.assertEqual(mediafile.path, mediafile.title + "/")
|
||||||
|
|
||||||
def test_with_access_groups_wrong_json(self):
|
def test_with_access_groups_wrong_json(self):
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
|
Loading…
Reference in New Issue
Block a user