From 42d3cd7886bc0c25bfea6c54b1c9b7a8326242f2 Mon Sep 17 00:00:00 2001 From: undefined Date: Sat, 7 May 2022 21:15:52 +0800 Subject: [PATCH] ui: bug fixes , bump version Co-authored-by: panda --- packages/hydrooj/package.json | 2 +- .../problemconfig/ProblemConfigEditor.tsx | 2 +- .../components/problemconfig/ProblemType.tsx | 2 +- .../components/problemconfig/SubtaskTable.tsx | 23 +++++--- .../problemconfig/TestCasesTable.tsx | 15 +++-- packages/ui-default/locales/zh.yaml | 4 ++ packages/ui-default/package.json | 2 +- .../ui-default/pages/problem_config.page.tsx | 4 +- .../ui-default/templates/problem_config.html | 2 +- packages/utils/lib/common.ts | 56 +++++++++---------- packages/utils/package.json | 2 +- 11 files changed, 64 insertions(+), 50 deletions(-) diff --git a/packages/hydrooj/package.json b/packages/hydrooj/package.json index 430c41b69..5fd810d26 100644 --- a/packages/hydrooj/package.json +++ b/packages/hydrooj/package.json @@ -1,6 +1,6 @@ { "name": "hydrooj", - "version": "3.12.3", + "version": "3.13.0", "bin": "bin/hydrooj.js", "main": "src/loader", "module": "src/loader", diff --git a/packages/ui-default/components/problemconfig/ProblemConfigEditor.tsx b/packages/ui-default/components/problemconfig/ProblemConfigEditor.tsx index 6da3b1baf..27ab9d6e1 100644 --- a/packages/ui-default/components/problemconfig/ProblemConfigEditor.tsx +++ b/packages/ui-default/components/problemconfig/ProblemConfigEditor.tsx @@ -52,7 +52,7 @@ export function configYamlFormat(config: ProblemConfigFile) { config[key].forEach((subtask) => { const formatSubtask: object = {}; subtasksKey.forEach((subtaskKey) => { - if (subtask[subtaskKey] !== undefined) { + if (subtask && subtask[subtaskKey] !== undefined) { formatSubtask[subtaskKey] = subtask[subtaskKey]; } }); diff --git a/packages/ui-default/components/problemconfig/ProblemType.tsx b/packages/ui-default/components/problemconfig/ProblemType.tsx index f81c88fe1..3525a6d80 100644 --- a/packages/ui-default/components/problemconfig/ProblemType.tsx +++ b/packages/ui-default/components/problemconfig/ProblemType.tsx @@ -120,7 +120,7 @@ export default function ProblemType() { 暂不支持快捷配置此类题目。请参照文档操作。

)} + panel={(

{i18n('Unsupported configure this type of problem. Please refer to the documentation.')}

)} /> diff --git a/packages/ui-default/components/problemconfig/SubtaskTable.tsx b/packages/ui-default/components/problemconfig/SubtaskTable.tsx index 145b23e7f..94339d245 100644 --- a/packages/ui-default/components/problemconfig/SubtaskTable.tsx +++ b/packages/ui-default/components/problemconfig/SubtaskTable.tsx @@ -4,6 +4,7 @@ import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { SubtaskConfig } from 'hydrooj/src/interface'; import { parseTimeMS, parseMemoryMB } from '@hydrooj/utils/lib/common'; +import i18n from 'vj/utils/i18n'; import CustomSelectAutoComplete from '../autocomplete/components/CustomSelectAutoComplete'; import { FormItem } from './BasicForm'; import { RootState } from './reducer'; @@ -12,7 +13,7 @@ import { CasesTable } from './TestCasesTable'; const eq = (a: SubtaskConfig, b: SubtaskConfig) => isEqual(a, b); export function SubtasksIds({ index }) { - const ids = useSelector((state: RootState) => state.config.subtasks.map((i) => i.id).filter((i) => i !== undefined).join(',')); + const ids = useSelector((state: RootState) => state.config.subtasks.map((i) => (i ? i.id : undefined)).filter((i) => i !== undefined).join(',')); const subtaskIf = useSelector((state: RootState) => state.config.subtasks[index].if); const dispatch = useDispatch(); return ( @@ -46,6 +47,14 @@ export function SubtasksTable({ index }) { type: 'CONFIG_SUBTASK_UPDATE', id: index, key, value, }); }; + if (!subtask || Object.keys(subtask).length === 0) { + return ( + + Subtasks #{index + 1} +

{i18n('Failed to parse subtask.')}

+
+ ); + } return ( Subtasks #{index + 1} @@ -90,8 +99,8 @@ export function SubtasksTable({ index }) { ms} - value={subtask.time ? parseTimeMS(subtask.time).toString() : ''} - placeholder={parseTimeMS(defaultTime || '1000ms').toString()} + value={subtask.time ? parseTimeMS(subtask.time, false).toString() : ''} + placeholder={parseTimeMS(defaultTime || '1000ms', false).toString()} onValueChange={dispatcher('time', 'ms')} buttonPosition="none" fill @@ -100,8 +109,8 @@ export function SubtasksTable({ index }) { MB} - value={subtask.memory ? parseMemoryMB(subtask.memory).toString() : ''} - placeholder={parseMemoryMB(defaultMemory || '256m').toString()} + value={subtask.memory ? parseMemoryMB(subtask.memory, false).toString() : ''} + placeholder={parseMemoryMB(defaultMemory || '256m', false).toString()} onValueChange={dispatcher('memory', 'MB')} buttonPosition="none" fill @@ -131,7 +140,7 @@ function GlobalTaskConfig() { ms} - value={time ? parseTimeMS(time).toString() : ''} + value={time ? parseTimeMS(time, false).toString() : ''} placeholder={parseTimeMS('1000ms').toString()} onValueChange={dispatcher('time', 'ms')} buttonPosition="none" @@ -141,7 +150,7 @@ function GlobalTaskConfig() { MB} - value={memory ? parseMemoryMB(memory).toString() : ''} + value={memory ? parseMemoryMB(memory, false).toString() : ''} placeholder={parseMemoryMB('256m').toString()} onValueChange={dispatcher('memory', 'MB')} buttonPosition="none" diff --git a/packages/ui-default/components/problemconfig/TestCasesTable.tsx b/packages/ui-default/components/problemconfig/TestCasesTable.tsx index 23c2ff287..e587798cf 100644 --- a/packages/ui-default/components/problemconfig/TestCasesTable.tsx +++ b/packages/ui-default/components/problemconfig/TestCasesTable.tsx @@ -30,13 +30,18 @@ export function TestCaseEntry({ index, subindex }) { value, }); }; + if (!testcase || Object.keys(testcase).length === 0) { + return ( + {i18n('Failed to parse testcase.')} + ); + } return ( ms} - value={testcase.time ? parseTimeMS(testcase.time).toString() : ''} - placeholder={parseTimeMS(defaultTime || '1000ms').toString()} + value={testcase.time ? parseTimeMS(testcase.time, false).toString() : ''} + placeholder={parseTimeMS(defaultTime || '1000ms', false).toString()} onValueChange={dispatcher('time', 'ms')} buttonPosition="none" fill @@ -45,8 +50,8 @@ export function TestCaseEntry({ index, subindex }) { MB} - value={testcase.memory ? parseMemoryMB(testcase.memory).toString() : ''} - placeholder={parseMemoryMB(defaultMemory || '256m').toString()} + value={testcase.memory ? parseMemoryMB(testcase.memory, false).toString() : ''} + placeholder={parseMemoryMB(defaultMemory || '256m', false).toString()} onValueChange={dispatcher('memory', 'MB')} buttonPosition="none" fill @@ -116,7 +121,7 @@ export function CasesTable({ index }) { - {[...Array(len).keys()].map((i) => )} + {len && [...Array(len).keys()].map((i) => )} ); diff --git a/packages/ui-default/locales/zh.yaml b/packages/ui-default/locales/zh.yaml index 58c29c0ec..4a7ae97f2 100644 --- a/packages/ui-default/locales/zh.yaml +++ b/packages/ui-default/locales/zh.yaml @@ -325,6 +325,8 @@ Extension (days): 最长延期 (日) Extension Score Penalty: 延期递交扣分规则 Extra Files Config: 额外文件设置 Failed to join the domain. You are already a member.: 加入域失败,您已是该域的成员。 +Failed to parse subtask.: 无法解析子任务信息。 +Failed to parse testcase.: 无法解析测试点信息。 Feedback: 反馈 Field {0} or {1} validation failed.: 字段 {0} 或 {1} 验证失败。 Field {0} validation failed.: 字段 {0} 验证失败。 @@ -595,6 +597,7 @@ Problem Solution List: 题解列表 Problem Solution: 题解列表 Problem Tags Visibility: 题目标签可见性 Problem Tags: 题目标签 +Problem Type: 题目类型 problem_config: 评测设置 problem_create: 创建题目 problem_edit: 编辑题目 @@ -830,6 +833,7 @@ Unlimited: 无限制 Unordered List: 无序列表 Unstar Topic: 取消收藏 Unstar: 取消收藏 +Unsupported configure this type of problem. Please refer to the documentation.: 暂不支持快捷配置此类题目。请参照文档操作。 Update Permission: 更新权限 Update Settings: 更新设置 Update: 更新 diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index 9df5f5240..dbac78c0e 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -1,6 +1,6 @@ { "name": "@hydrooj/ui-default", - "version": "4.35.6", + "version": "4.36.0", "author": "undefined ", "license": "AGPL-3.0", "main": "hydro.js", diff --git a/packages/ui-default/pages/problem_config.page.tsx b/packages/ui-default/pages/problem_config.page.tsx index 3e3c26691..43d7eed60 100644 --- a/packages/ui-default/pages/problem_config.page.tsx +++ b/packages/ui-default/pages/problem_config.page.tsx @@ -6,7 +6,7 @@ import yaml from 'js-yaml'; import Notification from 'vj/components/notification'; import Dialog, { ConfirmDialog } from 'vj/components/dialog/index'; import download from 'vj/components/zipDownloader'; -import { size, readSubtasksFromFiles } from '@hydrooj/utils/lib/common'; +import { size, readSubtasksFromFiles, normalizeSubtasks } from '@hydrooj/utils/lib/common'; import tpl from 'vj/utils/tpl'; import { SubtaskType } from 'hydrooj/src/interface'; import { configYamlFormat } from 'vj/components/problemconfig/ProblemConfigEditor'; @@ -176,7 +176,7 @@ const page = new NamedPage('problem_config', () => { const subtasks = readSubtasksFromFiles(testdata, checkFile, state.config); store.dispatch({ type: 'CONFIG_AUTOCASES_UPDATE', - subtasks, + subtasks: normalizeSubtasks(subtasks, (i) => i, state.config.time, state.config.memory, true), }); }); render( diff --git a/packages/ui-default/templates/problem_config.html b/packages/ui-default/templates/problem_config.html index 5a2753cbd..a894fd765 100644 --- a/packages/ui-default/templates/problem_config.html +++ b/packages/ui-default/templates/problem_config.html @@ -4,7 +4,7 @@
-

{{_('Judge Config')}}

+

{{_('Judge Config')}}(Beta)

{{ noscript_note.render() }} diff --git a/packages/utils/lib/common.ts b/packages/utils/lib/common.ts index 829419522..d8c7437f5 100644 --- a/packages/utils/lib/common.ts +++ b/packages/utils/lib/common.ts @@ -136,17 +136,19 @@ const TIME_UNITS = { '': 1000, m: 1, u: 0.001 }; const MEMORY_RE = /^([0-9]+(?:\.[0-9]*)?)([kmg])b?$/i; const MEMORY_UNITS = { k: 1 / 1024, m: 1, g: 1024 }; -export function parseTimeMS(str: string | number) { +export function parseTimeMS(str: string | number, throwOnError = true) { if (typeof str === 'number' || Number.isSafeInteger(+str)) return +str; const match = TIME_RE.exec(str); - if (!match) throw new Error(`${str} error parsing time`); + if (!match && throwOnError) throw new Error(`${str} error parsing time`); + if (!match) return 1000; return Math.floor(parseFloat(match[1]) * TIME_UNITS[match[2].toLowerCase()]); } -export function parseMemoryMB(str: string | number) { +export function parseMemoryMB(str: string | number, throwOnError = true) { if (typeof str === 'number' || Number.isSafeInteger(+str)) return +str; const match = MEMORY_RE.exec(str); - if (!match) throw new Error(`${str} error parsing memory`); + if (!match && throwOnError) throw new Error(`${str} error parsing memory`); + if (!match) return 256; return Math.ceil(parseFloat(match[1]) * MEMORY_UNITS[match[2].toLowerCase()]); } @@ -210,7 +212,7 @@ function* getScore(totalScore: number, count: number) { } } -interface CaseInput { +interface ParsedCase { id?: number; time?: number | string; memory?: number | string; @@ -218,8 +220,8 @@ interface CaseInput { input?: string; output?: string; } -interface SubtaskInput { - cases: CaseInput[]; +interface ParsedSubtask { + cases: ParsedCase[]; type: 'min' | 'max' | 'sum'; time?: number | string; memory?: number | string; @@ -227,15 +229,6 @@ interface SubtaskInput { id?: number; if?: number[]; } -interface ParsedCase extends CaseInput { - time?: number; - memory?: number; -} -interface ParsedSubtask extends SubtaskInput { - cases: ParsedCase[]; - time?: number; - memory?: number; -} export function readSubtasksFromFiles(files: string[], checkFile, config) { const subtask: Record = {}; @@ -252,8 +245,8 @@ export function readSubtasksFromFiles(files: string[], checkFile, config) { if (c.output === '/dev/null' || checkFile(c.output)) { if (!subtask[sid]) { subtask[sid] = { - time: parseTimeMS(config.time || '1s'), - memory: parseMemoryMB(config.memory || '256m'), + time: config.time, + memory: config.memory, type: rule.preferredScorerType, cases: [c], }; @@ -264,10 +257,7 @@ export function readSubtasksFromFiles(files: string[], checkFile, config) { } } } - const subtaskScore = getScore(100, Object.keys(subtask).length); - return Object.keys(subtask).map((i) => - ({ ...subtask[i], score: subtaskScore.next().value as number }), - ); + return Object.values(subtask); } type NormalizedCase = Required; @@ -276,34 +266,40 @@ interface NormalizedSubtask extends Required { } export function normalizeSubtasks( - subtasks: SubtaskInput[], checkFile: (name: string, errMsg: string) => string, - time: number | string, memory: number | string, + subtasks: ParsedSubtask[], checkFile: (name: string, errMsg: string) => string, + time: number | string, memory: number | string, ignoreParseError = false, ): NormalizedSubtask[] { subtasks.sort((a, b) => (a.id - b.id)); - const subtaskScore = getScore(100, subtasks.length); + const subtaskScore = getScore( + Math.max(100 - Math.sum(subtasks.map((i) => i.score || 0)), 0), + subtasks.filter((i) => !i.score).length, + ); let id = 0; let count = 0; return subtasks.map((s) => { id++; s.cases.sort((a, b) => (a.id - b.id)); const score = subtaskScore.next().value as number; - const caseScore = getScore(score, s.cases.length); + const caseScore = getScore( + Math.max(score - Math.sum(s.cases.map((i) => i.score || 0)), 0), + s.cases.filter((i) => !i.score).length, + ); return { id, score, type: 'min', if: [], ...s, - time: parseTimeMS(s.time || time), - memory: parseMemoryMB(s.memory || memory), + time: parseTimeMS(s.time || time, !ignoreParseError), + memory: parseMemoryMB(s.memory || memory, !ignoreParseError), cases: s.cases.map((c) => { count++; return { id: count, score: s.type === 'sum' ? caseScore.next().value as number : score, ...c, - time: parseTimeMS(c.time || s.time || time), - memory: parseMemoryMB(c.memory || s.memory || memory), + time: parseTimeMS(c.time || s.time || time, !ignoreParseError), + memory: parseMemoryMB(c.memory || s.memory || memory, !ignoreParseError), input: c.input ? checkFile(c.input, 'Cannot find input file {0}.') : '/dev/null', output: c.output ? checkFile(c.output, 'Cannot find output file {0}.') : '/dev/null', }; diff --git a/packages/utils/package.json b/packages/utils/package.json index e9e782ba2..0c5b58539 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@hydrooj/utils", - "version": "1.1.17", + "version": "1.2.0", "description": "hydrooj utils", "main": "package.json", "repository": "https://github.com/hydro-dev/Hydro.git",