Skip to content

fixed bugs and clean up#47

Merged
daichengxin merged 3 commits into
bigbio:mainfrom
daichengxin:main
Jun 13, 2026
Merged

fixed bugs and clean up#47
daichengxin merged 3 commits into
bigbio:mainfrom
daichengxin:main

Conversation

@daichengxin

@daichengxin daichengxin commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Loads spectra up front and preserves key spectrum/scan metadata per identification for more reliable output.
  • Bug Fixes

    • Improved error handling for missing PSMs or spectra (process exits on empty inputs).
    • Stronger fallback for base sequence derivation when parsing fails.
    • Corrected total identifications to reflect actual PSM count.
  • Refactor

    • Input loading and scoring flow simplified for cleaner processing.

@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The CLI now loads spectra and constructs/scores PSMs inside load_input_files(config), stashing mz/rt/scan and reference metadata on PSMs. run() calls the refactored loader (exits on no PSMs/spectra). Output formatting reads stashed metadata and uses a regex-based fallback for base sequences; sequence conversion preserves non-target mods.

Changes

PSM Early Construction and Output Integration

Layer / File(s) Summary
Load infra & regex import
onsite/lucxor/cli.py
Add re import; load_input_files now accepts config, loads spectra up-front, builds a scan-number lookup, and initializes PEP→(1-PEP) scoring context.
Per-row PSM construction and metadata stashing
onsite/lucxor/cli.py
Iterate identification rows to populate hit meta, create Peptide and PSM from spectrum peaks and config, compute/assign normalized scores, and stash mz/rt/scan and spectrum_reference/reference_file_name on private PSM attributes.
get_psm_score signature update
onsite/lucxor/cli.py
Remove unused pep_id parameter; get_psm_score extracts and normalizes the requested score from the hit object only.
run() integration and early-exit
onsite/lucxor/cli.py
run() calls load_input_files(..., config) and returns 1 when all_psms is empty or spectra (exp) are missing; previous in-run PSM reconstruction removed.
Output generation and sequence fallback
onsite/lucxor/cli.py
Output rows read stashed PSM metadata (mz, rt, scan_num, spectrum_reference, reference_file_name). Base-sequence derivation strips parenthesized modification annotations via regex and uppercases when AASequence.fromString fails. Run summary uses len(all_psms).
Non-target modification conversion
onsite/lucxor/psm.py
convert_sequence_to_standard_format docs updated; conversion extended to map other lowercase mod markers using self.non_target_mods, emitting X(<mod_name>) or falling back to the uppercase residue.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • bigbio/onsite#38: Overlapping refactor and score-type handling work in onsite/lucxor/cli.py.

Suggested reviewers

  • ypriverol

Poem

"I built PSMs at dawn, with spectra in tow,
Stashed tiny secrets where outputs now go.
Regex trims the trimmings, mods bow in line,
A rabbit hops proudly — the results look fine! 🐇"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'fixed bugs and clean up' is vague and generic, using non-descriptive terms that don't convey meaningful information about the specific changes made to the codebase. Replace with a specific title that describes the main change, such as 'Refactor PSM loading and scoring logic to handle input configuration' or 'Update score calculation and sequence formatting for PSM handling'.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@codacy-production

codacy-production Bot commented Jun 12, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics -10 complexity · 0 duplication

Metric Results
Complexity -10
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
onsite/lucxor/cli.py (1)

424-479: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Carry the source row identity instead of rejoining by pep_idx.

all_psms is built from every psms_df row, but the output path later indexes into self._psms_df_template[self._psms_df_template["hit_index"] == 0] using pep_idx. As soon as the input contains hit_index != 0 rows, this reattaches the wrong psm_metavalues and emits synthetic peptide_identification_index values that no longer match the source identification. Stash the original row/index metadata on the PSM during construction and reuse that directly when writing results.

Also applies to: 797-845

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@onsite/lucxor/cli.py` around lines 424 - 479, The loop building all_psms
currently loses the original psms_df row identity and later reindexes by pep_idx
causing mismatched psm_metavalues; when constructing each PSM (in the block
creating `psm`), attach the source row identifier and/or the DataFrame index
(e.g., set psm._source_row_index = scan_num or psm._source_df_index = row.name
and/or stash the entire row via psm._source_row) so the writer can look up
metadata directly from the original row instead of rejoining by `pep_idx`; apply
the same change in the other similar block mentioned (lines ~797-845) and update
downstream writer logic to use these new psm._source_* fields when building
psm_metavalues and peptide_identification_index.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@onsite/lucxor/cli.py`:
- Around line 418-420: The code is forcing PEP scoring by overwriting
self.score_type and self.higher_score_better in load_input_files() and seeding
every PeptideHit from posterior_error_probability; revert/remove the hard-coded
assignments to self.score_type/self.higher_score_better and instead read the
dataset's declared score_type/higher_score_better per input (use the row's
score_type and additional_scores fields) and seed each PeptideHit with the
appropriate score field (use posterior_error_probability only when score_type ==
"Posterior Error Probability", otherwise use the primary score or the matching
key in additional_scores); update load_input_files() and any code that
constructs PeptideHit to respect per-row score_type/additional_scores so run()'s
E-value/raw-score branches remain reachable.

---

Outside diff comments:
In `@onsite/lucxor/cli.py`:
- Around line 424-479: The loop building all_psms currently loses the original
psms_df row identity and later reindexes by pep_idx causing mismatched
psm_metavalues; when constructing each PSM (in the block creating `psm`), attach
the source row identifier and/or the DataFrame index (e.g., set
psm._source_row_index = scan_num or psm._source_df_index = row.name and/or stash
the entire row via psm._source_row) so the writer can look up metadata directly
from the original row instead of rejoining by `pep_idx`; apply the same change
in the other similar block mentioned (lines ~797-845) and update downstream
writer logic to use these new psm._source_* fields when building psm_metavalues
and peptide_identification_index.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 72eed42c-ec73-4191-ae7b-f26979166fb6

📥 Commits

Reviewing files that changed from the base of the PR and between 1fb2a7d and 61278bb.

📒 Files selected for processing (1)
  • onsite/lucxor/cli.py

Comment thread onsite/lucxor/cli.py
Comment on lines +418 to +420
# PEP converted 1-PEP — set early so get_psm_score uses it inline
self.score_type = "Posterior Error Probability"
self.higher_score_better = True

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't force every dataset onto PEP scoring.

load_input_files() now hard-codes self.score_type/self.higher_score_better to PEP and seeds every PeptideHit from posterior_error_probability, even though each row still carries score_type and additional_scores. That makes the E-value/raw-score branches in run() unreachable and can train on the wrong PSM subset whenever the input's primary score is something like expect, SpecEValue, or xcorr.

Also applies to: 432-433, 466-470

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@onsite/lucxor/cli.py` around lines 418 - 420, The code is forcing PEP scoring
by overwriting self.score_type and self.higher_score_better in
load_input_files() and seeding every PeptideHit from
posterior_error_probability; revert/remove the hard-coded assignments to
self.score_type/self.higher_score_better and instead read the dataset's declared
score_type/higher_score_better per input (use the row's score_type and
additional_scores fields) and seed each PeptideHit with the appropriate score
field (use posterior_error_probability only when score_type == "Posterior Error
Probability", otherwise use the primary score or the matching key in
additional_scores); update load_input_files() and any code that constructs
PeptideHit to respect per-row score_type/additional_scores so run()'s
E-value/raw-score branches remain reachable.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
onsite/lucxor/psm.py (1)

1673-1699: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Non-target mod mapping is still incorrect for several lowercase residues.

At Line 1694, lookup uses raw index i, but self.non_target_mods is residue-position based. Also, Lines 1674-1689 consume s/t/y/m/w/f/y before checking self.non_target_mods, so non-target mods on those residues are serialized as Phospho/Oxidation instead of their true mod names.

Suggested fix
         try:
             if not sequence:
                 return sequence
 
             result = ""
             i = 0
+            residue_pos = 0
             while i < len(sequence):
-                if sequence[i].islower() and sequence[i].upper() in ["S", "T", "Y"]:
-                    # Convert lowercase to uppercase and add (Phospho)
-                    result += sequence[i].upper() + "(Phospho)"
-                elif sequence[i] == "a":
+                ch = sequence[i]
+
+                if ch in "[]()":
+                    result += ch
+                    i += 1
+                    continue
+
+                if ch == "a":
                     # Lowercase 'a' = PhosphoDecoy on Alanine; emit the standard
                     # annotation so an A-win is serializable and countable
                     # downstream (see bigbio/onsite#40).
                     result += "A(PhosphoDecoy)"
-                elif sequence[i].islower() and sequence[i].upper() in [
-                    "M",
-                    "W",
-                    "F",
-                    "Y",
-                ]:
-                    # Convert lowercase to uppercase and add (Oxidation)
-                    result += sequence[i].upper() + "(Oxidation)"
-                elif sequence[i].islower():
+                    residue_pos += 1
+                elif ch.islower():
                     # Non-target modification (e.g. Carbamidomethyl on C) —
                     # look up the modification name from self.non_target_mods.
-                    aa_upper = sequence[i].upper()
-                    mod_name = self.non_target_mods.get(i)
+                    aa_upper = ch.upper()
+                    mod_name = self.non_target_mods.get(residue_pos)
                     if mod_name:
                         result += f"{aa_upper}({mod_name})"
+                    elif aa_upper in ["S", "T", "Y"]:
+                        result += aa_upper + "(Phospho)"
+                    elif aa_upper in ["M", "W", "F", "Y"]:
+                        result += aa_upper + "(Oxidation)"
                     else:
                         # Fallback: emit the bare upper-case letter.
                         result += aa_upper
+                    residue_pos += 1
                 else:
-                    result += sequence[i]
+                    result += ch
+                    if ch.isalpha():
+                        residue_pos += 1
                 i += 1
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@onsite/lucxor/psm.py` around lines 1673 - 1699, The code currently checks
residue-type branches (Phospho/Oxidation) before consulting self.non_target_mods
and also looks up non-target mods with the raw loop index i, causing wrong
serialization; fix by computing the residue-position key (e.g., residue_pos =
count of alphabetic residues up to i) and consult self.non_target_mods at the
top of the loop: if a mod_name exists for that residue_pos, append
aa_upper+"("+mod_name+")" to result and skip other branches; otherwise continue
with the existing Phospho/Oxidation/PhosphoDecoy/fallback logic using sequence,
i, result, and aa_upper as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@onsite/lucxor/psm.py`:
- Around line 1673-1699: The code currently checks residue-type branches
(Phospho/Oxidation) before consulting self.non_target_mods and also looks up
non-target mods with the raw loop index i, causing wrong serialization; fix by
computing the residue-position key (e.g., residue_pos = count of alphabetic
residues up to i) and consult self.non_target_mods at the top of the loop: if a
mod_name exists for that residue_pos, append aa_upper+"("+mod_name+")" to result
and skip other branches; otherwise continue with the existing
Phospho/Oxidation/PhosphoDecoy/fallback logic using sequence, i, result, and
aa_upper as before.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c06bdac9-caab-45c4-bbf3-36cefdd37293

📥 Commits

Reviewing files that changed from the base of the PR and between 61278bb and 542157b.

📒 Files selected for processing (2)
  • onsite/lucxor/cli.py
  • onsite/lucxor/psm.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • onsite/lucxor/cli.py

@daichengxin daichengxin changed the title fixed bugs and clean up (DON'T MERGE) fixed bugs and clean up Jun 13, 2026
@daichengxin daichengxin merged commit 2c62bad into bigbio:main Jun 13, 2026
3 checks passed
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.

1 participant