balancr 0.1.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.
balancr/cli/config.py ADDED
@@ -0,0 +1,165 @@
1
+ """
2
+ config.py - Configuration management for the balancr CLI.
3
+
4
+ This module handles loading, saving, and updating the configuration
5
+ file that stores user settings between command executions.
6
+ """
7
+
8
+ import json
9
+ import logging
10
+ from pathlib import Path
11
+
12
+
13
+ def initialise_config(config_path: str, force: bool = False) -> None:
14
+ """
15
+ Initialise a new configuration file with default settings.
16
+
17
+ Args:
18
+ config_path: Path to the configuration file
19
+ force: Whether to overwrite an existing file
20
+
21
+ Raises:
22
+ IOError: If the file exists and force is False
23
+ """
24
+ config_path = Path(config_path)
25
+
26
+ # Check if file already exists
27
+ if config_path.exists() and not force:
28
+ logging.info(f"Configuration file already exists at {config_path}")
29
+ return
30
+
31
+ # Create default configuration
32
+ default_config = {
33
+ "preprocessing": {
34
+ "handle_missing": "mean",
35
+ "scale": "standard",
36
+ "encode": "auto",
37
+ },
38
+ "evaluation": {
39
+ "test_size": 0.2,
40
+ "cross_validation": 0,
41
+ "random_state": 42,
42
+ "learning_curve_folds": 5,
43
+ "learning_curve_points": 10
44
+ },
45
+ "output": {
46
+ "metrics": ["precision", "recall", "f1", "roc_auc"],
47
+ "visualisations": ["all"],
48
+ "display_visualisations": False,
49
+ "save_metrics_formats": ["csv"],
50
+ "save_vis_formats": ["png"],
51
+ },
52
+ }
53
+
54
+ # Ensure directory exists
55
+ config_path.parent.mkdir(parents=True, exist_ok=True)
56
+
57
+ # Write configuration file
58
+ with open(config_path, "w") as f:
59
+ json.dump(default_config, f, indent=2)
60
+
61
+ logging.info(f"Initialised configuration file at {config_path}")
62
+
63
+
64
+ def load_config(config_path: str) -> dict:
65
+ """
66
+ Load configuration from file.
67
+
68
+ Args:
69
+ config_path: Path to the configuration file
70
+
71
+ Returns:
72
+ Dictionary containing configuration
73
+
74
+ Raises:
75
+ FileNotFoundError: If config file doesn't exist
76
+ json.JSONDecodeError: If config file is invalid JSON
77
+ """
78
+ config_path = Path(config_path)
79
+
80
+ if not config_path.exists():
81
+ raise FileNotFoundError(f"Configuration file not found at {config_path}")
82
+
83
+ with open(config_path, "r") as f:
84
+ config = json.load(f)
85
+
86
+ return config
87
+
88
+
89
+ def update_config(config_path: str, settings: dict) -> None:
90
+ """
91
+ Update existing configuration with new settings.
92
+
93
+ Args:
94
+ config_path: Path to the configuration file
95
+ settings: Dictionary containing settings to update
96
+
97
+ Raises:
98
+ FileNotFoundError: If config file doesn't exist
99
+ """
100
+ config_path = Path(config_path)
101
+
102
+ # Load existing config
103
+ try:
104
+ current_config = load_config(config_path)
105
+ except FileNotFoundError:
106
+ # Initialise if it doesn't exist
107
+ initialise_config(config_path)
108
+ current_config = load_config(config_path)
109
+
110
+ # Update configuration recursively
111
+ deep_update(current_config, settings)
112
+
113
+ # Write updated configuration
114
+ with open(config_path, "w") as f:
115
+ json.dump(current_config, f, indent=2)
116
+
117
+ logging.debug(f"Updated configuration at {config_path}")
118
+
119
+
120
+ def deep_update(original: dict, update: dict) -> dict:
121
+ """
122
+ Recursively update a dictionary.
123
+
124
+ Args:
125
+ original: Dictionary to update
126
+ update: Dictionary with updates
127
+
128
+ Returns:
129
+ The updated original dictionary
130
+ """
131
+ for key, value in update.items():
132
+ if (
133
+ isinstance(value, dict)
134
+ and key in original
135
+ and isinstance(original[key], dict)
136
+ ):
137
+ # Recursively update nested dictionaries
138
+ deep_update(original[key], value)
139
+ else:
140
+ # Update or add values
141
+ original[key] = value
142
+
143
+ return original
144
+
145
+
146
+ def print_config(config_path: str) -> int:
147
+ """
148
+ Print the current configuration.
149
+
150
+ Args:
151
+ config_path: Path to the configuration file
152
+
153
+ Returns:
154
+ Exit code
155
+ """
156
+ try:
157
+ config = load_config(config_path)
158
+
159
+ print("\nCurrent Configuration:")
160
+ print(json.dumps(config, indent=2))
161
+
162
+ return 0
163
+ except Exception as e:
164
+ logging.error(f"Error reading configuration: {e}")
165
+ return 1