Merge pull request #6134 from jsangmeister/fix-projected-analog-motion-poll
Fix projection of analog polls & prevent percent base 'entitled' for analog polls
This commit is contained in:
commit
b4b0a958d5
@ -108,7 +108,7 @@
|
||||
"karma-jasmine": "~4.0.1",
|
||||
"karma-jasmine-html-reporter": "^1.4.0",
|
||||
"npm-license-crawler": "^0.2.1",
|
||||
"prettier": "^2.1.1",
|
||||
"prettier": "~2.1.1",
|
||||
"protractor": "^7.0.0",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"ts-node": "~9.0.0",
|
||||
|
@ -43,7 +43,7 @@
|
||||
</table>
|
||||
|
||||
<!-- Chart -->
|
||||
<div *ngIf="!poll.hasSpecialVoteValues" class="doughnut-chart">
|
||||
<div *ngIf="showChart()" class="doughnut-chart">
|
||||
<os-charts type="doughnut" [data]="chartData"></os-charts>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
@ -72,6 +72,10 @@ export class MotionPollDetailContentComponent extends BaseComponent {
|
||||
super(titleService, translate);
|
||||
}
|
||||
|
||||
public showChart(): boolean {
|
||||
return this.pollService.showChart(this.poll);
|
||||
}
|
||||
|
||||
private setTableData(): void {
|
||||
this.tableData = this.pollService.generateTableData(this.poll);
|
||||
}
|
||||
|
@ -109,7 +109,11 @@ export class MotionPollService extends PollService {
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -273,7 +273,9 @@ export abstract class BasePollDetailComponentDirective<V extends ViewBasePoll, S
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
super.ngOnDestroy();
|
||||
this.entitledUsersSubscription.unsubscribe();
|
||||
this.entitledUsersSubscription = null;
|
||||
if (this.entitledUsersSubscription) {
|
||||
this.entitledUsersSubscription.unsubscribe();
|
||||
this.entitledUsersSubscription = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,7 +172,7 @@ export class PollFormComponent<T extends ViewBasePoll, S extends PollService>
|
||||
this.patchForm(this.contentForm);
|
||||
}
|
||||
this.updatePollValues(this.contentForm.value);
|
||||
this.updatePercentBases(this.pollMethodControl.value);
|
||||
this.updatePercentBases(this.pollMethodControl.value, this.pollTypeControl.value);
|
||||
|
||||
this.subscriptions.push(
|
||||
// changes to whole form
|
||||
@ -183,13 +183,12 @@ export class PollFormComponent<T extends ViewBasePoll, S extends PollService>
|
||||
}),
|
||||
// poll method changes
|
||||
this.pollMethodControl.valueChanges.subscribe((method: AssignmentPollMethod) => {
|
||||
if (method) {
|
||||
this.updatePercentBases(method);
|
||||
this.setWarning();
|
||||
}
|
||||
this.updatePercentBases(method, this.pollTypeControl.value);
|
||||
this.setWarning();
|
||||
}),
|
||||
// poll type changes
|
||||
this.pollTypeControl.valueChanges.subscribe(() => {
|
||||
this.pollTypeControl.valueChanges.subscribe((type: PollType) => {
|
||||
this.updatePercentBases(this.pollMethodControl.value, type);
|
||||
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 type the currently chosen type
|
||||
*/
|
||||
private updatePercentBases(method: AssignmentPollMethod): void {
|
||||
if (method) {
|
||||
private updatePercentBases(method: AssignmentPollMethod, type: PollType): void {
|
||||
if (method || type) {
|
||||
let forbiddenBases = [];
|
||||
if (method === AssignmentPollMethod.YN) {
|
||||
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) {
|
||||
forbiddenBases = [PercentBase.YN, PercentBase.YNA];
|
||||
}
|
||||
if (type === PollType.Analog) {
|
||||
forbiddenBases.push(PercentBase.Entitled);
|
||||
}
|
||||
|
||||
const bases = {};
|
||||
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
|
||||
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;
|
||||
}
|
||||
@ -256,7 +259,8 @@ export class PollFormComponent<T extends ViewBasePoll, S extends PollService>
|
||||
|
||||
private getNormedPercentBase(
|
||||
base: AssignmentPollPercentBase,
|
||||
method: AssignmentPollMethod
|
||||
method: AssignmentPollMethod,
|
||||
type: PollType
|
||||
): AssignmentPollPercentBase {
|
||||
if (
|
||||
method === AssignmentPollMethod.YN &&
|
||||
@ -270,6 +274,8 @@ export class PollFormComponent<T extends ViewBasePoll, S extends PollService>
|
||||
(base === AssignmentPollPercentBase.YN || base === AssignmentPollPercentBase.YNA)
|
||||
) {
|
||||
return AssignmentPollPercentBase.Y;
|
||||
} else if (type === PollType.Analog && base === AssignmentPollPercentBase.Entitled) {
|
||||
return AssignmentPollPercentBase.Cast;
|
||||
}
|
||||
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 percentBaseVerbose(): string;
|
||||
|
@ -37,10 +37,6 @@ export class MotionPollSlideComponent extends BasePollSlideComponentDirective<Mo
|
||||
return MotionPoll.DECIMAL_FIELDS;
|
||||
}
|
||||
|
||||
public showChart(): boolean {
|
||||
return this.pollService.showChart(this.pollData);
|
||||
}
|
||||
|
||||
public getTableData(): PollTableData[] {
|
||||
return this.pollService.generateTableData(this.pollData);
|
||||
}
|
||||
|
@ -110,8 +110,8 @@ class AssignmentPollSerializer(BasePollSerializer):
|
||||
validated_data.pop("assignment", None)
|
||||
return super().update(instance, validated_data)
|
||||
|
||||
def norm_100_percent_base_to_pollmethod(
|
||||
self, onehundred_percent_base, pollmethod, old_100_percent_base=None
|
||||
def norm_100_percent_base(
|
||||
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.
|
||||
@ -140,7 +140,9 @@ class AssignmentPollSerializer(BasePollSerializer):
|
||||
AssignmentPoll.PERCENT_BASE_YNA,
|
||||
):
|
||||
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):
|
||||
|
@ -260,15 +260,17 @@ class MotionPollSerializer(BasePollSerializer):
|
||||
validated_data.pop("motion", None)
|
||||
return super().update(instance, validated_data)
|
||||
|
||||
def norm_100_percent_base_to_pollmethod(
|
||||
self, onehundred_percent_base, pollmethod, old_100_percent_base=None
|
||||
def norm_100_percent_base(
|
||||
self, onehundred_percent_base, pollmethod, polltype, old_100_percent_base=None
|
||||
):
|
||||
if (
|
||||
pollmethod == MotionPoll.POLLMETHOD_YN
|
||||
and onehundred_percent_base == MotionPoll.PERCENT_BASE_YNA
|
||||
):
|
||||
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):
|
||||
|
@ -77,12 +77,14 @@ class BasePollSerializer(ModelSerializer):
|
||||
|
||||
def create(self, validated_data):
|
||||
"""
|
||||
Match the 100 percent base to the pollmethod. Change the base, if it does not
|
||||
fit to the pollmethod.
|
||||
Match the 100 percent base to the pollmethod and type. Change the base, if it does not
|
||||
fit to the pollmethod or type.
|
||||
Set is_pseudoanonymized if type is pseudoanonymous.
|
||||
"""
|
||||
new_100_percent_base = self.norm_100_percent_base_to_pollmethod(
|
||||
validated_data["onehundred_percent_base"], validated_data["pollmethod"]
|
||||
new_100_percent_base = self.norm_100_percent_base(
|
||||
validated_data["onehundred_percent_base"],
|
||||
validated_data["pollmethod"],
|
||||
validated_data["type"],
|
||||
)
|
||||
if new_100_percent_base is not None:
|
||||
validated_data["onehundred_percent_base"] = new_100_percent_base
|
||||
@ -92,7 +94,7 @@ class BasePollSerializer(ModelSerializer):
|
||||
|
||||
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
|
||||
also handled by the client, but here we make it sure aswell!
|
||||
|
||||
@ -109,8 +111,11 @@ class BasePollSerializer(ModelSerializer):
|
||||
validated_data["is_pseudoanonymized"] = False
|
||||
instance = super().update(instance, validated_data)
|
||||
|
||||
new_100_percent_base = self.norm_100_percent_base_to_pollmethod(
|
||||
instance.onehundred_percent_base, instance.pollmethod, old_100_percent_base
|
||||
new_100_percent_base = self.norm_100_percent_base(
|
||||
instance.onehundred_percent_base,
|
||||
instance.pollmethod,
|
||||
instance.type,
|
||||
old_100_percent_base,
|
||||
)
|
||||
if new_100_percent_base is not None:
|
||||
instance.onehundred_percent_base = new_100_percent_base
|
||||
@ -136,7 +141,17 @@ class BasePollSerializer(ModelSerializer):
|
||||
)
|
||||
return data
|
||||
|
||||
def norm_100_percent_base_to_pollmethod(
|
||||
self, onehundred_percent_base, pollmethod, old_100_percent_base=None
|
||||
def norm_100_percent_base(
|
||||
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(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):
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user