mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 09:36:08 +08:00
ui: bug fixes , bump version
Co-authored-by: panda <panda_dtdyy@outlook.com>
This commit is contained in:
1 parent
3b97159879
commit
42d3cd7886
11 files changed
+64
-50
No files matched your search
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hydrooj",
|
||||
"version": "3.12.3",
|
||||
"version": "3.13.0",
|
||||
"bin": "bin/hydrooj.js",
|
||||
"main": "src/loader",
|
||||
"module": "src/loader",
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
});
|
||||
|
||||
@@ -120,7 +120,7 @@ export default function ProblemType() {
|
||||
<Tab
|
||||
id="objective"
|
||||
title={i18n('problem_type.objective')}
|
||||
panel={(<p>暂不支持快捷配置此类题目。请参照文档操作。</p>)}
|
||||
panel={(<p>{i18n('Unsupported configure this type of problem. Please refer to the documentation.')}</p>)}
|
||||
/>
|
||||
</Tabs>
|
||||
</Card>
|
||||
|
||||
@@ -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 (
|
||||
<Card style={{ padding: 0 }}>
|
||||
<span>Subtasks #{index + 1} </span>
|
||||
<p>{i18n('Failed to parse subtask.')}</p>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Card style={{ padding: 0 }}>
|
||||
<span>Subtasks #{index + 1} </span>
|
||||
@@ -90,8 +99,8 @@ export function SubtasksTable({ index }) {
|
||||
<td>
|
||||
<NumericInput
|
||||
rightElement={<Tag minimal>ms</Tag>}
|
||||
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 }) {
|
||||
<td>
|
||||
<NumericInput
|
||||
rightElement={<Tag minimal>MB</Tag>}
|
||||
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() {
|
||||
<FormItem columns={6} label="Time">
|
||||
<NumericInput
|
||||
rightElement={<Tag minimal>ms</Tag>}
|
||||
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() {
|
||||
<FormItem columns={6} label="Memory">
|
||||
<NumericInput
|
||||
rightElement={<Tag minimal>MB</Tag>}
|
||||
value={memory ? parseMemoryMB(memory).toString() : ''}
|
||||
value={memory ? parseMemoryMB(memory, false).toString() : ''}
|
||||
placeholder={parseMemoryMB('256m').toString()}
|
||||
onValueChange={dispatcher('memory', 'MB')}
|
||||
buttonPosition="none"
|
||||
|
||||
@@ -30,13 +30,18 @@ export function TestCaseEntry({ index, subindex }) {
|
||||
value,
|
||||
});
|
||||
};
|
||||
if (!testcase || Object.keys(testcase).length === 0) {
|
||||
return (
|
||||
<tr><td colSpan={5}>{i18n('Failed to parse testcase.')}</td></tr>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<tr>
|
||||
<td>
|
||||
<NumericInput
|
||||
rightElement={<Tag minimal>ms</Tag>}
|
||||
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 }) {
|
||||
<td>
|
||||
<NumericInput
|
||||
rightElement={<Tag minimal>MB</Tag>}
|
||||
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 }) {
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{[...Array(len).keys()].map((i) => <TestCaseEntry index={index} subindex={i} key={i} />)}
|
||||
{len && [...Array(len).keys()].map((i) => <TestCaseEntry index={index} subindex={i} key={i} />)}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
|
||||
@@ -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: 更新
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hydrooj/ui-default",
|
||||
"version": "4.35.6",
|
||||
"version": "4.36.0",
|
||||
"author": "undefined <i@undefined.moe>",
|
||||
"license": "AGPL-3.0",
|
||||
"main": "hydro.js",
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="medium-9 columns">
|
||||
<div class="section">
|
||||
<div class="section__header">
|
||||
<h1 class="section__title">{{_('Judge Config')}}</h1>
|
||||
<h1 class="section__title">{{_('Judge Config')}}(Beta)</h1>
|
||||
</div>
|
||||
<div id="ProblemConfig" class="section__body">
|
||||
{{ noscript_note.render() }}
|
||||
|
||||
@@ -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<number, ParsedSubtask> = {};
|
||||
@@ -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<ParsedCase>;
|
||||
@@ -276,34 +266,40 @@ interface NormalizedSubtask extends Required<ParsedSubtask> {
|
||||
}
|
||||
|
||||
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',
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in new issue
Block a user