Skip to content

Commit

Permalink
feat(Codeforces): 添加 评测状态文本替换 功能 (#144)
Browse files Browse the repository at this point in the history
* Add files via upload

* Update codeforces-better.user.js

* chore: 改进帮助文本

* feat(Codeforces): 完善"评测状态文本替换"功能

---------

Co-authored-by: 北极小狐 <60805870+beijixiaohu@users.noreply.github.com>
Co-authored-by: wrk-123 <83344959+wrk-123@users.noreply.github.com>
  • Loading branch information
wrk-123 and beijixiaohu committed Jun 11, 2024
1 parent c87e2e1 commit 58972dd
Show file tree
Hide file tree
Showing 2 changed files with 228 additions and 4 deletions.
7 changes: 6 additions & 1 deletion resources/locales/codeforces-better/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,11 @@
"title": "图标按钮大小",
"helpText": "<p>设置翻译按钮的大小</p>",
"placeholder": "请输入按钮大小"
},
"judgeStatusReplaceText": {
"title": "评测状态文本替换",
"helpText": "<p>将题目的评测状态文本替换为指定的文本。</p><p>支持的变量:</p><p><strong><code>{Status}</code></strong> 评测结果的完整描述,例如 \"Accepted\"。</p><p><strong><code>{Stat}</code></strong> 评测结果的缩写形式,例如 \"AC\"。</p><p><strong><code>{Number}</code></strong> 评测结果中的最后一个数字,评测状态为AC时不显示。</p><p><strong><code>{标识符:xxx}</code> </strong>当标识符为真时显示括号内的内容。支持的标识符:<code>ac</code>、<code>wa</code>、<code>tle</code>、<code>mle</code>、<code>re</code>、<code>ce</code>、<code>hacked</code>、<code>skipped</code>、<code>ile</code>、<code>pc</code>、<code>pending</code></p><div style=\"border: 1px solid #795548; padding: 10px;\"><p>示例:</p><p><code>我怎么{Stat}了,{wa:呜呜}</code></p><p>此时如果评测结果为\"Wrong answer\",则会显示:<p></p>我怎么WA了,呜呜</p></div>",
"placeholder": "请输入替换的模式,为空则不替换。"
}
},
"about": {
Expand Down Expand Up @@ -417,4 +422,4 @@
}
}
}
}
}
225 changes: 222 additions & 3 deletions script/dev/codeforces-better.user.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// ==UserScript==
// @name Codeforces Better!
// @namespace https://greasyfork.org/users/747162
// @version 1.75.1
// @version 1.75.2
// @author 北极小狐
// @match *://*.codeforces.com/*
// @match *://*.codeforc.es/*
Expand Down Expand Up @@ -426,6 +426,9 @@ OJBetter.preference = {
hoverTargetAreaDisplay: undefined,
/** @type {string?} 按钮图标大小 */
iconButtonSize: undefined,
/** @type {string?} 评测状态文本替换规则 */
judgeStatusReplaceText: undefined,

};

/**
Expand Down Expand Up @@ -952,6 +955,7 @@ async function initVar() {
OJBetter.preference.hoverTargetAreaDisplay = OJB_getGMValue("hoverTargetAreaDisplay", false);
OJBetter.basic.expandFoldingblocks = OJB_getGMValue("expandFoldingblocks", true);
OJBetter.preference.iconButtonSize = OJB_getGMValue("iconButtonSize", "16");
OJBetter.preference.judgeStatusReplaceText = OJB_getGMValue("judgeStatusReplaceText", "");
OJBetter.dev.isRuleMarkingEnabled = OJB_getGMValue("isRuleMarkingEnabled", false);
OJBetter.about.updateChannel = OJB_getGMValue("updateChannel", "release");
OJBetter.about.updateSource = OJB_getGMValue("updateSource", "greasyfork");
Expand Down Expand Up @@ -6097,6 +6101,16 @@ const preference_settings_HTML = `
<input type='number' id='iconButtonSize' class='no_default' require=true data-i18n="[placeholder]settings:preference.iconButtonSize.placeholder">
<span>px</span>
</div>
<div class='OJBetter_setting_list'>
<label for='judgeStatusReplaceText'>
<div style="display: flex;align-items: center;" data-i18n="settings:preference.judgeStatusReplaceText.title"></div>
</label>
<div class="help_tip">
${helpCircleHTML}
<div class="tip_text" data-i18n="[html]settings:preference.judgeStatusReplaceText.helpText"></div>
</div>
<input type="text" id='judgeStatusReplaceText' class='no_default' data-i18n="[placeholder]settings:preference.judgeStatusReplaceText.placeholder">
</div>
</div>
`;

Expand Down Expand Up @@ -6763,6 +6777,7 @@ async function initSettingsPanel() {
$('#openai_customPrompt').val(GM_getValue("openai_customPrompt"));
$('#comment_translation_choice').val(GM_getValue("commentTranslationChoice"));
$('#iconButtonSize').val(GM_getValue("iconButtonSize"));
$('#judgeStatusReplaceText').val(GM_getValue("judgeStatusReplaceText"));
$("#autoTranslation").prop("checked", GM_getValue("autoTranslation") === true);
$('#shortTextLength').val(GM_getValue("shortTextLength"));
$("#allowMixTrans").prop("checked", GM_getValue("allowMixTrans") === true);
Expand Down Expand Up @@ -6852,6 +6867,7 @@ async function initSettingsPanel() {
openai_customPrompt: $('#openai_customPrompt').val(),
commentTranslationChoice: $('#comment_translation_choice').val(),
iconButtonSize: $('#iconButtonSize').val(),
judgeStatusReplaceText: $('#judgeStatusReplaceText').val(),
autoTranslation: $("#autoTranslation").prop("checked"),
shortTextLength: $('#shortTextLength').val(),
allowMixTrans: $("#allowMixTrans").prop("checked"),
Expand Down Expand Up @@ -9985,7 +10001,7 @@ async function showRatingByClist_problemset() {

// 检测clist连接
if (!await validateClistConnection()) {
for (let i = 0; i < rating.length; i++) {
for (let i = 0; i < ratingBadges.length; i++) {
ratingBadges[i].rating.text(i18next.t('state.netError', { ns: 'button' }));
}
return;
Expand Down Expand Up @@ -10046,6 +10062,204 @@ async function recolorStandings() {
});
}

/**
* 评测结果的简写
*/
const StatusAcronyms = {
"Accepted": "AC",
"Wrong answer": "WA",
"Time limit exceeded": "TLE",
"Memory limit exceeded": "MLE",
"Runtime error": "RE",
"Compilation error": "CE",
"Hacked": "Hacked",
"Skipped": "Skipped",
"Idleness limit exceeded": "ILE",
"Perfect result:": "AC",
"Partial result:": "PC",
"Running": "PENDING"
};

/**
* 评测结果的颜色
*/
const StatusColors = {
"AC": "#52c41a",
"WA": "#e74c3c",
"PENDING": "#808080",
};

/**
* 替换评测结果
*/
async function judgeStatusReplace() {

/**
* 获取评测状态的名称和编号
*
* @param {string} text 评测状态
* @returns {object} 评测状态的名称和编号
*/
const getStatusName = (text) => {
const words = text.split(' ');
const number_of_words = words.length;
let status_name = "", number = "";
let status_name_is_over = false;
for (let i = 0; i < number_of_words; i++) {
if (words[i] === "on") {
status_name_is_over = true;
}
if (!status_name_is_over) {
status_name += words[i] + " ";
} else if (parseInt(words[i]).toString() !== "NaN") {
number = words[i];
}
if (words[i] === "result:") {
status_name_is_over = true;
}
}
status_name = status_name.trim();
return {
status_name: status_name,
number: number
}
};

/**
* 获取当前评测状态的缩写
*
* @param {string} status_name 评测状态
* @returns {string} 评测状态的缩写
*/
const getStatusAcronym = (status_name) => {
return StatusAcronyms[status_name];
};

/**
* 根据评测状态的缩写获取颜色
*
* @param {string} statusAcronym 评测状态的缩写
* @returns {string} 颜色值
*/
const getStatusColor = (statusAcronym) => {
let color = StatusColors[statusAcronym];
return color ? color : StatusColors["WA"];
};

/**
* 解析模板
*
* @param {string} template 模板
* @param {object} display 各个前置标识符的bool值,决定了是否显示这一部分
* @returns {string} 解析后的文本
*
* @example
* parseTemplate("{ac:!}{wa:呜呜}{pending:别急}", {ac: false, wa: true, pending: false}) => "呜呜"
*/
const parseTemplate = (template, display) => {
const regex = /{(\w+):([^}]*)}/g;
let result = '';
let lastIndex = 0;

template.replace(regex, (match, key, text, offset) => {
// 添加模板前的普通文本部分
result += template.slice(lastIndex, offset);

// 根据布尔值决定是否添加模板部分
if (display[key]) {
result += text;
}
lastIndex = offset + match.length;
});

// 添加最后一段普通文本部分
result += template.slice(lastIndex);

return result;
}

/**
* 根据替换规则获取替换后的文本
*
* @param {string} status_name 评测状态
* @param {string} number 评测编号
* @returns {string} 替换后的文本
*/
const getReplaceText = (status_name, number) => {
const statusAcronym = getStatusAcronym(status_name);

let result = OJBetter.preference.judgeStatusReplaceText;
result = result.replace("{Status}", status_name);
result = result.replace("{Stat}", statusAcronym);
result = result.replace("{Number}", number);

const statusMap = {
ac: statusAcronym === "AC",
wa: statusAcronym === "WA",
tle: statusAcronym === "TLE",
mle: statusAcronym === "MLE",
re: statusAcronym === "RE",
ce: statusAcronym === "CE",
hacked: statusAcronym === "Hacked",
skipped: statusAcronym === "Skipped",
ile: statusAcronym === "ILE",
pc: statusAcronym === "PC",
pending: statusAcronym === "PENDING"
}
const final_result = parseTemplate(result, statusMap);
return final_result;
};

/**
* 处理评测结果
* @param {string} text 代替换的文本
* @returns {object} 处理后的文本和颜色
*/
const process = (text) => {
/**
* 当前评测状态
*/
const { status_name, number } = getStatusName(text);

/**
* 当前评测状态的缩写
*/
const statusAcronym = getStatusAcronym(status_name);

return {
text: statusAcronym ? getReplaceText(status_name, number) : text,
color: getStatusColor(statusAcronym)
}
};

OJB_observeElement({
selector: '.datatable',
callback: (node) => {
const updateElement = (element) => {
const { text, color } = process($(element).text());
$(element).text(text);
$(element).css({
"color": color,
"font-weight": "700"
});
};

// 选择器
const selectorCondition = "[class^='verdict-'], [submissionverdict='COMPILATION_ERROR']";

// 检查[node]本身是否符合选择器条件
if ($(node).is(selectorCondition)) {
updateElement(node);
}

// 更新[node]内部符合选择器条件的所有元素
$(node).find(selectorCondition).each(function () {
updateElement(this);
});
}
});
}

/**
* 存放编辑器语言select的值与Monaco语言对应关系的map.
* @type {Object.<string, string>}
Expand Down Expand Up @@ -13680,6 +13894,9 @@ function initOnDOMReady() {
if (OJBetter.typeOfPage.is_problem && OJBetter.monaco.enableOnProblemPage) {
addProblemPageCodeEditor(); // 添加题目页代码编辑器
}
if (OJBetter.preference.judgeStatusReplaceText && (OJBetter.typeOfPage.is_submissions || OJBetter.typeOfPage.is_statePage)) {
judgeStatusReplace(); // 评测结果替换
}
}

/**
Expand Down Expand Up @@ -13716,7 +13933,9 @@ async function initializeSequentially(loadingMessage) {
if (OJBetter.basic.standingsRecolor && OJBetter.typeOfPage.is_cfStandings) {
await recolorStandings(); // cf赛制榜单重新着色
}
if (OJBetter.preference.showLoading) loadingMessage.updateStatus(`${OJBetter.state.name} —— ${i18next.t('loadSuccess', { ns: 'alert' })}`, 'success', 3000);
if (OJBetter.preference.showLoading) {
loadingMessage.updateStatus(`${OJBetter.state.name} —— ${i18next.t('loadSuccess', { ns: 'alert' })}`, 'success', 3000);
}
}

/**
Expand Down

0 comments on commit 58972dd

Please sign in to comment.