accusleepy 0.10.1__py3-none-any.whl → 0.11.0__py3-none-any.whl

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.
accusleepy/constants.py CHANGED
@@ -26,8 +26,9 @@ MIN_EPOCHS_PER_STATE = 3
26
26
 
27
27
 
28
28
  # very unlikely you will want to change values from here onwards
29
- # config file location
30
- CONFIG_FILE = "config.json"
29
+ # config file names
30
+ DEFAULT_CONFIG_FILE = "default_config.json"
31
+ USER_CONFIG_FILE = "config.json"
31
32
  # number of times to include the EMG power in a training image
32
33
  EMG_COPIES = 9
33
34
  # minimum spectrogram window length, in seconds
accusleepy/fileio.py CHANGED
@@ -2,14 +2,17 @@
2
2
 
3
3
  import json
4
4
  import os
5
+ import shutil
5
6
  from dataclasses import dataclass
6
- from importlib.metadata import version, PackageNotFoundError
7
+ from importlib.metadata import PackageNotFoundError, version
8
+ from importlib.resources import files
7
9
 
8
10
  import numpy as np
9
11
  import pandas as pd
12
+ from platformdirs import user_config_dir
10
13
 
11
- from accusleepy.brain_state_set import BRAIN_STATES_KEY, BrainState, BrainStateSet
12
14
  import accusleepy.constants as c
15
+ from accusleepy.brain_state_set import BRAIN_STATES_KEY, BrainState, BrainStateSet
13
16
 
14
17
 
15
18
  @dataclass
@@ -128,6 +131,16 @@ def save_labels(
128
131
  pd.DataFrame({c.BRAIN_STATE_COL: labels}).to_csv(filename, index=False)
129
132
 
130
133
 
134
+ def _get_user_config_path() -> str:
135
+ """Return the path to the user's config file in the platform config directory."""
136
+ return os.path.join(user_config_dir("accusleepy"), c.USER_CONFIG_FILE)
137
+
138
+
139
+ def _get_default_config_path() -> str:
140
+ """Return the path to the bundled default config file."""
141
+ return str(files("accusleepy").joinpath(c.DEFAULT_CONFIG_FILE))
142
+
143
+
131
144
  def load_config() -> AccuSleePyConfig:
132
145
  """Load configuration file with brain state options
133
146
 
@@ -143,9 +156,11 @@ def load_config() -> AccuSleePyConfig:
143
156
  default autoscroll state for manual scoring,
144
157
  setting to delete training images automatically
145
158
  """
146
- with open(
147
- os.path.join(os.path.dirname(os.path.abspath(__file__)), c.CONFIG_FILE), "r"
148
- ) as f:
159
+ user_config = _get_user_config_path()
160
+ if not os.path.exists(user_config):
161
+ os.makedirs(os.path.dirname(user_config), exist_ok=True)
162
+ shutil.copy2(_get_default_config_path(), user_config)
163
+ with open(user_config) as f:
149
164
  data = json.load(f)
150
165
 
151
166
  return AccuSleePyConfig(
@@ -229,9 +244,9 @@ def save_config(
229
244
  output_dict.update({c.EPOCHS_TO_SHOW_KEY: epochs_to_show})
230
245
  output_dict.update({c.AUTOSCROLL_KEY: autoscroll_state})
231
246
  output_dict.update({c.DELETE_TRAINING_IMAGES_KEY: delete_training_images})
232
- with open(
233
- os.path.join(os.path.dirname(os.path.abspath(__file__)), c.CONFIG_FILE), "w"
234
- ) as f:
247
+ user_config = _get_user_config_path()
248
+ os.makedirs(os.path.dirname(user_config), exist_ok=True)
249
+ with open(user_config, "w") as f:
235
250
  json.dump(output_dict, f, indent=4)
236
251
  f.write("\n")
237
252
 
@@ -242,7 +257,7 @@ def load_recording_list(filename: str) -> list[Recording]:
242
257
  :param filename: filename of list of recordings
243
258
  :return: list of recordings
244
259
  """
245
- with open(filename, "r") as f:
260
+ with open(filename) as f:
246
261
  data = json.load(f)
247
262
  recording_list = [Recording(**r) for r in data[c.RECORDING_LIST_NAME]]
248
263
  for i, r in enumerate(recording_list):
Binary file
accusleepy/gui/main.py CHANGED
@@ -41,10 +41,10 @@ from accusleepy.constants import (
41
41
  UNDEFINED_LABEL,
42
42
  )
43
43
  from accusleepy.fileio import (
44
+ get_version,
44
45
  load_config,
45
46
  load_labels,
46
47
  load_recording,
47
- get_version,
48
48
  )
49
49
  from accusleepy.gui.dialogs import select_existing_file, select_save_location
50
50
  from accusleepy.gui.manual_scoring import ManualScoringWindow
@@ -58,9 +58,8 @@ from accusleepy.services import (
58
58
  create_calibration,
59
59
  score_recording_list,
60
60
  )
61
- from accusleepy.validation import validate_and_correct_labels
62
61
  from accusleepy.signal_processing import resample_and_standardize
63
- from accusleepy.validation import check_config_consistency
62
+ from accusleepy.validation import check_config_consistency, validate_and_correct_labels
64
63
 
65
64
  logger = logging.getLogger(__name__)
66
65
 
@@ -95,7 +94,7 @@ class AccuSleepWindow(QMainWindow):
95
94
  """AccuSleePy primary window"""
96
95
 
97
96
  def __init__(self):
98
- super(AccuSleepWindow, self).__init__()
97
+ super().__init__()
99
98
 
100
99
  # initialize the UI
101
100
  self.ui = Ui_PrimaryWindow()
@@ -363,22 +362,18 @@ class AccuSleepWindow(QMainWindow):
363
362
  except Exception:
364
363
  logger.exception("Failed to load %s", filename)
365
364
  self.show_message(
366
- (
367
- "ERROR: could not load classification model. Check "
368
- "user manual for instructions on creating this file."
369
- )
365
+ "ERROR: could not load classification model. Check "
366
+ "user manual for instructions on creating this file."
370
367
  )
371
368
  return
372
369
 
373
370
  # make sure only "default" model type is loaded
374
371
  if model_type != DEFAULT_MODEL_TYPE:
375
372
  self.show_message(
376
- (
377
- "ERROR: only 'default'-style models can be used. "
378
- "'Real-time' models are not supported. "
379
- "See classification.example_real_time_scoring_function.py "
380
- "for an example of how to classify brain states in real time."
381
- )
373
+ "ERROR: only 'default'-style models can be used. "
374
+ "'Real-time' models are not supported. "
375
+ "See classification.example_real_time_scoring_function.py "
376
+ "for an example of how to classify brain states in real time."
382
377
  )
383
378
  return
384
379
 
@@ -433,10 +428,8 @@ class AccuSleepWindow(QMainWindow):
433
428
  )
434
429
  status_widget.setText("could not load recording")
435
430
  self.show_message(
436
- (
437
- "ERROR: could not load recording. "
438
- "Check user manual for formatting instructions."
439
- )
431
+ "ERROR: could not load recording. "
432
+ "Check user manual for formatting instructions."
440
433
  )
441
434
  return None, None, None, False
442
435
 
@@ -506,10 +499,8 @@ class AccuSleepWindow(QMainWindow):
506
499
  logger.exception("Failed to load %s", label_file)
507
500
  self.ui.manual_scoring_status.setText("could not load labels")
508
501
  self.show_message(
509
- (
510
- "ERROR: could not load labels. "
511
- "Check user manual for formatting instructions."
512
- )
502
+ "ERROR: could not load labels. "
503
+ "Check user manual for formatting instructions."
513
504
  )
514
505
  return
515
506
  else:
@@ -34,7 +34,7 @@ from PySide6.QtWidgets import (
34
34
  )
35
35
 
36
36
  from accusleepy.constants import UNDEFINED_LABEL
37
- from accusleepy.fileio import load_config, save_labels, EMGFilter
37
+ from accusleepy.fileio import EMGFilter, load_config, save_labels
38
38
  from accusleepy.gui.mplwidget import resample_x_ticks
39
39
  from accusleepy.gui.viewer_window import Ui_ViewerWindow
40
40
  from accusleepy.signal_processing import create_spectrogram, get_emg_power
@@ -124,7 +124,7 @@ class ManualScoringWindow(QDialog):
124
124
  :param epoch_length: epoch length, in seconds
125
125
  :param emg_filter: EMG filter parameters
126
126
  """
127
- super(ManualScoringWindow, self).__init__()
127
+ super().__init__()
128
128
 
129
129
  self.label_file = label_file
130
130
  self.eeg = eeg
@@ -915,9 +915,7 @@ class ManualScoringWindow(QDialog):
915
915
  )
916
916
  self.ui.lowerfigure.canvas.axes[1].set_xticklabels(
917
917
  [
918
- "{:02d}:{:02d}:{:05.2f}".format(
919
- int(x // 3600), int(x // 60) % 60, (x % 60)
920
- )
918
+ f"{int(x // 3600):02d}:{int(x // 60) % 60:02d}:{x % 60:05.2f}"
921
919
  for x in x_ticks * self.epoch_length
922
920
  ]
923
921
  )
@@ -294,7 +294,7 @@ class MplWidget(QtWidgets.QWidget):
294
294
  [1 - marker_dy, 1],
295
295
  ]
296
296
  )
297
- for x, y in zip(marker_x, marker_y):
297
+ for x, y in zip(marker_x, marker_y, strict=True):
298
298
  self.top_marker.append(axes[0].plot(x, y - marker_y_offset_top, "r")[0])
299
299
 
300
300
  # EMG subplot
@@ -318,7 +318,7 @@ class MplWidget(QtWidgets.QWidget):
318
318
  linewidth=0.5,
319
319
  )[0]
320
320
 
321
- for x, y in zip(marker_x, marker_y):
321
+ for x, y in zip(marker_x, marker_y, strict=True):
322
322
  self.bottom_marker.append(
323
323
  axes[1].plot(x, -1 * (y - marker_y_offset_bottom), "r")[0]
324
324
  )
@@ -356,9 +356,7 @@ class MplWidget(QtWidgets.QWidget):
356
356
 
357
357
  def time_formatter(self, x, pos):
358
358
  x = x * self.epoch_length
359
- return "{:02d}:{:02d}:{:05.2f}".format(
360
- int(x // 3600), int(x // 60) % 60, (x % 60)
361
- )
359
+ return f"{int(x // 3600):02d}:{int(x // 60) % 60:02d}:{x % 60:05.2f}"
362
360
 
363
361
 
364
362
  def resample_x_ticks(x_ticks: np.array) -> np.array:
@@ -1,9 +1,7 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  ################################################################################
4
2
  ## Form generated from reading UI file 'primary_window.ui'
5
3
  ##
6
- ## Created by: Qt User Interface Compiler version 6.9.2
4
+ ## Created by: Qt User Interface Compiler version 6.10.2
7
5
  ##
8
6
  ## WARNING! All changes made in this file will be lost when recompiling UI file!
9
7
  ################################################################################
@@ -32,10 +30,11 @@ from PySide6.QtWidgets import (
32
30
  QVBoxLayout,
33
31
  QWidget,
34
32
  )
33
+
35
34
  import accusleepy.gui.resources_rc # noqa F401
36
35
 
37
36
 
38
- class Ui_PrimaryWindow(object):
37
+ class Ui_PrimaryWindow:
39
38
  def setupUi(self, PrimaryWindow):
40
39
  if not PrimaryWindow.objectName():
41
40
  PrimaryWindow.setObjectName("PrimaryWindow")
@@ -3148,7 +3147,6 @@ class Ui_PrimaryWindow(object):
3148
3147
  "This is the current set of brain states. Important notes:\n"
3149
3148
  "- You must click 'Save settings' for changes to take effect.\n"
3150
3149
  "- Changing these settings can prevent existing label files, calibration files, and trained models from working properly.\n"
3151
- "- Reinstalling AccuSleePy will overwrite this configuration.\n"
3152
3150
  "\n"
3153
3151
  "Each brain state has several attributes:\n"
3154
3152
  "- Digit: the indicator for this state in label files, and the keyboard shortcut for this state in manual scoring.\n"
@@ -3701,7 +3701,6 @@ color: rgb(244, 195, 68);</string>
3701
3701
  <string>This is the current set of brain states. Important notes:
3702
3702
  - You must click 'Save settings' for changes to take effect.
3703
3703
  - Changing these settings can prevent existing label files, calibration files, and trained models from working properly.
3704
- - Reinstalling AccuSleePy will overwrite this configuration.
3705
3704
 
3706
3705
  Each brain state has several attributes:
3707
3706
  - Digit: the indicator for this state in label files, and the keyboard shortcut for this state in manual scoring.
@@ -197,7 +197,7 @@ class SettingsWidget(QObject):
197
197
  # Brain states
198
198
  states = {b.digit: b for b in self._brain_state_set.brain_states}
199
199
  for digit in range(10):
200
- if digit in states.keys():
200
+ if digit in states:
201
201
  self._settings_widgets[digit].enabled_widget.setChecked(True)
202
202
  self._settings_widgets[digit].name_widget.setText(states[digit].name)
203
203
  self._settings_widgets[digit].is_scored_widget.setChecked(
@@ -1,5 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  ################################################################################
4
2
  ## Form generated from reading UI file 'viewer_window.ui'
5
3
  ##
@@ -23,11 +21,11 @@ from PySide6.QtWidgets import (
23
21
  QVBoxLayout,
24
22
  )
25
23
 
26
- from accusleepy.gui.mplwidget import MplWidget
27
24
  import accusleepy.gui.resources_rc # noqa F401
25
+ from accusleepy.gui.mplwidget import MplWidget
28
26
 
29
27
 
30
- class Ui_ViewerWindow(object):
28
+ class Ui_ViewerWindow:
31
29
  def setupUi(self, ViewerWindow):
32
30
  if not ViewerWindow.objectName():
33
31
  ViewerWindow.setObjectName("ViewerWindow")
accusleepy/multitaper.py CHANGED
@@ -331,7 +331,8 @@ def process_input(
331
331
  + str(frequency_range[0])
332
332
  + ", "
333
333
  + str(frequency_range[1])
334
- + "]"
334
+ + "]",
335
+ stacklevel=2,
335
336
  )
336
337
 
337
338
  # Set number of tapers if none provided
@@ -342,7 +343,8 @@ def process_input(
342
343
  if num_tapers != math.floor(2 * time_bandwidth) - 1:
343
344
  warnings.warn(
344
345
  "Number of tapers is optimal at floor(2*TW) - 1. consider using "
345
- + str(math.floor(2 * time_bandwidth) - 1)
346
+ + str(math.floor(2 * time_bandwidth) - 1),
347
+ stacklevel=2,
346
348
  )
347
349
 
348
350
  # If no window params provided, set to defaults
@@ -355,7 +357,8 @@ def process_input(
355
357
  warnings.warn(
356
358
  "Window size is not divisible by sampling frequency. Adjusting window size to "
357
359
  + str(winsize_samples / fs)
358
- + " seconds"
360
+ + " seconds",
361
+ stacklevel=2,
359
362
  )
360
363
  else:
361
364
  winsize_samples = window_params[0] * fs
@@ -366,7 +369,8 @@ def process_input(
366
369
  warnings.warn(
367
370
  "Window step size is not divisible by sampling frequency. Adjusting window step size to "
368
371
  + str(winstep_samples / fs)
369
- + " seconds"
372
+ + " seconds",
373
+ stacklevel=2,
370
374
  )
371
375
  else:
372
376
  winstep_samples = window_params[1] * fs
@@ -508,7 +512,7 @@ def nanpow2db(y):
508
512
  ydB (float or np array): inputs converted to dB with 0s and negatives resulting in nans
509
513
  """
510
514
 
511
- if isinstance(y, int) or isinstance(y, float):
515
+ if isinstance(y, int | float):
512
516
  if y == 0:
513
517
  return np.nan
514
518
  else:
@@ -581,7 +585,7 @@ def calc_mts_segment(
581
585
  spower_iter = np.mean(spower[:, 0:2], 1)
582
586
  spower_iter = spower_iter[:, np.newaxis]
583
587
  a = (1 - dpss_eigen) * tpower
584
- for i in range(3): # 3 iterations only
588
+ for _i in range(3): # 3 iterations only
585
589
  # Calc the MSE weights
586
590
  b = np.dot(spower_iter, np.ones((1, num_tapers))) / (
587
591
  (np.dot(spower_iter, np.transpose(dpss_eigen)))
accusleepy/services.py CHANGED
@@ -8,8 +8,8 @@ import datetime
8
8
  import logging
9
9
  import os
10
10
  import shutil
11
+ from collections.abc import Callable
11
12
  from dataclasses import dataclass, field
12
- from typing import Callable
13
13
 
14
14
  import numpy as np
15
15
  import pandas as pd
@@ -21,9 +21,9 @@ from accusleepy.constants import (
21
21
  CALIBRATION_ANNOTATION_FILENAME,
22
22
  DEFAULT_MODEL_TYPE,
23
23
  MIN_EPOCHS_PER_STATE,
24
- UNDEFINED_LABEL,
25
24
  MIXTURE_MEAN_COL,
26
25
  MIXTURE_SD_COL,
26
+ UNDEFINED_LABEL,
27
27
  )
28
28
  from accusleepy.fileio import (
29
29
  EMGFilter,
@@ -36,10 +36,10 @@ from accusleepy.fileio import (
36
36
  )
37
37
  from accusleepy.models import SSANN
38
38
  from accusleepy.signal_processing import (
39
- create_training_images,
40
- resample_and_standardize,
41
39
  create_eeg_emg_image,
40
+ create_training_images,
42
41
  get_mixture_values,
42
+ resample_and_standardize,
43
43
  )
44
44
  from accusleepy.validation import check_label_validity
45
45
 
@@ -21,10 +21,10 @@ from accusleepy.constants import (
21
21
  LABEL_COL,
22
22
  MIN_EPOCHS_PER_STATE,
23
23
  MIN_WINDOW_LEN,
24
- UPPER_FREQ,
25
24
  SPECTROGRAM_UPPER_FREQ,
25
+ UPPER_FREQ,
26
26
  )
27
- from accusleepy.fileio import Recording, load_labels, load_recording, EMGFilter
27
+ from accusleepy.fileio import EMGFilter, Recording, load_labels, load_recording
28
28
  from accusleepy.multitaper import spectrogram
29
29
 
30
30
  # note: scipy is lazily imported
@@ -151,7 +151,7 @@ def create_spectrogram(
151
151
  # pad the EEG signal so that the first spectrogram window is centered
152
152
  # on the first epoch
153
153
  # it's possible there's some jank here, if this isn't close to an integer
154
- pad_length = round((sampling_rate * (window_length_sec - epoch_length) / 2))
154
+ pad_length = round(sampling_rate * (window_length_sec - epoch_length) / 2)
155
155
  padded_eeg = np.concatenate(
156
156
  [eeg[:pad_length][::-1], eeg, eeg[(len(eeg) - pad_length) :][::-1]]
157
157
  )
@@ -328,7 +328,10 @@ def mixture_z_score_img(
328
328
  if labels is None and (mixture_means is None or mixture_sds is None):
329
329
  raise ValueError("must provide either labels or mixture means+SDs")
330
330
  if labels is not None and ((mixture_means is not None) ^ (mixture_sds is not None)):
331
- warnings.warn("labels were given, mixture means / SDs will be ignored")
331
+ warnings.warn(
332
+ "labels were given, mixture means / SDs will be ignored",
333
+ stacklevel=2,
334
+ )
332
335
 
333
336
  if labels is not None:
334
337
  mixture_means, mixture_sds = get_mixture_values(
@@ -20,7 +20,7 @@ class ModelWithTemperature(nn.Module):
20
20
  """
21
21
 
22
22
  def __init__(self, model):
23
- super(ModelWithTemperature, self).__init__()
23
+ super().__init__()
24
24
  self.model = model
25
25
  # https://github.com/gpleiss/temperature_scaling/issues/20
26
26
  # for another approach, see https://github.com/gpleiss/temperature_scaling/issues/36
@@ -142,7 +142,7 @@ class _ECELoss(nn.Module):
142
142
  """
143
143
  n_bins (int): number of confidence interval bins
144
144
  """
145
- super(_ECELoss, self).__init__()
145
+ super().__init__()
146
146
  bin_boundaries = torch.linspace(0, 1, n_bins + 1)
147
147
  self.bin_lowers = bin_boundaries[:-1]
148
148
  self.bin_uppers = bin_boundaries[1:]
@@ -153,7 +153,7 @@ class _ECELoss(nn.Module):
153
153
  accuracies = predictions.eq(labels)
154
154
 
155
155
  ece = torch.zeros(1, device=logits.device)
156
- for bin_lower, bin_upper in zip(self.bin_lowers, self.bin_uppers):
156
+ for bin_lower, bin_upper in zip(self.bin_lowers, self.bin_uppers, strict=True):
157
157
  # Calculated |confidence - accuracy| in each bin
158
158
  in_bin = confidences.gt(bin_lower.item()) * confidences.le(bin_upper.item())
159
159
  prop_in_bin = in_bin.float().mean()
accusleepy/validation.py CHANGED
@@ -41,9 +41,10 @@ def check_label_validity(
41
41
  ):
42
42
  return "label file contains invalid entries"
43
43
 
44
- if confidence_scores is not None:
45
- if np.min(confidence_scores) < 0 or np.max(confidence_scores) > 1:
46
- return "label file contains invalid confidence scores"
44
+ if confidence_scores is not None and (
45
+ np.min(confidence_scores) < 0 or np.max(confidence_scores) > 1
46
+ ):
47
+ return "label file contains invalid confidence scores"
47
48
 
48
49
  return None
49
50
 
@@ -143,7 +144,9 @@ def check_config_consistency(
143
144
  # generate message comparing the brain state configs
144
145
  config_comparisons = list()
145
146
  for config, config_name in zip(
146
- [current_scored_states, model_scored_states], ["current", "model's"]
147
+ [current_scored_states, model_scored_states],
148
+ ["current", "model's"],
149
+ strict=True,
147
150
  ):
148
151
  config_comparisons.append(
149
152
  f"Scored brain states in {config_name} configuration: "
@@ -154,6 +157,7 @@ def check_config_consistency(
154
157
  for x, y in zip(
155
158
  config["digit"],
156
159
  config["name"],
160
+ strict=True,
157
161
  )
158
162
  ]
159
163
  )
@@ -164,32 +168,26 @@ def check_config_consistency(
164
168
  len_diff = len(current_scored_states["name"]) - len(model_scored_states["name"])
165
169
  if len_diff != 0:
166
170
  output.append(
167
- (
168
- "WARNING: current brain state configuration has "
169
- f"{'fewer' if len_diff < 0 else 'more'} "
170
- "scored brain states than the model's configuration."
171
- )
171
+ "WARNING: current brain state configuration has "
172
+ f"{'fewer' if len_diff < 0 else 'more'} "
173
+ "scored brain states than the model's configuration."
172
174
  )
173
175
  output = output + config_comparisons
174
176
  else:
175
177
  # the length is the same, but names might be different
176
178
  if current_scored_states["name"] != model_scored_states["name"]:
177
179
  output.append(
178
- (
179
- "WARNING: current brain state configuration appears "
180
- "to contain different brain states than "
181
- "the model's configuration."
182
- )
180
+ "WARNING: current brain state configuration appears "
181
+ "to contain different brain states than "
182
+ "the model's configuration."
183
183
  )
184
184
  output = output + config_comparisons
185
185
 
186
186
  if current_epoch_length != model_epoch_length:
187
187
  output.append(
188
- (
189
- "Warning: the epoch length used when training this model "
190
- f"({model_epoch_length} seconds) "
191
- "does not match the current epoch length setting."
192
- )
188
+ "Warning: the epoch length used when training this model "
189
+ f"({model_epoch_length} seconds) "
190
+ "does not match the current epoch length setting."
193
191
  )
194
192
 
195
193
  return output
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: accusleepy
3
- Version: 0.10.1
3
+ Version: 0.11.0
4
4
  Summary: Python implementation of AccuSleep
5
5
  License: GPL-3.0-only
6
6
  Author: Zeke Barger
@@ -15,6 +15,7 @@ Requires-Dist: matplotlib (>=3.10.1,<4.0.0)
15
15
  Requires-Dist: numpy (>=2.2.4,<3.0.0)
16
16
  Requires-Dist: pandas (>=2.2.3,<3.0.0)
17
17
  Requires-Dist: pillow (>=11.1.0,<12.0.0)
18
+ Requires-Dist: platformdirs (>=4.0.0,<5.0.0)
18
19
  Requires-Dist: pyarrow (>=23.0.0,<24.0.0)
19
20
  Requires-Dist: pyside6 (>=6.10.1,<7.0.0)
20
21
  Requires-Dist: scipy (>=1.15.2,<2.0.0)
@@ -60,13 +61,14 @@ etc.) with python >=3.11,<3.14
60
61
  - `pip install accusleepy`
61
62
  - (optional) download a classification model from https://osf.io/py5eb/ under /python_format/models/
62
63
 
63
- Note that upgrading or reinstalling the package will overwrite any changes
64
- to the [config file](accusleepy/config.json).
65
64
 
66
65
  ## Usage
67
66
 
68
67
  `python -m accusleepy` will open the primary interface.
69
68
 
69
+ Your settings are saved to a platform-specific location
70
+ (e.g., `~/Library/Application Support/accusleepy/config.json` on macOS)
71
+
70
72
  [Guide to the primary interface](accusleepy/gui/text/main_guide.md)
71
73
 
72
74
  [Guide to the manual scoring interface](accusleepy/gui/text/manual_scoring_guide.md)
@@ -77,6 +79,7 @@ please consult the [developer guide](accusleepy/gui/text/dev_guide.md).
77
79
 
78
80
  ## Changelog
79
81
 
82
+ - 0.11.0: Store config file in a user directory
80
83
  - 0.10.0-0.10.1: Improved zoom behavior, updated dependencies
81
84
  - 0.7.1-0.9.3: Bugfixes, code cleanup, additional config settings
82
85
  - 0.7.0: More settings can be configured in the UI
@@ -3,9 +3,9 @@ accusleepy/__main__.py,sha256=yRSJnSLFjt9BqzmdemfMzejp1TPS0VGIAvyYcd-yg3w,182
3
3
  accusleepy/bouts.py,sha256=s77xtimYWFJ9CjTzqMUu98A2odoT8waoyuE6FVmj1dA,6089
4
4
  accusleepy/brain_state_set.py,sha256=la7CjgwMByXEIE__Yzv-G9mbuNYFIB6G41rDPP2T21k,3314
5
5
  accusleepy/classification.py,sha256=Zh-iMjvttRWf-Oarr1urgT-FkjcCbzYgndooAdeZbTA,8726
6
- accusleepy/config.json,sha256=vtzWKXSwZRNlYQhyaIl-TvOPrOxgRC7Pt3fUcWChVeo,911
7
- accusleepy/constants.py,sha256=zB8z5-PWpnfZMROkR7iZusqP3cxRyr5sVBbGSCxAAto,3281
8
- accusleepy/fileio.py,sha256=50_eC6rJ98cEdspJfhQSyOWoTNX0Q9FG5ZvU2e6Y9Ls,9283
6
+ accusleepy/constants.py,sha256=i7fl1d54leGkL8YoSPK5ZXZo9FKe9k8Ka-gEIy4m9PM,3327
7
+ accusleepy/default_config.json,sha256=vtzWKXSwZRNlYQhyaIl-TvOPrOxgRC7Pt3fUcWChVeo,911
8
+ accusleepy/fileio.py,sha256=Z95oaZrSfcc7GQnFlsmEP-Q3V1UMLCRdJUs-lVbZhP0,9900
9
9
  accusleepy/gui/__init__.py,sha256=bv2FO3aQKzcBifoEfzg3ZeZHQaHUY_SuEceIE5mzZ7k,58
10
10
  accusleepy/gui/dialogs.py,sha256=DqkarJKgRovTuDDJhqPaw_o8ZfopuR7R6M8C49QWZu0,1230
11
11
  accusleepy/gui/icons/brightness_down.png,sha256=PLT1fb83RHIhSRuU7MMMx0G7oJAY7o9wUcnqM8veZfM,12432
@@ -19,29 +19,29 @@ accusleepy/gui/icons/save.png,sha256=J3EA8iU1BqLYRSsrq_OdoZlqrv2yfL7oV54DklTy_DI
19
19
  accusleepy/gui/icons/up_arrow.png,sha256=V9yF9t1WgjPaUu-mF1YGe_DfaRHg2dUpR_sUVVcvVvY,3329
20
20
  accusleepy/gui/icons/zoom_in.png,sha256=MFWnKZp7Rvh4bLPq4Cqo4sB_jQYedUUtT8-ZO8tNYyc,13589
21
21
  accusleepy/gui/icons/zoom_out.png,sha256=IB8Jecb3i0U4qjWRR46ridjLpvLCSe7PozBaLqQqYSw,13055
22
- accusleepy/gui/images/primary_window.png,sha256=1L-82Zl2adVpfRuFQKecYwz9G_Dthcoyq-XvmFXQbrg,568426
22
+ accusleepy/gui/images/primary_window.png,sha256=lLk-VxOt9U3igta5MBalwbItANn_hyG7zsMyTltxqdI,567825
23
23
  accusleepy/gui/images/viewer_window.png,sha256=b_B7m9WSLMAOzNjctq76SyekO1WfC6qYZVNnYfhjPe8,977197
24
24
  accusleepy/gui/images/viewer_window_annotated.png,sha256=uMNUmsZIdzDlQpyoiS3lJGoWlg_T325Oj5hDZhM3Y14,146817
25
- accusleepy/gui/main.py,sha256=CqVlUw7ICrxNUaz97tPNwoLW7fhJoAORyHsH43mzKS8,24939
26
- accusleepy/gui/manual_scoring.py,sha256=F_o6rh9tXLMv21s8zWJXu7YZKUkQ-GvsXRBvOGPBx0I,43365
27
- accusleepy/gui/mplwidget.py,sha256=FRH3A86L_b7cZR6BVY-fvqzJ6iFh-KdNYIwJQbMZPP4,13491
28
- accusleepy/gui/primary_window.py,sha256=QwYpzE47AU-Pt6Ftvyvjivz2U9QgElTWrOLq8jXG5co,141860
29
- accusleepy/gui/primary_window.ui,sha256=iy24sjevVI3Kr_lt7t1fjazlA-wE1dq94jsOBU6Oloc,207947
25
+ accusleepy/gui/main.py,sha256=eTj7u83vWXCd1Suts7D9OX_chisqE3yVU9uVZvd4vEk,24693
26
+ accusleepy/gui/manual_scoring.py,sha256=S8aonPdrb4tUwaFzXX4N4oXjv5CvREGYMnQm30b0AbY,43288
27
+ accusleepy/gui/mplwidget.py,sha256=yZTd4lQi9LG5gtM43RYgr3BSf4DFf96HnwEKGq0CNgI,13481
28
+ accusleepy/gui/primary_window.py,sha256=2KPIrVPygxpgefZkHDeseKJZAo9KreC_XfRZdV89Kvo,141748
29
+ accusleepy/gui/primary_window.ui,sha256=a8Xzvq_U08Os-ES2BB_vs7D8JVsUj2kdLt6s3VztRhw,207886
30
30
  accusleepy/gui/recording_manager.py,sha256=CqgP8Q2lI7Vir-zAOhVlZbIVBxRAbgC6ydmvzlMLYz4,4207
31
31
  accusleepy/gui/resources.qrc,sha256=wqPendnTLAuKfVI6v2lKHiRqAWM0oaz2ZuF5cucJdS4,803
32
32
  accusleepy/gui/resources_rc.py,sha256=Z2e34h30U4snJjnYdZVV9B6yjATKxxfvgTRt5uXtQdo,329727
33
- accusleepy/gui/settings_widget.py,sha256=9KIqFg7s-a7VyED-lNxS2Zux1hTVJLy6AdSlw2expD8,17489
33
+ accusleepy/gui/settings_widget.py,sha256=G5tpH8bDkbRGAH1sGn0wAOFN2KhtDgpWkZcgS7viAHE,17482
34
34
  accusleepy/gui/text/dev_guide.md,sha256=PgOXfGvN17fCtnsfGvPhrhK4FUWFGP_TsHA6t9skP3U,2060
35
35
  accusleepy/gui/text/main_guide.md,sha256=XDJU2anRsA72zXOSkhstSy66uEIA4jitD4uCHIxDP_w,8456
36
36
  accusleepy/gui/text/manual_scoring_guide.md,sha256=mzIqYJ5IOkzIpAWNZUh82ve_lI9SHjVvbQzn5kDWP_k,1065
37
- accusleepy/gui/viewer_window.py,sha256=jysFw7C_Tr7mtK1XNWhIpHblBvatwduE3RF2GP4lrro,24479
37
+ accusleepy/gui/viewer_window.py,sha256=KnaNjknF_Lf3vMKtDGOgcsUj3kzhaa6OUtrgFeLUi7I,24446
38
38
  accusleepy/gui/viewer_window.ui,sha256=a89iVLk1sJg9N6ZvWAV6YNPStb2Tm4-rs-W7TmIDkb4,31658
39
39
  accusleepy/models.py,sha256=Kmbt6a8kIbPD8fIPVHIo60oywQNNH86_lj1dSsjPkFE,3588
40
- accusleepy/multitaper.py,sha256=X5ZGNfbYVmsX4EPsv1IqpiFaCZYR0KQruyMGUUomJfk,23623
41
- accusleepy/services.py,sha256=4xl9uWDaJXIpJTYH08mSgipkkEYM9fDLIcMj7-k9qf0,20228
42
- accusleepy/signal_processing.py,sha256=-itwidKi7Gy9rukSezJ_1IJ9XFI_D-0j4IpUZaFnTKE,20057
43
- accusleepy/temperature_scaling.py,sha256=PE-xzFeZ0vdLJKmaJ1mjDUeqsb-pSp_y9BsaINXHFas,5909
44
- accusleepy/validation.py,sha256=4cR70x0yombDA5EWISmXRpbGY5YLsjzaLzDBBa3wnUc,7000
45
- accusleepy-0.10.1.dist-info/METADATA,sha256=UEVePOEL8dZbVfvJa5-zAtDhMLOVfG15GACvEpilAN4,4582
46
- accusleepy-0.10.1.dist-info/WHEEL,sha256=kJCRJT_g0adfAJzTx2GUMmS80rTJIVHRCfG0DQgLq3o,88
47
- accusleepy-0.10.1.dist-info/RECORD,,
40
+ accusleepy/multitaper.py,sha256=faBADd0C9ZVSi5u3kleE6DcztW-9orUzMzT5dvTQet0,23716
41
+ accusleepy/services.py,sha256=cLy60v_Rh5d84wXb_NivwVBSnCtVu8Ny9FHGRu-ff90,20237
42
+ accusleepy/signal_processing.py,sha256=npxok73jfW2Er0OU1x6Nh6VdO7WdV-hRX7iwZaKaFxI,20104
43
+ accusleepy/temperature_scaling.py,sha256=P9iKyay73fPqLa-nTYi-IaH_pIsER4RS2SH5WPTvNQ0,5882
44
+ accusleepy/validation.py,sha256=SKPiW0xPo2wXUif1xSvC7GL5Ooam7BgtYvvc48xIbEA,6947
45
+ accusleepy-0.11.0.dist-info/METADATA,sha256=OD-oUROHExjI6A5mv66uQa3ghVvWWBEGLtkEjUmMZOM,4683
46
+ accusleepy-0.11.0.dist-info/WHEEL,sha256=kJCRJT_g0adfAJzTx2GUMmS80rTJIVHRCfG0DQgLq3o,88
47
+ accusleepy-0.11.0.dist-info/RECORD,,
File without changes