-
Notifications
You must be signed in to change notification settings - Fork 211
PM- 3026 Update Manage Submissions to accommodate AI workflows #7150
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
Changes from 4 commits
8f7393c
8d0fae4
a293808
c2ae9ce
c7d1569
70544e5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -177,6 +177,9 @@ module.exports = { | |
| * object should be considered outdated, and updated as soon as possible. */ | ||
| USER_GROUP_MAXAGE: 24 * 60 * 60 * 1000, | ||
|
|
||
| REVIEW_APP_URL: 'https://review.topcoder-dev.com', | ||
|
|
||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [💡 |
||
| /* Maximum time to wait before timeout on searching past challenges (seconds) | ||
| * when no result at all. | ||
| * Default: 30 seconds. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,35 @@ | ||
| import _ from 'lodash'; | ||
| import { redux } from 'topcoder-react-utils'; | ||
| import { redux, config } from 'topcoder-react-utils'; | ||
| import { services } from 'topcoder-react-lib'; | ||
|
|
||
| const Api = services.api.default; | ||
|
|
||
| function loadAiWorkflowRunsInit() {} | ||
|
|
||
| function loadAiWorkflowRunsDone(tokenV3, submissionId, aiWorkflowId) { | ||
| const api = new Api(config.API.V6, tokenV3); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [❗❗ |
||
| const url = `/workflows/${aiWorkflowId}/runs?submissionId=${submissionId}`; | ||
|
|
||
| return api.get(url) | ||
| .then(res => res.json()) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [ |
||
| .then(data => ({ | ||
| submissionId, | ||
| aiWorkflowId, | ||
| runs: data, | ||
| })) | ||
| .catch((err) => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [💡 |
||
| throw err; | ||
| }); | ||
| } | ||
|
|
||
| export default redux.createActions({ | ||
| PAGE: { | ||
| SUBMISSION_MANAGEMENT: { | ||
| SHOW_DETAILS: _.identity, | ||
| CANCEL_DELETE: _.noop, | ||
| CONFIRM_DELETE: _.identity, | ||
| LOAD_AI_WORKFLOW_RUNS_INIT: loadAiWorkflowRunsInit, | ||
| LOAD_AI_WORKFLOW_RUNS_DONE: loadAiWorkflowRunsDone, | ||
| }, | ||
| }, | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,7 @@ import _ from 'lodash'; | |
| import moment from 'moment'; | ||
| import React from 'react'; | ||
| import { CHALLENGE_STATUS, COMPETITION_TRACKS, safeForDownload } from 'utils/tc'; | ||
| import { config } from 'topcoder-react-utils'; | ||
|
|
||
| import PT from 'prop-types'; | ||
|
|
||
|
|
@@ -25,10 +26,10 @@ import ArtifactsDownloadIcon from '../Icons/IconDownloadArtifacts.svg'; | |
| import ReviewRatingListIcon from '../Icons/IconReviewRatingList.svg'; | ||
| import ExpandIcon from '../Icons/IconMinimalDown.svg'; | ||
| import ScreeningStatus from '../ScreeningStatus'; | ||
| import IconShare from '../Icons/IconShare.svg'; | ||
|
|
||
| import './styles.scss'; | ||
|
|
||
|
|
||
| export default function Submission(props) { | ||
| const { | ||
| challenge, | ||
|
|
@@ -48,6 +49,11 @@ export default function Submission(props) { | |
| const safeForDownloadCheck = safeForDownload(submissionObject.url); | ||
| const onDownloadArtifacts = onOpenDownloadArtifactsModal.bind(1, submissionObject.id); | ||
| const onOpenRatingsList = onOpenRatingsListModal.bind(1, submissionObject.id); | ||
| const onOpenReviewApp = () => { | ||
| if (!challenge || !challenge.id) return; | ||
| const url = `${config.REVIEW_APP_URL}/active-challenges/${challenge.id}/challenge-details?tab=submission`; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [ |
||
| window.open(url, '_blank', 'noopener,noreferrer'); | ||
| }; | ||
|
|
||
| // Determine if a challenge is for Topcrowd so we can edit the UI accordingly | ||
| let isTopCrowdChallenge = false; | ||
|
|
@@ -117,7 +123,20 @@ export default function Submission(props) { | |
| : <span /> } | ||
| { !isTopCrowdChallenge | ||
| ? ( | ||
| <Tooltip content={() => <div styleName="tooltip-content">Show Scores</div>}> | ||
| <Tooltip content={() => <div styleName="tooltip-content">View Review Info</div>}> | ||
| <button | ||
| onClick={() => onOpenReviewApp()} | ||
|
||
| type="button" | ||
| styleName="download-artifacts-button" | ||
| > | ||
| {safeForDownloadCheck === true && <IconShare />} | ||
| </button> | ||
| </Tooltip> | ||
| ) | ||
| : <span />} | ||
| { !isTopCrowdChallenge | ||
| ? ( | ||
| <Tooltip content={() => <div styleName="tooltip-content">Show scores</div>}> | ||
| <button | ||
| onClick={() => onOpenRatingsList()} | ||
| type="button" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,6 +33,7 @@ export default function SubmissionManagement(props) { | |
| submissions, | ||
| loadingSubmissions, | ||
| showDetails, | ||
| submissionWorkflowRuns, | ||
| onDelete, | ||
| helpPageUrl, | ||
| onDownload, | ||
|
|
@@ -182,6 +183,7 @@ export default function SubmissionManagement(props) { | |
| <SubmissionsTable | ||
| challenge={challenge} | ||
| submissionObjects={submissions} | ||
| submissionWorkflowRuns={submissionWorkflowRuns} | ||
| showDetails={showDetails} | ||
| track={trackName} | ||
| status={challenge.status} | ||
|
|
@@ -227,6 +229,7 @@ SubmissionManagement.defaultProps = { | |
| SubmissionManagement.propTypes = { | ||
| challenge: PT.shape().isRequired, | ||
| showDetails: PT.shape().isRequired, | ||
| submissionWorkflowRuns: PT.shape().isRequired, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [ |
||
| onDelete: PT.func, | ||
| onlineReviewUrl: PT.string, | ||
| helpPageUrl: PT.string, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,6 +24,7 @@ import ScreeningDetails from '../ScreeningDetails'; | |
| import DownloadArtifactsModal from '../DownloadArtifactsModal'; | ||
| import Submission from '../Submission'; | ||
| import RatingsListModal from '../RatingsListModal'; | ||
| import TableWorkflowRuns from '../TableWorkflowRuns'; | ||
|
|
||
| import './styles.scss'; | ||
|
|
||
|
|
@@ -35,6 +36,7 @@ export default function SubmissionsTable(props) { | |
| const { | ||
| challenge, | ||
| submissionObjects, | ||
| submissionWorkflowRuns, | ||
| showDetails, | ||
| track, | ||
| onDelete, | ||
|
|
@@ -92,12 +94,22 @@ export default function SubmissionsTable(props) { | |
| /> | ||
| ); | ||
| submissionsWithDetails.push(submission); | ||
| const workflowRunsForSubmission = submissionWorkflowRuns | ||
| && submissionWorkflowRuns[subObject.id] | ||
| ? submissionWorkflowRuns[subObject.id] | ||
| : null; | ||
|
|
||
| const submissionDetail = ( | ||
| <tr key={subObject.id} styleName="submission-row"> | ||
| {showDetails[subObject.id] | ||
| && ( | ||
| <td colSpan="6" styleName="dev-details"> | ||
| <div styleName="workflow-table"> | ||
| <TableWorkflowRuns | ||
| workflowRuns={workflowRunsForSubmission} | ||
| /> | ||
| </div> | ||
|
|
||
| <ScreeningDetails | ||
| screeningObject={subObject.screening} | ||
| helpPageUrl={helpPageUrl} | ||
|
|
@@ -186,10 +198,12 @@ SubmissionsTable.defaultProps = { | |
| onlineReviewUrl: '', | ||
| helpPageUrl: '', | ||
| getSubmissionScores: _.noop, | ||
| submissionWorkflowRuns: [], | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [❗❗ |
||
| }; | ||
|
|
||
| SubmissionsTable.propTypes = { | ||
| challenge: PT.shape().isRequired, | ||
| submissionWorkflowRuns: PT.shape(), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [ |
||
| submissionObjects: PT.arrayOf(SubShape), | ||
| showDetails: PT.shape().isRequired, | ||
| track: PT.string.isRequired, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -91,6 +91,14 @@ $submission-space-50: $base-unit * 10; | |
| border-top: 0; | ||
| padding-top: 0; | ||
|
|
||
| .workflow-table { | ||
| padding-left: 80px; | ||
|
||
|
|
||
| @media (max-width: 768px) { | ||
| display: none; | ||
| } | ||
| } | ||
|
|
||
| .upload-artifact-btn { | ||
| @include roboto-medium; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| import React from 'react'; | ||
| import PT from 'prop-types'; | ||
| import moment from 'moment-timezone'; | ||
| import { config } from 'topcoder-react-utils'; | ||
|
|
||
| import './styles.scss'; | ||
|
|
||
| const TABLE_DATE_FORMAT = 'MMM DD YYYY, HH:mm A'; | ||
|
|
||
| const getRunStatusText = (run) => { | ||
| if (!run) return ''; | ||
|
|
||
| if (run.status === 'IN_PROGRESS' || run.status === 'QUEUED') return 'Pending'; | ||
| if (run.status === 'FAILED') return 'Failed'; | ||
| if (run.status === 'SUCCESS') { | ||
| const passingScore = run.workflow && run.workflow.scorecard | ||
| ? run.workflow.scorecard.minimumPassingScore | ||
| : 0; | ||
| return run.score >= passingScore ? 'Passed' : 'Failed Score'; | ||
| } | ||
|
|
||
| return run.status; | ||
| }; | ||
|
|
||
| export default function TableWorkflowRuns(props) { | ||
| const { workflowRuns } = props; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [❗❗ |
||
| if (!workflowRuns || Object.keys(workflowRuns).length === 0) { | ||
| return null; | ||
| } | ||
| return ( | ||
| <div style={{ marginBottom: '15px' }}> | ||
| <table styleName="workflow-table"> | ||
| <thead> | ||
| <tr> | ||
| <th>AI Reviewer</th> | ||
| <th>Review Date</th> | ||
| <th>Score</th> | ||
| <th>Result</th> | ||
| </tr> | ||
| </thead> | ||
| <tbody> | ||
| {Object.entries(workflowRuns).map(([workflowId, run]) => ( | ||
| <tr key={workflowId}> | ||
| <td>{run.workflow.name}</td> | ||
| <td>{run.status === 'SUCCESS' && ( | ||
| moment(run.completedAt) | ||
| .local() | ||
| .format(TABLE_DATE_FORMAT) | ||
| )} | ||
| </td> | ||
| <td> | ||
| {(() => { | ||
| if (run.status !== 'SUCCESS') return '-'; | ||
| if (run.workflow.id) { | ||
| return ( | ||
| <a | ||
| href={`${config.REVIEW_APP_URL}/scorecard/${run.workflow.scorecard.id}`} | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| > | ||
| {run.score} | ||
| </a> | ||
| ); | ||
| } | ||
| return run.score; | ||
| })()} | ||
| </td> | ||
| <td>{getRunStatusText(run)}</td> | ||
| </tr> | ||
| ))} | ||
| </tbody> | ||
| </table> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| TableWorkflowRuns.defaultProps = { | ||
| workflowRuns: [], | ||
| }; | ||
|
|
||
| TableWorkflowRuns.propTypes = { | ||
| workflowRuns: PT.shape(), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [ |
||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[❗❗
correctness]Passing
nulltoworkflowRunsmight lead to runtime errors ifTableWorkflowRunsdoes not handlenullvalues gracefully. Consider providing a default value or ensuringTableWorkflowRunscan handlenullinputs.