diff --git a/client/src/component/DOS/DefineAudit/GenerateAssertionsPage.tsx b/client/src/component/DOS/DefineAudit/GenerateAssertionsPage.tsx index ad38b549..04c0af74 100644 --- a/client/src/component/DOS/DefineAudit/GenerateAssertionsPage.tsx +++ b/client/src/component/DOS/DefineAudit/GenerateAssertionsPage.tsx @@ -1,8 +1,8 @@ +import {Breadcrumb, Button, Card, Intent, Spinner} from '@blueprintjs/core'; +import dashboardRefresh from 'corla/action/dos/dashboardRefresh'; import * as _ from 'lodash'; import * as React from 'react'; -import {Breadcrumb, Button, Card, Intent, Spinner} from '@blueprintjs/core'; - import exportAssertionsAsCsv from 'corla/action/dos/exportAssertionsAsCsv'; import exportAssertionsAsJson from 'corla/action/dos/exportAssertionsAsJson'; import generateAssertions from 'corla/action/dos/generateAssertions'; @@ -25,9 +25,9 @@ interface GenerateAssertionsPageProps { } interface GenerateAssertionsPageState { - canGenerateAssertions: boolean; generatingAssertions: boolean; generationTimeOutSeconds: number; + generationWasRun: boolean; } class GenerateAssertionsPage extends React.Component { @@ -35,9 +35,9 @@ class GenerateAssertionsPage extends React.Component { this.setState({generatingAssertions: true}); - this.setState({canGenerateAssertions: false}); this.render(); const timeoutQueryParams = new URLSearchParams(); timeoutQueryParams.set(generationTimeoutParam, this.state.generationTimeOutSeconds.toString()); generateAssertions(timeoutQueryParams).then() - .catch(reason => { - alert('generateAssertions error in fetchAction ' + reason); - }); - - this.setState({generatingAssertions: false}); - this.setState({canGenerateAssertions: true}); + .catch(reason => { + alert('generateAssertions error in fetchAction ' + reason); + }).finally(() => { + this.setState({generatingAssertions: false}); + this.setState({generationWasRun: true}); + // Refresh dashboard to collect full assertion info + dashboardRefresh(); + }); }; const main = @@ -96,6 +97,7 @@ class GenerateAssertionsPage extends React.Component @@ -118,16 +120,29 @@ class GenerateAssertionsPage extends React.Component 0; + const atLeastOneIrvContest = Object.values(this.props.dosState.contests) + .some(contest => contest.description === 'IRV'); + + // Activate button if any attempt to generate is made + if (this.state.generationWasRun) { + return false; + } + + return (!assertionsAlreadyGenerated && atLeastOneIrvContest); + } + private getAssertionGenerationStatusTable() { interface CombinedData { - contestName: string, - succeeded: boolean | undefined, - retry: boolean | undefined, - winner: string, - error: string, - warning: string, - message: string + contestName: string; + succeeded: boolean | undefined; + retry: boolean | undefined; + winner: string; + error: string; + warning: string; + message: string; } interface CombinationSummary { @@ -139,15 +154,15 @@ class GenerateAssertionsPage extends React.Component {combinedData.contestName} - {combinedData.succeeded == undefined ? '' : successString} + {combinedData.succeeded === undefined ? '' : successString} - {combinedData.retry == undefined ? '' : retryString} + {combinedData.retry === undefined ? '' : retryString} {combinedData.winner} {combinedData.error} {combinedData.warning} @@ -160,55 +175,57 @@ class GenerateAssertionsPage extends React.Component { return { contestName: s.contestName, - succeeded: undefined, - retry: undefined, - winner: s.winner, error: s.error, + message: s.message, + retry: undefined, + succeeded: undefined, warning: s.warning, - message: s.message + winner: s.winner, }; - } + }; // Make a CombinedData structure out of an AssertionsStatus by filling in blank summary data. const fillBlankSummary = (s: DOS.AssertionStatus): CombinedData => { return { contestName: s.contestName, - succeeded: s.succeeded, - retry: s.retry, - winner: '', error: '', + message: '', + retry: s.retry, + succeeded: s.succeeded, warning: '', - message: '' + winner: '', }; - } + }; // Make a CombinedData structure out of an AssertionsStatus and a GenerateAssertionsSummary. const combineSummaryAndStatus = (s: DOS.AssertionStatus, t: DOS.GenerateAssertionsSummary): CombinedData => { return { contestName: s.contestName, - succeeded: s.succeeded, - retry: s.retry, - winner: t.winner, error: t.error, + message: t.message, + retry: s.retry, + succeeded: s.succeeded, warning: t.warning, - message: t.message + winner: t.winner, }; - } + }; // Join up the rows by contest name if matching. If there is no matching contest name in the other // list, add a row with blanks for the missing data. // Various kinds of absences are possible, because there may be empty summaries at the start; // conversely, in later phases we may rerun generation (and hence get status) for only a few contests. - const joinRows = (statuses: DOS.AssertionGenerationStatuses | undefined, summaries: DOS.GenerateAssertionsSummary[]) => { + const joinRows = (statuses: DOS.AssertionGenerationStatuses + | undefined, summaries: DOS.GenerateAssertionsSummary[]) => { summaries.sort((a, b) => a.contestName < b.contestName ? -1 : 1); - let rows: CombinedData[] = []; - let i = 0, j = 0; + const rows: CombinedData[] = []; + let i = 0; + let j = 0; if (statuses === undefined) { // No status yet. Just print summary data. return summaries.map(s => { return fillBlankStatus(s); - }) + }); } else { // Iterate along the two sorted lists at once, combining them if the contest name matches, and @@ -238,11 +255,12 @@ class GenerateAssertionsPage extends React.Component ( - - )) + const combinedRows = _.map(joinRows(this.props.dosState.assertionGenerationStatuses, + this.props.dosState.generateAssertionsSummaries), d => ( + + )); if (this.state.generatingAssertions) { return ( @@ -251,7 +269,8 @@ class GenerateAssertionsPage extends React.ComponentGenerating Assertions... ); - } else if (this.props.dosState.assertionGenerationStatuses || this.props.dosState.generateAssertionsSummaries.length > 0) { + } else if (this.props.dosState.assertionGenerationStatuses + || this.props.dosState.generateAssertionsSummaries.length > 0) { return (
diff --git a/client/src/component/DOS/DefineAudit/StandardizeChoicesPageContainer.tsx b/client/src/component/DOS/DefineAudit/StandardizeChoicesPageContainer.tsx index 58d1434e..d69f7f16 100644 --- a/client/src/component/DOS/DefineAudit/StandardizeChoicesPageContainer.tsx +++ b/client/src/component/DOS/DefineAudit/StandardizeChoicesPageContainer.tsx @@ -16,12 +16,6 @@ import withPoll from 'corla/component/withPoll'; import counties from 'corla/data/counties'; -// The next URL path to transition to. -const NEXT_PATH = '/sos/audit/generate-assertions'; - -// The previous URL path to transition to. -// const PREV_PATH = '/sos/audit'; - /** * Denormalize the DOS.Contests data structure from the application state into * something that can be easily displayed in a tabular format. @@ -67,7 +61,6 @@ interface Props { history: History; } - const PageContainer = (props: Props) => { const { areChoicesLoaded, @@ -77,24 +70,23 @@ const PageContainer = (props: Props) => { history, } = props; - let isRequestInProgress = false; + let isRequestInProgress = false; const nextPage = (data: DOS.Form.StandardizeChoices.FormData) => { if (isRequestInProgress) { return; } isRequestInProgress = true; -console.log('!!!!!!!!!!!!! calling submit set-contest-name'); + standardizeChoices(contests, data).then(r => { isRequestInProgress = false; // use the result here if (r.ok) { - history.push(NEXT_PATH); - } + history.push(getNextPath()); + } }) .catch(reason => { alert('standardizeChoices error in submitAction ' + reason); - }) - + }); }; const previousPage = async () => { @@ -102,6 +94,15 @@ console.log('!!!!!!!!!!!!! calling submit set-contest-name'); history.push('/sos/audit'); }; + function getNextPath() { + // Only route to Assertion Generation page if IRV contests exist + if (Object.values(contests).some(contest => contest.description === 'IRV')) { + return '/sos/audit/generate-assertions'; + } else { + return '/sos/audit/estimate-sample-sizes'; + } + } + if (asm === 'DOS_AUDIT_ONGOING') { return ; } @@ -112,7 +113,7 @@ console.log('!!!!!!!!!!!!! calling submit set-contest-name'); rows = filterRows(flattenContests(contests, canonicalChoices)); if (_.isEmpty(rows)) { - return ; + return ; } }