Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client changes from prototype #148

Merged
merged 7 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions client/src/action/dos/estimateSampleSizes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { endpoint } from 'corla/config';

export default () => {
const url = `${endpoint('estimate-sample-sizes')}`;

window.location.replace(url);
};
7 changes: 7 additions & 0 deletions client/src/action/dos/exportAssertionsAsCsv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { endpoint } from 'corla/config';

export default () => {
const url = `${endpoint('get-assertions?format=csv')}`;

window.location.replace(url);
};
7 changes: 7 additions & 0 deletions client/src/action/dos/exportAssertionsAsJson.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { endpoint } from 'corla/config';

export default () => {
const url = `${endpoint('get-assertions')}`;

window.location.replace(url);
};
13 changes: 13 additions & 0 deletions client/src/action/dos/generateAssertions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { endpoint } from 'corla/config';

import createFetchAction from 'corla/action/createFetchAction';

const url = endpoint('generate-assertions');

export default createFetchAction({
failType: 'GENERATE_ASSERTIONS_FAIL',
networkFailType: 'GENERATE_ASSERTIONS_NETWORK_FAIL',
okType: 'GENERATE_ASSERTIONS_OK',
sendType: 'GENERATE_ASSERTIONS_SEND',
url,
});
35 changes: 29 additions & 6 deletions client/src/component/County/Audit/Wizard/BallotAuditStage.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as React from "react";
import * as React from 'react';

import * as _ from 'lodash';

import { Button, Checkbox, EditableText, Intent, Dialog } from '@blueprintjs/core';
import { Button, Checkbox, Dialog, EditableText, Intent } from '@blueprintjs/core';

import BackButton from './BackButton';
import WaitingForNextBallot from './WaitingForNextBallot';
Expand All @@ -18,7 +18,7 @@ interface NotFoundProps {

const BallotNotFoundForm = (props: NotFoundProps) => {

const { notFound, currentBallot, } = props;
const { notFound, currentBallot } = props;

const onClick = () => {
if (confirm('By continuing, this ballot will be recorded as “not found”'
Expand Down Expand Up @@ -190,6 +190,29 @@ const BallotContestMarkForm = (props: MarkFormProps) => {
const acvr = countyState.acvrs![currentBallot.id];
const contestMarks = acvr[contest.id];

const updateChoices = (candidates: ContestChoice[], desc: string): ContestChoice[] => {
if (desc === 'PLURALITY') {
return candidates;
}
// Replace each candidate 'c' in 'cands' with 'c(1)', 'c(2)', etc.
// For now, let's assume the number of votes on the ballot can be as high as
// the number of candidates. (This is not correct, but will be good enough
// for prototyping purposes).
const ranks = candidates.length;

const newChoices: ContestChoice[] = [];
candidates.forEach(c => {
for (let i = 1; i <= ranks; i++) {
const newChoice: ContestChoice = {
description: c.description,
name: c.name + '(' + i + ')',
};
newChoices.push(newChoice);
}
});
return newChoices;
};

const updateComments = (comments: string) => {
updateBallotMarks({ comments });
};
Expand Down Expand Up @@ -221,7 +244,7 @@ const BallotContestMarkForm = (props: MarkFormProps) => {
<ContestInfo contest={contest} />
<ContestChoices
key={contest.id}
choices={choices}
choices={updateChoices(choices, description)}
marks={contestMarks}
noConsensus={!!contestMarks.noConsensus}
updateBallotMarks={updateBallotMarks}
Expand Down Expand Up @@ -376,7 +399,7 @@ class BallotAuditStage extends React.Component<StageProps, BallotAuditStageState
return (
<div className='rla-page'>
<div>
<Dialog
<Dialog
isOpen={this.state.showDialog}
>
<div className='pt-dialog-body'>
Expand All @@ -390,7 +413,7 @@ class BallotAuditStage extends React.Component<StageProps, BallotAuditStageState
<div className='pt-dialog-footer'>
<div className='pt-dialog-footer-actions'>
<Button intent={Intent.PRIMARY}
onClick={() =>this.closeDialog()}
onClick={() => this.closeDialog()}
text='Continue' />
</div>
</div>
Expand Down
61 changes: 61 additions & 0 deletions client/src/component/DOS/DefineAudit/EstimateSampleSizesPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import * as React from 'react';

import { Breadcrumb, Button, Card, Intent } from '@blueprintjs/core';

import estimateSampleSizes from 'corla/action/dos/estimateSampleSizes';
import DOSLayout from 'corla/component/DOSLayout';

const Breadcrumbs = () => (
<ul className='pt-breadcrumbs mb-default'>
<li><Breadcrumb href='/sos' text='SoS' />></li>
<li><Breadcrumb href='/sos/audit' text='Audit Admin' /></li>
<li><Breadcrumb className='pt-breadcrumb-current' text='Estimate Sample Sizes' /></li>
</ul>
);

interface EstimateSampleSizesPageProps {
forward: OnClick;
}


class EstimateSampleSizesPage extends React.Component<EstimateSampleSizesPageProps> {
public constructor(props: EstimateSampleSizesPageProps) {
super(props);
}

public render() {
const {
forward,
} = this.props;


const main =
<div>
<Breadcrumbs/>
<h2>Estimate Sample Sizes for all Contests</h2>
<p>
Clicking on the estimate button will produce a CSV file containing
sample sizes for all contests in the database, which will be
automatically downloaded.
</p>
<div className='control-buttons mt-default'>
<Button onClick={estimateSampleSizes}
className='pt-button pt-intent-primary'>
Estimate
</Button>
</div>

<div className='control-buttons mt-default'>
<Button onClick={forward}
className='pt-button pt-intent-primary'>
Next
</Button>
</div>
</div>;

return <DOSLayout main={ main } />;
}

}

export default EstimateSampleSizesPage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import * as React from 'react';

import { Redirect } from 'react-router-dom';

import { History } from 'history';

import EstimateSampleSizesPage from './EstimateSampleSizesPage';

import withDOSState from 'corla/component/withDOSState';
import withSync from 'corla/component/withSync';

interface ContainerProps {
dosState: DOS.AppState;
history: History;
}

class EstimateSampleSizesPageContainer extends React.Component<ContainerProps> {

public render() {
const {
dosState,
history,
} = this.props;

if (!dosState) {
return <div />;
}

if (!dosState.asm) {
return <div />;
}

if (dosState.asm === 'DOS_AUDIT_ONGOING') {
return <Redirect to='/sos' />;
}

const props = {
dosState,
forward: () => history.push('/sos/audit/select-contests'),
};

return <EstimateSampleSizesPage { ...props } />;
}
}

const mapStateToProps = (dosState: DOS.AppState) => {
if (!dosState) { return {}; }

return {
dosState,
};
};

export default withSync(
withDOSState(EstimateSampleSizesPageContainer),
'DOS_ESTIMATE_SAMPLE_SIZES_SYNC',
mapStateToProps,
);
115 changes: 115 additions & 0 deletions client/src/component/DOS/DefineAudit/GenerateAssertionsPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import * as React from 'react';

import { Breadcrumb, Button, Card, Intent } from '@blueprintjs/core';

import generateAssertions from 'corla/action/dos/generateAssertions';
import DOSLayout from 'corla/component/DOSLayout';
import exportAssertionsAsJson from "corla/action/dos/exportAssertionsAsJson";
import exportAssertionsAsCsv from "corla/action/dos/exportAssertionsAsCsv";

const Breadcrumbs = () => (
<ul className='pt-breadcrumbs mb-default'>
<li><Breadcrumb href='/sos' text='SoS' />></li>
<li><Breadcrumb href='/sos/audit' text='Audit Admin' /></li>
<li><Breadcrumb className='pt-breadcrumb-current' text='Generate Assertions' /></li>
</ul>
);

interface GenerateAssertionsPageProps {
dosState: DOS.AppState;
forward: OnClick;
readyToGenerate: boolean;
}

interface GenerateAssertionsPageState {
canGenerateAssertions: boolean;
}

class GenerateAssertionsPage extends React.Component<GenerateAssertionsPageProps, GenerateAssertionsPageState> {
public constructor(props: GenerateAssertionsPageProps) {
super(props);

this.state = {
canGenerateAssertions: !props.dosState.assertionsGenerated && props.readyToGenerate,
};
}

public render() {
const {
dosState,
forward,
readyToGenerate,
} = this.props;

const generate = async () => {
this.setState({canGenerateAssertions: false});
this.render();

generateAssertions().then()
.catch(reason => {
alert('generateAssertions error in fetchAction ' + reason);
});

this.setState({canGenerateAssertions: true});
};

const main =
<div>
<Breadcrumbs/>
<h2>Generate Assertions for IRV Contests</h2>
<p>
Assertions will be generated for all IRV contests to
support opportunistic discrepancy computation.
</p>
<p>
Only click on 'Generate Assertions' once! This prototype is
is not sophisticated enough to replace a contest's assertions
in the database (re-generating assertions will just replicate
those that already exist).
</p>
<p>
This prototype is also not sophisticated enough to give you
feedback on how assertion generation is progressing ... but
once it is done either a green alert will tell you that it
was successful or a red one will tell you it has failed.
</p>
<p>
Then click on 'Export Assertions' and the generated assertions
will be exported to a JSON file and automatically downloaded.
</p>
<div className='control-buttons mt-default'>
<Button onClick={generate}
disabled={!this.state.canGenerateAssertions}
className='pt-button pt-intent-primary'>
Generate Assertions
</Button>
</div>

<div className='control-buttons mt-default'>
<Button onClick={exportAssertionsAsJson}
className='pt-button pt-intent-primary'>
Export Assertions as JSON
</Button>
</div>

<div className='control-buttons mt-default'>
<Button onClick={exportAssertionsAsCsv}
className='pt-button pt-intent-primary'>
Export Assertions as CSV
</Button>
</div>

<div className='control-buttons mt-default'>
<Button onClick={forward}
className='pt-button pt-intent-primary'>
Next
</Button>
</div>
</div>;

return <DOSLayout main={main}/>;
}

}

export default GenerateAssertionsPage;
Loading
Loading