Skip to content

feat: Add crud for getMergedTasksMap (#2748)#2749

Merged
KATO-Hiro merged 3 commits intostagingfrom
#2748
Oct 25, 2025
Merged

feat: Add crud for getMergedTasksMap (#2748)#2749
KATO-Hiro merged 3 commits intostagingfrom
#2748

Conversation

@KATO-Hiro
Copy link
Collaborator

@KATO-Hiro KATO-Hiro commented Oct 25, 2025

close #2748

Summary by CodeRabbit

  • New Features

    • Added merged task mapping to consolidate tasks per contest-task pairing.
    • Task records now include contest-specific metadata (contest type, contest id, table index) and updated titles.
    • Added a validated utility to generate contest-task pair identifiers.
  • Documentation

    • New developer guidance on refactoring, functional patterns, non-destructive updates, complexity, and readability.
  • Tests

    • Comprehensive tests for contest-task pair key generation covering normal, edge, and anomaly cases.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 25, 2025

Walkthrough

Adds a new exported async service getMergedTasksMap to merge base tasks with contest-task pairs into a Map keyed by contest:task pairs; introduces a key utility, type updates for task/contest metadata, extensive tests for the key helper, and a developer lesson doc for the refactor.

Changes

Cohort / File(s) Summary
Documentation
docs/dev-notes/2025-10-25/refactor-getMergedTasksMap/lesson.md
New lesson documenting the getMergedTasksMap refactor, copy vs reference semantics, TypeScript idioms, map/flatMap guidance, complexity analysis (O(N+M)), testing patterns, and documentation recommendations.
Service Function
src/lib/services/tasks.ts
Added export async function getMergedTasksMap(): Promise<TaskMapByContestTaskPair> and internal createMergedTask() to produce a Map of Tasks keyed by contest:task pair by merging tasks and contest-task pairs, with classification and validation logic.
Type Definitions
src/lib/types/task.ts, src/lib/types/contest_task_pair.ts
Task gains optional contest_type?: ContestType; added TaskMapByContestTaskPair = Map<ContestTaskPairKey, Task> and imported Task where needed.
Utility Function
src/lib/utils/contest_task_pair.ts
New createContestTaskPairKey(contestId: string, taskId: string): ContestTaskPairKey that trims and validates inputs and returns a ${contestId}:${taskId} key.
Tests
src/test/lib/utils/contest_task_pair.test.ts
Extensive test suite for createContestTaskPairKey, covering normal, edge, anomaly cases, parsing/format validation, uniqueness, and error conditions.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant Service as getMergedTasksMap
    participant Tasks as getTasks
    participant Pairs as getContestTaskPairs
    participant KeyUtil as createContestTaskPairKey
    participant Merger as createMergedTask

    Caller->>Service: call()
    Service->>Tasks: fetch base tasks
    Tasks-->>Service: tasks[]
    Service->>Pairs: fetch contest-task pairs
    Pairs-->>Service: pairs[]

    rect #e6f7ff
    Note over Service,KeyUtil: Build base map keyed by `contestId:taskId` using KeyUtil
    end

    rect #fff7e6
    Note over Service,Merger: For each pair not present: classify contest, validate fields,\nand call Merger to produce merged Task
    Merger-->>Service: [key, mergedTask]
    end

    Service-->>Caller: TaskMapByContestTaskPair
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review merging logic and skip/validation conditions in src/lib/services/tasks.ts
  • Verify contest_type typing and imports in src/lib/types/task.ts
  • Confirm trimming, validation, and error messages in src/lib/utils/contest_task_pair.ts
  • Inspect test coverage and edge-case assertions in src/test/lib/utils/contest_task_pair.test.ts
  • Validate documentation for accuracy in docs/dev-notes/.../lesson.md

Poem

🐰 I hopped through keys of colon and light,
Merged tasks for contests from morning to night.
I trimmed all the edges and kept things polite,
A rabbit's small cheer for the code now polite. 🥕

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "feat: Add crud for getMergedTasksMap (#2748)" clearly references the primary deliverable of this changeset—the new getMergedTasksMap function. However, the term "crud" is somewhat misleading since the PR primarily adds read/retrieval functionality rather than full Create, Read, Update, Delete operations. Despite this terminology concern, the title does accurately identify the main addition to the codebase and communicates that a new feature is being introduced for this function.
Linked Issues Check ✅ Passed The linked issue #2748 requests a feature to retrieve a map of problems that share the same problem ID but are presented in different contests. The PR implements this requirement through the new getMergedTasksMap function that returns a TaskMapByContestTaskPair mapping contest-task pairs to Task objects, directly fulfilling the stated objective. Supporting changes include the createContestTaskPairKey utility function, new types, and comprehensive test coverage, all of which enable the core feature.
Out of Scope Changes Check ✅ Passed All code changes in this PR are directly scoped to implementing the feature requested in issue #2748. The additions include the core getMergedTasksMap function, supporting helper createMergedTask, new type definitions (TaskMapByContestTaskPair and extending the Task interface with contest_type), the utility function createContestTaskPairKey, comprehensive test coverage, and documentation. Each change serves the primary objective of enabling retrieval of tasks mapped by contest-task pairs.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch #2748

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

Comment @coderabbitai help to get the list of available commands and usage tips.

@KATO-Hiro KATO-Hiro changed the title feat: Add crud fro getMergedTasksMap (#2748) feat: Add crud for getMergedTasksMap (#2748) Oct 25, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

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)
src/lib/utils/contest_task_pair.ts (1)

14-23: LGTM with optional simplification suggestion.

The function correctly validates inputs and constructs the key. The validation logic is sound but could be slightly simplified.

Optional: Simplify validation logic

The condition !contestId || contestId.trim() === '' is correct but slightly redundant. Since ''.trim() === '' is true, you could simplify to just:

-  if (!contestId || contestId.trim() === '') {
+  if (contestId.trim() === '') {
     throw new Error('contestId must be a non-empty string');
   }
-  if (!taskId || taskId.trim() === '') {
+  if (taskId.trim() === '') {
     throw new Error('taskId must be a non-empty string');
   }

The current code is perfectly functional—this is purely a style suggestion.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 88d4913 and 99bbc25.

📒 Files selected for processing (5)
  • docs/dev-notes/2025-10-25/refactor-getMergedTasksMap/lesson.md (1 hunks)
  • src/lib/services/tasks.ts (2 hunks)
  • src/lib/types/contest_task_pair.ts (2 hunks)
  • src/lib/types/task.ts (1 hunks)
  • src/lib/utils/contest_task_pair.ts (1 hunks)
🔇 Additional comments (8)
docs/dev-notes/2025-10-25/refactor-getMergedTasksMap/lesson.md (1)

1-61: Excellent documentation of refactoring lessons.

This documentation provides valuable context and best practices for the implementation. The lessons on reference vs. copy, functional programming patterns, and complexity analysis are well-explained and will be helpful for future maintainers.

src/lib/types/task.ts (2)

3-3: LGTM: Type-only import is appropriate.

The use of import type is correct for the ContestType type used in the Task interface.


6-6: LGTM: Optional field maintains backward compatibility.

The optional contest_type field extends the Task interface without breaking existing code, properly supporting the new merged task functionality.

src/lib/types/contest_task_pair.ts (2)

3-3: LGTM: Import update for new type.

Adding Task to the imports is necessary for the new TaskMapByContestTaskPair type definition.


23-23: LGTM: Consistent type definition.

The new TaskMapByContestTaskPair type follows the existing pattern and naming convention used by TaskResultMapByContestTaskPair.

src/lib/services/tasks.ts (3)

3-14: LGTM: Imports are complete and properly organized.

All new imports are necessary for the getMergedTasksMap functionality and follow proper TypeScript conventions.


40-65: LGTM: Well-implemented merge logic.

The function correctly merges tasks with contest-task pairs, building a comprehensive map. The documentation is thorough, including complexity analysis and usage examples. The logic appropriately filters pairs not in the base map and handles missing data gracefully.


77-93: Verify title replacement behavior.

The implementation correctly merges task data with contest-specific information. However, the title replacement on Line 89 has a potential edge case:

title: task.title.replace(task.task_table_index, pair.taskTableIndex),

String.replace() only replaces the first occurrence. If task.task_table_index is a common substring or appears in an unexpected position, it might replace the wrong text.

Example edge case:

  • If task.task_table_index = "A" and title = "Amazing Algorithm A Problem"
  • Result would be: "Bmazing Algorithm A Problem" (if pair.taskTableIndex = "B")

Given the domain context, task titles likely follow a predictable format (e.g., "A - Problem Name"), so this is probably safe. However, consider whether this assumption holds for all cases in your data.

Would you like me to generate a script to verify the title format across your existing tasks?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/test/lib/utils/contest_task_pair.test.ts (3)

58-62: Optional: beforeEach creates unnecessary copies.

The test data is declared as const and contains only primitive values, making it immutable. Since the tests never mutate these arrays, the beforeEach hook creates unnecessary copies on every test run.

Consider removing the beforeEach hook and using the const arrays directly:

-  let testNormalPairs: TestPairs;
-  let testEdgePairs: TestPairs;
-  let testAnomalyPairs: TestPairs;
-
-  beforeEach(() => {
-    testNormalPairs = [...pairs.normal];
-    testEdgePairs = [...pairs.edge];
-    testAnomalyPairs = [...pairs.anomaly];
-  });

Then update references to use pairs.normal, pairs.edge, and pairs.anomaly directly, or define them as constants at module level if preferred.


144-149: Strengthen the assertion for consistency.

This test uses toContain to check if the key includes the taskId, which is weaker than the exact format validation used in other base case tests. For consistency with tests at lines 104, 133, and 158, use an exact match.

Apply this diff:

     test('expects to work with numeric task identifiers', () => {
       const pair = testNormalPairs[5]; // typical90_a
       const key = createTestKey(pair);
 
-      expect(key).toContain(pair.taskId);
+      expect(key).toBe(getExpectedKey(pair.contestId, pair.taskId));
     });

284-293: Optional: Consider a more efficient uniqueness check.

The nested loop has O(n²) complexity. While this works fine with the current test data size (11 pairs), the uniqueness test at lines 277-282 already verifies this property more efficiently using a Set.

Consider removing this test as redundant, or simplify it:

-    test('expects keys from different pairs to be different', () => {
-      const selectedPairs = testNormalPairs.slice(0, 3);
-      const keys = selectedPairs.map((pair) => createTestKey(pair));
-
-      for (let i = 0; i < keys.length; i++) {
-        for (let j = i + 1; j < keys.length; j++) {
-          expect(keys[i]).not.toBe(keys[j]);
-        }
-      }
-    });

The test at lines 277-282 already verifies that all keys are unique, making this pairwise check redundant.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 99bbc25 and c611499.

📒 Files selected for processing (2)
  • docs/dev-notes/2025-10-25/refactor-getMergedTasksMap/lesson.md (1 hunks)
  • src/test/lib/utils/contest_task_pair.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/dev-notes/2025-10-25/refactor-getMergedTasksMap/lesson.md
⏰ 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). (1)
  • GitHub Check: preview
🔇 Additional comments (2)
src/test/lib/utils/contest_task_pair.test.ts (2)

162-245: Excellent coverage of edge and anomaly cases!

The edge case tests thoroughly validate error handling for empty/whitespace inputs with clear error messages. The anomaly tests comprehensively cover special characters, unicode, very long IDs, and explicitly test delimiter-like characters (pipes) to ensure they're preserved correctly in the key format.


248-257: Verify parsing behavior with colon-containing IDs.

This test parses keys using split(':') and assumes exactly 2 components. This approach works with the current test data but will produce ambiguous results if contestId or taskId contains a colon character.

This concern is related to the missing colon test cases flagged at lines 41-51. Once colon-containing test data is added, verify that:

  1. Either the implementation rejects colons (validation error), OR
  2. The parsing strategy handles embedded colons correctly (e.g., using split(':', 2) or escaping)

If colons are valid in IDs, consider using a more robust parsing approach:

// Example: split only on the first colon
const [parsedContestId, ...rest] = key.split(':');
const parsedTaskId = rest.join(':');

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

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

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between c611499 and ae6fa67.

📒 Files selected for processing (2)
  • docs/dev-notes/2025-10-25/refactor-getMergedTasksMap/lesson.md (1 hunks)
  • src/test/lib/utils/contest_task_pair.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/test/lib/utils/contest_task_pair.test.ts
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
docs/dev-notes/2025-10-25/refactor-getMergedTasksMap/lesson.md

164-164: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

Copy link
Collaborator Author

@KATO-Hiro KATO-Hiro left a comment

Choose a reason for hiding this comment

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

LGTM

@KATO-Hiro KATO-Hiro merged commit db9aec3 into staging Oct 25, 2025
3 checks passed
@KATO-Hiro KATO-Hiro deleted the #2748 branch October 25, 2025 12:43
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.

[Feature] 同一問題id、かつ、異なるコンテストで出題されている問題をMapとして取得できるようにしましょう

1 participant

Comments