From 13f20a368a379acf63dba89156afee1203328781 Mon Sep 17 00:00:00 2001 From: Brian Pratt Date: Wed, 7 Aug 2024 15:15:45 -0700 Subject: [PATCH 01/12] In ExceptionWeb report https://skyline.ms/announcements/home/issues/exceptions/thread.view?entityId=14a60bd2-3604-103d-a666-22f53556a042&_anchor=65275 a call to ViewLibraryDlg.AddAllPeptides with an water loss adduct on a molecule with no O in it throws an exception. Instead, let's just skip it and put a warning in the immediate window. (Ideally it wouldn't be in the library in the first place, but we don't have any data on how the library was created) --- .../Model/AbstractModificationMatcher.cs | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs b/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs index f1e136ce71f..de3b18f0726 100644 --- a/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs +++ b/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs @@ -18,6 +18,7 @@ */ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; @@ -473,24 +474,31 @@ public PeptideDocNode CreateDocNodeFromSettings(LibKey key, Peptide peptide, Srm SpectrumHeaderInfo libInfo; if (nodePep != null && Settings.PeptideSettings.Libraries.TryGetLibInfo(key, out libInfo)) { - var isotopeLabelType = key.Adduct.HasIsotopeLabels ? IsotopeLabelType.heavy : IsotopeLabelType.light; - var group = new TransitionGroup(peptide, key.Adduct, isotopeLabelType); - nodeGroupMatched = new TransitionGroupDocNode(group, Annotations.EMPTY, Settings, null, libInfo, ExplicitTransitionGroupValues.EMPTY, null, null, false); - SpectrumPeaksInfo spectrum; - if (Settings.PeptideSettings.Libraries.TryLoadSpectrum(key, out spectrum)) + try { - // Add fragment and precursor transitions as needed - var transitionDocNodes = - Settings.TransitionSettings.Filter.SmallMoleculeIonTypes.Contains(IonType.precursor) - ? nodeGroupMatched.GetPrecursorChoices(Settings, null, true) // Gives list of precursors - : new List(); - - if (Settings.TransitionSettings.Filter.SmallMoleculeIonTypes.Contains(IonType.custom)) + var isotopeLabelType = key.Adduct.HasIsotopeLabels ? IsotopeLabelType.heavy : IsotopeLabelType.light; + var group = new TransitionGroup(peptide, key.Adduct, isotopeLabelType); + nodeGroupMatched = new TransitionGroupDocNode(group, Annotations.EMPTY, Settings, null, libInfo, ExplicitTransitionGroupValues.EMPTY, null, null, false); + SpectrumPeaksInfo spectrum; + if (Settings.PeptideSettings.Libraries.TryLoadSpectrum(key, out spectrum)) { - GetSmallMoleculeFragments(key, nodeGroupMatched, spectrum, transitionDocNodes); + // Add fragment and precursor transitions as needed + var transitionDocNodes = + Settings.TransitionSettings.Filter.SmallMoleculeIonTypes.Contains(IonType.precursor) + ? nodeGroupMatched.GetPrecursorChoices(Settings, null, true) // Gives list of precursors + : new List(); + + if (Settings.TransitionSettings.Filter.SmallMoleculeIonTypes.Contains(IonType.custom)) + { + GetSmallMoleculeFragments(key, nodeGroupMatched, spectrum, transitionDocNodes); + } + nodeGroupMatched = (TransitionGroupDocNode)nodeGroupMatched.ChangeChildren(transitionDocNodes); + return (PeptideDocNode)nodePep.ChangeChildren(new List() { nodeGroupMatched }); } - nodeGroupMatched = (TransitionGroupDocNode)nodeGroupMatched.ChangeChildren(transitionDocNodes); - return (PeptideDocNode)nodePep.ChangeChildren(new List() { nodeGroupMatched }); + } + catch (InvalidDataException e) + { + Trace.WriteLine(e.Message); // Adduct makes no sense for target formula etc } } } From c4df609635eb5b8beb521c99ed9a26cd7ca19645 Mon Sep 17 00:00:00 2001 From: Brian Pratt Date: Wed, 7 Aug 2024 15:23:46 -0700 Subject: [PATCH 02/12] was using wrong Trace method for immediate window placement --- pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs b/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs index de3b18f0726..b677cc33f02 100644 --- a/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs +++ b/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs @@ -498,7 +498,7 @@ public PeptideDocNode CreateDocNodeFromSettings(LibKey key, Peptide peptide, Srm } catch (InvalidDataException e) { - Trace.WriteLine(e.Message); // Adduct makes no sense for target formula etc + Trace.TraceWarning(e.Message); // Adduct makes no sense for target formula etc } } } From d95436c07d29504dfd0ffdc109d615061153d529 Mon Sep 17 00:00:00 2001 From: Brian Pratt Date: Wed, 14 Aug 2024 11:05:30 -0700 Subject: [PATCH 03/12] Introducing public class InvalidChemicalModificationException : IOException which is thrown when a loss would remove more atoms than are found in a molecular formula e.g. water loss from C2S5H12 (no oxygen), or label would change more atoms than are in the formula e.g. 3C' on C2S5H12 We now catch this exception in LibraryExplorer and adjust display accordingly --- .../Model/AbstractModificationMatcher.cs | 4 +-- pwiz_tools/Skyline/Model/Import.cs | 2 +- .../Skyline/Model/Lib/ViewLibraryPepInfo.cs | 15 +++++++++-- .../SmallMoleculeTransitionListReader.cs | 1 + .../Skyline/SettingsUI/ViewLibraryDlg.cs | 26 +++++++++++++++---- .../TestFunctional/LibraryBuildTest.cs | 15 +++++++++++ pwiz_tools/Skyline/Util/Adduct.cs | 15 +++++++++-- 7 files changed, 66 insertions(+), 12 deletions(-) diff --git a/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs b/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs index b677cc33f02..c280538095f 100644 --- a/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs +++ b/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs @@ -496,9 +496,9 @@ public PeptideDocNode CreateDocNodeFromSettings(LibKey key, Peptide peptide, Srm return (PeptideDocNode)nodePep.ChangeChildren(new List() { nodeGroupMatched }); } } - catch (InvalidDataException e) + catch (InvalidChemicalModificationException e) { - Trace.TraceWarning(e.Message); // Adduct makes no sense for target formula etc + Trace.TraceWarning(e.Message); // Adduct makes no sense for target formula } } } diff --git a/pwiz_tools/Skyline/Model/Import.cs b/pwiz_tools/Skyline/Model/Import.cs index feec83b1552..278fdaadede 100644 --- a/pwiz_tools/Skyline/Model/Import.cs +++ b/pwiz_tools/Skyline/Model/Import.cs @@ -832,7 +832,7 @@ private PeptideGroupBuilder AddRow(PeptideGroupBuilder seqBuilder, { seqBuilder.AppendTransition(info, irt, explicitRT, libraryIntensity, productMz, note, lineText, lineNum); } - catch (InvalidDataException x) + catch (Exception x) when (!ExceptionUtil.IsProgrammingDefect(x)) { throw new LineColNumberedIoException(x.Message, lineNum, -1, x); } diff --git a/pwiz_tools/Skyline/Model/Lib/ViewLibraryPepInfo.cs b/pwiz_tools/Skyline/Model/Lib/ViewLibraryPepInfo.cs index 2e724ca24b3..37a6379b0dc 100644 --- a/pwiz_tools/Skyline/Model/Lib/ViewLibraryPepInfo.cs +++ b/pwiz_tools/Skyline/Model/Lib/ViewLibraryPepInfo.cs @@ -236,8 +236,19 @@ public double CalcMz(SrmSettings settings, { TypedMass massH; if (Target != null) - massH = settings.GetPrecursorCalc(transitionGroup.TransitionGroup.LabelType, mods) - .GetPrecursorMass(Target); + { + if (!Target.IsProteomic) + { + massH = SequenceMassCalc.FormulaMass(BioMassCalc.MONOISOTOPIC, + transitionGroup.PrecursorAdduct.ApplyToMolecule(Target.Molecule.ParsedMolecule)); + return SequenceMassCalc.PersistentMZ(SequenceMassCalc.GetMZ(massH, transitionGroup.PrecursorAdduct.AdductCharge)); + } + else + { + massH = settings.GetPrecursorCalc(transitionGroup.TransitionGroup.LabelType, mods) + .GetPrecursorMass(Target); + } + } else massH = new TypedMass(Key.PrecursorMz ?? 0, MassType.Monoisotopic); return SequenceMassCalc.PersistentMZ(SequenceMassCalc.GetMZ(massH, transitionGroup.PrecursorAdduct)); diff --git a/pwiz_tools/Skyline/Model/SmallMoleculeTransitionListReader.cs b/pwiz_tools/Skyline/Model/SmallMoleculeTransitionListReader.cs index 8b4905f6d10..cc1b208f11e 100644 --- a/pwiz_tools/Skyline/Model/SmallMoleculeTransitionListReader.cs +++ b/pwiz_tools/Skyline/Model/SmallMoleculeTransitionListReader.cs @@ -381,6 +381,7 @@ public static bool IsParserException(Exception exception) { return exception is InvalidOperationException || exception is InvalidDataException + || exception is InvalidChemicalModificationException || exception is ArgumentException; } diff --git a/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.cs b/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.cs index f78a89a3936..88613cb74b1 100644 --- a/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.cs +++ b/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.cs @@ -921,6 +921,11 @@ public void UpdateUI(bool selectionChanged = true) SetGraphItem(new NoDataMSGraphItem(SettingsUIResources.ViewLibraryDlg_UpdateUI_Unauthorized_access_attempting_to_read_from_library_)); return; } + catch (InvalidChemicalModificationException e) + { + SetGraphItem(new NoDataMSGraphItem(e.Message)); + return; + } catch (IOException) { SetGraphItem(new NoDataMSGraphItem(SettingsUIResources.ViewLibraryDlg_UpdateUI_Failure_loading_spectrum_Library_may_be_corrupted)); @@ -2677,8 +2682,16 @@ public PeptideTipProvider(ViewLibraryPepInfo pepInfo, LibKeyModificationMatcher if (_pepInfo.Target != null) { // build mz range parts to draw - _mz = _pepInfo.CalcMz(_settings, transitionGroup, mods); - _mzRangePartsToDraw = GetMzRangeItemsToDraw(_mz); + try + { + _mz = _pepInfo.CalcMz(_settings, transitionGroup, mods); + _mzRangePartsToDraw = GetMzRangeItemsToDraw(_mz); + } + catch (InvalidChemicalModificationException e) + { + _mz = double.NaN; + _mzRangePartsToDraw = new List() { new TextColor(e.Message) }; + } } else { @@ -3016,11 +3029,14 @@ public void UpdateRedundantComboItems() if (!_comboBoxUpdated) { comboRedundantSpectra.BeginUpdate(); - foreach (ComboOption opt in _currentOptions) + if (_currentOptions != null) { - if (!opt.SpectrumInfoLibrary.IsBest) + foreach (ComboOption opt in _currentOptions) { - comboRedundantSpectra.Items.Add(opt); + if (!opt.SpectrumInfoLibrary.IsBest) + { + comboRedundantSpectra.Items.Add(opt); + } } } diff --git a/pwiz_tools/Skyline/TestFunctional/LibraryBuildTest.cs b/pwiz_tools/Skyline/TestFunctional/LibraryBuildTest.cs index 4ea520ee800..d35772d4ab8 100644 --- a/pwiz_tools/Skyline/TestFunctional/LibraryBuildTest.cs +++ b/pwiz_tools/Skyline/TestFunctional/LibraryBuildTest.cs @@ -21,6 +21,7 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Windows.Forms; using Microsoft.VisualStudio.TestTools.UnitTesting; using pwiz.BiblioSpec; using pwiz.Skyline.Alerts; @@ -175,6 +176,20 @@ private void MainTest() RunUI(() => AssertEx.IsTrue(viewLibUI.GraphItem.IonLabels.Any())); OkDialog(viewLibUI, viewLibUI.CancelDialog); + // Now check for handling when adduct tries to label more atoms than are present in the molecule + var sslLines = File.ReadAllLines(TestFilesDir.GetTestPath("library_valid\\heavy_adduct.ssl")).ToList(); + var badSSL = sslLines[1].Replace("ms2\t2","ms2\t3").Replace(@"M6C13", @"M66C13"); // Molecule is C54H83N15O20 so this makes no sense + sslLines.Add(badSSL); + File.WriteAllLines(TestFilesDir.GetTestPath("library_valid\\heavy_adduct_bad.ssl"), sslLines); + BuildLibraryValid("heavy_adduct_bad.ssl", true, false, false, 2); + // Make sure explorer handles this adduct, which is a bad match for the molecule + viewLibUI = ShowDialog(SkylineWindow.ViewSpectralLibraries); + RunUI(() => AssertEx.IsTrue(viewLibUI.GraphItem.IonLabels.Any())); + // Add All should cause some notifications since one of them is bad + var filterMatchedDlg = ShowDialog(() => viewLibUI.AddAllPeptides()); + OkDialog(filterMatchedDlg, filterMatchedDlg.CancelDialog); + OkDialog(viewLibUI, viewLibUI.CancelDialog); + // Barbara added code to ProteoWizard to rebuild a missing or invalid mzXML index // BuildLibraryError("bad_mzxml.pep.XML", " not found"); BuildLibraryValid(TestFilesDir.GetTestPath("library_errors"), new[] { "bad_mzxml.pep.XML" }, false, false, false, 1, 0, false); diff --git a/pwiz_tools/Skyline/Util/Adduct.cs b/pwiz_tools/Skyline/Util/Adduct.cs index b6c19fe57a0..18877da6a76 100644 --- a/pwiz_tools/Skyline/Util/Adduct.cs +++ b/pwiz_tools/Skyline/Util/Adduct.cs @@ -66,6 +66,17 @@ public T this[Adduct a] public IEnumerable Keys { get { return _dict.Keys; } } } + /// + /// Thrown when a loss would remove more atoms than are found in a molecular formula e.g. water loss from C2S5H12 (no oxygen), + /// or label would change more atoms than are in the formula e.g. 3C' on C2S5H12 + /// + public class InvalidChemicalModificationException : IOException + { + public InvalidChemicalModificationException(string message) : base(message) + { + } + } + public class Adduct : Immutable, IComparable, IEquatable, IAuditLogObject { private Molecule Composition { get; set; } // The chemical makeup of the adduct - the "2H" part in 4M3Cl37+2H @@ -1401,7 +1412,7 @@ public ParsedMolecule ApplyToMolecule(ParsedMolecule molecule) } if (count < 0 && !Equals(pair.Key, BioMassCalc.H)) // Treat H loss as a general proton loss { - throw new InvalidDataException( + throw new InvalidChemicalModificationException( string.Format(Resources.Adduct_ApplyToMolecule_Adduct___0___calls_for_removing_more__1__atoms_than_are_found_in_the_molecule__2_, this, pair.Key, molecule.ToString())); } @@ -1462,7 +1473,7 @@ private ParsedMolecule ApplyIsotopeValues(ParsedMolecule molecule, int massMulti } else // Can't remove that which is not there { - throw new InvalidDataException( + throw new InvalidChemicalModificationException( string.Format( UtilResources .Adduct_ApplyToMolecule_Adduct___0___calls_for_labeling_more__1__atoms_than_are_found_in_the_molecule__2_, From 21fbde28c2470f980889ca59fd5a4e01807917b7 Mon Sep 17 00:00:00 2001 From: Brian Pratt Date: Wed, 14 Aug 2024 12:19:53 -0700 Subject: [PATCH 04/12] update test to accommodate new exception type --- pwiz_tools/Skyline/Test/AdductTest.cs | 37 +++++++++++++++++---------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/pwiz_tools/Skyline/Test/AdductTest.cs b/pwiz_tools/Skyline/Test/AdductTest.cs index 1576dd49d36..67aa00b08d4 100644 --- a/pwiz_tools/Skyline/Test/AdductTest.cs +++ b/pwiz_tools/Skyline/Test/AdductTest.cs @@ -99,7 +99,7 @@ private void TestTaxolAdduct(string adductText, double expectedMz, int expectedC coverage.Add(adduct.AsFormula()); } - private void TestException(string formula, string adductText) + private void TestInvalidDataException(string formula, string adductText) { AssertEx.ThrowsException(() => { @@ -108,6 +108,15 @@ private void TestException(string formula, string adductText) }); } + private void TestInvalidChemicalModificationException(string formula, string adductText) + { + AssertEx.ThrowsException(() => + { + var adduct = Adduct.FromStringAssumeProtonated(adductText); + IonInfo.ApplyAdductToFormula(formula, adduct); + }); + } + private string RoundtripFormulaString(string f) { ParsedMolecule.TryParseFormula(f, out var mol, out _); @@ -417,19 +426,19 @@ public void AdductParserTest() mz = BioMassCalc.CalculateIonMass(new TypedMass(massHectochlorin, MassType.Monoisotopic), heavy); Assert.AreEqual(2 * (massHectochlorin + 1.23456), mz, .001); - TestException(PENTANE, "zM+2H"); // That "z" doesn't make any sense as a mass multiplier (must be a positive integer) - TestException(PENTANE, "-2M+2H"); // That "-2" doesn't make any sense as a mass multiplier (must be a positive integer) - TestException("", "+M"); // Meaningless, used to cause an exception in our parser - TestException(Hectochlorin, "M3Cl37+H"); // Trying to label more chlorines than exist in the molecule - TestException(Hectochlorin, "M-3Cl+H"); // Trying to remove more chlorines than exist in the molecule - TestException(PENTANE, "M+foo+H"); // Unknown adduct - TestException(PENTANE, "M2Cl37H+H"); // nonsense label ("2Cl37H2" would make sense, but regular H doesn't belong) - TestException(PENTANE, "M+2H+"); // Trailing sign - we now understand this as a charge state declaration, but this one doesn't match described charge - TestException(PENTANE, "[M-2H]3-"); // Declared charge doesn't match described charge - TestException(PENTANE, "[M-]3-"); // Declared charge doesn't match described charge - TestException(PENTANE, "[M+]-"); // Declared charge doesn't match described charge - TestException(PENTANE, "[M+2]-"); // Declared charge doesn't match described charge - TestException(PENTANE, "[M+2]+3"); // Declared charge doesn't match described charge + TestInvalidDataException(PENTANE, "zM+2H"); // That "z" doesn't make any sense as a mass multiplier (must be a positive integer) + TestInvalidDataException(PENTANE, "-2M+2H"); // That "-2" doesn't make any sense as a mass multiplier (must be a positive integer) + TestInvalidDataException("", "+M"); // Meaningless, used to cause an exception in our parser + TestInvalidChemicalModificationException(Hectochlorin, "M3Cl37+H"); // Trying to label more chlorines than exist in the molecule + TestInvalidChemicalModificationException(Hectochlorin, "M-3Cl+H"); // Trying to remove more chlorines than exist in the molecule + TestInvalidDataException(PENTANE, "M+foo+H"); // Unknown adduct + TestInvalidDataException(PENTANE, "M2Cl37H+H"); // nonsense label ("2Cl37H2" would make sense, but regular H doesn't belong) + TestInvalidDataException(PENTANE, "M+2H+"); // Trailing sign - we now understand this as a charge state declaration, but this one doesn't match described charge + TestInvalidDataException(PENTANE, "[M-2H]3-"); // Declared charge doesn't match described charge + TestInvalidDataException(PENTANE, "[M-]3-"); // Declared charge doesn't match described charge + TestInvalidDataException(PENTANE, "[M+]-"); // Declared charge doesn't match described charge + TestInvalidDataException(PENTANE, "[M+2]-"); // Declared charge doesn't match described charge + TestInvalidDataException(PENTANE, "[M+2]+3"); // Declared charge doesn't match described charge // Test label stripping Assert.AreEqual("C5H9NO2S", (new IonInfo("C5H9H'3NO2S[M-3H]")).UnlabeledFormula.ToString()); From be1cfbeb892dbc1c0c537fe3250833194dd489e1 Mon Sep 17 00:00:00 2001 From: Brian Pratt Date: Wed, 4 Sep 2024 12:12:59 -0700 Subject: [PATCH 05/12] clear up an unwanted using directive --- pwiz_tools/Skyline/TestFunctional/LibraryBuildTest.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/pwiz_tools/Skyline/TestFunctional/LibraryBuildTest.cs b/pwiz_tools/Skyline/TestFunctional/LibraryBuildTest.cs index d35772d4ab8..3a01fc18e19 100644 --- a/pwiz_tools/Skyline/TestFunctional/LibraryBuildTest.cs +++ b/pwiz_tools/Skyline/TestFunctional/LibraryBuildTest.cs @@ -21,7 +21,6 @@ using System.Diagnostics; using System.IO; using System.Linq; -using System.Windows.Forms; using Microsoft.VisualStudio.TestTools.UnitTesting; using pwiz.BiblioSpec; using pwiz.Skyline.Alerts; From 4aca7ae649384113c7b4792d387bca31d8d5181a Mon Sep 17 00:00:00 2001 From: Brian Pratt Date: Tue, 10 Sep 2024 14:33:40 -0700 Subject: [PATCH 06/12] manual merge of commit 721ae29282b14b2aa0596e2c98f9d9c7d7d5060b * Fix a bug in m/z calculation where the mass of the adduct was not being added to molecules described only as a mass with no chemical formula. (#3151) --- pwiz_tools/Skyline/Test/AdductTest.cs | 12 +++++- pwiz_tools/Skyline/Util/Adduct.cs | 62 +++++++++++++-------------- 2 files changed, 41 insertions(+), 33 deletions(-) diff --git a/pwiz_tools/Skyline/Test/AdductTest.cs b/pwiz_tools/Skyline/Test/AdductTest.cs index 67aa00b08d4..b0a4320be66 100644 --- a/pwiz_tools/Skyline/Test/AdductTest.cs +++ b/pwiz_tools/Skyline/Test/AdductTest.cs @@ -356,12 +356,22 @@ private static void CheckLabel(string label) private void TestMassOnly(IEnumerable[] adductLists) { var formula = @"[456.78]"; // Mass-only molecule description + var molecule = ParsedMolecule.Create(formula); + var massMolecule = molecule.MonoMassOffset; + AssertEx.AreEqual(456.78,massMolecule); foreach (var adductList in adductLists) { foreach (var adductStr in adductList) { var adduct = Adduct.FromString(adductStr, Adduct.ADDUCT_TYPE.proteomic, null); - IonInfo.ApplyAdductToFormula(formula, adduct); + var ion = IonInfo.ApplyAdductToFormula(formula, adduct); + var massAdduct = adduct.MonoMassAdduct + adduct.IsotopesIncrementalMonoMass + massMolecule * (adduct.GetMassMultiplier()-1); + var massIon = BioMassCalc.MONOISOTOPIC.CalculateMass(ion); + if (!adduct.IsChargeOnly) + { + AssertEx.AreNotEqual(massIon, massMolecule, "adduct has no effect?"); + } + AssertEx.AreEqual(massIon, massMolecule + massAdduct, .0001, $"ion {ion} mass {massIon} vs mol {molecule} mass + adduct {adduct} mass ({massMolecule}+{massAdduct}"); } } } diff --git a/pwiz_tools/Skyline/Util/Adduct.cs b/pwiz_tools/Skyline/Util/Adduct.cs index 18877da6a76..e57c6e11eda 100644 --- a/pwiz_tools/Skyline/Util/Adduct.cs +++ b/pwiz_tools/Skyline/Util/Adduct.cs @@ -81,11 +81,11 @@ public class Adduct : Immutable, IComparable, IEquatable, IAuditLogObjec { private Molecule Composition { get; set; } // The chemical makeup of the adduct - the "2H" part in 4M3Cl37+2H private string Description { get; set; } // The text description (will be empty for protonation, we just use charge) - private TypedMass AverageMassAdduct { get; set; } // Average mass of the adduct itself - the "2H" in 4M3Cl37+2H - private TypedMass MonoMassAdduct { get; set; } // Monoisotopic mass of the adduct itself - the "2H" in 4M3Cl37+2H + public TypedMass AverageMassAdduct { get; private set; } // Average mass of the adduct itself - the "2H" in 4M3Cl37+2H + public TypedMass MonoMassAdduct { get; private set; } // Monoisotopic mass of the adduct itself - the "2H" in 4M3Cl37+2H private int MassMultiplier { get; set; } // Returns, for example, the 2 in "[2M+Na]", which means the ion is two molecules + the adduct mass. - private TypedMass IsotopesIncrementalAverageMass { get; set; } // The incremental average mass due to (4*3) (Cl37 - Cl) in 4M3Cl37+2H - private TypedMass IsotopesIncrementalMonoMass { get; set; } // The incremental mono mass due to (4*3) (Cl37 - Cl) in 4M3Cl37+2H + public TypedMass IsotopesIncrementalAverageMass { get; private set; } // The incremental average mass due to (4*3) (Cl37 - Cl) in 4M3Cl37+2H + public TypedMass IsotopesIncrementalMonoMass { get; private set; } // The incremental mono mass due to (4*3) (Cl37 - Cl) in 4M3Cl37+2H private int _hashCode; // We want comparisons to be on the same order as comparing ints, as when we used to just use integer charge instead of proper adducts @@ -1379,44 +1379,42 @@ public ParsedMolecule ApplyToMolecule(ParsedMolecule molecule) var resultDict = new Dictionary(molecule.Molecule); - if (!molecule.IsMassOnly) + // Deal with any mass multiplier (the 2 in "[2M+Na]") + if (MassMultiplier != 1) { - // Deal with any mass multiplier (the 2 in "[2M+Na]") - if (MassMultiplier != 1) + foreach (var element in resultDict.Keys.ToArray()) { - foreach (var element in resultDict.Keys.ToArray()) - { - resultDict[element] *= MassMultiplier; - } + resultDict[element] *= MassMultiplier; } + } - // Add in the "Na" of [M+Na] (or remove the 4H in [M-4H]) - foreach (var pair in Composition) + // Add in the "Na" of [M+Na] (or remove the 4H in [M-4H]) + foreach (var pair in Composition) + { + if (resultDict.TryGetValue(pair.Key, out var count)) { - if (resultDict.TryGetValue(pair.Key, out var count)) + count += pair.Value; + if (count == 0) { - count += pair.Value; - if (count == 0) - { - resultDict.Remove(pair.Key); - } - else - { - resultDict[pair.Key] = count; - } + resultDict.Remove(pair.Key); } - else if (pair.Value != 0) - { - resultDict.Add(pair.Key, pair.Value); - count = pair.Value; - } - if (count < 0 && !Equals(pair.Key, BioMassCalc.H)) // Treat H loss as a general proton loss + else { - throw new InvalidChemicalModificationException( - string.Format(Resources.Adduct_ApplyToMolecule_Adduct___0___calls_for_removing_more__1__atoms_than_are_found_in_the_molecule__2_, - this, pair.Key, molecule.ToString())); + resultDict[pair.Key] = count; } } + else if (pair.Value != 0) + { + resultDict.Add(pair.Key, pair.Value); + count = pair.Value; + } + if (!molecule.IsMassOnly && + count < 0 && !Equals(pair.Key, BioMassCalc.H)) // Treat H loss as a general proton loss + { + throw new InvalidChemicalModificationException( + string.Format(Resources.Adduct_ApplyToMolecule_Adduct___0___calls_for_removing_more__1__atoms_than_are_found_in_the_molecule__2_, + this, pair.Key, molecule.ToString())); + } } return ApplyIsotopeValues(molecule, MassMultiplier, resultDict); From 725fed3f6d80abc13e06e49c5f971784d94886ae Mon Sep 17 00:00:00 2001 From: Brian Pratt Date: Tue, 8 Oct 2024 15:51:30 -0700 Subject: [PATCH 07/12] Make the error message more prominent Show the spectrum even if m/z can't be calculated --- .../Skyline/Model/Lib/SpectrumRanker.cs | 18 ++++++++++---- .../Skyline/SettingsUI/ViewLibraryDlg.cs | 24 ++++++++++++++----- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/pwiz_tools/Skyline/Model/Lib/SpectrumRanker.cs b/pwiz_tools/Skyline/Model/Lib/SpectrumRanker.cs index 24048b1f0da..0089ff2eae4 100644 --- a/pwiz_tools/Skyline/Model/Lib/SpectrumRanker.cs +++ b/pwiz_tools/Skyline/Model/Lib/SpectrumRanker.cs @@ -173,11 +173,19 @@ public SpectrumRanker(TargetInfo targetInfo, SrmSettings settings, var ionMasses = new IonMasses(calcMatch.GetPrecursorFragmentMass(Sequence), IonTable.EMPTY) .ChangeKnownFragments(knownFragments); - moleculeMasses = - new MoleculeMasses( - SequenceMassCalc.GetMZ( - calcMatchPre.GetPrecursorMass(Sequence.Molecule, null, PrecursorAdduct, - out _), PrecursorAdduct), ionMasses); + try + { + moleculeMasses = + new MoleculeMasses( + SequenceMassCalc.GetMZ( + calcMatchPre.GetPrecursorMass(Sequence.Molecule, null, PrecursorAdduct, + out _), PrecursorAdduct), ionMasses); + } + catch (InvalidChemicalModificationException) + { + moleculeMasses = + new MoleculeMasses(double.NaN, ionMasses); // Precursor m/z can't be calculated + } } else { diff --git a/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.cs b/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.cs index 49cee39d3f3..138003f4a08 100644 --- a/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.cs +++ b/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.cs @@ -2679,6 +2679,7 @@ public PeptideTipProvider(ViewLibraryPepInfo pepInfo, LibKeyModificationMatcher _smallMoleculePartsToDraw = smallMolInfo.LocalizedKeyValuePairs; } + _mzRangePartsToDraw = new List(); if (_pepInfo.Target != null) { // build mz range parts to draw @@ -2690,12 +2691,13 @@ public PeptideTipProvider(ViewLibraryPepInfo pepInfo, LibKeyModificationMatcher catch (InvalidChemicalModificationException e) { _mz = double.NaN; - _mzRangePartsToDraw = new List() { new TextColor(e.Message) }; + // Show the error at the top of the tip + _seqPartsToDraw.Insert(0,new TextColor(e.Message, Brushes.Red)); + _seqPartsToDraw.Insert(1, new TextColor(Environment.NewLine)); } } else { - _mzRangePartsToDraw = new List(); var precursorKey = _pepInfo.Key.LibraryKey as PrecursorLibraryKey; if (precursorKey != null) { @@ -2938,13 +2940,23 @@ private SizeF DrawTextParts(Graphics g, float startX, float startY, List Date: Thu, 17 Oct 2024 11:57:40 -0700 Subject: [PATCH 08/12] use Messages.WriteAsyncUserMessage instead of Trace.Warning --- pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs b/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs index af8c5ac1d51..41d61ecf7ee 100644 --- a/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs +++ b/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs @@ -18,11 +18,11 @@ */ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Text; +using pwiz.Common.SystemUtil; using pwiz.Skyline.Model.Crosslinking; using pwiz.Skyline.Model.DocSettings; using pwiz.Skyline.Model.Lib; @@ -498,7 +498,7 @@ public PeptideDocNode CreateDocNodeFromSettings(LibKey key, Peptide peptide, Srm } catch (InvalidChemicalModificationException e) { - Trace.TraceWarning(e.Message); // Adduct makes no sense for target formula + Messages.WriteAsyncUserMessage(e.Message); // Adduct makes no sense for target formula } } } From 1e7a4664e46d4bd4ce772e5a1a55aecc023107c5 Mon Sep 17 00:00:00 2001 From: Brian Pratt Date: Tue, 12 Nov 2024 16:18:21 -0800 Subject: [PATCH 09/12] Add detail library name to immediate window message when adduct does not make sense for library molecule --- .../Skyline/Model/AbstractModificationMatcher.cs | 11 +++++++++-- pwiz_tools/Skyline/Model/ImportPeptideSearch.cs | 2 +- pwiz_tools/Skyline/Model/LibKeyModificationMatcher.cs | 6 ++++-- pwiz_tools/Skyline/Model/ModificationMatcher.cs | 2 +- pwiz_tools/Skyline/SettingsUI/AddModificationsDlg.cs | 4 +++- pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.cs | 3 ++- pwiz_tools/Skyline/Test/CrosslinkModTest.cs | 2 +- pwiz_tools/Skyline/Test/ModificationMatcherTest.cs | 4 ++-- 8 files changed, 23 insertions(+), 11 deletions(-) diff --git a/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs b/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs index 41d61ecf7ee..7f501ba46c7 100644 --- a/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs +++ b/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs @@ -39,6 +39,8 @@ public abstract class AbstractModificationMatcher protected SrmSettings Settings { get; set; } protected bool Initialized { get; set; } + protected internal string LibraryName { get; set; } + protected MappedList DefSetStatic { get; private set; } protected MappedList DefSetHeavy { get; private set; } protected IsotopeLabelType DocDefHeavyLabelType { get; private set; } @@ -54,13 +56,15 @@ public static double GetDefaultModMass(char aa, StaticMod mod) } internal void InitMatcherSettings(SrmSettings settings, - MappedList defSetStatic, MappedList defSetHeavy) + MappedList defSetStatic, MappedList defSetHeavy, string libraryName) { DefSetStatic = defSetStatic; DefSetHeavy = defSetHeavy; Settings = settings; + LibraryName = libraryName; + var modifications = settings.PeptideSettings.Modifications; UserDefinedTypedMods = new Dictionary(); @@ -498,7 +502,10 @@ public PeptideDocNode CreateDocNodeFromSettings(LibKey key, Peptide peptide, Srm } catch (InvalidChemicalModificationException e) { - Messages.WriteAsyncUserMessage(e.Message); // Adduct makes no sense for target formula + var message = string.IsNullOrEmpty(LibraryName) ? + string.Format("In \"{0}\": {1}", key.Target.DisplayName, e.Message) : + string.Format("In entry \"{0}\" of \"{1}\": {2}", key.Target.DisplayName, LibraryName, e.Message); + Messages.WriteAsyncUserMessage(message); // Adduct makes no sense for target formula } } } diff --git a/pwiz_tools/Skyline/Model/ImportPeptideSearch.cs b/pwiz_tools/Skyline/Model/ImportPeptideSearch.cs index 8ce7ba29214..1ee42e1db8c 100644 --- a/pwiz_tools/Skyline/Model/ImportPeptideSearch.cs +++ b/pwiz_tools/Skyline/Model/ImportPeptideSearch.cs @@ -478,7 +478,7 @@ private void InitializeUserDefinedTypedMods(SrmDocument document) public void UpdateModificationMatches(SrmDocument document) { _matcher.ClearMatches(); - _matcher.CreateMatches(document.Settings, DocLib.Keys, Settings.Default.StaticModList, Settings.Default.HeavyModList); + _matcher.CreateMatches(document.Settings, DocLib.Keys, Settings.Default.StaticModList, Settings.Default.HeavyModList, DocLib.Name); } public IEnumerable GetMatchedMods() diff --git a/pwiz_tools/Skyline/Model/LibKeyModificationMatcher.cs b/pwiz_tools/Skyline/Model/LibKeyModificationMatcher.cs index a20a1b5ea4d..6894b2a7bd2 100644 --- a/pwiz_tools/Skyline/Model/LibKeyModificationMatcher.cs +++ b/pwiz_tools/Skyline/Model/LibKeyModificationMatcher.cs @@ -35,11 +35,13 @@ public class LibKeyModificationMatcher : AbstractModificationMatcher public PeptideModifications MatcherPepMods { get; set; } public void CreateMatches(SrmSettings settings, IEnumerable libKeys, - MappedList defSetStatic, MappedList defSetHeavy) + MappedList defSetStatic, MappedList defSetHeavy, + string libraryName) { _dictAAMassPairs = new Dictionary>(); _libKeys = libKeys.GetEnumerator(); - InitMatcherSettings(settings, defSetStatic, defSetHeavy); + LibraryName = libraryName; + InitMatcherSettings(settings, defSetStatic, defSetHeavy, libraryName); MatcherPepMods = CreateMatcherPeptideSettings(settings); } diff --git a/pwiz_tools/Skyline/Model/ModificationMatcher.cs b/pwiz_tools/Skyline/Model/ModificationMatcher.cs index 8d89724bf61..aeb828d1202 100644 --- a/pwiz_tools/Skyline/Model/ModificationMatcher.cs +++ b/pwiz_tools/Skyline/Model/ModificationMatcher.cs @@ -61,7 +61,7 @@ public void CreateMatches(SrmSettings settings, IEnumerable sequences, _sequences = sequences.GetEnumerator(); - InitMatcherSettings(settings, defSetStatic, defSetHeavy); + InitMatcherSettings(settings, defSetStatic, defSetHeavy, null); if (UnmatchedSequences.Count > 0) { UnmatchedSequences.Sort(); diff --git a/pwiz_tools/Skyline/SettingsUI/AddModificationsDlg.cs b/pwiz_tools/Skyline/SettingsUI/AddModificationsDlg.cs index 5926346cf9c..7cafe5d6a45 100644 --- a/pwiz_tools/Skyline/SettingsUI/AddModificationsDlg.cs +++ b/pwiz_tools/Skyline/SettingsUI/AddModificationsDlg.cs @@ -33,6 +33,7 @@ public override string ToString() public LibKeyModificationMatcher Matcher { get; private set; } private readonly LibKey[] _libKeys; + private readonly string _libraryName; private readonly HashSet _userDefinedTypedMods; private readonly IsotopeLabelType _defaultHeavyLabelType; @@ -57,6 +58,7 @@ public AddModificationsDlg(SrmSettings settings, Library library) Matcher = new LibKeyModificationMatcher(); _libKeys = library.Keys.ToArray(); + _libraryName = library.Name; _userDefinedTypedMods = new HashSet(); _settings = settings; NewDocumentModsStatic = new StaticMod[0]; @@ -94,7 +96,7 @@ private void UpdateModificationMatches() { // Update matcher Matcher.ClearMatches(); - Matcher.CreateMatches(_settings, _libKeys, DefaultStatic, DefaultHeavy); + Matcher.CreateMatches(_settings, _libKeys, DefaultStatic, DefaultHeavy, _libraryName); // Update UI listMatched.Items.Clear(); diff --git a/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.cs b/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.cs index 138003f4a08..7fd83e88758 100644 --- a/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.cs +++ b/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.cs @@ -481,7 +481,7 @@ public bool MatchModifications() return false; var matcher = new LibKeyModificationMatcher(); - matcher.CreateMatches(Document.Settings, _selectedLibrary.Keys, Settings.Default.StaticModList, Settings.Default.HeavyModList); + matcher.CreateMatches(Document.Settings, _selectedLibrary.Keys, Settings.Default.StaticModList, Settings.Default.HeavyModList, _selectedLibrary.Name); if (string.IsNullOrEmpty(matcher.FoundMatches) && !matcher.UnmatchedSequences.Any()) { _matcher = matcher; @@ -570,6 +570,7 @@ private void UpdateStatusArea() /// private void UpdateListPeptide(int selectPeptideIndex) { + _matcher.LibraryName = _selectedLibName; var pepMatcher = new ViewLibraryPepMatching(Document, _selectedLibrary, _selectedSpec, _matcher, _peptides); listPeptide.BeginUpdate(); diff --git a/pwiz_tools/Skyline/Test/CrosslinkModTest.cs b/pwiz_tools/Skyline/Test/CrosslinkModTest.cs index 4e80903dcc6..7fee05a8db0 100644 --- a/pwiz_tools/Skyline/Test/CrosslinkModTest.cs +++ b/pwiz_tools/Skyline/Test/CrosslinkModTest.cs @@ -379,7 +379,7 @@ public void TestLooplinkCrosslinkLibraryKey() ))); var libKeyModificationMatcher = new LibKeyModificationMatcher(); libKeyModificationMatcher.CreateMatches(srmSettings, new[] {new LibKey(crosslinkLibraryKey)}, - new MappedList(), new MappedList()); + new MappedList(), new MappedList(), null); var peptideDocNode = libKeyModificationMatcher.CreateDocNodeFromSettings(new LibKey(crosslinkLibraryKey), new Peptide("AKIQDKEGIPPDQQR"), SrmSettingsDiff.ALL, out _); diff --git a/pwiz_tools/Skyline/Test/ModificationMatcherTest.cs b/pwiz_tools/Skyline/Test/ModificationMatcherTest.cs index 97c1a5797ae..e70ccb5bf6b 100644 --- a/pwiz_tools/Skyline/Test/ModificationMatcherTest.cs +++ b/pwiz_tools/Skyline/Test/ModificationMatcherTest.cs @@ -247,7 +247,7 @@ public void TestModificationMatcher() int yeastLibIndex = docLibraries.IndexOf(library => Equals(library.Name, yeastLibSpec.Name)); libkeyModMatcher.CreateMatches(modMatchDocContainer.Document.Settings, - docLibraries[anlLibIndex].Keys, defSetSetLight, defSetHeavy); + docLibraries[anlLibIndex].Keys, defSetSetLight, defSetHeavy, anlLibSpec.Name); // Test can match 15N Assert.IsTrue(libkeyModMatcher.Matches.Values.Contains(match => @@ -266,7 +266,7 @@ public void TestModificationMatcher() // Test can match Cysteine (Implicit) and Met Ox (variable) libkeyModMatcher.CreateMatches(modMatchDocContainer.Document.Settings, - docLibraries[yeastLibIndex].Keys, defSetSetLight, defSetHeavy); + docLibraries[yeastLibIndex].Keys, defSetSetLight, defSetHeavy, yeastLibSpec.Name); Assert.IsTrue(libkeyModMatcher.MatcherPepMods.StaticModifications.Contains(mod => mod.ParsedMolecule.Equals(UniMod.GetModification(StaticModList.DEFAULT_NAME, true).ParsedMolecule) && !mod.IsVariable)); Assert.IsTrue(libkeyModMatcher.MatcherPepMods.StaticModifications.Contains(mod => From 83fa52f59acc72238ee49413d62ef61576c0222f Mon Sep 17 00:00:00 2001 From: Brian Pratt Date: Wed, 13 Nov 2024 13:02:51 -0800 Subject: [PATCH 10/12] Move strings to resource file --- .../Model/AbstractModificationMatcher.cs | 4 ++-- .../Skyline/Model/ModelResources.designer.cs | 21 ++++++++++++++++++- pwiz_tools/Skyline/Model/ModelResources.resx | 6 ++++++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs b/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs index 7f501ba46c7..c58ff5bcfea 100644 --- a/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs +++ b/pwiz_tools/Skyline/Model/AbstractModificationMatcher.cs @@ -503,8 +503,8 @@ public PeptideDocNode CreateDocNodeFromSettings(LibKey key, Peptide peptide, Srm catch (InvalidChemicalModificationException e) { var message = string.IsNullOrEmpty(LibraryName) ? - string.Format("In \"{0}\": {1}", key.Target.DisplayName, e.Message) : - string.Format("In entry \"{0}\" of \"{1}\": {2}", key.Target.DisplayName, LibraryName, e.Message); + string.Format(ModelResources.AbstractModificationMatcher_CreateDocNodeFromSettings_In___0_____1_, key.Target.DisplayName, e.Message) : + string.Format(ModelResources.AbstractModificationMatcher_CreateDocNodeFromSettings_In_entry___0___of___1_____2_, key.Target.DisplayName, LibraryName, e.Message); Messages.WriteAsyncUserMessage(message); // Adduct makes no sense for target formula } } diff --git a/pwiz_tools/Skyline/Model/ModelResources.designer.cs b/pwiz_tools/Skyline/Model/ModelResources.designer.cs index 2d0f6fd36d7..b532d581a95 100644 --- a/pwiz_tools/Skyline/Model/ModelResources.designer.cs +++ b/pwiz_tools/Skyline/Model/ModelResources.designer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 @@ -202,6 +202,25 @@ public static string AbstractMassListExporter_ExportScheduledBuckets_transitions } } + /// + /// Looks up a localized string similar to In "{0}": {1}. + /// + public static string AbstractModificationMatcher_CreateDocNodeFromSettings_In___0_____1_ { + get { + return ResourceManager.GetString("AbstractModificationMatcher_CreateDocNodeFromSettings_In___0_____1_", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to In entry "{0}" of "{1}": {2}. + /// + public static string AbstractModificationMatcher_CreateDocNodeFromSettings_In_entry___0___of___1_____2_ { + get { + return ResourceManager.GetString("AbstractModificationMatcher_CreateDocNodeFromSettings_In_entry___0___of___1_____2" + + "_", resourceCulture); + } + } + /// /// Looks up a localized string similar to The following modifications could not be interpreted.. /// diff --git a/pwiz_tools/Skyline/Model/ModelResources.resx b/pwiz_tools/Skyline/Model/ModelResources.resx index 49a09904748..533fbc37706 100644 --- a/pwiz_tools/Skyline/Model/ModelResources.resx +++ b/pwiz_tools/Skyline/Model/ModelResources.resx @@ -821,4 +821,10 @@ Failed to interpret modified sequence {0} + + In "{0}": {1} + + + In entry "{0}" of "{1}": {2} + \ No newline at end of file From 850be294072c177c286ba8f8950d0ec67b550521 Mon Sep 17 00:00:00 2001 From: Brian Pratt Date: Wed, 13 Nov 2024 13:05:45 -0800 Subject: [PATCH 11/12] Avoid repeated messages (within the last 500msec) in Messages.WriteAsyncUserMessage --- .../Skyline/Util/TraceWarningListener.cs | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/pwiz_tools/Skyline/Util/TraceWarningListener.cs b/pwiz_tools/Skyline/Util/TraceWarningListener.cs index 38a1b5c7e6b..0676f2c4ece 100644 --- a/pwiz_tools/Skyline/Util/TraceWarningListener.cs +++ b/pwiz_tools/Skyline/Util/TraceWarningListener.cs @@ -16,19 +16,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; namespace pwiz.Skyline.Util { public class TraceWarningListener : TraceListener { private TextWriter _textWriter; + + private Dictionary _recentMessages; + public TraceWarningListener(TextWriter textWriter) { // N.B. if you want to see DebugMessage.AsynchWrite messages too, add .Information to the flags here Filter = new EventTypeFilter(SourceLevels.Warning); _textWriter = textWriter; + _recentMessages = new Dictionary(); } public override void Write(string message) @@ -41,6 +49,31 @@ public override void WriteLine(string message) // Ignore messages which have no TraceEventType } + private bool IsRepeated(string message) + { + var now = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; + lock (_recentMessages) + { + // Clear out any older messages + foreach (var staleMessage in _recentMessages.Where(staleMessage => staleMessage.Value + 500 < now).ToArray()) + { + _recentMessages.Remove(staleMessage.Key); + } + + // If this message has been shown recently, don't show it again + if (_recentMessages.ContainsKey(message)) + { + return true; + } + + // Be ready to ignore rapidly repeating messages + _recentMessages.Add(message, now); + + // Not a recent repeat + return false; + } + } + /// /// Override the TraceEvent method to prevent "WriteHeader" and "WriteFooter" from being called. /// @@ -54,14 +87,17 @@ public override void TraceEvent( { if (Filter != null && !Filter.ShouldTrace(eventCache, source, eventType, id, format, args, null, null)) return; - _textWriter.WriteLine(format, args); + var message = string.Format(format, args); + if (!IsRepeated(message)) + _textWriter.WriteLine(message); } public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message) { if (Filter != null && !Filter.ShouldTrace(eventCache, source, eventType, id, message, null, null, null)) return; - _textWriter.WriteLine(message); + if (!IsRepeated(message)) + _textWriter.WriteLine(message); } } } From d5c4dc9820c1f7a09884f1abf6dd39c7bfc07997 Mon Sep 17 00:00:00 2001 From: Brian Pratt Date: Wed, 13 Nov 2024 15:07:35 -0800 Subject: [PATCH 12/12] use Messages.WriteAsyncUserMessage to non-blockingly explain why library entries are filtered out as being invalid instead of just doing it silently --- pwiz_tools/Skyline/Model/Lib/BiblioSpecLite.cs | 2 +- pwiz_tools/Skyline/Model/Lib/EncylopeDiaLibrary.cs | 2 +- pwiz_tools/Skyline/Model/Lib/Library.cs | 12 ++++++++---- pwiz_tools/Skyline/Model/Lib/NistLibSpec.cs | 2 +- pwiz_tools/Skyline/Model/Lib/XHunterSpec.cs | 2 +- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/pwiz_tools/Skyline/Model/Lib/BiblioSpecLite.cs b/pwiz_tools/Skyline/Model/Lib/BiblioSpecLite.cs index 28c3b5bfeb5..c162a2fb1da 100644 --- a/pwiz_tools/Skyline/Model/Lib/BiblioSpecLite.cs +++ b/pwiz_tools/Skyline/Model/Lib/BiblioSpecLite.cs @@ -856,7 +856,7 @@ private MemoryStream CreateCache(ILoadMonitor loader, IProgressStatus status, in } } - libraryEntries = FilterInvalidLibraryEntries(ref status, libraryEntries); + libraryEntries = FilterInvalidLibraryEntries(ref status, libraryEntries, Path.GetFileName(FilePath)); if (schemaVer > 0) { diff --git a/pwiz_tools/Skyline/Model/Lib/EncylopeDiaLibrary.cs b/pwiz_tools/Skyline/Model/Lib/EncylopeDiaLibrary.cs index 3559bcba321..b6fb0a3e693 100644 --- a/pwiz_tools/Skyline/Model/Lib/EncylopeDiaLibrary.cs +++ b/pwiz_tools/Skyline/Model/Lib/EncylopeDiaLibrary.cs @@ -340,7 +340,7 @@ private bool LoadLibraryFromDatabase(ILoadMonitor loader) var spectrumInfos = libKeySourceFileDatas .Where(entry => quantPeptides.Contains(entry.Key)) .Select(entry => MakeSpectrumInfo(entry.Key, entry.Value, sourceFileIds)); - SetLibraryEntries(FilterInvalidLibraryEntries(ref status, spectrumInfos)); + SetLibraryEntries(FilterInvalidLibraryEntries(ref status, spectrumInfos, Path.GetFileName(FilePath))); _sourceFiles = new LibraryFiles(sourceFiles); // ReSharper restore PossibleMultipleEnumeration loader.UpdateProgress(status.Complete()); diff --git a/pwiz_tools/Skyline/Model/Lib/Library.cs b/pwiz_tools/Skyline/Model/Lib/Library.cs index 99efe194c97..224dfa557fd 100644 --- a/pwiz_tools/Skyline/Model/Lib/Library.cs +++ b/pwiz_tools/Skyline/Model/Lib/Library.cs @@ -1077,15 +1077,17 @@ protected virtual void SetLibraryEntries(IEnumerable entries) _libraryEntries = new LibKeyMap(entryList, entryList.Select(entry=>entry.Key.LibraryKey)); } - protected List FilterInvalidLibraryEntries(ref IProgressStatus status, IEnumerable entries) + protected List FilterInvalidLibraryEntries(ref IProgressStatus status, IEnumerable entries, string dataSource) { var validEntries = new List(); var invalidKeys = new List(); foreach (var entry in entries) { - if (!IsValidLibKey(entry.Key)) + if (!IsValidLibKey(entry.Key, out var whyNot)) { invalidKeys.Add(entry.Key); + Messages.WriteAsyncUserMessage(ModelResources.AbstractModificationMatcher_CreateDocNodeFromSettings_In_entry___0___of___1_____2_, + entry.Key, dataSource, whyNot); } else { @@ -1097,15 +1099,17 @@ protected List FilterInvalidLibraryEntries(ref IProgressStatus status, IE return validEntries; } - protected bool IsValidLibKey(LibKey libKey) + protected bool IsValidLibKey(LibKey libKey, out string errorMessage) { try { + errorMessage = null; var unused = libKey.LibraryKey.CreatePeptideIdentityObj(); return true; } - catch (Exception) + catch (Exception e) { + errorMessage = e.Message; return false; } } diff --git a/pwiz_tools/Skyline/Model/Lib/NistLibSpec.cs b/pwiz_tools/Skyline/Model/Lib/NistLibSpec.cs index cda7cafdc7c..192b1e358bd 100644 --- a/pwiz_tools/Skyline/Model/Lib/NistLibSpec.cs +++ b/pwiz_tools/Skyline/Model/Lib/NistLibSpec.cs @@ -1172,7 +1172,7 @@ private bool CreateCache(ILoadMonitor loader, IProgressStatus status, int percen libraryEntries.Add(info); } - libraryEntries = FilterInvalidLibraryEntries(ref status, libraryEntries); + libraryEntries = FilterInvalidLibraryEntries(ref status, libraryEntries, Path.GetFileName(FilePath)); long locationHeaders = outStream.Position; foreach (var info in libraryEntries) diff --git a/pwiz_tools/Skyline/Model/Lib/XHunterSpec.cs b/pwiz_tools/Skyline/Model/Lib/XHunterSpec.cs index c93f9c5659d..1443968c31b 100644 --- a/pwiz_tools/Skyline/Model/Lib/XHunterSpec.cs +++ b/pwiz_tools/Skyline/Model/Lib/XHunterSpec.cs @@ -485,7 +485,7 @@ private bool CreateCache(ILoadMonitor loader, IProgressStatus status, int percen } } - libraryEntries = FilterInvalidLibraryEntries(ref status, libraryEntries); + libraryEntries = FilterInvalidLibraryEntries(ref status, libraryEntries, Path.GetFileName(FilePath)); using (FileSaver fs = new FileSaver(CachePath, sm)) using (Stream outStream = sm.CreateStream(fs.SafeName, FileMode.Create, true))