Skip to content

Conversation

@david-roper
Copy link
Collaborator

@david-roper david-roper commented Oct 22, 2025

This pr add a group column to the subject download table containing the group id of the subject.

the group id is also removed from the subjectids in the datahub export as well.

closes issue #1005

Summary by CodeRabbit

  • New Features

    • Added a GroupID column to CSV and TSV export files.
  • Enhancements

    • Normalized SubjectID values in exported records for consistent formatting.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 22, 2025

Walkthrough

Introduces a helper function afterFirstDollar() to strip leading text up to and including the first dollar sign from subject IDs. Applies this transformation to export logic in both API service and web visualization hook. Adds GroupID column to exported records alongside the modified subject ID handling.

Changes

Cohort / File(s) Summary
Core implementation
apps/api/src/instrument-records/instrument-records.service.ts, apps/web/src/hooks/useInstrumentVisualization.ts
Adds afterFirstDollar() helper function to transform subject IDs by removing text up to and including the first dollar sign. Updates export row construction in both wide and long formats to include GroupID field and apply subject ID transformation.
Test updates
apps/web/src/hooks/__tests__/useInstrumentVisualization.test.ts
Updates CSV and TSV test expectations to include GroupID as the first column in headers and test data rows for both wide and long export formats.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Suggested reviewers

  • joshunrau

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "Add group column, remove group from subject id" directly and accurately reflects the main changes across all modified files. The first part is supported by the addition of GroupID fields in export row construction and updated test expectations in both the API and web hooks. The second part is supported by the introduction and application of the afterFirstDollar helper function that extracts the substring after the first dollar sign from subjectIds. The title is concise, specific, and clearly conveys the primary intent without vagueness.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 110b2ec and d7cf820.

📒 Files selected for processing (2)
  • apps/api/src/instrument-records/instrument-records.service.ts (3 hunks)
  • apps/web/src/hooks/useInstrumentVisualization.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/hooks/useInstrumentVisualization.ts
🔇 Additional comments (3)
apps/api/src/instrument-records/instrument-records.service.ts (3)

191-191: Verify behavior when subject ID lacks dollar sign.

The transformation applies correctly here, but confirm the intended behavior when record.subject.id contains no $ character—the function returns the ID unchanged. Ensure this aligns with the requirements from issue #1005.


214-214: Transformation applied consistently.

The afterFirstDollar transformation is correctly applied in both the non-array and array measure branches, maintaining consistency across the export logic.


41-47: Type safety concern in falsy check.

Line 42's guard if (!str) return str; addresses the past review's validation concern, but returning null/undefined when the return type is string breaks type safety. Since record.subject.id should always be a string, either remove the falsy check or return '' for falsy inputs.

Apply this diff if defensive null-handling is desired:

 function afterFirstDollar(str: string) {
-  if (!str) return str;
+  if (!str) return '';
   const match = /\$(.*)/.exec(str);
   if (!match) return str;
   if (!match[1]) return str;
   return match[1];
 }

Alternatively, verify that record.subject.id is always non-null and remove the check entirely.


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.

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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/hooks/useInstrumentVisualization.ts (1)

166-171: Apply consistent transformations to JSON export.

The JSON export uses raw params.subjectId without the afterFirstDollar transformation and doesn't include the GroupID field. This is inconsistent with CSV/TSV exports.

       case 'JSON': {
         exportRecords.map((item) => {
-          item.subjectID = params.subjectId;
+          item.GroupID = currentGroup ? currentGroup.id : 'root';
+          item.subjectID = afterFirstDollar(params.subjectId);
         });
         void download(`${baseFilename}.json`, () => Promise.resolve(JSON.stringify(exportRecords, null, 2)));
         break;
       }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 50e4dc7 and 110b2ec.

📒 Files selected for processing (3)
  • apps/api/src/instrument-records/instrument-records.service.ts (3 hunks)
  • apps/web/src/hooks/__tests__/useInstrumentVisualization.test.ts (4 hunks)
  • apps/web/src/hooks/useInstrumentVisualization.ts (4 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). (1)
  • GitHub Check: lint-and-test
🔇 Additional comments (5)
apps/api/src/instrument-records/instrument-records.service.ts (1)

190-190: LGTM!

The transformation is applied consistently to subject IDs in both export paths.

Also applies to: 213-213

apps/web/src/hooks/useInstrumentVisualization.ts (2)

77-94: LGTM!

Wide-format export correctly adds GroupID and applies the subject ID transformation.


96-136: LGTM!

Long-format export correctly includes GroupID and transformed SubjectID in both array and non-array paths.

apps/web/src/hooks/__tests__/useInstrumentVisualization.test.ts (2)

75-77: LGTM!

Test expectations correctly reflect the new GroupID column in CSV and TSV exports (both wide and long formats).

Also applies to: 90-92, 106-108, 122-124


138-139: Update test after fixing JSON export.

Once the JSON export in useInstrumentVisualization.ts is updated to include GroupID, this test should verify its presence.

After applying the suggested fix in apps/web/src/hooks/useInstrumentVisualization.ts (lines 166-171), update this assertion:

       const jsonContents = await getContentFn();
+      expect(jsonContents).toContain('"GroupID": "testGroupId"');
       expect(jsonContents).toContain('"someValue": "abc"');
       expect(jsonContents).toContain('"subjectID": "testId"');

Comment on lines 41 to 46
function afterFirstDollar(str: string) {
const match = /\$(.*)/.exec(str);
if (!match) return str;
if (!match[1]) return str;
return match[1];
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add input validation.

The function assumes str is a string but doesn't validate it. If called with null or undefined, the regex exec will throw.

 function afterFirstDollar(str: string) {
+  if (!str) return str;
   const match = /\$(.*)/.exec(str);
   if (!match) return str;
   if (!match[1]) return str;
   return match[1];
 }
🤖 Prompt for AI Agents
In apps/api/src/instrument-records/instrument-records.service.ts around lines 41
to 46, the helper assumes `str` is a string and will throw if
`null`/`undefined`; add input validation by first checking `if (str == null)
return str;` then ensure a safe string by doing `const s = typeof str ===
'string' ? str : String(str);` and run the regex against `s`, returning early if
no match or empty capture — this prevents runtime errors for null/undefined and
handles non-string inputs safely.

Comment on lines 39 to 44
function afterFirstDollar(str: string) {
const match = /\$(.*)/.exec(str);
if (!match) return str;
if (!match[1]) return str;
return match[1];
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Extract duplicated helper to shared utility.

This function is duplicated in apps/api/src/instrument-records/instrument-records.service.ts (lines 41-46). Extract it to a shared utility module to maintain a single source of truth.

Consider creating a shared utility file like packages/utils/src/subject-id.ts:

export function afterFirstDollar(str: string): string {
  if (!str) return str;
  const match = /\$(.*)/.exec(str);
  if (!match) return str;
  if (!match[1]) return str;
  return match[1];
}
🤖 Prompt for AI Agents
In apps/web/src/hooks/useInstrumentVisualization.ts around lines 39 to 44, the
helper function afterFirstDollar is duplicated in
apps/api/src/instrument-records/instrument-records.service.ts; extract it into a
shared utility (e.g., packages/utils/src/subject-id.ts) and import it from both
locations. Create the new utility file exporting afterFirstDollar with the same
behavior, update both files to remove the local function and import the shared
function, and run type checks to ensure imports/exports and any path aliases are
correct.

@joshunrau joshunrau merged commit caad09f into DouglasNeuroInformatics:main Oct 22, 2025
2 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Nov 14, 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