From 173c69305abbb07e83128766c16b164259e247ae Mon Sep 17 00:00:00 2001 From: "Lewis Winterberger (RIT Student)" Date: Thu, 25 Jun 2026 17:40:35 -0400 Subject: [PATCH 01/20] spectrum_meta_data --- .../wrappers/test_spectrum_meta_data.py | 46 +++++++ .../ms_data_extraction/1_0_0/__init__.py | 1 + .../1_0_0/extract_meta_data.py | 122 +++++++++++++++++ .../resources/ms_data_extraction/__init__.py | 1 + urgap/unodes/ms_data_extraction/__init__.py | 1 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 232 bytes .../spectrum_meta_data/__init__.py | 1 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 251 bytes .../spectrum_meta_data.cpython-311.pyc | Bin 0 -> 5411 bytes .../spectrum_meta_data/spectrum_meta_data.py | 123 ++++++++++++++++++ 10 files changed, 295 insertions(+) create mode 100644 tests/integrationtests/wrappers/test_spectrum_meta_data.py create mode 100644 urgap/resources/ms_data_extraction/1_0_0/__init__.py create mode 100644 urgap/resources/ms_data_extraction/1_0_0/extract_meta_data.py create mode 100644 urgap/resources/ms_data_extraction/__init__.py create mode 100644 urgap/unodes/ms_data_extraction/__init__.py create mode 100644 urgap/unodes/ms_data_extraction/__pycache__/__init__.cpython-311.pyc create mode 100644 urgap/unodes/ms_data_extraction/spectrum_meta_data/__init__.py create mode 100644 urgap/unodes/ms_data_extraction/spectrum_meta_data/__pycache__/__init__.cpython-311.pyc create mode 100644 urgap/unodes/ms_data_extraction/spectrum_meta_data/__pycache__/spectrum_meta_data.cpython-311.pyc create mode 100644 urgap/unodes/ms_data_extraction/spectrum_meta_data/spectrum_meta_data.py diff --git a/tests/integrationtests/wrappers/test_spectrum_meta_data.py b/tests/integrationtests/wrappers/test_spectrum_meta_data.py new file mode 100644 index 0000000..b74079d --- /dev/null +++ b/tests/integrationtests/wrappers/test_spectrum_meta_data.py @@ -0,0 +1,46 @@ +import tempfile + +import numpy as np +import pandas as pd +import pytest + +import urgap + + +def test_spectrum_meta_data_wrapper_bsa_mzml(): + with tempfile.TemporaryDirectory() as tmp_dir: + ufiles = urgap.UFileList( + [ + urgap.UFile( + uri=f"https://raw.githubusercontent.com/OpenMS/OpenMS/develop/share/OpenMS/examples/BSA" + f"?uftype={urgap.uftypes.ms.converter.mzml.THERMORAWPARSER_MZML}#BSA1.mzML" + ) + ] + ) + urd = urgap.URunDict( + { + "parameters": { + "ExtractSpectrumMetaData:1.0.0": {"-tf": "%Y-%m-%d %H:%M:%S"}, + }, + "unode_parameters": { + "force": True, + "storage_base_uri": f"file://{tmp_dir}", + }, + } + ) + test_node = urgap.init_unode("ExtractSpectrumMetaData:1.0.0") + response = test_node.run(ufiles, urd) + expected = {"run_shape": (1, 6), "size": 13.64, "median_mz": 532.68} + + # 1. Assert the run metadata + assert response[0].path.exists() + df = pd.read_csv(response[0].path) + assert df.shape == expected["run_shape"] + assert df["file"][0] == ufiles[0].object_name == df["lineage_root"][0] + assert pytest.approx(df["file_size_mb"][0], 0.01) == expected["size"] + # assert np.isnan(df.stop_time[0]) + + # 2. Assert the spectrum metadata + assert response[1].path.exists() + df2 = pd.read_csv(response[1].path) + assert pytest.approx(df2.precursor_mz.median(), 0.01) == expected["median_mz"] diff --git a/urgap/resources/ms_data_extraction/1_0_0/__init__.py b/urgap/resources/ms_data_extraction/1_0_0/__init__.py new file mode 100644 index 0000000..49e6275 --- /dev/null +++ b/urgap/resources/ms_data_extraction/1_0_0/__init__.py @@ -0,0 +1 @@ +"""Init SpectrumMetaData Unode.""" diff --git a/urgap/resources/ms_data_extraction/1_0_0/extract_meta_data.py b/urgap/resources/ms_data_extraction/1_0_0/extract_meta_data.py new file mode 100644 index 0000000..cf9a6b4 --- /dev/null +++ b/urgap/resources/ms_data_extraction/1_0_0/extract_meta_data.py @@ -0,0 +1,122 @@ +"""Init SpectrumMetaData Unode.""" + +import argparse + +from pathlib import Path + +from simepy.extract_meta_data import extract_meta_data + + +def main( + input_file: str, + run_output_file: str | None, + spec_output_file: str | None, + noise_output_file: str | None, + unit_output_file: str | None, + time_format: str, + object_name: str, + lineage_root: str, +) -> None: + """Extract scan info from mzml file using extract_scan_data(). + + Args: + input_file (PosixPath): path to input file + run_output_file (str/NoneType): path to run metadata output_file + spec_output_file (str/NoneType): path to spectrum metadata output_file + noise_output_file (str/NoneType): path to spectrum noise output_file + unit_output_file (str/NoneType): path to instrument units output_file + time_format (str): string defining the time_format to format into + object_name (str/NoneType): string to be used as file identifier + lineage_root (str/NoneType): name of the root file associated to the one, from which metadata is queried + """ + rmd_df, imu_df, smd_df, sn_df = extract_meta_data( + input_file=input_file, + time_format=time_format, + object_name=object_name, + lineage_root=lineage_root, + ) + + if run_output_file is not None: + rmd_df.to_csv(run_output_file, index=False) + + if spec_output_file is not None: + smd_df.to_csv(spec_output_file, index=False) + + if unit_output_file is not None: + imu_df.to_csv(unit_output_file, index=False) + + if noise_output_file is not None: + sn_df.to_csv(noise_output_file, index=False) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument( + "-i", + "--input_file", + dest="input_file", + help="input file", + ) + parser.add_argument( + "-ro", + "--run_output_file", + type=lambda x: None if x == "None" else str(x), + dest="run_output_file", + help="run metadata output file", + ) + parser.add_argument( + "-so", + "--spec_output_file", + type=lambda x: None if x == "None" else str(x), + dest="spec_output_file", + help="spectrum metadata output file", + ) + parser.add_argument( + "-sno", + "--spec_noise_output_file", + type=lambda x: None if x == "None" else str(x), + dest="spec_noise_output_file", + help="spectrum noise output file", + ) + parser.add_argument( + "-iuo", + "--instrument_unit_output_file", + type=lambda x: None if x == "None" else str(x), + dest="instrument_unit_output_file", + help="instrument unit output file", + ) + parser.add_argument( + "-tf", + "--time_format", + default="%Y-%m-%d %H:%M:%S", + dest="time_format", + help="time format string", + ) + parser.add_argument( + "-on", + "--object_name", + dest="object_name", + help="object_name string", + ) + parser.add_argument( + "-lr", + "--lineage_root", + dest="lineage_root", + help="lineage_root string", + ) + args = parser.parse_args() + + if args.object_name is None: + args.object_name = Path(args.input_file).name + if args.lineage_root is None: + args.lineage_root = Path(args.input_file).resolve() + main( + input_file=args.input_file, + run_output_file=args.run_output_file, + spec_output_file=args.spec_output_file, + noise_output_file=args.spec_noise_output_file, + unit_output_file=args.instrument_unit_output_file, + time_format=args.time_format, + object_name=args.object_name, + lineage_root=args.lineage_root, + ) diff --git a/urgap/resources/ms_data_extraction/__init__.py b/urgap/resources/ms_data_extraction/__init__.py new file mode 100644 index 0000000..49e6275 --- /dev/null +++ b/urgap/resources/ms_data_extraction/__init__.py @@ -0,0 +1 @@ +"""Init SpectrumMetaData Unode.""" diff --git a/urgap/unodes/ms_data_extraction/__init__.py b/urgap/unodes/ms_data_extraction/__init__.py new file mode 100644 index 0000000..49e6275 --- /dev/null +++ b/urgap/unodes/ms_data_extraction/__init__.py @@ -0,0 +1 @@ +"""Init SpectrumMetaData Unode.""" diff --git a/urgap/unodes/ms_data_extraction/__pycache__/__init__.cpython-311.pyc b/urgap/unodes/ms_data_extraction/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5bc5ba946dee6dd4f1e73da5cd4b8e7985cbb33a GIT binary patch literal 232 zcmZ3^%ge<81g+C-vy_4KV-N=h7@>^MASKfoQW&BbQW%37G?}VoJo7S36oLy5dv{4^PFvB$@!xg;|`Pd`3B6KHXKyk0@& nFAkgB{FKt1RJ$TJpba3m77GH256p~=j5ipRF2GO`3s4jQ+Z{qW literal 0 HcmV?d00001 diff --git a/urgap/unodes/ms_data_extraction/spectrum_meta_data/__init__.py b/urgap/unodes/ms_data_extraction/spectrum_meta_data/__init__.py new file mode 100644 index 0000000..49e6275 --- /dev/null +++ b/urgap/unodes/ms_data_extraction/spectrum_meta_data/__init__.py @@ -0,0 +1 @@ +"""Init SpectrumMetaData Unode.""" diff --git a/urgap/unodes/ms_data_extraction/spectrum_meta_data/__pycache__/__init__.cpython-311.pyc b/urgap/unodes/ms_data_extraction/spectrum_meta_data/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..226a9fb5b7a214ce7e864dd951dfe7cf18e77a3a GIT binary patch literal 251 zcmZ3^%ge<81m+1QS;|2AF^B^Lj8MjBkdo;PDGX5zDU87knoLzPo_U!i3c&@b$t6Xl zxxT3-i7tsHi3*{4`6;P-ewvK8*yH0<@{{A^S2BDC8S*Pdzc?eYC^bdDC|SRtD8D2% zKQ~psv?x8XKtCt7JhK?ehKQ7cj49U7EsjqCni`*4QBstcT#}ier(X=UF+LY)C0JHJ xK0XuZmiTzRg34bUHo5sJr8%i~MQlJzLC!B01QH*Z85tRGFeqJsp&}NbC;&O%Nnrp0 literal 0 HcmV?d00001 diff --git a/urgap/unodes/ms_data_extraction/spectrum_meta_data/__pycache__/spectrum_meta_data.cpython-311.pyc b/urgap/unodes/ms_data_extraction/spectrum_meta_data/__pycache__/spectrum_meta_data.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc641d61c245b6eeffe8fa5ce05ae44d28558989 GIT binary patch literal 5411 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 None: + """Initialize ExtractSpectrumMetaData class.""" + super().__init__(*args, **kwargs) + + def preflight( + self, + utrace: urgap.UTrace, + ) -> urgap.UTrace: + """Preflight routine for ExtractSpectrumMetaData wrapper. + + Checks that only one file at a time is processed and if so, provides the file + path to the main function of the resource to extract metadata from. + + Args: + utrace: Combination of urun_dict, ufile_list and unode.meta. + + Returns: + UTrace object, combination of urun_dict, ufile_list and unode.meta. + """ + index_dict = utrace.input_files.get_index_groups_by_uftypes() + uftype_to_process = None + for uftype, index_list in index_dict.items(): + if len(index_list) > 0: + if uftype_to_process is None: + uftype_to_process = uftype + else: + logging.warning( + "Meta node received multiple mass spec files, " + "however node can only process one at the time!" + "Therefore will process %s but not %s!", + uftype_to_process, + uftype, + ) + + idx_ms = index_dict[uftype_to_process][0] + input_file = utrace.input_files[idx_ms] + + run_meta_output_file = utrace.output_files.get_path_objects_by_uftype( + urgap.uftypes.ms.RUN_META_CSV, + )[0] + spec_meta_output_file = utrace.output_files.get_path_objects_by_uftype( + urgap.uftypes.ms.SPECTRA_META_CSV, + )[0] + spec_noise_output_file = utrace.output_files.get_path_objects_by_uftype( + urgap.uftypes.ms.SPECTRA_NOISE_CSV, + )[0] + instrument_unit_output_file = utrace.output_files.get_path_objects_by_uftype( + urgap.uftypes.ms.INSTRUMENT_UNIT_CSV, + )[0] + object_name = input_file.object_name + utrace.urun_dict.command_list = [ + "python", + str(self.exe_path), + "-i", + str(input_file.path), + "-ro", + str(run_meta_output_file), + "-so", + str(spec_meta_output_file), + "-sno", + str(spec_noise_output_file), + "-iuo", + str(instrument_unit_output_file), + "-on", + str(object_name), + "-lr", + str(object_name), + ] + for parameter_key, parameter_value in utrace.urun_dict.parameters[ + f"{self.META_INFO['unode_full_identifier']}" + ].items(): + utrace.urun_dict.command_list.extend([parameter_key, parameter_value]) + + return utrace From ce6613f0d2e121ce7418ba3c0da80bf0f240d388 Mon Sep 17 00:00:00 2001 From: lzw-gif Date: Thu, 25 Jun 2026 17:44:36 -0400 Subject: [PATCH 02/20] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c953689..0a815bf 100644 --- a/README.md +++ b/README.md @@ -3,4 +3,4 @@ This repository contains unodes, resources, and tests for the following engines: - PymzMLToIDXGZ - ThermoRawFileParser - +- SpectrumMetaData From 2d7d5b124e2863cfd3828c699e7626c423c510ba Mon Sep 17 00:00:00 2001 From: lzw-gif Date: Thu, 25 Jun 2026 20:56:05 -0400 Subject: [PATCH 03/20] Delete urgap/unodes/ms_data_extraction/__pycache__ directory --- .../__pycache__/__init__.cpython-311.pyc | Bin 232 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 urgap/unodes/ms_data_extraction/__pycache__/__init__.cpython-311.pyc diff --git a/urgap/unodes/ms_data_extraction/__pycache__/__init__.cpython-311.pyc b/urgap/unodes/ms_data_extraction/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index 5bc5ba946dee6dd4f1e73da5cd4b8e7985cbb33a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 232 zcmZ3^%ge<81g+C-vy_4KV-N=h7@>^MASKfoQW&BbQW%37G?}VoJo7S36oLy5dv{4^PFvB$@!xg;|`Pd`3B6KHXKyk0@& nFAkgB{FKt1RJ$TJpba3m77GH256p~=j5ipRF2GO`3s4jQ+Z{qW From 64194d4b94062576c73333b802ee386ccf448e6f Mon Sep 17 00:00:00 2001 From: lzw-gif Date: Thu, 25 Jun 2026 21:01:29 -0400 Subject: [PATCH 04/20] Delete urgap/unodes/ms_data_extraction/spectrum_meta_data/__pycache__/__init__.cpython-311.pyc --- .../__pycache__/__init__.cpython-311.pyc | Bin 251 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 urgap/unodes/ms_data_extraction/spectrum_meta_data/__pycache__/__init__.cpython-311.pyc diff --git a/urgap/unodes/ms_data_extraction/spectrum_meta_data/__pycache__/__init__.cpython-311.pyc b/urgap/unodes/ms_data_extraction/spectrum_meta_data/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index 226a9fb5b7a214ce7e864dd951dfe7cf18e77a3a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 251 zcmZ3^%ge<81m+1QS;|2AF^B^Lj8MjBkdo;PDGX5zDU87knoLzPo_U!i3c&@b$t6Xl zxxT3-i7tsHi3*{4`6;P-ewvK8*yH0<@{{A^S2BDC8S*Pdzc?eYC^bdDC|SRtD8D2% zKQ~psv?x8XKtCt7JhK?ehKQ7cj49U7EsjqCni`*4QBstcT#}ier(X=UF+LY)C0JHJ xK0XuZmiTzRg34bUHo5sJr8%i~MQlJzLC!B01QH*Z85tRGFeqJsp&}NbC;&O%Nnrp0 From 2d6705455d9700ea13eedb22fdce58b3eb77bc32 Mon Sep 17 00:00:00 2001 From: lzw-gif Date: Thu, 25 Jun 2026 21:01:49 -0400 Subject: [PATCH 05/20] Delete urgap/unodes/ms_data_extraction/spectrum_meta_data/__pycache__/spectrum_meta_data.cpython-311.pyc --- .../spectrum_meta_data.cpython-311.pyc | Bin 5411 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 urgap/unodes/ms_data_extraction/spectrum_meta_data/__pycache__/spectrum_meta_data.cpython-311.pyc diff --git a/urgap/unodes/ms_data_extraction/spectrum_meta_data/__pycache__/spectrum_meta_data.cpython-311.pyc b/urgap/unodes/ms_data_extraction/spectrum_meta_data/__pycache__/spectrum_meta_data.cpython-311.pyc deleted file mode 100644 index fc641d61c245b6eeffe8fa5ce05ae44d28558989..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5411 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 Date: Tue, 30 Jun 2026 16:00:40 -0400 Subject: [PATCH 06/20] modified pyproject.toml --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 9f96e88..7b4094c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,7 @@ dev = [ "pytest", "pytest-asyncio>=0.23", "urgap[dev]", + "simepy", ] proteomics = [ From 6f49f149b1cb7e991c8a398a3427313fe6bb3e51 Mon Sep 17 00:00:00 2001 From: lzw-gif Date: Tue, 30 Jun 2026 16:08:33 -0400 Subject: [PATCH 07/20] Update pyproject.toml --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7b4094c..f8558e6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,8 @@ description = "Urgap proteomics specific extension" requires-python = ">=3.11" dependencies = [ "urgap[all]", - "pymzml" + "pymzml", + "simepy", ] [project.optional-dependencies] @@ -17,7 +18,6 @@ dev = [ "pytest", "pytest-asyncio>=0.23", "urgap[dev]", - "simepy", ] proteomics = [ From c00bccdecd5915187d48d11f6421541ae19b2961 Mon Sep 17 00:00:00 2001 From: lzw-gif Date: Tue, 30 Jun 2026 16:24:05 -0400 Subject: [PATCH 08/20] Update pyproject.toml --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 58718d1..5a984b9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,9 +9,9 @@ description = "Urgap proteomics specific extension" requires-python = ">=3.11" dependencies = [ "urgap[all]", + "simepy>=0.3.2", "pymzml>=2.5.0", - "pyiohat>=1.9.0", - "simepy>=0.2.1", + "pyiohat>=1.9.0" ] [project.optional-dependencies] From db9e01bd01a1b7295e4af67e9f3b72f1222d65a0 Mon Sep 17 00:00:00 2001 From: lzw-gif Date: Tue, 30 Jun 2026 16:33:18 -0400 Subject: [PATCH 09/20] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 5a984b9..da82945 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ description = "Urgap proteomics specific extension" requires-python = ">=3.11" dependencies = [ "urgap[all]", - "simepy>=0.3.2", + "simepy", "pymzml>=2.5.0", "pyiohat>=1.9.0" ] From ef350ed1371096ebe3544b2c994e67a6e181cb05 Mon Sep 17 00:00:00 2001 From: lzw-gif Date: Fri, 3 Jul 2026 16:31:47 -0400 Subject: [PATCH 10/20] Update pyproject.toml urgap[cloud] --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index da82945..180f2d3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ version = "0.0.1" description = "Urgap proteomics specific extension" requires-python = ">=3.11" dependencies = [ - "urgap[all]", + "urgap[cloud]", "simepy", "pymzml>=2.5.0", "pyiohat>=1.9.0" From ed91dd209b998a2da798ada52c5bef1f6af35ae5 Mon Sep 17 00:00:00 2001 From: lzw-gif Date: Fri, 3 Jul 2026 16:36:39 -0400 Subject: [PATCH 11/20] Update pyproject.toml urgap[databases] --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 180f2d3..802e3d6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ version = "0.0.1" description = "Urgap proteomics specific extension" requires-python = ">=3.11" dependencies = [ - "urgap[cloud]", + "urgap[databases]", "simepy", "pymzml>=2.5.0", "pyiohat>=1.9.0" From d559623112fa261cda81d4d11b3b609b551f1e33 Mon Sep 17 00:00:00 2001 From: lzw-gif Date: Fri, 3 Jul 2026 16:39:51 -0400 Subject: [PATCH 12/20] Update pyproject.toml urgap[dev] --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 802e3d6..15a5d46 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ version = "0.0.1" description = "Urgap proteomics specific extension" requires-python = ">=3.11" dependencies = [ - "urgap[databases]", + "urgap[dev]", "simepy", "pymzml>=2.5.0", "pyiohat>=1.9.0" From f4f0864893e3e17d41fc0e21cee4e545be5354f0 Mon Sep 17 00:00:00 2001 From: lzw-gif Date: Fri, 3 Jul 2026 16:42:31 -0400 Subject: [PATCH 13/20] Update pyproject.toml urgap[docs] --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 15a5d46..c0b2faa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ version = "0.0.1" description = "Urgap proteomics specific extension" requires-python = ">=3.11" dependencies = [ - "urgap[dev]", + "urgap[docs]", "simepy", "pymzml>=2.5.0", "pyiohat>=1.9.0" From 00de8c2dbbafa1ba1779f8cd739172c590aedf28 Mon Sep 17 00:00:00 2001 From: lzw-gif Date: Fri, 3 Jul 2026 17:05:33 -0400 Subject: [PATCH 14/20] Update pyproject.toml urgap[dev] --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index c0b2faa..ecc2901 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,6 +10,7 @@ requires-python = ">=3.11" dependencies = [ "urgap[docs]", "simepy", + "numpy>=2.0", "pymzml>=2.5.0", "pyiohat>=1.9.0" ] From a45f7aebbe5c383c0f0f19cbb98d4799beaba5d3 Mon Sep 17 00:00:00 2001 From: lzw-gif Date: Fri, 3 Jul 2026 17:06:46 -0400 Subject: [PATCH 15/20] Update pyproject.toml urgap[dev] --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ecc2901..74b9d76 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ version = "0.0.1" description = "Urgap proteomics specific extension" requires-python = ">=3.11" dependencies = [ - "urgap[docs]", + "urgap[dev]", "simepy", "numpy>=2.0", "pymzml>=2.5.0", From 247526b9121c5050dff3d4348889c9a7a3c11631 Mon Sep 17 00:00:00 2001 From: lzw-gif Date: Fri, 3 Jul 2026 17:16:15 -0400 Subject: [PATCH 16/20] Update pyproject.toml urgap[cloud] urgap[databases] urgap[docs] urgap[dev] --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 74b9d76..b1042c6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,9 @@ version = "0.0.1" description = "Urgap proteomics specific extension" requires-python = ">=3.11" dependencies = [ + "urgap[cloud]", + "urgap[databases]", + "urgap[docs]", "urgap[dev]", "simepy", "numpy>=2.0", From 0bdc677707927156f0cbfc36397c6a9d3d7452ea Mon Sep 17 00:00:00 2001 From: lzw-gif Date: Fri, 3 Jul 2026 17:16:34 -0400 Subject: [PATCH 17/20] Update pyproject.toml --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b1042c6..2dd09ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,6 @@ dependencies = [ "urgap[docs]", "urgap[dev]", "simepy", - "numpy>=2.0", "pymzml>=2.5.0", "pyiohat>=1.9.0" ] From 973f06461a0dacda210c0ce54b52ef0a63bd87e9 Mon Sep 17 00:00:00 2001 From: lzw-gif Date: Tue, 7 Jul 2026 14:36:16 -0400 Subject: [PATCH 18/20] Update pyproject.toml --- pyproject.toml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2dd09ac..a04bdba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,10 +8,9 @@ version = "0.0.1" description = "Urgap proteomics specific extension" requires-python = ">=3.11" dependencies = [ - "urgap[cloud]", - "urgap[databases]", - "urgap[docs]", - "urgap[dev]", + "urgap[all]", + "numpy>=1.26,<2.0", + "pandas>2.0.0,<2.1.0", "simepy", "pymzml>=2.5.0", "pyiohat>=1.9.0" From 174fd3cece37aa5b7d62d9a6c15631ef46140179 Mon Sep 17 00:00:00 2001 From: lzw-gif Date: Tue, 7 Jul 2026 14:49:09 -0400 Subject: [PATCH 19/20] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a04bdba..c73b5b8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ dependencies = [ "urgap[all]", "numpy>=1.26,<2.0", "pandas>2.0.0,<2.1.0", - "simepy", + "simepy>=0.3.2", "pymzml>=2.5.0", "pyiohat>=1.9.0" ] From 794218db09e6f9d3acfa7241269d9c49c74a48ee Mon Sep 17 00:00:00 2001 From: lzw-gif Date: Tue, 7 Jul 2026 15:43:47 -0400 Subject: [PATCH 20/20] Delete tests/__pycache__ directory --- tests/__pycache__/__init__.cpython-39.pyc | Bin 169 -> 0 bytes .../conftest.cpython-39-pytest-8.4.2.pyc | Bin 7737 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 tests/__pycache__/__init__.cpython-39.pyc delete mode 100644 tests/__pycache__/conftest.cpython-39-pytest-8.4.2.pyc diff --git a/tests/__pycache__/__init__.cpython-39.pyc b/tests/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index cc9a100f92e37451d956dc02fd0d9e7b1e58fdfd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 169 zcmYe~<>g`kf)|givUq{?V-N=!FakLaKwQiLBvKfn7*ZI688n%y*fR6-GE4OQG#PKP z$H%ASC&$OHWGG?+DgqO~Z1jsW5{pt(^ox@93yShfQuA|D^-GJ=6AScnQp+=oVS<^- m#rh?w#U;i1@$s2J6XWCc3My}L*yQG?l;)(`fvo=w#0&uT#VhLo diff --git a/tests/__pycache__/conftest.cpython-39-pytest-8.4.2.pyc b/tests/__pycache__/conftest.cpython-39-pytest-8.4.2.pyc deleted file mode 100644 index 2c00381f36b7137688b4de0fa9a602dcb752838a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7737 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