Skip to content

Commit

Permalink
delete tally.options and use tally.results (#274)
Browse files Browse the repository at this point in the history
* delete tally.options and use tally.results

* clean up

* fix tests

* fix some type error complaints

* remove legacy endpoint

Co-authored-by: Rafael Ventura <rafinskipg@gmail.com>
  • Loading branch information
b-pmcg and rafinskipg authored Dec 15, 2021
1 parent c0c2fbe commit fba92b9
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 99 deletions.
2 changes: 1 addition & 1 deletion modules/app/components/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ type Props = {
tabPanels: React.ReactNode[];
tabListStyles?: ThemeUIStyleObject;
hashRoute?: boolean;
banner?: JSX.Element;
banner?: JSX.Element | null;
};

const TabbedLayout = ({
Expand Down
22 changes: 8 additions & 14 deletions modules/polling/components/PollVotePluralityResultsCompact.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import BigNumber from 'bignumber.js';
import { Box } from 'theme-ui';
import { PollTally, PollTallyPluralityOption } from '../types';
import { PollTally, PluralityResult } from '../types';
import { YesNoAbstainBar } from './YesNoAbstainBar';

export function PollVotePluralityResultsCompact({
Expand All @@ -10,24 +10,18 @@ export function PollVotePluralityResultsCompact({
tally: PollTally;
showTitles?: boolean;
}): React.ReactElement {
const max = new BigNumber(tally.totalMkrParticipation);
const voteTallyResults = tally.results as PluralityResult[];

const voteTallyOptions = tally.options as PollTallyPluralityOption;

const abstainValue = new BigNumber(voteTallyOptions['0'] ? voteTallyOptions['0'].mkrSupport : 0);
const yesValue = new BigNumber(voteTallyOptions['1'] ? voteTallyOptions['1'].mkrSupport : 0);
const noValue = new BigNumber(voteTallyOptions['2'] ? voteTallyOptions['2'].mkrSupport : 0);

const yesPercent = max.isGreaterThan(0) ? yesValue.dividedBy(max).multipliedBy(100).toFixed(0) : 0;
const abstainPercent = max.isGreaterThan(0) ? abstainValue.dividedBy(max).multipliedBy(100).toFixed(0) : 0;
const noPercent = max.isGreaterThan(0) ? noValue.dividedBy(max).multipliedBy(100).toFixed(0) : 0;
const yesPercent = voteTallyResults.find(({ optionId }) => optionId === '1')?.firstPct || 0;
const abstainPercent = voteTallyResults.find(({ optionId }) => optionId === '0')?.firstPct || 0;
const noPercent = voteTallyResults.find(({ optionId }) => optionId === '2')?.firstPct || 0;

return (
<Box>
<YesNoAbstainBar
yesPercent={yesPercent}
noPercent={noPercent}
abstainPercent={abstainPercent}
yesPercent={new BigNumber(yesPercent).toFixed(0)}
noPercent={new BigNumber(noPercent).toFixed(0)}
abstainPercent={new BigNumber(abstainPercent).toFixed(0)}
showTitles={showTitles}
/>
</Box>
Expand Down
18 changes: 13 additions & 5 deletions modules/polling/components/PollWinningOptionBox.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { Poll, PollTally } from '../types';
import { PluralityResult, Poll, PollTally, RankedChoiceResult } from '../types';
import { Text, Flex } from 'theme-ui';
import BigNumber from 'bignumber.js';
import { isActivePoll } from '../helpers/utils';
Expand All @@ -18,17 +18,25 @@ export default function PollWinningOptionBox({

return (
<Flex sx={{ py: 2, justifyContent: 'center', fontSize: [1, 2], color: 'onSecondary' }}>
{tally && Object.keys(tally.options).length > 0 && tally.winningOptionName ? (
{tally && tally.results.length > 0 && tally.winningOptionName ? (
<Text as="p" sx={{ textAlign: 'center', px: [3, 4], mb: 1, wordBreak: 'break-word' }}>
{textWin}:{' '}
<span sx={{ color: getVoteColor(parseInt(tally.winner), poll.voteType) }}>
<span sx={{ color: getVoteColor(parseInt(tally?.winner || '0'), poll.voteType) }}>
{tally?.winningOptionName}
</span>{' '}
{tally.pollVoteType === POLL_VOTE_TYPE.PLURALITY_VOTE &&
'with ' + new BigNumber(tally.options[tally.winner].mkrSupport).toFormat(2) + ' MKR supporting.'}
'with ' +
new BigNumber(
(tally.results as PluralityResult[]).find(({ optionId }) => optionId === tally.winner)
?.mkrSupport || '0'
).toFormat(2) +
' MKR supporting.'}
{tally.pollVoteType === (POLL_VOTE_TYPE.RANKED_VOTE || POLL_VOTE_TYPE.UNKNOWN) &&
'with ' +
new BigNumber(tally.options[tally.winner].firstChoice).toFormat(2) +
new BigNumber(
(tally.results as RankedChoiceResult[]).find(({ optionId }) => optionId === tally.winner)
?.firstChoice || '0'
).toFormat(2) +
' MKR supporting as first choice.'}
</Text>
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,20 @@ describe('Poll vote history item', () => {
rounds: 1,
winner: 1,
totalMkrParticipation: 70288.579356787892861292,
options: {
0: {
results: [
{
firstChoice: 2.001309,
transfer: 0,
winner: false,
eliminated: false
},
1: {
{
firstChoice: 70286.578047787892861292,
transfer: 0,
winner: true,
eliminated: false
}
},
],
numVoters: 13
});
});
Expand Down
53 changes: 44 additions & 9 deletions modules/polling/helpers/__tests__/getPollTally.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { getPollTally } from '../getPollTaly';
import { fetchPollTally } from '../../api/fetchPollTally';
import { fetchVotesByAddresForPoll } from '../../api/fetchVotesByAddress';
import { Poll } from '../../types';
import { PluralityResult, Poll } from '../../types';
import BigNumber from 'bignumber.js';
import * as PRT from '../parseRawTally';

jest.mock('../../api/fetchPollTally');
jest.mock('../../api/fetchVotesByAddress');
Expand All @@ -13,7 +14,7 @@ const mockPoll: Poll = {
discussionLink: '',
endDate: new Date(2011, 10, 30),
multiHash: '',
options: {},
options: { '0': 'Abstain', '1': 'Yes', '2': 'No' },
pollId: 1,
startDate: new Date(2011, 10, 30),
slug: '',
Expand All @@ -23,9 +24,31 @@ const mockPoll: Poll = {
ctx: {} as any
};

const expectedPRTArg = {
numVoters: 0,
pollVoteType: 'Plurality Voting',
totalMkrParticipation: new BigNumber(0),
votesByAddress: [],
winner: '0',
options: {
'0': {
mkrSupport: new BigNumber(0),
winner: false
},
'1': {
mkrSupport: new BigNumber(0),
winner: false
},
'2': {
mkrSupport: new BigNumber(0),
winner: false
}
}
};

describe('getPollTally', () => {
// We add this test to check that we always return a non-empty options object to the FE so the components that rely on populated data won't crash
it('Returns a correct tally even when the options are empty', async () => {
it('Returns a correct tally for plurality votes even when the options are empty', async () => {
(fetchPollTally as jest.Mock).mockReturnValue(
Promise.resolve({
totalMkrParticipation: new BigNumber(0),
Expand All @@ -35,14 +58,26 @@ describe('getPollTally', () => {
})
);
(fetchVotesByAddresForPoll as jest.Mock).mockReturnValue(Promise.resolve([]));
jest.spyOn(PRT, 'parseRawPollTally');

const poll = mockPoll;
const tally = await getPollTally(poll);
expect(tally.options['0']).not.toBeUndefined();
expect(tally.options['0'].mkrSupport.toString()).toEqual('0');
expect(tally.options['1']).not.toBeUndefined();
expect(tally.options['1'].mkrSupport.toString()).toEqual('0');
expect(tally.options['2']).not.toBeUndefined();
expect(tally.options['2'].mkrSupport.toString()).toEqual('0');
const results = tally.results as PluralityResult[];

// When no options returned with tally, we manually add them in 'getPollTally'
expect(PRT.parseRawPollTally).toHaveBeenCalledWith(expectedPRTArg, poll);

// 'parseRawTally' returns the tally without the 'options' prop
expect('options' in tally).toBe(false);

// The tally results are created by looping over the poll options
expect(results.length).toBe(Object.keys(poll.options).length);

expect(results[0]).not.toBeUndefined();
expect(results[0].mkrSupport.toString()).toEqual('0');
expect(results[1]).not.toBeUndefined();
expect(results[1].mkrSupport.toString()).toEqual('0');
expect(results[2]).not.toBeUndefined();
expect(results[2].mkrSupport.toString()).toEqual('0');
});
});
11 changes: 8 additions & 3 deletions modules/polling/helpers/parseRawTally.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { MKR } from 'lib/maker';
import BigNumber from 'bignumber.js';
import invariant from 'tiny-invariant';
import { Poll, PollTally, RawPollTally, PluralityResult, RankedChoiceResult } from '../types';
Expand Down Expand Up @@ -57,10 +56,16 @@ export function parseRawPollTally(rawTally: RawPollTally, poll: Poll): PollTally
return valueA.gt(valueB) ? -1 : 1;
});

// Now we have created the results object, we intentionally omit 'options' to avoid confusion
const {
options: _, // eslint-disable-line @typescript-eslint/no-unused-vars
...remainder
} = rawTally;

return {
...rawTally,
...remainder,
results: poll.voteType === POLL_VOTE_TYPE.PLURALITY_VOTE ? pluralityResult : rankedChoiceResult,
totalMkrParticipation,
totalMkrParticipation: totalMkrParticipation.toNumber(),
winningOptionName
} as PollTally;
}
26 changes: 21 additions & 5 deletions modules/polling/types/pollTally.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import CurrencyObject from './currency';
import BigNumber from 'bignumber.js';
import { PollVoteType } from './pollVoteType';

Expand All @@ -16,7 +15,7 @@ export type RankedChoiceResult = {
export type PluralityResult = {
optionId: string;
optionName: string;
winner;
winner: boolean;
mkrSupport: BigNumber;
winner: boolean;
firstPct: BigNumber; // TODO rename to "percent"?
Expand Down Expand Up @@ -45,11 +44,9 @@ export type PollTallyVote = {
export type RawPollTallyRankedChoice = {
pollVoteType: PollVoteType;
winner: string | null;
rounds?: number;
rounds: number;
numVoters: number;
options: Record<number, PollTallyRankedChoiceOption>;
results: RankedChoiceResult[];
winningOptionName: string;
totalMkrParticipation: number;
votesByAddress?: PollTallyVote[];
};
Expand All @@ -59,6 +56,25 @@ export type RawPollTallyPlurality = {
winner: string | null;
numVoters: number;
options: Record<number, PollTallyPluralityOption>;
totalMkrParticipation: number;
votesByAddress?: PollTallyVote[];
};

export type PollTallyRankedChoice = {
pollVoteType: PollVoteType;
winner: string | null;
numVoters: number;
results: RankedChoiceResult[];
winningOptionName: string;
totalMkrParticipation: number;
votesByAddress?: PollTallyVote[];
rounds?: number;
};

export type PollTallyPlurality = {
pollVoteType: PollVoteType;
winner: string | null;
numVoters: number;
results: PluralityResult[];
winningOptionName: string;
totalMkrParticipation: number;
Expand Down
54 changes: 0 additions & 54 deletions pages/api/polling/tally/legacy/[poll-id].ts

This file was deleted.

7 changes: 3 additions & 4 deletions pages/polling/[poll-hash].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { formatDateWithTime } from 'lib/datetime';

// api
import { getPolls, getPoll } from 'modules/polling/api/fetchPolls';
import { Poll, PollTally } from 'modules/polling/types';
import { Poll } from 'modules/polling/types';

// stores
import useAccountsStore from 'modules/app/stores/accounts';
Expand Down Expand Up @@ -282,14 +282,13 @@ const PollView = ({ poll }: { poll: Poll }) => {
)
]}
banner={
tally &&
tally.totalMkrParticipation > 0 && (
tally && tally.totalMkrParticipation > 0 ? (
<Box>
<Divider my={0} />
<PollWinningOptionBox tally={tally} poll={poll} />
<Divider my={0} />
</Box>
)
) : null
}
/>
</Card>
Expand Down

0 comments on commit fba92b9

Please sign in to comment.