Skip to content

Commit

Permalink
feat: support rejudge (#38)
Browse files Browse the repository at this point in the history
* feat(server): support rejudge

* feat(ui): support rejudge

* chore: bump versions

* feat: enhanced rejudge

* feat: support problem rejudge & pull

* feat: support pull rejudge
  • Loading branch information
thezzisu authored Jan 23, 2024
1 parent 97f794c commit 7dc7a7a
Show file tree
Hide file tree
Showing 15 changed files with 463 additions and 46 deletions.
3 changes: 3 additions & 0 deletions .yarn/versions/a308bfd1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
releases:
"@aoi-js/frontend": patch
"@aoi-js/server": patch
38 changes: 38 additions & 0 deletions apps/frontend/src/components/contest/ProblemTabAdmin.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,25 @@
</template>
</SettingsEditor>
<VCardActions>
<VBtn
color="red"
variant="elevated"
@click="submitAllTask.execute()"
:loading="submitAllTask.isLoading.value"
>
{{ t('action.submit-all') }}
</VBtn>
<VBtn
color="red"
variant="elevated"
:append-icon="pull ? 'mdi-source-pull' : 'mdi-pin'"
@click="rejudgeAllTask.execute()"
@click.middle="pull = !pull"
:loading="rejudgeAllTask.isLoading.value"
>
{{ t('action.rejudge-all') }}
</VBtn>

<VBtn color="error" variant="elevated" @click="deleteProblem()">
{{ t('action.delete') }}
</VBtn>
Expand All @@ -25,6 +44,8 @@ import type { IContestProblemDTO } from './types'
import { useRoute, useRouter } from 'vue-router'
import SettingsEditor from '@/components/utils/SettingsEditor.vue'
import ContestProblemSettingsInput from './ContestProblemSettingsInput.vue'
import { useAsyncTask, withMessage } from '@/utils/async'
import { ref } from 'vue'
const { t } = useI18n()
const router = useRouter()
Expand All @@ -38,6 +59,23 @@ const emit = defineEmits<{
(ev: 'updated'): void
}>()
const submitAllTask = useAsyncTask(async () => {
const { modifiedCount } = await http
.post(`contest/${props.contestId}/problem/${props.problem._id}/submit-all`)
.json<{ modifiedCount: number }>()
return withMessage(t('msg.submit-all-success', { count: modifiedCount }))
})
const pull = ref(false)
const rejudgeAllTask = useAsyncTask(async () => {
const { modifiedCount } = await http
.post(`contest/${props.contestId}/problem/${props.problem._id}/rejudge-all`, {
json: { pull: pull.value }
})
.json<{ modifiedCount: number }>()
return withMessage(t('msg.rejudge-all-success', { count: modifiedCount }))
})
async function deleteProblem() {
await http.delete(`contest/${props.contestId}/problem/${props.problem._id}`)
router.push(`/org/${route.params.orgId}/contest/${props.contestId}/problem`)
Expand Down
3 changes: 2 additions & 1 deletion apps/frontend/src/components/solution/SolutionList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
</RouterLink>
</template>
<template v-slot:[`item.status`]="{ item }">
<SolutionStatusChip :status="item.status" :to="rel(item._id)" />
<SolutionStatusChip v-if="item.status" :status="item.status" :to="rel(item._id)" />
<span v-else>-</span>
</template>
<template v-slot:[`item.score`]="{ item }">
<SolutionScoreDisplay :score="item.score" :to="rel(item._id)" />
Expand Down
38 changes: 28 additions & 10 deletions apps/frontend/src/components/solution/SolutionView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,21 @@
<VCardActions>
<DownloadBtn :endpoint="downloadEndpoint" />
<VBtn
v-if="admin"
:text="t('action.rejudge')"
v-if="admin || solution.state.value?.state === 0"
:text="t('action.submit')"
@click="submit.execute()"
:loading="submit.isLoading.value"
/>
<VBtn
v-if="admin"
:text="t('action.rejudge')"
@click="rejudge.execute()"
:loading="rejudge.isLoading.value"
/>
<VBtn
:text="t('action.refresh')"
@click="solution.execute()"
:loading="solution.isLoading.value"
:disabled="autoRefresh.isActive.value"
:loading="solution.isLoading.value || autoRefresh.isActive.value"
/>
<VBtn v-if="showData && !viewFile" :text="t('action.view')" @click="viewFile = true" />
</VCardActions>
Expand Down Expand Up @@ -185,13 +190,26 @@ const submit = useAsyncTask(async () => {
autoRefresh.resume()
})
const autoRefresh = useIntervalFn(() => {
if (solution.state.value?.state !== 4 && solution.state.value?.state !== 0) {
solution.execute()
} else {
autoRefresh.pause()
}
const rejudge = useAsyncTask(async () => {
const url = props.contestId
? `contest/${props.contestId}/solution/${props.solutionId}/rejudge`
: `problem/${props.problemId}/solution/${props.solutionId}/rejudge`
await http.post(url)
solution.execute()
autoRefresh.resume()
})
const autoRefresh = useIntervalFn(
() => {
if (solution.state.value?.state !== 4 && solution.state.value?.state !== 0) {
solution.execute()
} else {
autoRefresh.pause()
}
},
1500,
{ immediate: true }
)
</script>
<i18n>
en:
Expand Down
4 changes: 4 additions & 0 deletions apps/frontend/src/locales/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ action:
cancel-publish: Cancel Publish
export: Export
back: Go Back
submit-all: Submit all solutions
rejudge-all: Rejudge all solutions

term:
content: Content
Expand Down Expand Up @@ -141,6 +143,8 @@ msg:
operation-success: Operation Success
session-expired: Session Expired, Please Sign in Again.
no-joined-organization: You have not joined any organization yet.
submit-all-success: 'Submitted {count} solutions'
rejudge-all-success: 'Rejudged {count} solutions'

tabs:
description: Description
Expand Down
4 changes: 4 additions & 0 deletions apps/frontend/src/locales/zh-Hans.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ action:
cancel-publish: 取消发布
export: 导出
back: 返回
submit-all: 提交所有解答
rejudge-all: 重测所有解答

term:
content: 内容
Expand Down Expand Up @@ -141,6 +143,8 @@ msg:
operation-success: 操作成功
session-expired: 会话已过期,请重新登录
no-joined-organization: 您还未加入任何组织
submit-all-success: '提交了 {count} 个解答'
rejudge-all-success: '重判了 {count} 个解答'

tabs:
description: 描述
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@
>
{{ t('action.submit-all') }}
</VBtn>
<VBtn
color="red"
variant="elevated"
@click="rejudgeAllTask.execute()"
:loading="rejudgeAllTask.isLoading.value"
>
{{ t('action.rejudge-all') }}
</VBtn>

<VBtn
color="red"
Expand Down Expand Up @@ -61,7 +69,7 @@
<script setup lang="ts">
import type { IContestDTO } from '@/components/contest/types'
import AccessLevelEditor from '@/components/utils/AccessLevelEditor.vue'
import { useAsyncTask } from '@/utils/async'
import { useAsyncTask, withMessage } from '@/utils/async'
import { http } from '@/utils/http'
import { useAsyncState } from '@vueuse/core'
import { ref } from 'vue'
Expand All @@ -81,7 +89,19 @@ const emit = defineEmits<{
const { t } = useI18n()
const router = useRouter()
const submitAllTask = useAsyncTask(() => http.post(`contest/${props.contestId}/admin/submit-all`))
const submitAllTask = useAsyncTask(async () => {
const { modifiedCount } = await http
.post(`contest/${props.contestId}/admin/submit-all`)
.json<{ modifiedCount: number }>()
return withMessage(t('msg.submit-all-success', { count: modifiedCount }))
})
const rejudgeAllTask = useAsyncTask(async () => {
const { modifiedCount } = await http
.post(`contest/${props.contestId}/admin/rejudge-all`)
.json<{ modifiedCount: number }>()
return withMessage(t('msg.rejudge-all-success', { count: modifiedCount }))
})
const ranklistStates: Record<number, string> = {
'-1': '[Loading...]',
Expand Down Expand Up @@ -126,12 +146,10 @@ en:
ranklist-state: Ranklist State is {state}
reset-runner: Switch Reset Runner
action:
submit-all: Submit all solutions
update-ranklists: Update ranklists
zh-Hans:
ranklist-state: '排行榜状态: {state}'
reset-runner: 切换重置运行器
action:
submit-all: 提交所有解答
update-ranklists: 更新排行榜
</i18n>
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,25 @@
{{ t('term.danger-zone') }}
</VCardSubtitle>
<VCardText>
<VBtn
color="red"
variant="elevated"
@click="submitAllTask.execute()"
:loading="submitAllTask.isLoading.value"
>
{{ t('action.submit-all') }}
</VBtn>
<VBtn
color="red"
variant="elevated"
:append-icon="pull ? 'mdi-source-pull' : 'mdi-pin'"
@click="rejudgeAllTask.execute()"
@click.middle="pull = !pull"
:loading="rejudgeAllTask.isLoading.value"
>
{{ t('action.rejudge-all') }}
</VBtn>

<VBtn color="red" variant="elevated" @click="deleteProblem()">
{{ t('action.delete') }}
</VBtn>
Expand All @@ -32,6 +51,8 @@ import { useRouter } from 'vue-router'
import type { IProblemDTO } from '@/components/problem/types'
import ProblemSettingsInput from '@/components/problem/ProblemSettingsInput.vue'
import SettingsEditor from '@/components/utils/SettingsEditor.vue'
import { useAsyncTask, withMessage } from '@/utils/async'
import { ref } from 'vue'
const props = defineProps<{
orgId: string
Expand All @@ -46,6 +67,21 @@ const emit = defineEmits<{
const { t } = useI18n()
const router = useRouter()
const submitAllTask = useAsyncTask(async () => {
const { modifiedCount } = await http
.post(`problem/${props.problemId}/admin/submit-all`)
.json<{ modifiedCount: number }>()
return withMessage(t('msg.submit-all-success', { count: modifiedCount }))
})
const pull = ref(false)
const rejudgeAllTask = useAsyncTask(async () => {
const { modifiedCount } = await http
.post(`problem/${props.problemId}/admin/rejudge-all`, { json: { pull: pull.value } })
.json<{ modifiedCount: number }>()
return withMessage(t('msg.rejudge-all-success', { count: modifiedCount }))
})
async function deleteProblem() {
await http.delete(`problem/${props.problemId}/admin`)
router.push(`/org/${props.orgId}/problem`)
Expand Down
41 changes: 38 additions & 3 deletions apps/server/src/routes/contest/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,44 @@ export const contestAdminRoutes = defineRoutes(async (s) => {
metrics: {},
message: ''
}
}
],
{ ignoreUndefined: true }
},
{ $unset: ['taskId', 'runnerId'] }
]
)
return { modifiedCount }
}
)

s.post(
'/rejudge-all',
{
schema: {
description: 'Rejudge all solutions',
response: {
200: Type.Object({
modifiedCount: Type.Number()
})
}
}
},
async (req) => {
const { modifiedCount } = await solutions.updateMany(
{
contestId: req.inject(kContestContext)._contestId,
state: { $ne: SolutionState.CREATED }
},
[
{
$set: {
state: SolutionState.PENDING,
score: 0,
status: '',
metrics: {},
message: ''
}
},
{ $unset: ['taskId', 'runnerId'] }
]
)
return { modifiedCount }
}
Expand Down
Loading

0 comments on commit 7dc7a7a

Please sign in to comment.