oicontest
Version:
OI Contest Management Tool
182 lines (156 loc) • 5.32 kB
text/typescript
import yaml from "js-yaml";
import { mkdirp } from 'mkdirp';
import chalk from 'chalk';
import { rimraf } from 'rimraf';
import fs from 'fs-extra';
import { ContestConfig, loadConfig } from "../lib/config";
import { convertMarkdownImagePaths } from "./handleMardown";
//@ts-ignore
const path = require("path");
// 获取模板内容
export function getTemplate(templateDir: string, templateName: string): string {
return fs.readFileSync(
path.join(templateDir, templateName),
"utf-8"
);
}
// 写入文件,如果目录不存在则创建
export function writeFileWithDir(filePath: string, content: string): void {
const dir = path.dirname(filePath);
if (!fs.existsSync(dir)) {
mkdirp.sync(dir);
}
fs.writeFileSync(filePath, content);
}
// 读取 YAML 文件
export function readYaml(filePath: string) {
try {
return yaml.load(fs.readFileSync(filePath, "utf-8"));
} catch (e: any) {
console.error(
chalk.red(`Error reading YAML file ${filePath}: ${e.message}`)
);
return null;
}
}
// 写入 YAML 文件
export function writeYaml(filePath: string, data: Object) {
writeFileWithDir(filePath, yaml.dump(data));
}
// 检查文件是否存在
export function checkFileExists(filePath: string) {
return fs.existsSync(filePath);
}
export async function countSubdirectories(directoryPath: string) {
try {
const items = await fs.readdir(directoryPath);
let subdirCount = 0;
// 并行检查所有项目
const results = await Promise.all(
items.map(async (item: string) => {
const fullPath = path.join(directoryPath, item);
const stats = await fs.stat(fullPath);
return stats.isDirectory();
})
);
// 统计目录数量
results.forEach(isDirectory => {
if (isDirectory) subdirCount++;
});
return subdirCount;
} catch (error) {
console.error(`无法读取目录: ${directoryPath}`, error);
return -1;
}
}
export async function loadJsonFile(filePath: string) {
if (!fs.existsSync(filePath)) {
throw new Error(`JSON file not found. Are you in a contest directory?`);
}
return fs.readJSON(filePath);
}
export async function verifyProblems(problemDir: string) {
//验证题目
//1.读取各题目的status.json,返回已经验证的数量
const items = await fs.readdir(problemDir);
console.error(items);
let verifiedCount = 0;
// 并行检查所有项目
const results = await Promise.all(
items.map(async (item: string) => {
const fullPath = path.join(problemDir, item, "status.json");
console.error(fullPath)
return fullPath;
})
);
// 统计数量
// 由于 loadJsonFile 是异步函数,需要使用 await
// 因为 forEach 不支持 await,改用 for...of 循环
for (const fullPath of results) {
try {
const status = await loadJsonFile(fullPath);
if (status.ischecked && status.ischecked.status) verifiedCount++;
} catch (err) {
// 可以选择忽略错误或记录日志
console.error(`Error loading ${fullPath}:`, err);
}
}
return verifiedCount;
}
export async function contestInfoToMarkdown(config: ContestConfig)
{
const contestDir = process.cwd();
//先将contest信息转换成markdown格式
if (config.problems.length === 0) {
console.error(chalk.red('Error: No problems added to contest'));
process.exit(1);
}
let mdContent: string = "";
//add title
let mdTitle = "# " + config.description + '\n\n' + `开始时间: ${config.startTime.padEnd(20)}` +
`时长: ${(config.duration.toString() + '分钟').padEnd(10)}` +
`题目数量: ${config.problems.length} 题`;
const headers = ['题目名称', '提交目录', "提交源程序名", '输入文件名', '输出文件名', '时间限制', '内存限制', '分值'];
let mdProblemsTable = "\n|";
for (const s of headers)
mdProblemsTable += s + "|";
mdProblemsTable += "\n|";
headers.forEach(() => {
mdProblemsTable += "---|";
})
for (const problem of config.problems) {
const values = [
problem.title,
problem.id,
problem.id + ".cpp",
problem.id + ".in",
problem.id + ".out",
problem.timeLimit.toString() + 'ms',
problem.memoryLimit.toString() + 'MB',
problem.maxScore.toString(),
];
mdProblemsTable += "\n|";
values.forEach((s) => {
mdProblemsTable += s + "|"
})
}
//加入注意事项
const notice = await fs.readFile(path.join(__dirname, "../templates", "notice.md"));
mdContent = "\n" + notice + "\n\n";
for (const p of config.problems) {
let mdfile = path.resolve(contestDir, "problem", p.id, "problem.md");
if (!fs.existsSync(mdfile))
mdfile = path.resolve(contestDir, "problem", p.id, "problem_zh.md");
if (!fs.existsSync(mdfile)) {
console.error(chalk.red(`题目文件${mdfile}未找到!`));
process.exit(1);
}
const problemConfig = await fs.readJSON(path.join(contestDir, "problem", p.id, "config.json"));
mdContent = mdContent + "\n-----------------\n\n# " + problemConfig.title + "\n\n";
//将里面的所有图片转换成绝对路径
mdContent += await convertMarkdownImagePaths(mdfile);
}
const mdAll = mdTitle + "\n" + mdProblemsTable + mdContent;
//转换成html
return mdAll;
}