Skip to content

Commit

Permalink
feat: use db timestamp for critical times (#32)
Browse files Browse the repository at this point in the history
* feat: use database timestamp for critical timestamps

* chore: bump versions

* fix: ranklist update pipeline

* feat: update ranklist ux
  • Loading branch information
thezzisu authored Jan 21, 2024
1 parent 0d86b1d commit 43dc8a7
Show file tree
Hide file tree
Showing 10 changed files with 103 additions and 55 deletions.
3 changes: 3 additions & 0 deletions .yarn/versions/ecb1a3b6.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
releases:
"@aoi-js/frontend": patch
"@aoi-js/server": patch
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 @@ -50,6 +50,7 @@ import { computed, ref } from 'vue'
import { useAppState } from '@/stores/app'
import { useContestProblemTitle } from '@/utils/contest/problem/inject'
import type { ISolutionDTO } from './types'
import { useRouteQuery } from '@vueuse/router'
const { t } = useI18n()
const app = useAppState()
Expand Down Expand Up @@ -78,7 +79,7 @@ const headersProblem = [
{ title: t('common.submitted-at'), key: 'submittedAt', align: 'start', sortable: false }
] as const
const userId = ref(app.userId as string)
const userId = useRouteQuery('userId')
const {
page,
Expand Down
11 changes: 10 additions & 1 deletion apps/frontend/src/components/utils/RanklistViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@
<tr v-for="(man, i) in value.participant.list" :key="i">
<td>{{ man.rank }}</td>
<td class="u-flex u-items-center">
<PrincipalProfile :principal-id="man.userId" />
<PrincipalProfile
:principal-id="man.userId"
:to="admin ? participantUrl(man.userId) : undefined"
/>
<div v-if="man.tags" class="u-pl-2 u-flex u-items-center u-gap-2">
<div v-for="(tag, i) of man.tags" :key="i">
<VTooltip v-if="tag.startsWith('!star:')" :text="tag.slice(6)">
Expand Down Expand Up @@ -55,11 +58,17 @@ import { renderMarkdown } from '@/utils/md'
import PrincipalProfile from '@/components/utils/PrincipalProfile.vue'
import { useI18n } from 'vue-i18n'
import RanklistTopstars from '../contest/RanklistTopstars.vue'
import { useContestCapability, useContestData } from '@/utils/contest/inject'
const { t } = useI18n()
const props = defineProps<{
endpoint: string
}>()
const admin = useContestCapability('admin')
const contest = useContestData()
const participantUrl = (userId: string) =>
`/org/${contest.value.orgId}/contest/${contest.value._id}/participant/${userId}`
</script>

<i18n>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,27 @@
<VCardText>
<PrincipalProfile :principal-id="userId" />
</VCardText>
<VDivider />
<SettingsEditor v-if="admin" :endpoint="`contest/${contestId}/participant/admin/${userId}`">
<template v-slot="scoped">
<VCombobox v-model="scoped.value.tags" :label="t('term.tags')" multiple chips />
</template>
</SettingsEditor>
<template v-if="admin">
<VDivider />
<SettingsEditor :endpoint="`contest/${contestId}/participant/admin/${userId}`">
<template v-slot="scoped">
<VCombobox v-model="scoped.value.tags" :label="t('term.tags')" multiple chips />
</template>
</SettingsEditor>
<VDivider />
<VCardActions>
<VBtn
:to="`/org/${orgId}/contest/${contestId}/solution?userId=${userId}`"
:text="t('goto-solutions')"
/>
</VCardActions>
</template>
</template>

<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import type { IContestDTO } from '@/components/contest/types'
import { useContestCapability } from '@/utils/contest/inject'
import { useContestCapability, useContestSettings } from '@/utils/contest/inject'
import PrincipalProfile from '@/components/utils/PrincipalProfile.vue'
import SettingsEditor from '@/components/utils/SettingsEditor.vue'
Expand All @@ -27,3 +36,10 @@ defineProps<{
const { t } = useI18n()
const admin = useContestCapability('admin')
</script>

<i18n>
en:
goto-solutions: Go to solutions
zh-Hans:
goto-solutions: 查看提交
</i18n>
36 changes: 20 additions & 16 deletions apps/server/src/routes/contest/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,23 @@ export const contestAdminRoutes = defineRoutes(async (s) => {
}
},
async (req) => {
const { modifiedCount } = await solutions.updateOne(
const { modifiedCount } = await solutions.updateMany(
{
contestId: req.inject(kContestContext)._contestId,
state: SolutionState.CREATED
},
{
$set: {
state: SolutionState.PENDING,
submittedAt: req._now,
score: 0,
status: '',
metrics: {},
message: ''
[
{
$set: {
state: SolutionState.PENDING,
submittedAt: { $convert: { input: '$$NOW', to: 'double' } },
score: 0,
status: '',
metrics: {},
message: ''
}
}
},
],
{ ignoreUndefined: true }
)
return { modifiedCount }
Expand Down Expand Up @@ -120,13 +122,15 @@ export const contestAdminRoutes = defineRoutes(async (s) => {
_id: req.inject(kContestContext)._contestId,
ranklists: { $exists: true, $ne: [] }
},
{
$set: {
ranklistUpdatedAt: req._now,
ranklistState: ContestRanklistState.INVALID
[
{
$set: {
ranklistUpdatedAt: { $convert: { input: '$$NOW', to: 'double' } },
ranklistState: ContestRanklistState.INVALID
}
},
$unset: req.body.resetRunner ? { ranklistRunnerId: 1 } : {}
},
...(req.body.resetRunner ? [{ $unset: ['ranklistRunnerId'] }] : [])
],
{ ignoreUndefined: true }
)
return 0
Expand Down
12 changes: 8 additions & 4 deletions apps/server/src/routes/contest/participant/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,14 @@ const contestParticipantAdminRoutes = defineRoutes(async (s) => {
async (req) => {
const ctx = req.inject(kContestContext)
const userId = new BSON.UUID(req.params.userId)
await contestParticipants.updateOne(
{ contestId: ctx._contestId, userId },
{ $set: { tags: req.body.tags, updatedAt: req._now } }
)
await contestParticipants.updateOne({ contestId: ctx._contestId, userId }, [
{
$set: {
tags: req.body.tags,
updatedAt: { $convert: { input: '$$NOW', to: 'double' } }
}
}
])
return 0
}
)
Expand Down
2 changes: 2 additions & 0 deletions apps/server/src/routes/contest/problem/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,8 @@ const problemViewRoutes = defineRoutes(async (s) => {
metrics: {},
status: '',
message: '',
// createdAt is only a reference time,
// so use local time here
createdAt: req._now
})
const uploadUrl = await getUploadUrl(oss, solutionDataKey(insertedId), {
Expand Down
20 changes: 11 additions & 9 deletions apps/server/src/routes/contest/solution/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,18 @@ const solutionScopedRoutes = defineRoutes(async (s) => {
userId: admin ? undefined : req.user.userId,
state: admin ? undefined : SolutionState.CREATED
},
{
$set: {
state: SolutionState.PENDING,
submittedAt: req._now,
score: 0,
status: '',
metrics: {},
message: ''
[
{
$set: {
state: SolutionState.PENDING,
submittedAt: { $convert: { input: '$$NOW', to: 'double' } },
score: 0,
status: '',
metrics: {},
message: ''
}
}
},
],
{ ignoreUndefined: true }
)
if (modifiedCount === 0) return rep.notFound()
Expand Down
2 changes: 2 additions & 0 deletions apps/server/src/routes/problem/solution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ const solutionScopedRoutes = defineRoutes(async (s) => {
{
$set: {
state: SolutionState.PENDING,
// Problem solutions are not synced with rankers,
// so we can use local time here
submittedAt: req._now,
score: 0,
status: '',
Expand Down
39 changes: 22 additions & 17 deletions apps/server/src/routes/runner/solution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,16 @@ const runnerTaskRoutes = defineRoutes(async (s) => {
}
},
async (req, rep) => {
const now = req._now
const ctx = req.inject(kRunnerSolutionContext)
const value = await solutions.findOneAndUpdate(
{
_id: ctx._solutionId,
taskId: ctx._taskId,
state: { $in: [SolutionState.QUEUED, SolutionState.RUNNING] }
},
{ $set: { state: SolutionState.COMPLETED, completedAt: now } },
// Since completedAt is only for reference,
// use local time here
{ $set: { state: SolutionState.COMPLETED, completedAt: req._now } },
{ projection: { userId: 1, problemId: 1, contestId: 1, score: 1, status: 1 } }
)
if (!value) return rep.conflict()
Expand All @@ -120,17 +121,19 @@ const runnerTaskRoutes = defineRoutes(async (s) => {
contestId: value.contestId,
[`results.${value.problemId}.lastSolutionId`]: value._id
},
{
$set: {
[`results.${value.problemId}.lastSolution`]: {
_id: value._id,
score: value.score,
status: value.status,
completedAt: now
},
updatedAt: now
[
{
$set: {
[`results.${value.problemId}.lastSolution`]: {
_id: value._id,
score: value.score,
status: value.status,
completedAt: req._now
},
updatedAt: { $convert: { input: '$$NOW', to: 'double' } }
}
}
}
]
)
if (modifiedCount) {
// update contest ranklist state
Expand All @@ -139,12 +142,14 @@ const runnerTaskRoutes = defineRoutes(async (s) => {
_id: value.contestId,
ranklists: { $exists: true, $ne: [] }
},
{
$set: {
ranklistUpdatedAt: now,
ranklistState: ContestRanklistState.INVALID
[
{
$set: {
ranklistUpdatedAt: { $convert: { input: '$$NOW', to: 'double' } },
ranklistState: ContestRanklistState.INVALID
}
}
}
]
)
}
} else {
Expand Down

0 comments on commit 43dc8a7

Please sign in to comment.