Fix projection of analog polls & prevent percent base 'entitled' for analog polls
This commit is contained in:
parent
0322436cf5
commit
5d13f94e40
@ -108,7 +108,7 @@
|
|||||||
"karma-jasmine": "~4.0.1",
|
"karma-jasmine": "~4.0.1",
|
||||||
"karma-jasmine-html-reporter": "^1.4.0",
|
"karma-jasmine-html-reporter": "^1.4.0",
|
||||||
"npm-license-crawler": "^0.2.1",
|
"npm-license-crawler": "^0.2.1",
|
||||||
"prettier": "^2.1.1",
|
"prettier": "~2.1.1",
|
||||||
"protractor": "^7.0.0",
|
"protractor": "^7.0.0",
|
||||||
"resize-observer-polyfill": "^1.5.1",
|
"resize-observer-polyfill": "^1.5.1",
|
||||||
"ts-node": "~9.0.0",
|
"ts-node": "~9.0.0",
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
<!-- Chart -->
|
<!-- Chart -->
|
||||||
<div *ngIf="!poll.hasSpecialVoteValues" class="doughnut-chart">
|
<div *ngIf="showChart()" class="doughnut-chart">
|
||||||
<os-charts type="doughnut" [data]="chartData"></os-charts>
|
<os-charts type="doughnut" [data]="chartData"></os-charts>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -72,6 +72,10 @@ export class MotionPollDetailContentComponent extends BaseComponent {
|
|||||||
super(titleService, translate);
|
super(titleService, translate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public showChart(): boolean {
|
||||||
|
return this.pollService.showChart(this.poll);
|
||||||
|
}
|
||||||
|
|
||||||
private setTableData(): void {
|
private setTableData(): void {
|
||||||
this.tableData = this.pollService.generateTableData(this.poll);
|
this.tableData = this.pollService.generateTableData(this.poll);
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,11 @@ export class MotionPollService extends PollService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public showChart(poll: PollData): boolean {
|
public showChart(poll: PollData): boolean {
|
||||||
return poll && poll.options && poll.options.some(option => option.yes >= 0 && option.no >= 0);
|
return (
|
||||||
|
poll &&
|
||||||
|
poll.options &&
|
||||||
|
poll.options.some(option => option.yes >= 0 && option.no >= 0 && option.abstain >= 0)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPercentBase(poll: PollData): number {
|
public getPercentBase(poll: PollData): number {
|
||||||
|
@ -273,7 +273,9 @@ export abstract class BasePollDetailComponentDirective<V extends ViewBasePoll, S
|
|||||||
|
|
||||||
public ngOnDestroy(): void {
|
public ngOnDestroy(): void {
|
||||||
super.ngOnDestroy();
|
super.ngOnDestroy();
|
||||||
this.entitledUsersSubscription.unsubscribe();
|
if (this.entitledUsersSubscription) {
|
||||||
this.entitledUsersSubscription = null;
|
this.entitledUsersSubscription.unsubscribe();
|
||||||
|
this.entitledUsersSubscription = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,7 +172,7 @@ export class PollFormComponent<T extends ViewBasePoll, S extends PollService>
|
|||||||
this.patchForm(this.contentForm);
|
this.patchForm(this.contentForm);
|
||||||
}
|
}
|
||||||
this.updatePollValues(this.contentForm.value);
|
this.updatePollValues(this.contentForm.value);
|
||||||
this.updatePercentBases(this.pollMethodControl.value);
|
this.updatePercentBases(this.pollMethodControl.value, this.pollTypeControl.value);
|
||||||
|
|
||||||
this.subscriptions.push(
|
this.subscriptions.push(
|
||||||
// changes to whole form
|
// changes to whole form
|
||||||
@ -183,13 +183,12 @@ export class PollFormComponent<T extends ViewBasePoll, S extends PollService>
|
|||||||
}),
|
}),
|
||||||
// poll method changes
|
// poll method changes
|
||||||
this.pollMethodControl.valueChanges.subscribe((method: AssignmentPollMethod) => {
|
this.pollMethodControl.valueChanges.subscribe((method: AssignmentPollMethod) => {
|
||||||
if (method) {
|
this.updatePercentBases(method, this.pollTypeControl.value);
|
||||||
this.updatePercentBases(method);
|
this.setWarning();
|
||||||
this.setWarning();
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
// poll type changes
|
// poll type changes
|
||||||
this.pollTypeControl.valueChanges.subscribe(() => {
|
this.pollTypeControl.valueChanges.subscribe((type: PollType) => {
|
||||||
|
this.updatePercentBases(this.pollMethodControl.value, type);
|
||||||
this.setWarning();
|
this.setWarning();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -226,11 +225,12 @@ export class PollFormComponent<T extends ViewBasePoll, S extends PollService>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* updates the available percent bases according to the pollmethod
|
* updates the available percent bases according to the pollmethod and type
|
||||||
* @param method the currently chosen pollmethod
|
* @param method the currently chosen pollmethod
|
||||||
|
* @param type the currently chosen type
|
||||||
*/
|
*/
|
||||||
private updatePercentBases(method: AssignmentPollMethod): void {
|
private updatePercentBases(method: AssignmentPollMethod, type: PollType): void {
|
||||||
if (method) {
|
if (method || type) {
|
||||||
let forbiddenBases = [];
|
let forbiddenBases = [];
|
||||||
if (method === AssignmentPollMethod.YN) {
|
if (method === AssignmentPollMethod.YN) {
|
||||||
forbiddenBases = [PercentBase.YNA, AssignmentPollPercentBase.Y];
|
forbiddenBases = [PercentBase.YNA, AssignmentPollPercentBase.Y];
|
||||||
@ -239,6 +239,9 @@ export class PollFormComponent<T extends ViewBasePoll, S extends PollService>
|
|||||||
} else if (method === AssignmentPollMethod.Y || AssignmentPollMethod.N) {
|
} else if (method === AssignmentPollMethod.Y || AssignmentPollMethod.N) {
|
||||||
forbiddenBases = [PercentBase.YN, PercentBase.YNA];
|
forbiddenBases = [PercentBase.YN, PercentBase.YNA];
|
||||||
}
|
}
|
||||||
|
if (type === PollType.Analog) {
|
||||||
|
forbiddenBases.push(PercentBase.Entitled);
|
||||||
|
}
|
||||||
|
|
||||||
const bases = {};
|
const bases = {};
|
||||||
for (const [key, value] of Object.entries(this.percentBases)) {
|
for (const [key, value] of Object.entries(this.percentBases)) {
|
||||||
@ -248,7 +251,7 @@ export class PollFormComponent<T extends ViewBasePoll, S extends PollService>
|
|||||||
}
|
}
|
||||||
// update value in case that its no longer valid
|
// update value in case that its no longer valid
|
||||||
const percentBaseControl = this.contentForm.get('onehundred_percent_base');
|
const percentBaseControl = this.contentForm.get('onehundred_percent_base');
|
||||||
percentBaseControl.setValue(this.getNormedPercentBase(percentBaseControl.value, method));
|
percentBaseControl.setValue(this.getNormedPercentBase(percentBaseControl.value, method, type));
|
||||||
|
|
||||||
this.validPercentBases = bases;
|
this.validPercentBases = bases;
|
||||||
}
|
}
|
||||||
@ -256,7 +259,8 @@ export class PollFormComponent<T extends ViewBasePoll, S extends PollService>
|
|||||||
|
|
||||||
private getNormedPercentBase(
|
private getNormedPercentBase(
|
||||||
base: AssignmentPollPercentBase,
|
base: AssignmentPollPercentBase,
|
||||||
method: AssignmentPollMethod
|
method: AssignmentPollMethod,
|
||||||
|
type: PollType
|
||||||
): AssignmentPollPercentBase {
|
): AssignmentPollPercentBase {
|
||||||
if (
|
if (
|
||||||
method === AssignmentPollMethod.YN &&
|
method === AssignmentPollMethod.YN &&
|
||||||
@ -270,6 +274,8 @@ export class PollFormComponent<T extends ViewBasePoll, S extends PollService>
|
|||||||
(base === AssignmentPollPercentBase.YN || base === AssignmentPollPercentBase.YNA)
|
(base === AssignmentPollPercentBase.YN || base === AssignmentPollPercentBase.YNA)
|
||||||
) {
|
) {
|
||||||
return AssignmentPollPercentBase.Y;
|
return AssignmentPollPercentBase.Y;
|
||||||
|
} else if (type === PollType.Analog && base === AssignmentPollPercentBase.Entitled) {
|
||||||
|
return AssignmentPollPercentBase.Cast;
|
||||||
}
|
}
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
|
@ -112,10 +112,6 @@ export abstract class ViewBasePoll<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public get hasSpecialVoteValues(): boolean {
|
|
||||||
return this.poll.isAnalog && this.options.some(option => option.votes.some(vote => vote.weight < 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract get pollmethodVerbose(): string;
|
public abstract get pollmethodVerbose(): string;
|
||||||
|
|
||||||
public abstract get percentBaseVerbose(): string;
|
public abstract get percentBaseVerbose(): string;
|
||||||
|
@ -37,10 +37,6 @@ export class MotionPollSlideComponent extends BasePollSlideComponentDirective<Mo
|
|||||||
return MotionPoll.DECIMAL_FIELDS;
|
return MotionPoll.DECIMAL_FIELDS;
|
||||||
}
|
}
|
||||||
|
|
||||||
public showChart(): boolean {
|
|
||||||
return this.pollService.showChart(this.pollData);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getTableData(): PollTableData[] {
|
public getTableData(): PollTableData[] {
|
||||||
return this.pollService.generateTableData(this.pollData);
|
return this.pollService.generateTableData(this.pollData);
|
||||||
}
|
}
|
||||||
|
@ -110,8 +110,8 @@ class AssignmentPollSerializer(BasePollSerializer):
|
|||||||
validated_data.pop("assignment", None)
|
validated_data.pop("assignment", None)
|
||||||
return super().update(instance, validated_data)
|
return super().update(instance, validated_data)
|
||||||
|
|
||||||
def norm_100_percent_base_to_pollmethod(
|
def norm_100_percent_base(
|
||||||
self, onehundred_percent_base, pollmethod, old_100_percent_base=None
|
self, onehundred_percent_base, pollmethod, polltype, old_100_percent_base=None
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Returns None, if the 100-%-base must not be changed, otherwise the correct 100-%-base.
|
Returns None, if the 100-%-base must not be changed, otherwise the correct 100-%-base.
|
||||||
@ -140,7 +140,9 @@ class AssignmentPollSerializer(BasePollSerializer):
|
|||||||
AssignmentPoll.PERCENT_BASE_YNA,
|
AssignmentPoll.PERCENT_BASE_YNA,
|
||||||
):
|
):
|
||||||
return AssignmentPoll.PERCENT_BASE_Y
|
return AssignmentPoll.PERCENT_BASE_Y
|
||||||
return None
|
return super().norm_100_percent_base(
|
||||||
|
onehundred_percent_base, pollmethod, polltype, old_100_percent_base
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AssignmentSerializer(ModelSerializer):
|
class AssignmentSerializer(ModelSerializer):
|
||||||
|
@ -260,15 +260,17 @@ class MotionPollSerializer(BasePollSerializer):
|
|||||||
validated_data.pop("motion", None)
|
validated_data.pop("motion", None)
|
||||||
return super().update(instance, validated_data)
|
return super().update(instance, validated_data)
|
||||||
|
|
||||||
def norm_100_percent_base_to_pollmethod(
|
def norm_100_percent_base(
|
||||||
self, onehundred_percent_base, pollmethod, old_100_percent_base=None
|
self, onehundred_percent_base, pollmethod, polltype, old_100_percent_base=None
|
||||||
):
|
):
|
||||||
if (
|
if (
|
||||||
pollmethod == MotionPoll.POLLMETHOD_YN
|
pollmethod == MotionPoll.POLLMETHOD_YN
|
||||||
and onehundred_percent_base == MotionPoll.PERCENT_BASE_YNA
|
and onehundred_percent_base == MotionPoll.PERCENT_BASE_YNA
|
||||||
):
|
):
|
||||||
return MotionPoll.PERCENT_BASE_YN
|
return MotionPoll.PERCENT_BASE_YN
|
||||||
return None
|
return super().norm_100_percent_base(
|
||||||
|
onehundred_percent_base, pollmethod, polltype, old_100_percent_base
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class MotionChangeRecommendationSerializer(ModelSerializer):
|
class MotionChangeRecommendationSerializer(ModelSerializer):
|
||||||
|
@ -77,12 +77,14 @@ class BasePollSerializer(ModelSerializer):
|
|||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
"""
|
"""
|
||||||
Match the 100 percent base to the pollmethod. Change the base, if it does not
|
Match the 100 percent base to the pollmethod and type. Change the base, if it does not
|
||||||
fit to the pollmethod.
|
fit to the pollmethod or type.
|
||||||
Set is_pseudoanonymized if type is pseudoanonymous.
|
Set is_pseudoanonymized if type is pseudoanonymous.
|
||||||
"""
|
"""
|
||||||
new_100_percent_base = self.norm_100_percent_base_to_pollmethod(
|
new_100_percent_base = self.norm_100_percent_base(
|
||||||
validated_data["onehundred_percent_base"], validated_data["pollmethod"]
|
validated_data["onehundred_percent_base"],
|
||||||
|
validated_data["pollmethod"],
|
||||||
|
validated_data["type"],
|
||||||
)
|
)
|
||||||
if new_100_percent_base is not None:
|
if new_100_percent_base is not None:
|
||||||
validated_data["onehundred_percent_base"] = new_100_percent_base
|
validated_data["onehundred_percent_base"] = new_100_percent_base
|
||||||
@ -92,7 +94,7 @@ class BasePollSerializer(ModelSerializer):
|
|||||||
|
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
"""
|
"""
|
||||||
Adjusts the 100%-base to the pollmethod. This might be needed,
|
Adjusts the 100%-base to the pollmethod and type. This might be needed,
|
||||||
if at least one of them was changed. Wrong combinations should be
|
if at least one of them was changed. Wrong combinations should be
|
||||||
also handled by the client, but here we make it sure aswell!
|
also handled by the client, but here we make it sure aswell!
|
||||||
|
|
||||||
@ -109,8 +111,11 @@ class BasePollSerializer(ModelSerializer):
|
|||||||
validated_data["is_pseudoanonymized"] = False
|
validated_data["is_pseudoanonymized"] = False
|
||||||
instance = super().update(instance, validated_data)
|
instance = super().update(instance, validated_data)
|
||||||
|
|
||||||
new_100_percent_base = self.norm_100_percent_base_to_pollmethod(
|
new_100_percent_base = self.norm_100_percent_base(
|
||||||
instance.onehundred_percent_base, instance.pollmethod, old_100_percent_base
|
instance.onehundred_percent_base,
|
||||||
|
instance.pollmethod,
|
||||||
|
instance.type,
|
||||||
|
old_100_percent_base,
|
||||||
)
|
)
|
||||||
if new_100_percent_base is not None:
|
if new_100_percent_base is not None:
|
||||||
instance.onehundred_percent_base = new_100_percent_base
|
instance.onehundred_percent_base = new_100_percent_base
|
||||||
@ -136,7 +141,17 @@ class BasePollSerializer(ModelSerializer):
|
|||||||
)
|
)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def norm_100_percent_base_to_pollmethod(
|
def norm_100_percent_base(
|
||||||
self, onehundred_percent_base, pollmethod, old_100_percent_base=None
|
self, onehundred_percent_base, pollmethod, polltype, old_100_percent_base=None
|
||||||
):
|
):
|
||||||
raise NotImplementedError()
|
if (
|
||||||
|
polltype == BasePoll.TYPE_ANALOG
|
||||||
|
and onehundred_percent_base == BasePoll.PERCENT_BASE_ENTITLED
|
||||||
|
):
|
||||||
|
if (
|
||||||
|
old_100_percent_base
|
||||||
|
and old_100_percent_base != BasePoll.PERCENT_BASE_ENTITLED
|
||||||
|
):
|
||||||
|
return old_100_percent_base
|
||||||
|
return BasePoll.PERCENT_BASE_CAST
|
||||||
|
return None
|
||||||
|
@ -322,6 +322,22 @@ class CreateMotionPoll(TestCase):
|
|||||||
self.assertFalse(MotionPoll.objects.exists())
|
self.assertFalse(MotionPoll.objects.exists())
|
||||||
self.assertFalse(MotionVote.objects.exists())
|
self.assertFalse(MotionVote.objects.exists())
|
||||||
|
|
||||||
|
def test_create_with_invalid_percent_base(self):
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("motionpoll-list"),
|
||||||
|
{
|
||||||
|
"title": "test_title_PgvqRIvuKuVImEpQJAMZ",
|
||||||
|
"pollmethod": MotionPoll.POLLMETHOD_YN,
|
||||||
|
"type": MotionPoll.TYPE_ANALOG,
|
||||||
|
"motion_id": self.motion.id,
|
||||||
|
"onehundred_percent_base": MotionPoll.PERCENT_BASE_ENTITLED,
|
||||||
|
"majority_method": MotionPoll.MAJORITY_SIMPLE,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_201_CREATED)
|
||||||
|
poll = MotionPoll.objects.get()
|
||||||
|
self.assertEqual(poll.onehundred_percent_base, MotionPoll.PERCENT_BASE_CAST)
|
||||||
|
|
||||||
|
|
||||||
class UpdateMotionPoll(TestCase):
|
class UpdateMotionPoll(TestCase):
|
||||||
"""
|
"""
|
||||||
|
Loading…
Reference in New Issue
Block a user