masster 0.5.8__tar.gz → 0.5.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of masster might be problematic. Click here for more details.

Files changed (95) hide show
  1. {masster-0.5.8 → masster-0.5.9}/PKG-INFO +1 -1
  2. {masster-0.5.8 → masster-0.5.9}/pyproject.toml +1 -1
  3. {masster-0.5.8 → masster-0.5.9}/src/masster/_version.py +1 -1
  4. {masster-0.5.8 → masster-0.5.9}/src/masster/logger.py +58 -43
  5. {masster-0.5.8 → masster-0.5.9}/src/masster/sample/h5.py +1 -1
  6. {masster-0.5.8 → masster-0.5.9}/src/masster/sample/plot.py +4 -4
  7. {masster-0.5.8 → masster-0.5.9}/src/masster/sample/processing.py +3 -3
  8. {masster-0.5.8 → masster-0.5.9}/src/masster/sample/save.py +5 -5
  9. {masster-0.5.8 → masster-0.5.9}/src/masster/study/h5.py +1 -1
  10. {masster-0.5.8 → masster-0.5.9}/src/masster/study/helpers.py +4 -4
  11. {masster-0.5.8 → masster-0.5.9}/src/masster/study/id.py +3 -3
  12. {masster-0.5.8 → masster-0.5.9}/src/masster/study/merge.py +4 -4
  13. {masster-0.5.8 → masster-0.5.9}/src/masster/study/processing.py +2 -2
  14. {masster-0.5.8 → masster-0.5.9}/uv.lock +1 -1
  15. {masster-0.5.8 → masster-0.5.9}/.github/workflows/publish.yml +0 -0
  16. {masster-0.5.8 → masster-0.5.9}/.github/workflows/security.yml +0 -0
  17. {masster-0.5.8 → masster-0.5.9}/.github/workflows/test.yml +0 -0
  18. {masster-0.5.8 → masster-0.5.9}/.gitignore +0 -0
  19. {masster-0.5.8 → masster-0.5.9}/.pre-commit-config.yaml +0 -0
  20. {masster-0.5.8 → masster-0.5.9}/LICENSE +0 -0
  21. {masster-0.5.8 → masster-0.5.9}/Makefile +0 -0
  22. {masster-0.5.8 → masster-0.5.9}/README.md +0 -0
  23. {masster-0.5.8 → masster-0.5.9}/TESTING.md +0 -0
  24. {masster-0.5.8 → masster-0.5.9}/demo/example_batch_process.py +0 -0
  25. {masster-0.5.8 → masster-0.5.9}/demo/example_sample_process.py +0 -0
  26. {masster-0.5.8 → masster-0.5.9}/src/masster/__init__.py +0 -0
  27. {masster-0.5.8 → masster-0.5.9}/src/masster/chromatogram.py +0 -0
  28. {masster-0.5.8 → masster-0.5.9}/src/masster/data/dda/20250530_VH_IQX_KW_RP_HSST3_100mm_12min_pos_v4_DDA_OT_C-MiLUT_QC_dil2_01_20250602151849.sample5 +0 -0
  29. {masster-0.5.8 → masster-0.5.9}/src/masster/data/dda/20250530_VH_IQX_KW_RP_HSST3_100mm_12min_pos_v4_DDA_OT_C-MiLUT_QC_dil3_01_20250602150634.sample5 +0 -0
  30. {masster-0.5.8 → masster-0.5.9}/src/masster/data/dda/20250530_VH_IQX_KW_RP_HSST3_100mm_12min_pos_v4_MS1_C-MiLUT_C008_v6_r38_01.sample5 +0 -0
  31. {masster-0.5.8 → masster-0.5.9}/src/masster/data/dda/20250530_VH_IQX_KW_RP_HSST3_100mm_12min_pos_v4_MS1_C-MiLUT_C008_v7_r37_01.sample5 +0 -0
  32. {masster-0.5.8 → masster-0.5.9}/src/masster/data/dda/20250530_VH_IQX_KW_RP_HSST3_100mm_12min_pos_v4_MS1_C-MiLUT_C017_v5_r99_01.sample5 +0 -0
  33. {masster-0.5.8 → masster-0.5.9}/src/masster/data/libs/aa.csv +0 -0
  34. {masster-0.5.8 → masster-0.5.9}/src/masster/data/libs/ccm.csv +0 -0
  35. {masster-0.5.8 → masster-0.5.9}/src/masster/data/libs/hilic.csv +0 -0
  36. {masster-0.5.8 → masster-0.5.9}/src/masster/data/libs/urine.csv +0 -0
  37. {masster-0.5.8 → masster-0.5.9}/src/masster/data/wiff/2025_01_14_VW_7600_LpMx_DBS_CID_2min_TOP15_030msecMS1_005msecReac_CE35_DBS-ON_3.timeseries.data +0 -0
  38. {masster-0.5.8 → masster-0.5.9}/src/masster/data/wiff/2025_01_14_VW_7600_LpMx_DBS_CID_2min_TOP15_030msecMS1_005msecReac_CE35_DBS-ON_3.wiff +0 -0
  39. {masster-0.5.8 → masster-0.5.9}/src/masster/data/wiff/2025_01_14_VW_7600_LpMx_DBS_CID_2min_TOP15_030msecMS1_005msecReac_CE35_DBS-ON_3.wiff.scan +0 -0
  40. {masster-0.5.8 → masster-0.5.9}/src/masster/data/wiff/2025_01_14_VW_7600_LpMx_DBS_CID_2min_TOP15_030msecMS1_005msecReac_CE35_DBS-ON_3.wiff2 +0 -0
  41. {masster-0.5.8 → masster-0.5.9}/src/masster/lib/__init__.py +0 -0
  42. {masster-0.5.8 → masster-0.5.9}/src/masster/lib/lib.py +0 -0
  43. {masster-0.5.8 → masster-0.5.9}/src/masster/sample/__init__.py +0 -0
  44. {masster-0.5.8 → masster-0.5.9}/src/masster/sample/adducts.py +0 -0
  45. {masster-0.5.8 → masster-0.5.9}/src/masster/sample/defaults/__init__.py +0 -0
  46. {masster-0.5.8 → masster-0.5.9}/src/masster/sample/defaults/find_adducts_def.py +0 -0
  47. {masster-0.5.8 → masster-0.5.9}/src/masster/sample/defaults/find_features_def.py +0 -0
  48. {masster-0.5.8 → masster-0.5.9}/src/masster/sample/defaults/find_ms2_def.py +0 -0
  49. {masster-0.5.8 → masster-0.5.9}/src/masster/sample/defaults/get_spectrum_def.py +0 -0
  50. {masster-0.5.8 → masster-0.5.9}/src/masster/sample/defaults/sample_def.py +0 -0
  51. {masster-0.5.8 → masster-0.5.9}/src/masster/sample/helpers.py +0 -0
  52. {masster-0.5.8 → masster-0.5.9}/src/masster/sample/lib.py +0 -0
  53. {masster-0.5.8 → masster-0.5.9}/src/masster/sample/load.py +0 -0
  54. {masster-0.5.8 → masster-0.5.9}/src/masster/sample/parameters.py +0 -0
  55. {masster-0.5.8 → masster-0.5.9}/src/masster/sample/quant.py +0 -0
  56. {masster-0.5.8 → masster-0.5.9}/src/masster/sample/sample.py +0 -0
  57. {masster-0.5.8 → masster-0.5.9}/src/masster/sample/sample5_schema.json +0 -0
  58. {masster-0.5.8 → masster-0.5.9}/src/masster/sample/sciex.py +0 -0
  59. {masster-0.5.8 → masster-0.5.9}/src/masster/spectrum.py +0 -0
  60. {masster-0.5.8 → masster-0.5.9}/src/masster/study/__init__.py +0 -0
  61. {masster-0.5.8 → masster-0.5.9}/src/masster/study/analysis.py +0 -0
  62. {masster-0.5.8 → masster-0.5.9}/src/masster/study/defaults/__init__.py +0 -0
  63. {masster-0.5.8 → masster-0.5.9}/src/masster/study/defaults/align_def.py +0 -0
  64. {masster-0.5.8 → masster-0.5.9}/src/masster/study/defaults/export_def.py +0 -0
  65. {masster-0.5.8 → masster-0.5.9}/src/masster/study/defaults/fill_def.py +0 -0
  66. {masster-0.5.8 → masster-0.5.9}/src/masster/study/defaults/find_consensus_def.py +0 -0
  67. {masster-0.5.8 → masster-0.5.9}/src/masster/study/defaults/find_ms2_def.py +0 -0
  68. {masster-0.5.8 → masster-0.5.9}/src/masster/study/defaults/identify_def.py +0 -0
  69. {masster-0.5.8 → masster-0.5.9}/src/masster/study/defaults/integrate_chrom_def.py +0 -0
  70. {masster-0.5.8 → masster-0.5.9}/src/masster/study/defaults/integrate_def.py +0 -0
  71. {masster-0.5.8 → masster-0.5.9}/src/masster/study/defaults/merge_def.py +0 -0
  72. {masster-0.5.8 → masster-0.5.9}/src/masster/study/defaults/study_def.py +0 -0
  73. {masster-0.5.8 → masster-0.5.9}/src/masster/study/export.py +0 -0
  74. {masster-0.5.8 → masster-0.5.9}/src/masster/study/load.py +0 -0
  75. {masster-0.5.8 → masster-0.5.9}/src/masster/study/parameters.py +0 -0
  76. {masster-0.5.8 → masster-0.5.9}/src/masster/study/plot.py +0 -0
  77. {masster-0.5.8 → masster-0.5.9}/src/masster/study/save.py +0 -0
  78. {masster-0.5.8 → masster-0.5.9}/src/masster/study/study.py +0 -0
  79. {masster-0.5.8 → masster-0.5.9}/src/masster/study/study5_schema.json +0 -0
  80. {masster-0.5.8 → masster-0.5.9}/src/masster/wizard/README.md +0 -0
  81. {masster-0.5.8 → masster-0.5.9}/src/masster/wizard/__init__.py +0 -0
  82. {masster-0.5.8 → masster-0.5.9}/src/masster/wizard/example.py +0 -0
  83. {masster-0.5.8 → masster-0.5.9}/src/masster/wizard/wizard.py +0 -0
  84. {masster-0.5.8 → masster-0.5.9}/tests/conftest.py +0 -0
  85. {masster-0.5.8 → masster-0.5.9}/tests/test_chromatogram.py +0 -0
  86. {masster-0.5.8 → masster-0.5.9}/tests/test_defaults.py +0 -0
  87. {masster-0.5.8 → masster-0.5.9}/tests/test_imports.py +0 -0
  88. {masster-0.5.8 → masster-0.5.9}/tests/test_integration.py +0 -0
  89. {masster-0.5.8 → masster-0.5.9}/tests/test_logger.py +0 -0
  90. {masster-0.5.8 → masster-0.5.9}/tests/test_parameters.py +0 -0
  91. {masster-0.5.8 → masster-0.5.9}/tests/test_sample.py +0 -0
  92. {masster-0.5.8 → masster-0.5.9}/tests/test_spectrum.py +0 -0
  93. {masster-0.5.8 → masster-0.5.9}/tests/test_study.py +0 -0
  94. {masster-0.5.8 → masster-0.5.9}/tests/test_version.py +0 -0
  95. {masster-0.5.8 → masster-0.5.9}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: masster
3
- Version: 0.5.8
3
+ Version: 0.5.9
4
4
  Summary: Mass spectrometry data analysis package
5
5
  Project-URL: homepage, https://github.com/zamboni-lab/masster
6
6
  Project-URL: repository, https://github.com/zamboni-lab/masster
@@ -1,7 +1,7 @@
1
1
 
2
2
  [project]
3
3
  name = "masster"
4
- version = "0.5.8"
4
+ version = "0.5.9"
5
5
  description = "Mass spectrometry data analysis package"
6
6
  authors = [
7
7
  { name = "Zamboni Lab" }
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
 
4
- __version__ = "0.5.8"
4
+ __version__ = "0.5.9"
5
5
 
6
6
 
7
7
  def get_version():
@@ -3,15 +3,15 @@
3
3
  Simple logger system for masster Study and Sample instances.
4
4
  Uses basic Python logging timestamp = dt.strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
5
5
 
6
- # Loguru-style colors for different log levels
6
+ # Universal colors compatible with both dark and light themes
7
7
  level_colors = {
8
- 'TRACE': '\x1b[34m', # blue
9
- 'DEBUG': '\x1b[36m', # cyan
10
- 'INFO': '\x1b[37m', # white
11
- 'SUCCESS': '\x1b[32m', # green
12
- 'WARNING': '\x1b[33m', # yellow
13
- 'ERROR': '\x1b[31m', # red
14
- 'CRITICAL': '\x1b[35m', # magenta
8
+ 'TRACE': '\x1b[94m', # bright blue (readable on both dark/light)
9
+ 'DEBUG': '\x1b[96m', # bright cyan (readable on both dark/light)
10
+ 'INFO': '\x1b[90m', # bright black/gray (readable on both dark/light)
11
+ 'SUCCESS': '\x1b[92m', # bright green (readable on both dark/light)
12
+ 'WARNING': '\x1b[93m', # bright yellow (readable on both dark/light)
13
+ 'ERROR': '\x1b[91m', # bright red (readable on both dark/light)
14
+ 'CRITICAL': '\x1b[95m', # bright magenta (readable on both dark/light)
15
15
  }
16
16
 
17
17
  level_str = record.levelname.ljust(8)complex loguru filtering.
@@ -102,19 +102,19 @@ class MassterLogger:
102
102
  dt = datetime.datetime.fromtimestamp(record.created)
103
103
  timestamp = dt.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] # Remove last 3 digits for milliseconds
104
104
 
105
- # Loguru-style colors for different log levels
105
+ # Universal colors compatible with both dark and light themes
106
106
  level_colors = {
107
- "TRACE": "\x1b[34m", # blue
108
- "DEBUG": "\x1b[36m", # cyan
109
- "INFO": "\x1b[37m", # white
110
- "SUCCESS": "\x1b[32m", # green
111
- "WARNING": "\x1b[33m", # yellow
112
- "ERROR": "\x1b[31m", # red
113
- "CRITICAL": "\x1b[35m", # magenta
107
+ "TRACE": "\x1b[94m", # bright blue (readable on both dark/light)
108
+ "DEBUG": "\x1b[96m", # bright cyan (readable on both dark/light)
109
+ "INFO": "\x1b[90m", # bright black/gray (readable on both dark/light)
110
+ "SUCCESS": "\x1b[92m", # bright green (readable on both dark/light)
111
+ "WARNING": "\x1b[93m", # bright yellow (readable on both dark/light)
112
+ "ERROR": "\x1b[91m", # bright red (readable on both dark/light)
113
+ "CRITICAL": "\x1b[95m", # bright magenta (readable on both dark/light)
114
114
  }
115
115
 
116
116
  level_str = record.levelname.ljust(8)
117
- level_color = level_colors.get(record.levelname, "\x1b[37m") # default white
117
+ level_color = level_colors.get(record.levelname, "\x1b[90m") # default to gray instead of white
118
118
  label_part = self.label + " | " if self.label else ""
119
119
 
120
120
  # For DEBUG and TRACE levels, add module/location information
@@ -133,9 +133,9 @@ class MassterLogger:
133
133
  f"\x1b[90m{module_name}:{func_name}:{line_no}\x1b[0m | " # dim gray for location info
134
134
  )
135
135
 
136
- # Loguru-style format: <white>timestamp</white> | <level>LEVEL</level> | <location> | <cyan>label</cyan> - <level>message</level>
136
+ # Universal format: timestamp | level | location | label - message
137
137
  return (
138
- f"\x1b[37m{timestamp}\x1b[0m | " # white timestamp
138
+ f"\x1b[90m{timestamp}\x1b[0m | " # gray timestamp (universal for both themes)
139
139
  f"{level_color}{level_str}\x1b[0m | " # colored level
140
140
  f"{location_info}" # location info for DEBUG/TRACE
141
141
  f"{level_color}{label_part}\x1b[0m" # colored label
@@ -181,19 +181,19 @@ class MassterLogger:
181
181
  dt = datetime.datetime.fromtimestamp(record.created)
182
182
  timestamp = dt.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
183
183
 
184
- # Loguru-style colors for different log levels
184
+ # Universal colors compatible with both dark and light themes
185
185
  level_colors = {
186
- "TRACE": "\x1b[34m", # blue
187
- "DEBUG": "\x1b[36m", # cyan
188
- "INFO": "\x1b[37m", # white
189
- "SUCCESS": "\x1b[32m", # green
190
- "WARNING": "\x1b[33m", # yellow
191
- "ERROR": "\x1b[31m", # red
192
- "CRITICAL": "\x1b[35m", # magenta
186
+ "TRACE": "\x1b[94m", # bright blue (readable on both dark/light)
187
+ "DEBUG": "\x1b[96m", # bright cyan (readable on both dark/light)
188
+ "INFO": "\x1b[90m", # bright black/gray (readable on both dark/light)
189
+ "SUCCESS": "\x1b[92m", # bright green (readable on both dark/light)
190
+ "WARNING": "\x1b[93m", # bright yellow (readable on both dark/light)
191
+ "ERROR": "\x1b[91m", # bright red (readable on both dark/light)
192
+ "CRITICAL": "\x1b[95m", # bright magenta (readable on both dark/light)
193
193
  }
194
194
 
195
195
  level_str = record.levelname.ljust(8)
196
- level_color = level_colors.get(record.levelname, "\x1b[37m") # default white
196
+ level_color = level_colors.get(record.levelname, "\x1b[90m") # default to gray instead of white
197
197
  label_part = self.label + " | " if self.label else ""
198
198
 
199
199
  # For DEBUG and TRACE levels, add module/location information
@@ -212,9 +212,9 @@ class MassterLogger:
212
212
  f"\x1b[90m{module_name}:{func_name}:{line_no}\x1b[0m | " # dim gray for location info
213
213
  )
214
214
 
215
- # Loguru-style format: <white>timestamp</white> | <level>LEVEL</level> | <location> | <cyan>label</cyan> - <level>message</level>
215
+ # Universal format: timestamp | level | location | label - message
216
216
  return (
217
- f"\x1b[37m{timestamp}\x1b[0m | " # white timestamp
217
+ f"\x1b[90m{timestamp}\x1b[0m | " # gray timestamp (universal for both themes)
218
218
  f"{level_color}{level_str}\x1b[0m | " # colored level
219
219
  f"{location_info}" # location info for DEBUG/TRACE
220
220
  f"{level_color}{label_part}\x1b[0m" # colored label
@@ -245,19 +245,19 @@ class MassterLogger:
245
245
  dt = datetime.datetime.fromtimestamp(record.created)
246
246
  timestamp = dt.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
247
247
 
248
- # Loguru-style colors for different log levels
248
+ # Universal colors compatible with both dark and light themes
249
249
  level_colors = {
250
- "TRACE": "\x1b[34m", # blue
251
- "DEBUG": "\x1b[36m", # cyan
252
- "INFO": "\x1b[37m", # white
253
- "SUCCESS": "\x1b[32m", # green
254
- "WARNING": "\x1b[33m", # yellow
255
- "ERROR": "\x1b[31m", # red
256
- "CRITICAL": "\x1b[35m", # magenta
250
+ "TRACE": "\x1b[94m", # bright blue (readable on both dark/light)
251
+ "DEBUG": "\x1b[96m", # bright cyan (readable on both dark/light)
252
+ "INFO": "\x1b[90m", # bright black/gray (readable on both dark/light)
253
+ "SUCCESS": "\x1b[92m", # bright green (readable on both dark/light)
254
+ "WARNING": "\x1b[93m", # bright yellow (readable on both dark/light)
255
+ "ERROR": "\x1b[91m", # bright red (readable on both dark/light)
256
+ "CRITICAL": "\x1b[95m", # bright magenta (readable on both dark/light)
257
257
  }
258
258
 
259
259
  level_str = record.levelname.ljust(8)
260
- level_color = level_colors.get(record.levelname, "\x1b[37m") # default white
260
+ level_color = level_colors.get(record.levelname, "\x1b[90m") # default to gray instead of white
261
261
  label_part = self.label + " | " if self.label else ""
262
262
 
263
263
  # For DEBUG and TRACE levels, add module/location information
@@ -276,9 +276,9 @@ class MassterLogger:
276
276
  f"\x1b[90m{module_name}:{func_name}:{line_no}\x1b[0m | " # dim gray for location info
277
277
  )
278
278
 
279
- # Loguru-style format: <white>timestamp</white> | <level>LEVEL</level> | <location> | <cyan>label</cyan> - <level>message</level>
279
+ # Universal format: timestamp | level | location | label - message
280
280
  return (
281
- f"\x1b[37m{timestamp}\x1b[0m | " # white timestamp
281
+ f"\x1b[90m{timestamp}\x1b[0m | " # gray timestamp (universal for both themes)
282
282
  f"{level_color}{level_str}\x1b[0m | " # colored level
283
283
  f"{location_info}" # location info for DEBUG/TRACE
284
284
  f"{level_color}{label_part}\x1b[0m" # colored label
@@ -332,8 +332,22 @@ class MassterLogger:
332
332
  self.logger_instance.info(message, *args, **kwargs)
333
333
 
334
334
  def success(self, message: str, *args, **kwargs):
335
- """Log a SUCCESS level message (mapped to INFO)."""
336
- self.info(message, *args, **kwargs)
335
+ """Log a SUCCESS level message (custom level)."""
336
+ # Create a custom log record with SUCCESS level
337
+ import logging
338
+
339
+ # Create a LogRecord manually with SUCCESS level
340
+ record = self.logger_instance.makeRecord(
341
+ self.logger_instance.name,
342
+ logging.INFO, # Use INFO level for Python's filtering
343
+ "", 0, message, args, None, func="success"
344
+ )
345
+ # Override the levelname for display
346
+ record.levelname = "SUCCESS"
347
+
348
+ # Handle the record directly through our handler
349
+ if self.handler:
350
+ self.handler.handle(record)
337
351
 
338
352
  def warning(self, message: str, *args, **kwargs):
339
353
  """Log a WARNING level message."""
@@ -372,3 +386,4 @@ class MassterLogger:
372
386
 
373
387
  def __repr__(self):
374
388
  return f"MassterLogger(type={self.instance_type}, id={self.instance_id}, level={self.level})"
389
+
@@ -295,7 +295,7 @@ def _save_sample5(
295
295
 
296
296
  # Store lib and lib_match - removed (no longer saving lib data)
297
297
 
298
- self.logger.info(f"Sample saved to {filename}")
298
+ self.logger.success(f"Sample saved to {filename}")
299
299
  if save_featurexml:
300
300
  # Get or recreate the feature map if needed
301
301
  feature_map = self._get_feature_map()
@@ -234,7 +234,7 @@ def _handle_sample_plot_output(self, plot_obj, filename=None, plot_type="bokeh")
234
234
  from bokeh.io import save
235
235
  output_file(filename)
236
236
  save(plot_obj)
237
- self.logger.info(f"Plot saved to: {abs_filename}")
237
+ self.logger.success(f"Plot saved to: {abs_filename}")
238
238
  elif filename.endswith(".png"):
239
239
  try:
240
240
  if plot_type == "bokeh":
@@ -243,7 +243,7 @@ def _handle_sample_plot_output(self, plot_obj, filename=None, plot_type="bokeh")
243
243
  elif plot_type in ["panel", "holoviews"]:
244
244
  import holoviews as hv
245
245
  hv.save(plot_obj, filename, fmt="png")
246
- self.logger.info(f"Plot saved to: {abs_filename}")
246
+ self.logger.success(f"Plot saved to: {abs_filename}")
247
247
  except Exception:
248
248
  # Fall back to HTML if PNG export not available
249
249
  html_filename = filename.replace('.png', '.html')
@@ -268,7 +268,7 @@ def _handle_sample_plot_output(self, plot_obj, filename=None, plot_type="bokeh")
268
268
  elif plot_type in ["panel", "holoviews"]:
269
269
  import holoviews as hv
270
270
  hv.save(plot_obj, filename, fmt="pdf")
271
- self.logger.info(f"Plot saved to: {abs_filename}")
271
+ self.logger.success(f"Plot saved to: {abs_filename}")
272
272
  except ImportError:
273
273
  # Fall back to HTML if PDF export not available
274
274
  html_filename = filename.replace('.pdf', '.html')
@@ -296,7 +296,7 @@ def _handle_sample_plot_output(self, plot_obj, filename=None, plot_type="bokeh")
296
296
  from bokeh.io import save
297
297
  output_file(filename)
298
298
  save(plot_obj)
299
- self.logger.info(f"Plot saved to: {abs_filename}")
299
+ self.logger.success(f"Plot saved to: {abs_filename}")
300
300
  else:
301
301
  # Show in notebook when no filename provided
302
302
  if plot_type == "panel":
@@ -796,7 +796,7 @@ def find_features(self, **kwargs):
796
796
 
797
797
  self.features_df = df
798
798
  #self._features_sync()
799
- self.logger.info(f"Feature detection completed. Total features: {len(df)}")
799
+ self.logger.success(f"Feature detection completed. Total features: {len(df)}")
800
800
 
801
801
  # store params
802
802
  self.update_history(["find_features"], params.to_dict())
@@ -1263,7 +1263,7 @@ def find_ms2(self, **kwargs):
1263
1263
  )
1264
1264
 
1265
1265
  # Log completion
1266
- self.logger.info(
1266
+ self.logger.success(
1267
1267
  f"MS2 linking completed. Total features with MS2 data: {c}",
1268
1268
  )
1269
1269
  self.features_df = features_df
@@ -1425,7 +1425,7 @@ def find_iso(self, rt_tolerance: float = 0.1, **kwargs):
1425
1425
 
1426
1426
  # Log results
1427
1427
  non_null_count = len([spec for spec in ms1_specs if spec is not None])
1428
- self.logger.info(f"Extracted isotopic distributions for {non_null_count}/{len(ms1_specs)} features.")
1428
+ self.logger.success(f"Extracted isotopic distributions for {non_null_count}/{len(ms1_specs)} features.")
1429
1429
 
1430
1430
  # Store parameters in history
1431
1431
  params_dict = {"rt_tolerance": rt_tolerance}
@@ -148,10 +148,10 @@ def export_features(self, filename="features.csv"):
148
148
  )
149
149
  if filename.lower().endswith((".xls", ".xlsx")):
150
150
  clean_df.to_pandas().to_excel(filename, index=False)
151
- self.logger.info(f"Features exported to {filename} (Excel format)")
151
+ self.logger.success(f"Features exported to {filename} (Excel format)")
152
152
  else:
153
153
  clean_df.write_csv(filename)
154
- self.logger.info(f"Features exported to {filename}")
154
+ self.logger.success(f"Features exported to {filename}")
155
155
 
156
156
 
157
157
  def export_mgf(
@@ -649,7 +649,7 @@ def export_mgf(
649
649
  elif result == "empty_ms2":
650
650
  empty_ms2_count += 1
651
651
 
652
- self.logger.info(f"Exported {ms1_spec_used_count} MS1 spectra and {c} MS2 spectra to {filename}")
652
+ self.logger.success(f"Exported {ms1_spec_used_count} MS1 spectra and {c} MS2 spectra to {filename}")
653
653
  if empty_ms2_count > 0:
654
654
  self.logger.info(f"Skipped {empty_ms2_count} empty MS2 spectra")
655
655
  if ms1_fallback_count > 0:
@@ -824,7 +824,7 @@ def export_dda_stats(self, filename="stats.csv"):
824
824
  for line in lines:
825
825
  f.write(line + "\n")
826
826
 
827
- self.logger.info(f"DDA statistics exported to {filename}")
827
+ self.logger.success(f"DDA statistics exported to {filename}")
828
828
 
829
829
 
830
830
  def export_xlsx(self, filename="features.xlsx"):
@@ -877,7 +877,7 @@ def export_xlsx(self, filename="features.xlsx"):
877
877
  pandas_df = clean_df.to_pandas()
878
878
  pandas_df.to_excel(filename, index=False)
879
879
 
880
- self.logger.info(f"Features exported to {filename} (Excel format)")
880
+ self.logger.success(f"Features exported to {filename} (Excel format)")
881
881
  self.logger.debug(f"Exported {len(clean_df)} features with {len(exportable_columns)} columns")
882
882
 
883
883
 
@@ -1738,7 +1738,7 @@ def _save_study5(self, filename):
1738
1738
  )
1739
1739
  pbar.update(1)
1740
1740
 
1741
- self.logger.info(f"Study saved successfully to {filename}")
1741
+ self.logger.success(f"Study saved successfully to {filename}")
1742
1742
  self.logger.debug(f"Save completed for {filename}")
1743
1743
  self.logger.debug(f"Save completed for {filename}")
1744
1744
 
@@ -1440,7 +1440,7 @@ def compress(self, features=True, ms2=True, chrom=False, ms2_max=5):
1440
1440
  self.compress_ms2(max_replicates=ms2_max)
1441
1441
  if chrom:
1442
1442
  self.compress_chrom()
1443
- self.logger.info("Compression completed")
1443
+ self.logger.success("Compression completed")
1444
1444
 
1445
1445
 
1446
1446
  def compress_features(self):
@@ -1886,7 +1886,7 @@ def restore_chrom(self, samples=None, mz_tol=0.010, rt_tol=10.0):
1886
1886
  self.logger.error(f"Failed to gap-fill sample {sample_name}: {e}")
1887
1887
  continue
1888
1888
 
1889
- self.logger.info(f"Phase 2 complete: Gap-filled {filled_count} chromatograms")
1889
+ self.logger.success(f"Phase 2 complete: Gap-filled {filled_count} chromatograms")
1890
1890
 
1891
1891
  # Final summary
1892
1892
  final_non_null = self.features_df.filter(pl.col("chrom").is_not_null()).height
@@ -2051,7 +2051,7 @@ def sample_name_replace(self, replace_dict):
2051
2051
  pl.Series("sample_name", new_names).alias("sample_name"),
2052
2052
  )
2053
2053
 
2054
- self.logger.info(f"Successfully replaced {replaced_count} sample names")
2054
+ self.logger.success(f"Successfully replaced {replaced_count} sample names")
2055
2055
 
2056
2056
 
2057
2057
  def sample_name_reset(self):
@@ -4622,7 +4622,7 @@ def decompress(self, features=True, ms2=True, chrom=True, samples=None, **kwargs
4622
4622
 
4623
4623
  self.restore_ms2(samples=samples, **ms2_kwargs)
4624
4624
 
4625
- self.logger.info("Adaptive decompression completed successfully")
4625
+ self.logger.success("Adaptive decompression completed successfully")
4626
4626
 
4627
4627
  except Exception as e:
4628
4628
  self.logger.error(f"Decompression failed: {e}")
@@ -1093,7 +1093,7 @@ def id_reset(study):
1093
1093
  del study.history["identify"]
1094
1094
 
1095
1095
  if logger:
1096
- logger.info("Identification data reset completed")
1096
+ logger.success("Identification data reset completed")
1097
1097
 
1098
1098
 
1099
1099
  def lib_reset(study):
@@ -1198,7 +1198,7 @@ def lib_reset(study):
1198
1198
  del study.history["lib_to_consensus"]
1199
1199
 
1200
1200
  if logger:
1201
- logger.info("Library and identification data reset completed")
1201
+ logger.success("Library and identification data reset completed")
1202
1202
 
1203
1203
 
1204
1204
  def _get_adducts(study, adducts_list: list | None = None, **kwargs):
@@ -1978,4 +1978,4 @@ def lib_to_consensus(study, chrom_fhwm: float = 5.0, mz_tol: float = 0.01, rt_to
1978
1978
  logger.warning(f"find_ms2 failed: {e}")
1979
1979
 
1980
1980
  if logger:
1981
- logger.info(f"lib_to_consensus completed: {len(consensus_metadata)} features added")
1981
+ logger.success(f"lib_to_consensus completed: {len(consensus_metadata)} features added")
@@ -818,7 +818,7 @@ def _merge_kd_chunked(study, params: merge_defaults, cached_adducts_df=None, cac
818
818
  serialized_chunk_results.append((chunk_start_idx, consensus_features))
819
819
  completed_chunks += 1
820
820
  n_samples_in_chunk = len(chunk_data_list[chunk_idx]['chunk_samples_data'])
821
- study.logger.info(f"Completed chunk {completed_chunks}/{total_chunks} (samples {chunk_start_idx + 1}-{chunk_start_idx + n_samples_in_chunk})")
821
+ study.logger.success(f"Completed chunk {completed_chunks}/{total_chunks} (samples {chunk_start_idx + 1}-{chunk_start_idx + n_samples_in_chunk})")
822
822
  except Exception as exc:
823
823
  # Check if this is a BrokenProcessPool exception from Windows multiprocessing issues
824
824
  if isinstance(exc, BrokenProcessPool) or "process pool" in str(exc).lower():
@@ -852,7 +852,7 @@ def _merge_kd_chunked(study, params: merge_defaults, cached_adducts_df=None, cac
852
852
  serialized_chunk_results.append((chunk_start_idx, consensus_features))
853
853
  completed_chunks += 1
854
854
  n_samples_in_chunk = len(chunk_data_list[chunk_idx]['chunk_samples_data'])
855
- study.logger.info(f"Completed chunk {completed_chunks}/{total_chunks} (samples {chunk_start_idx + 1}-{chunk_start_idx + n_samples_in_chunk})")
855
+ study.logger.success(f"Completed chunk {completed_chunks}/{total_chunks} (samples {chunk_start_idx + 1}-{chunk_start_idx + n_samples_in_chunk})")
856
856
  except Exception as exc:
857
857
  study.logger.error(f"Chunk {chunk_idx} generated an exception: {exc}")
858
858
  raise exc
@@ -993,7 +993,7 @@ def _merge_qt_chunked(study, params: merge_defaults, cached_adducts_df=None, cac
993
993
  serialized_chunk_results.append((chunk_start_idx, consensus_features))
994
994
  completed_chunks += 1
995
995
  n_samples_in_chunk = len(chunk_data_list[chunk_idx]['chunk_samples_data'])
996
- study.logger.info(f"Completed chunk {completed_chunks}/{total_chunks} (samples {chunk_start_idx + 1}-{chunk_start_idx + n_samples_in_chunk})")
996
+ study.logger.success(f"Completed chunk {completed_chunks}/{total_chunks} (samples {chunk_start_idx + 1}-{chunk_start_idx + n_samples_in_chunk})")
997
997
  except Exception as exc:
998
998
  # Check if this is a BrokenProcessPool exception from Windows multiprocessing issues
999
999
  if isinstance(exc, BrokenProcessPool) or "process pool" in str(exc).lower():
@@ -1027,7 +1027,7 @@ def _merge_qt_chunked(study, params: merge_defaults, cached_adducts_df=None, cac
1027
1027
  serialized_chunk_results.append((chunk_start_idx, consensus_features))
1028
1028
  completed_chunks += 1
1029
1029
  n_samples_in_chunk = len(chunk_data_list[chunk_idx]['chunk_samples_data'])
1030
- study.logger.info(f"Completed chunk {completed_chunks}/{total_chunks} (samples {chunk_start_idx + 1}-{chunk_start_idx + n_samples_in_chunk})")
1030
+ study.logger.success(f"Completed chunk {completed_chunks}/{total_chunks} (samples {chunk_start_idx + 1}-{chunk_start_idx + n_samples_in_chunk})")
1031
1031
  except Exception as exc:
1032
1032
  study.logger.error(f"Chunk {chunk_idx} generated an exception: {exc}")
1033
1033
  raise exc
@@ -290,7 +290,7 @@ def filter_consensus(
290
290
  f"Filtered {after_quality - after_number_samples} entries based on number of samples. Remaining {after_number_samples} entries.",
291
291
  )
292
292
 
293
- self.logger.info(f"Filtering completed. {len(cons)} entries remaining.")
293
+ self.logger.success(f"Filtering completed. {len(cons)} entries remaining.")
294
294
 
295
295
  if inplace:
296
296
  self.consensus_df = cons
@@ -1365,7 +1365,7 @@ def find_iso(self, rt_tol=0.1, mz_tol=0.01, uids=None):
1365
1365
  # Count how many consensus features have isotope data
1366
1366
  iso_count = sum(1 for data in consensus_iso_data.values() if data is not None and len(data) > 0)
1367
1367
 
1368
- self.logger.info(f"Optimized isotope detection completed. Found isotope patterns for {iso_count}/{len(self.consensus_df)} consensus features.")
1368
+ self.logger.success(f"Optimized isotope detection completed. Found isotope patterns for {iso_count}/{len(self.consensus_df)} consensus features.")
1369
1369
 
1370
1370
 
1371
1371
  def reset_iso(self):
@@ -1393,7 +1393,7 @@ wheels = [
1393
1393
 
1394
1394
  [[package]]
1395
1395
  name = "masster"
1396
- version = "0.5.8"
1396
+ version = "0.5.9"
1397
1397
  source = { editable = "." }
1398
1398
  dependencies = [
1399
1399
  { name = "alpharaw" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes