Skip to content

core&ui: contest can limit submission language#1007

Closed
bhscer wants to merge 10 commits into
hydro-dev:masterfrom
bhscer:contest-lang-limit
Closed

core&ui: contest can limit submission language#1007
bhscer wants to merge 10 commits into
hydro-dev:masterfrom
bhscer:contest-lang-limit

Conversation

@bhscer

@bhscer bhscer commented Jul 2, 2025

Copy link
Copy Markdown
Contributor

image

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Added the ability to restrict allowed submission languages for contests and homework. Organizers can now specify which languages participants may use.
    • Contest and homework editing pages now include a section to select allowed submission languages.
    • Contest and homework details display the selected language restrictions.
  • Localization

    • Added new Chinese translations for language selection features and related prompts.

@coderabbitai

coderabbitai Bot commented Jul 2, 2025

Copy link
Copy Markdown

Walkthrough

The changes introduce a new feature allowing contests and homework assignments to restrict the set of programming languages participants can use for submissions. A new optional property, limitLangs, representing allowed languages, is added to contest and homework data models, handlers, and interfaces. The backend handlers for contest and homework editing are updated to accept, store, and return this property as a comma-separated string. The problem handler now enforces these language restrictions when applicable. The user interface for contest and homework editing is updated to include a language selection input with autocomplete functionality. Relevant localization entries are added for new interface elements and labels. No changes are made to existing validation or error handling logic.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/ui-default/components/problemconfig/ProblemConfigForm.tsx

Oops! Something went wrong! :(

ESLint: 9.30.1

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@hydrooj/eslint-config' imported from /eslint.config.mjs
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:255:9)
at packageResolve (node:internal/modules/esm/resolve:767:81)
at moduleResolve (node:internal/modules/esm/resolve:853:18)
at defaultResolve (node:internal/modules/esm/resolve:983:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:801:12)
at #cachedDefaultResolve (node:internal/modules/esm/loader:725:25)
at ModuleLoader.resolve (node:internal/modules/esm/loader:708:38)
at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:309:38)
at #link (node:internal/modules/esm/module_job:202:49)

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
packages/hydrooj/src/interface.ts (1)

273-274: Consider using English for code comments to maintain consistency.

The Chinese comment should be translated to English to maintain consistency with the rest of the codebase. The type definition itself looks good for representing language restrictions.

-    // 允许使用的语言, undefined|null 则允许全部
+    // Allowed languages for submissions, undefined|null means all languages are allowed
     limitLangList?: string[] | null;
packages/ui-default/pages/contest_edit.page.ts (1)

43-51: Consider optimizing the change handler to avoid potential performance issues.

The current implementation triggers the change handler for each checkbox modification, which could be inefficient with many languages.

   $('.limitLangListItem').each(function () {
     $(this).removeAttr('disabled');
-  }).on('change', () => {
+  });
+  
+  function updateLimitLangList() {
     const selectedLang = [];
     $('.limitLangListItem:checked').each(function () {
       selectedLang.push($(this).val());
     });
     $('[name=limitLangList]').val(selectedLang.join(','));
-  }).trigger('change');
+  }
+  
+  $('.limitLangListItem').on('change', updateLimitLangList);
+  updateLimitLangList(); // Initial update
packages/ui-default/templates/contest_edit.html (1)

93-93: Address static analysis warnings about unescaped characters.

The HTMLHint warnings about unescaped characters in style attributes can be resolved by using proper Jinja2 escaping or moving styles to CSS classes.

Consider moving the inline styles to CSS classes or using Jinja2's |safe filter if the HTML is trusted:

-            <div class="medium-3 columns" id="limitLang_btnBox" {% if not isLimitLang %}style="display:none;"{% endif %}>
+            <div class="medium-3 columns" id="limitLang_btnBox" {% if not isLimitLang %}style="display:none;"|safe{% endif %}>

Or preferably, use CSS classes:

-            <div class="medium-3 columns" id="limitLang_btnBox" {% if not isLimitLang %}style="display:none;"{% endif %}>
+            <div class="medium-3 columns{% if not isLimitLang %} hidden{% endif %}" id="limitLang_btnBox">

Also applies to: 102-102

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 10f1517 and f21c6ea.

📒 Files selected for processing (7)
  • packages/hydrooj/src/handler/contest.ts (5 hunks)
  • packages/hydrooj/src/handler/problem.ts (3 hunks)
  • packages/hydrooj/src/interface.ts (1 hunks)
  • packages/ui-default/locales/zh.yaml (2 hunks)
  • packages/ui-default/pages/contest_edit.page.ts (1 hunks)
  • packages/ui-default/pages/problem_submit.page.tsx (1 hunks)
  • packages/ui-default/templates/contest_edit.html (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
packages/ui-default/pages/problem_submit.page.tsx (2)
packages/ui-default/components/scratchpad/ScratchpadToolbarContainer.jsx (1)
  • availableLangs (73-73)
packages/ui-default/utils/index.ts (1)
  • getAvailableLangs (11-22)
🪛 HTMLHint (1.5.0)
packages/ui-default/templates/contest_edit.html

[error] 93-93: Special characters must be escaped : [ < ].

(spec-char-escape)


[error] 93-93: Special characters must be escaped : [ > ].

(spec-char-escape)


[error] 102-102: Special characters must be escaped : [ < ].

(spec-char-escape)


[error] 102-102: Special characters must be escaped : [ > ].

(spec-char-escape)

🔇 Additional comments (10)
packages/ui-default/locales/zh.yaml (1)

527-527: LGTM! Localization entries are well formatted.

The Chinese translations for the language limitation feature are accurate and properly positioned within the file.

Also applies to: 825-826

packages/ui-default/pages/problem_submit.page.tsx (1)

14-14: Verified UiContext.submitableLangs Initialization

The backend handler in packages/hydrooj/src/handler/problem.ts correctly populates UiContext.submitableLangs by:

  • Initializing it from (this.pdoc.config as ProblemConfig).langs
  • Intersecting with this.tdoc.limitLangList when present
  • Assigning the result to this.UiContext.submitableLangs

No further changes are needed.

packages/ui-default/templates/contest_edit.html (1)

91-91: Disabled Attributes Properly Handled by JavaScript

The contest_edit.page.ts file includes multiple calls to remove the disabled attribute, ensuring form elements are enabled when needed:

  • $(.contest-rule--\${rule} input).removeAttr('disabled');
  • $('[name="permission"]').removeAttr('disabled').on('change', …)
  • $([data-perm="\${type}"] input).removeAttr('disabled');
  • $('[name=limitLang]').removeAttr('disabled').on('change', …)

No further action is required.

packages/hydrooj/src/handler/contest.ts (4)

293-307: LGTM! Language filtering logic is correct.

The logic properly handles the case where limitLangList exists by creating a Set for efficient lookup and filtering the language list accordingly. The fallback to include all languages when no limitation exists is also appropriate.


338-339: LGTM! Parameter additions are consistent.

The new limitLang and limitLangList parameters are properly defined with appropriate types and default values.

Also applies to: 345-345


386-386: LGTM! Conditional assignment handles the language limitation correctly.

The logic properly sets limitLangList to the provided array when limitLang is true, or null when language limiting is disabled.


284-290: Fix inconsistent handling of the language range data structure.

The code checks if setting.SETTINGS_BY_KEY.codeLang.range is an array, but then the else branch assumes it's an array and calls .map() on it. This creates inconsistent logic.

Apply this diff to fix the inconsistency:

-        if (!Array.isArray(setting.SETTINGS_BY_KEY.codeLang.range)) {
-            Object.keys(setting.SETTINGS_BY_KEY.codeLang.range).forEach((key) => {
-                langList.push([key, setting.SETTINGS_BY_KEY.codeLang.range[key], true]);
-            });
-        } else {
-            langList = setting.SETTINGS_BY_KEY.codeLang.range.map((el) => [...el, true]);
-        }
+        if (Array.isArray(setting.SETTINGS_BY_KEY.codeLang.range)) {
+            langList = setting.SETTINGS_BY_KEY.codeLang.range.map((el) => [...el, true]);
+        } else {
+            Object.keys(setting.SETTINGS_BY_KEY.codeLang.range).forEach((key) => {
+                langList.push([key, setting.SETTINGS_BY_KEY.codeLang.range[key], true]);
+            });
+        }

Likely an incorrect or invalid review comment.

packages/hydrooj/src/handler/problem.ts (3)

472-476: LGTM! Language intersection logic is correctly implemented.

The code properly filters the problem's configured languages by intersecting with the contest's limitLangList when it exists. Using Set.intersection ensures efficient filtering and maintains the correct language restrictions.


478-478: LGTM! UI context properly updated for frontend consistency.

Setting this.UiContext.submitableLangs ensures the frontend receives the filtered language list, maintaining consistency between backend restrictions and frontend options.


498-500: LGTM! Validation logic correctly enforces contest language restrictions.

The validation properly checks that both the contest document exists and has a limitLangList array before enforcing the restriction. The error thrown is appropriate for language validation failures.

Comment on lines +52 to +61
$('#limitLangBtn_selectAll').on('click', () => {
$('.limitLangListItem').each(function () {
$(this).attr('checked', 'true');
});
});
$('#limitLangBtn_selectNone').on('click', () => {
$('.limitLangListItem').each(function () {
$(this).removeAttr('checked');
});
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix checkbox manipulation to use proper jQuery methods.

The current approach to setting checkbox states is incorrect and may not work consistently across browsers.

   $('#limitLangBtn_selectAll').on('click', () => {
     $('.limitLangListItem').each(function () {
-      $(this).attr('checked', 'true');
+      $(this).prop('checked', true);
     });
+    $('.limitLangListItem').trigger('change');
   });
   $('#limitLangBtn_selectNone').on('click', () => {
     $('.limitLangListItem').each(function () {
-      $(this).removeAttr('checked');
+      $(this).prop('checked', false);
     });
+    $('.limitLangListItem').trigger('change');
   });
🤖 Prompt for AI Agents
In packages/ui-default/pages/contest_edit.page.ts around lines 52 to 61, the
code incorrectly uses attr and removeAttr to set checkbox states, which is
unreliable. Replace attr('checked', 'true') with the jQuery prop method
prop('checked', true) to check the boxes, and replace removeAttr('checked') with
prop('checked', false) to uncheck them, ensuring consistent behavior across
browsers.

</div>
<div id="limitLangListBox" {% if not isLimitLang %}style="display:none;"{% endif %}>
<div>
<p>对于有子类的语言,勾选时请详细至子分类!</p><br />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Replace hard-coded Chinese text with localized string.

The Chinese text should be replaced with a localized string to support internationalization.

-              <p>对于有子类的语言,勾选时请详细至子分类!</p><br />
+              <p>{{ _('For languages with subcategories, please select specific subcategories when checking!') }}</p><br />

You'll also need to add the corresponding translation to the localization files.

🤖 Prompt for AI Agents
In packages/ui-default/templates/contest_edit.html at line 104, replace the
hard-coded Chinese text inside the paragraph tag with a localized string
reference using the project's i18n method or syntax. Then, add the corresponding
translation for this string in the appropriate localization files to support
internationalization.

@undefined-moe

Copy link
Copy Markdown
Member

Language selection in scratchpad mode didn't get this limit; (Calc final language limits in handler/problem/ProblemDetailHandler)
Using autocomplete is preferred over checkboxes; (check problemconfig/ProblemConfigForm/LangConfig)
Please remove Chinese characters and commented codes in source files;
kebab-case is preferred for css classes.

@bhscer

bhscer commented Jul 2, 2025

Copy link
Copy Markdown
Contributor Author

Language selection in scratchpad mode didn't get this limit; (Calc final language limits in handler/problem/ProblemDetailHandler) Using autocomplete is preferred over checkboxes; (check problemconfig/ProblemConfigForm/LangConfig) Please remove Chinese characters and commented codes in source files; kebab-case is preferred for css classes.

OK I'll fix these problems soon. And what is "scratchpad mode" ?

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (3)
packages/ui-default/components/autocomplete/LanguageSelectAutoComplete.tsx (1)

22-43: Add proper TypeScript interfaces and improve constructor.

The class lacks proper TypeScript typing and the constructor could be more robust.

+interface LanguageSelectOptions {
+  multi?: boolean;
+  classes?: string;
+  [key: string]: any;
+}
+
-export default class LanguageSelectAutoComplete extends AutoComplete {
+export default class LanguageSelectAutoComplete extends AutoComplete {
   static DOMAttachKey = 'ucwLanguageSelectAutoCompleteInstance';
+  private ref: any;

-  constructor($dom, options) {
+  constructor($dom: any, options: LanguageSelectOptions = {}) {
     super($dom, {
       classes: 'language-select',
       ...options,
     });
   }

   attach() {
-    const value = this.$dom.val();
+    const value = this.$dom.val() || '';
     this.component.render(
       <Component
         ref={(ref) => { this.ref = ref; }}
         value={value}
-        multi={this.options.multi}
+        multi={this.options.multi ?? false}
         onChange={this.onChange}
       />,
     );
   }
 }
packages/ui-default/components/autocomplete/components/LanguageSelectAutoComplete.tsx (2)

28-28: Remove commented-out code.

The commented-out API call should be removed if it's not needed, or properly implemented if it serves a purpose.

     fetchItems={async (ids) => {
-      // api('problems', { ids: ids.map((i) => +i) }, ['docId', 'pid', 'title'])
       return processLanguages();
     }}

41-48: Improve CSS class naming consistency.

The CSS classes should follow kebab-case naming convention as mentioned in the PR feedback.

     renderItem={(pdoc) => (
       <div className="media">
         <div className="media__body medium">
-          <div className="language-select__name">{pdoc.name}</div>
-          <div className="language-select__id">{pdoc._id}</div>
+          <div className="language-select-name">{pdoc.name}</div>
+          <div className="language-select-id">{pdoc._id}</div>
         </div>
       </div>
     )}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f21c6ea and 51f7a66.

📒 Files selected for processing (6)
  • packages/hydrooj/src/handler/contest.ts (5 hunks)
  • packages/hydrooj/src/handler/problem.ts (2 hunks)
  • packages/ui-default/components/autocomplete/LanguageSelectAutoComplete.tsx (1 hunks)
  • packages/ui-default/components/autocomplete/components/LanguageSelectAutoComplete.tsx (1 hunks)
  • packages/ui-default/pages/contest_edit.page.ts (2 hunks)
  • packages/ui-default/templates/contest_edit.html (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/ui-default/pages/contest_edit.page.ts
  • packages/hydrooj/src/handler/problem.ts
  • packages/hydrooj/src/handler/contest.ts
🧰 Additional context used
🪛 HTMLHint (1.5.0)
packages/ui-default/templates/contest_edit.html

[error] 94-94: Special characters must be escaped : [ < ].

(spec-char-escape)


[error] 94-94: Special characters must be escaped : [ > ].

(spec-char-escape)

🔇 Additional comments (1)
packages/ui-default/templates/contest_edit.html (1)

94-94: Fix HTML template syntax error.

The conditional attribute syntax is invalid HTML and flagged by static analysis tools. Use proper Jinja2 conditional syntax for HTML attributes.

-          <div id="language-select-part" {% if not isLimitLang %}style="display:none;"{% endif %}>
+          <div id="language-select-part"{% if not isLimitLang %} style="display:none;"{% endif %}>

Likely an incorrect or invalid review comment.

Comment on lines +82 to +97
<div class="section__body">
<div class="row">
{{ form.form_checkbox({
columns:9,
label:'Limit language',
name:'limitLang',
placeholder:_('Limit contest submission language'),
value:isLimitLang,
row:false,
disabled:true
}) }}
</div>
<div id="language-select-part" {% if not isLimitLang %}style="display:none;"{% endif %}>
<input name="limitLangList" value="{{limitLangListString}}">
</div>
</div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve form accessibility and structure.

The language limitation section has several accessibility and usability issues:

  1. The hidden input field lacks proper labeling
  2. No clear indication this is a read-only display
  3. Missing form validation attributes
 <div class="section__body">
   <div class="row">
     {{ form.form_checkbox({
       columns:9,
       label:'Limit language',
       name:'limitLang',
       placeholder:_('Limit contest submission language'),
       value:isLimitLang,
       row:false,
-      disabled:true
+      disabled:true,
+      help_text:_('(Read-only)')
     }) }}
   </div>
   <div id="language-select-part"{% if not isLimitLang %} style="display:none;"{% endif %}>
-    <input name="limitLangList" value="{{limitLangListString}}">
+    <input type="hidden" name="limitLangList" value="{{limitLangListString}}" aria-label="Selected languages list">
+    {% if isLimitLang and limitLangListString %}
+      <div class="form-item">
+        <label class="form-item__label">{{ _('Selected Languages') }}</label>
+        <div class="form-item__body">
+          <span class="text-muted">{{ limitLangListString }}</span>
+        </div>
+      </div>
+    {% endif %}
   </div>
 </div>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div class="section__body">
<div class="row">
{{ form.form_checkbox({
columns:9,
label:'Limit language',
name:'limitLang',
placeholder:_('Limit contest submission language'),
value:isLimitLang,
row:false,
disabled:true
}) }}
</div>
<div id="language-select-part" {% if not isLimitLang %}style="display:none;"{% endif %}>
<input name="limitLangList" value="{{limitLangListString}}">
</div>
</div>
<div class="section__body">
<div class="row">
{{ form.form_checkbox({
columns:9,
label:'Limit language',
name:'limitLang',
placeholder:_('Limit contest submission language'),
value:isLimitLang,
row:false,
disabled:true,
help_text:_('(Read-only)')
}) }}
</div>
<div id="language-select-part"{% if not isLimitLang %} style="display:none;"{% endif %}>
<input type="hidden" name="limitLangList" value="{{limitLangListString}}" aria-label="Selected languages list">
{% if isLimitLang and limitLangListString %}
<div class="form-item">
<label class="form-item__label">{{ _('Selected Languages') }}</label>
<div class="form-item__body">
<span class="text-muted">{{ limitLangListString }}</span>
</div>
</div>
{% endif %}
</div>
</div>
🧰 Tools
🪛 HTMLHint (1.5.0)

[error] 94-94: Special characters must be escaped : [ < ].

(spec-char-escape)


[error] 94-94: Special characters must be escaped : [ > ].

(spec-char-escape)

🤖 Prompt for AI Agents
In packages/ui-default/templates/contest_edit.html around lines 82 to 97,
improve accessibility and usability of the language limitation section by adding
a proper label linked to the hidden input field, indicating that the field is
read-only or disabled, and including appropriate form validation attributes such
as required or aria attributes to clarify its state. Ensure the input is clearly
associated with its label and that users understand it is not editable.

Comment on lines +5 to +20
const Component = React.forwardRef<any, any>((props, ref) => {
const [value, setValue] = React.useState(props.value ?? '');
return (
<LanguageSelectAutoCompleteFC
ref={ref as any}
height="auto"
selectedKeys={value.split(',').map((i) => i.trim()).filter((i) => i)}
onChange={(v) => {
setValue(v);
props.onChange(v);
}}
multi={props.multi}
allowEmptyQuery={true}
/>
);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve value parsing robustness and error handling.

The current value splitting logic may fail with malformed input and lacks error handling for the onChange callback.

 const Component = React.forwardRef<any, any>((props, ref) => {
-  const [value, setValue] = React.useState(props.value ?? '');
+  const [value, setValue] = React.useState(() => {
+    const initialValue = props.value ?? '';
+    return typeof initialValue === 'string' ? initialValue : String(initialValue);
+  });
+  
+  const parseValue = (val: string) => {
+    if (!val || typeof val !== 'string') return [];
+    return val.split(',').map((i) => i.trim()).filter((i) => i);
+  };
+  
   return (
     <LanguageSelectAutoCompleteFC
       ref={ref as any}
       height="auto"
-      selectedKeys={value.split(',').map((i) => i.trim()).filter((i) => i)}
+      selectedKeys={parseValue(value)}
       onChange={(v) => {
-        setValue(v);
-        props.onChange(v);
+        try {
+          setValue(v);
+          props.onChange?.(v);
+        } catch (error) {
+          console.error('Error in LanguageSelectAutoComplete onChange:', error);
+        }
       }}
       multi={props.multi}
       allowEmptyQuery={true}
     />
   );
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const Component = React.forwardRef<any, any>((props, ref) => {
const [value, setValue] = React.useState(props.value ?? '');
return (
<LanguageSelectAutoCompleteFC
ref={ref as any}
height="auto"
selectedKeys={value.split(',').map((i) => i.trim()).filter((i) => i)}
onChange={(v) => {
setValue(v);
props.onChange(v);
}}
multi={props.multi}
allowEmptyQuery={true}
/>
);
});
const Component = React.forwardRef<any, any>((props, ref) => {
const [value, setValue] = React.useState(() => {
const initialValue = props.value ?? '';
return typeof initialValue === 'string' ? initialValue : String(initialValue);
});
const parseValue = (val: string) => {
if (!val || typeof val !== 'string') return [];
return val.split(',').map((i) => i.trim()).filter((i) => i);
};
return (
<LanguageSelectAutoCompleteFC
ref={ref as any}
height="auto"
selectedKeys={parseValue(value)}
onChange={(v) => {
try {
setValue(v);
props.onChange?.(v);
} catch (error) {
console.error('Error in LanguageSelectAutoComplete onChange:', error);
}
}}
multi={props.multi}
allowEmptyQuery={true}
/>
);
});
🤖 Prompt for AI Agents
In packages/ui-default/components/autocomplete/LanguageSelectAutoComplete.tsx
around lines 5 to 20, improve the robustness of the value parsing by safely
handling cases where props.value might be undefined or not a string, and ensure
the splitting and trimming logic gracefully handles malformed input.
Additionally, wrap the props.onChange call in a try-catch block to handle any
potential errors during the callback execution without breaking the component.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 102bbac and eff7801.

📒 Files selected for processing (4)
  • packages/hydrooj/src/handler/contest.ts (3 hunks)
  • packages/hydrooj/src/handler/homework.ts (4 hunks)
  • packages/hydrooj/src/model/contest.ts (3 hunks)
  • packages/ui-default/templates/homework_edit.html (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/hydrooj/src/handler/contest.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
packages/hydrooj/src/model/contest.ts (1)
packages/hydrooj/src/interface.ts (1)
  • Tdoc (250-289)
🪛 HTMLHint (1.5.0)
packages/ui-default/templates/homework_edit.html

[error] 104-104: Special characters must be escaped : [ < ].

(spec-char-escape)


[error] 104-104: Special characters must be escaped : [ > ].

(spec-char-escape)

🔇 Additional comments (6)
packages/hydrooj/src/handler/homework.ts (3)

167-167: LGTM! Clean integration with contest model.

The call to contest.getLimitLanguageConfig(tdoc) properly handles the case where tdoc might be null for new homework creation.


197-198: LGTM! Proper parameter definition and validation.

The new parameters limitLang and limitLangList are correctly typed and have appropriate default values. The parameter ordering is consistent with the existing pattern.

Also applies to: 204-204


235-235: LGTM! Correct conditional logic for language list storage.

The conditional assignment limitLangList: limitLang ? limitLangList : null ensures that the language restriction list is only saved when the feature is enabled, which prevents data inconsistency.

packages/ui-default/templates/homework_edit.html (1)

93-106: LGTM! Template implementation is consistent with contest edit.

The language limitation UI follows the same pattern as the contest edit template. The disabled checkbox appropriately reflects the read-only nature, and the conditional display logic for the hidden input is correct.

Note: The HTMLHint warnings about unescaped characters are false positives - this is valid Jinja2 template syntax, not HTML content that requires escaping.

packages/hydrooj/src/model/contest.ts (2)

19-19: LGTM! Necessary import for language settings access.

The import of the setting module is required for accessing the SETTINGS_BY_KEY.codeLang.range configuration.


1107-1107: LGTM! Proper export of the new function.

The getLimitLanguageConfig function is correctly added to the global export object, making it available to other modules.

Comment thread packages/hydrooj/src/model/contest.ts Outdated
Comment thread packages/hydrooj/src/handler/contest.ts Outdated
let ts = Date.now();
ts = ts - (ts % (15 * Time.minute)) + 15 * Time.minute;
const beginAt = moment(this.tdoc?.beginAt || new Date(ts)).tz(this.user.timeZone);
const { isLimitLang, limitLangListString } = contest.getLimitLanguageConfig(this?.tdoc || null);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check limits from pdoc.config.langs and domain.langs -- it shouldn't be such complicated to implement.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found a bug:
Unless you set domain's language limit in '/domain/edit', domain.langs is undefined.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a easier idea. If the limit list is empty, it also means do no limit. In that way the code will be very simple.

Comment thread packages/hydrooj/src/handler/problem.ts Outdated
}
this.pdoc.config.langs = ['objective', 'submit_answer'].includes(this.pdoc.config.type) ? ['_'] : intersection(baseLangs, ...t);
// apply contest language limits
if (this?.tdoc && Array.isArray(this.tdoc?.limitLangList) && this.tdoc?.limitLangList.length > 0) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: after this filter, objective or submit_answer problem will have empty langList.
Will be fixed in next commit.

@bhscer

bhscer commented Jul 4, 2025

Copy link
Copy Markdown
Contributor Author

I rewrite the code. Now no new components is needed, and the variable name has been shorten.

@bhscer

bhscer commented Jul 5, 2025

Copy link
Copy Markdown
Contributor Author

I find a bug:
Currently languageSelect in problemConfig/domainEdit page will select language's prefix when their sublanguage is selected.
For example, select 'cc.cc11' actually selected 'cc.cc11,cc'.
That means when limit 'cc.cc14' in problem and limit 'cc.cc20' in domain, the language 'cc' can still be submit (although the language option only display in legacy mode).
Or is this just a feature? @undefined-moe

@undefined-moe

Copy link
Copy Markdown
Member

The current expected behavior is: when user selects cc.cc11, then both cc and cc.cc11 are automatically selected, but cc should not be a valid submission language. This behavior may subject to change.

@bhscer

bhscer commented Jul 8, 2025

Copy link
Copy Markdown
Contributor Author

So that means cc is a hidden language which exists but not expected to be submitted.
My code draws on the logic of domain language config, so prefixes can be automatically added.

@bhscer

bhscer commented Jul 9, 2025

Copy link
Copy Markdown
Contributor Author

Also fixed: #939

This bug happens if:

  1. current system has c.c99,c
  2. config problem to use c.c99 (c.c99,c)
  3. remove c.c99 in system, don't remove c
  4. open the problem config page and the dead loop happened

Or more easily you can try to directly edit the config.yml add a unexisted language which system think it has a prefix, for example hydro. .

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/ui-default/components/problemconfig/ProblemConfigForm.tsx (1)

86-86: Consider refactoring this complex line for better readability.

The logic is functionally correct and properly filters prefixes to include only those in the prefixes set. However, the line is quite complex and could benefit from being split into multiple steps for better maintainability.

Consider this refactor for improved readability:

-                value.push(...Array.from(new Set(value.filter((i) => i.includes('.')).map((i) => i.split('.')[0]).filter((i) => prefixes.has(i)))));
+                const subLanguages = value.filter((i) => i.includes('.'));
+                const parentPrefixes = subLanguages.map((i) => i.split('.')[0]);
+                const validPrefixes = parentPrefixes.filter((i) => prefixes.has(i));
+                const uniquePrefixes = Array.from(new Set(validPrefixes));
+                value.push(...uniquePrefixes);

This maintains the same functionality while making the logic more readable and easier to debug.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb011cc and b461eb1.

📒 Files selected for processing (1)
  • packages/ui-default/components/problemconfig/ProblemConfigForm.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Analyze (javascript)
  • GitHub Check: build

@bhscer

bhscer commented Jul 15, 2025

Copy link
Copy Markdown
Contributor Author

A better PR is here: #1014

@bhscer bhscer closed this Jul 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants