gitflow-analytics 1.0.3__py3-none-any.whl → 1.3.6__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.
- gitflow_analytics/_version.py +1 -1
- gitflow_analytics/classification/__init__.py +31 -0
- gitflow_analytics/classification/batch_classifier.py +752 -0
- gitflow_analytics/classification/classifier.py +464 -0
- gitflow_analytics/classification/feature_extractor.py +725 -0
- gitflow_analytics/classification/linguist_analyzer.py +574 -0
- gitflow_analytics/classification/model.py +455 -0
- gitflow_analytics/cli.py +4108 -350
- gitflow_analytics/cli_rich.py +198 -48
- gitflow_analytics/config/__init__.py +43 -0
- gitflow_analytics/config/errors.py +261 -0
- gitflow_analytics/config/loader.py +904 -0
- gitflow_analytics/config/profiles.py +264 -0
- gitflow_analytics/config/repository.py +124 -0
- gitflow_analytics/config/schema.py +441 -0
- gitflow_analytics/config/validator.py +154 -0
- gitflow_analytics/config.py +44 -508
- gitflow_analytics/core/analyzer.py +1209 -98
- gitflow_analytics/core/cache.py +1337 -29
- gitflow_analytics/core/data_fetcher.py +1193 -0
- gitflow_analytics/core/identity.py +363 -14
- gitflow_analytics/core/metrics_storage.py +526 -0
- gitflow_analytics/core/progress.py +372 -0
- gitflow_analytics/core/schema_version.py +269 -0
- gitflow_analytics/extractors/ml_tickets.py +1100 -0
- gitflow_analytics/extractors/story_points.py +8 -1
- gitflow_analytics/extractors/tickets.py +749 -11
- gitflow_analytics/identity_llm/__init__.py +6 -0
- gitflow_analytics/identity_llm/analysis_pass.py +231 -0
- gitflow_analytics/identity_llm/analyzer.py +464 -0
- gitflow_analytics/identity_llm/models.py +76 -0
- gitflow_analytics/integrations/github_integration.py +175 -11
- gitflow_analytics/integrations/jira_integration.py +461 -24
- gitflow_analytics/integrations/orchestrator.py +124 -1
- gitflow_analytics/metrics/activity_scoring.py +322 -0
- gitflow_analytics/metrics/branch_health.py +470 -0
- gitflow_analytics/metrics/dora.py +379 -20
- gitflow_analytics/models/database.py +843 -53
- gitflow_analytics/pm_framework/__init__.py +115 -0
- gitflow_analytics/pm_framework/adapters/__init__.py +50 -0
- gitflow_analytics/pm_framework/adapters/jira_adapter.py +1845 -0
- gitflow_analytics/pm_framework/base.py +406 -0
- gitflow_analytics/pm_framework/models.py +211 -0
- gitflow_analytics/pm_framework/orchestrator.py +652 -0
- gitflow_analytics/pm_framework/registry.py +333 -0
- gitflow_analytics/qualitative/__init__.py +9 -10
- gitflow_analytics/qualitative/chatgpt_analyzer.py +259 -0
- gitflow_analytics/qualitative/classifiers/__init__.py +3 -3
- gitflow_analytics/qualitative/classifiers/change_type.py +518 -244
- gitflow_analytics/qualitative/classifiers/domain_classifier.py +272 -165
- gitflow_analytics/qualitative/classifiers/intent_analyzer.py +321 -222
- gitflow_analytics/qualitative/classifiers/llm/__init__.py +35 -0
- gitflow_analytics/qualitative/classifiers/llm/base.py +193 -0
- gitflow_analytics/qualitative/classifiers/llm/batch_processor.py +383 -0
- gitflow_analytics/qualitative/classifiers/llm/cache.py +479 -0
- gitflow_analytics/qualitative/classifiers/llm/cost_tracker.py +435 -0
- gitflow_analytics/qualitative/classifiers/llm/openai_client.py +403 -0
- gitflow_analytics/qualitative/classifiers/llm/prompts.py +373 -0
- gitflow_analytics/qualitative/classifiers/llm/response_parser.py +287 -0
- gitflow_analytics/qualitative/classifiers/llm_commit_classifier.py +607 -0
- gitflow_analytics/qualitative/classifiers/risk_analyzer.py +215 -189
- gitflow_analytics/qualitative/core/__init__.py +4 -4
- gitflow_analytics/qualitative/core/llm_fallback.py +239 -235
- gitflow_analytics/qualitative/core/nlp_engine.py +157 -148
- gitflow_analytics/qualitative/core/pattern_cache.py +214 -192
- gitflow_analytics/qualitative/core/processor.py +381 -248
- gitflow_analytics/qualitative/enhanced_analyzer.py +2236 -0
- gitflow_analytics/qualitative/example_enhanced_usage.py +420 -0
- gitflow_analytics/qualitative/models/__init__.py +7 -7
- gitflow_analytics/qualitative/models/schemas.py +155 -121
- gitflow_analytics/qualitative/utils/__init__.py +4 -4
- gitflow_analytics/qualitative/utils/batch_processor.py +136 -123
- gitflow_analytics/qualitative/utils/cost_tracker.py +142 -140
- gitflow_analytics/qualitative/utils/metrics.py +172 -158
- gitflow_analytics/qualitative/utils/text_processing.py +146 -104
- gitflow_analytics/reports/__init__.py +100 -0
- gitflow_analytics/reports/analytics_writer.py +539 -14
- gitflow_analytics/reports/base.py +648 -0
- gitflow_analytics/reports/branch_health_writer.py +322 -0
- gitflow_analytics/reports/classification_writer.py +924 -0
- gitflow_analytics/reports/cli_integration.py +427 -0
- gitflow_analytics/reports/csv_writer.py +1676 -212
- gitflow_analytics/reports/data_models.py +504 -0
- gitflow_analytics/reports/database_report_generator.py +427 -0
- gitflow_analytics/reports/example_usage.py +344 -0
- gitflow_analytics/reports/factory.py +499 -0
- gitflow_analytics/reports/formatters.py +698 -0
- gitflow_analytics/reports/html_generator.py +1116 -0
- gitflow_analytics/reports/interfaces.py +489 -0
- gitflow_analytics/reports/json_exporter.py +2770 -0
- gitflow_analytics/reports/narrative_writer.py +2287 -158
- gitflow_analytics/reports/story_point_correlation.py +1144 -0
- gitflow_analytics/reports/weekly_trends_writer.py +389 -0
- gitflow_analytics/training/__init__.py +5 -0
- gitflow_analytics/training/model_loader.py +377 -0
- gitflow_analytics/training/pipeline.py +550 -0
- gitflow_analytics/tui/__init__.py +1 -1
- gitflow_analytics/tui/app.py +129 -126
- gitflow_analytics/tui/screens/__init__.py +3 -3
- gitflow_analytics/tui/screens/analysis_progress_screen.py +188 -179
- gitflow_analytics/tui/screens/configuration_screen.py +154 -178
- gitflow_analytics/tui/screens/loading_screen.py +100 -110
- gitflow_analytics/tui/screens/main_screen.py +89 -72
- gitflow_analytics/tui/screens/results_screen.py +305 -281
- gitflow_analytics/tui/widgets/__init__.py +2 -2
- gitflow_analytics/tui/widgets/data_table.py +67 -69
- gitflow_analytics/tui/widgets/export_modal.py +76 -76
- gitflow_analytics/tui/widgets/progress_widget.py +41 -46
- gitflow_analytics-1.3.6.dist-info/METADATA +1015 -0
- gitflow_analytics-1.3.6.dist-info/RECORD +122 -0
- gitflow_analytics-1.0.3.dist-info/METADATA +0 -490
- gitflow_analytics-1.0.3.dist-info/RECORD +0 -62
- {gitflow_analytics-1.0.3.dist-info → gitflow_analytics-1.3.6.dist-info}/WHEEL +0 -0
- {gitflow_analytics-1.0.3.dist-info → gitflow_analytics-1.3.6.dist-info}/entry_points.txt +0 -0
- {gitflow_analytics-1.0.3.dist-info → gitflow_analytics-1.3.6.dist-info}/licenses/LICENSE +0 -0
- {gitflow_analytics-1.0.3.dist-info → gitflow_analytics-1.3.6.dist-info}/top_level.txt +0 -0
|
@@ -3,28 +3,27 @@
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Optional
|
|
5
5
|
|
|
6
|
-
from textual.widgets import Header, Footer, Button, Label, Static, Rule
|
|
7
|
-
from textual.containers import Container, Vertical, Horizontal
|
|
8
|
-
from textual.screen import Screen
|
|
9
6
|
from textual.binding import Binding
|
|
7
|
+
from textual.containers import Container, Vertical
|
|
10
8
|
from textual.message import Message
|
|
9
|
+
from textual.screen import Screen
|
|
10
|
+
from textual.widgets import Button, Footer, Header, Label, Rule, Static
|
|
11
11
|
|
|
12
|
-
from ..widgets.export_modal import ExportModal
|
|
13
12
|
from gitflow_analytics.config import Config
|
|
14
13
|
|
|
15
14
|
|
|
16
15
|
class MainScreen(Screen):
|
|
17
16
|
"""
|
|
18
17
|
Main dashboard screen showing project information and navigation options.
|
|
19
|
-
|
|
18
|
+
|
|
20
19
|
WHY: Serves as the primary entry point for the TUI, providing users with
|
|
21
20
|
an overview of their configuration and clear navigation to all major features.
|
|
22
|
-
|
|
21
|
+
|
|
23
22
|
DESIGN DECISION: Uses a dashboard layout rather than a menu-driven approach
|
|
24
23
|
to provide immediate visibility into the current configuration status and
|
|
25
24
|
quick access to common operations.
|
|
26
25
|
"""
|
|
27
|
-
|
|
26
|
+
|
|
28
27
|
BINDINGS = [
|
|
29
28
|
Binding("ctrl+q", "quit", "Quit"),
|
|
30
29
|
Binding("ctrl+n", "new_analysis", "New Analysis"),
|
|
@@ -33,73 +32,84 @@ class MainScreen(Screen):
|
|
|
33
32
|
Binding("c", "cache_status", "Cache Status"),
|
|
34
33
|
Binding("i", "manage_identities", "Identities"),
|
|
35
34
|
]
|
|
36
|
-
|
|
35
|
+
|
|
37
36
|
class NewAnalysisRequested(Message):
|
|
38
37
|
"""Message sent when new analysis is requested."""
|
|
38
|
+
|
|
39
39
|
def __init__(self) -> None:
|
|
40
40
|
super().__init__()
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
class ConfigurationRequested(Message):
|
|
43
43
|
"""Message sent when configuration is requested."""
|
|
44
|
+
|
|
44
45
|
def __init__(self) -> None:
|
|
45
46
|
super().__init__()
|
|
46
|
-
|
|
47
|
+
|
|
47
48
|
class CacheStatusRequested(Message):
|
|
48
49
|
"""Message sent when cache status is requested."""
|
|
50
|
+
|
|
49
51
|
def __init__(self) -> None:
|
|
50
52
|
super().__init__()
|
|
51
|
-
|
|
53
|
+
|
|
52
54
|
class IdentityManagementRequested(Message):
|
|
53
55
|
"""Message sent when identity management is requested."""
|
|
56
|
+
|
|
54
57
|
def __init__(self) -> None:
|
|
55
58
|
super().__init__()
|
|
56
|
-
|
|
59
|
+
|
|
57
60
|
class HelpRequested(Message):
|
|
58
61
|
"""Message sent when help is requested."""
|
|
62
|
+
|
|
59
63
|
def __init__(self) -> None:
|
|
60
64
|
super().__init__()
|
|
61
|
-
|
|
65
|
+
|
|
62
66
|
def __init__(
|
|
63
67
|
self,
|
|
64
68
|
config: Optional[Config] = None,
|
|
65
69
|
config_path: Optional[Path] = None,
|
|
66
70
|
*,
|
|
67
71
|
name: Optional[str] = None,
|
|
68
|
-
id: Optional[str] = None
|
|
72
|
+
id: Optional[str] = None,
|
|
69
73
|
) -> None:
|
|
70
74
|
super().__init__(name=name, id=id)
|
|
71
75
|
self.config = config
|
|
72
76
|
self.config_path = config_path
|
|
73
|
-
|
|
77
|
+
|
|
74
78
|
def compose(self):
|
|
75
79
|
"""Compose the main screen."""
|
|
76
80
|
yield Header()
|
|
77
|
-
|
|
81
|
+
|
|
78
82
|
with Container(id="main-container"):
|
|
79
83
|
yield Label("GitFlow Analytics", classes="screen-title")
|
|
80
84
|
yield Static("Developer Productivity Analysis Tool", id="subtitle")
|
|
81
|
-
|
|
85
|
+
|
|
82
86
|
# Configuration status section
|
|
83
87
|
with Container(classes="status-panel"):
|
|
84
88
|
yield Label("Configuration Status", classes="section-title")
|
|
85
|
-
|
|
89
|
+
|
|
86
90
|
if self.config:
|
|
87
|
-
config_status =
|
|
91
|
+
config_status = (
|
|
92
|
+
f"✅ Loaded: {self.config_path}"
|
|
93
|
+
if self.config_path
|
|
94
|
+
else "✅ Configuration loaded"
|
|
95
|
+
)
|
|
88
96
|
yield Static(config_status, id="config-status")
|
|
89
|
-
|
|
97
|
+
|
|
90
98
|
# Show key configuration details
|
|
91
99
|
details = self._get_config_summary()
|
|
92
100
|
yield Static(details, id="config-details")
|
|
93
101
|
else:
|
|
94
102
|
yield Static("❌ No configuration loaded", id="config-status")
|
|
95
|
-
yield Static(
|
|
96
|
-
|
|
103
|
+
yield Static(
|
|
104
|
+
"Load or create a configuration to begin analysis", id="config-help"
|
|
105
|
+
)
|
|
106
|
+
|
|
97
107
|
yield Rule()
|
|
98
|
-
|
|
108
|
+
|
|
99
109
|
# Main actions section
|
|
100
110
|
with Container(classes="actions-panel"):
|
|
101
111
|
yield Label("Available Actions", classes="section-title")
|
|
102
|
-
|
|
112
|
+
|
|
103
113
|
with Vertical(id="action-buttons"):
|
|
104
114
|
if self.config:
|
|
105
115
|
yield Button("🚀 Run Analysis", variant="primary", id="run-analysis")
|
|
@@ -109,84 +119,89 @@ class MainScreen(Screen):
|
|
|
109
119
|
else:
|
|
110
120
|
yield Button("📁 Load Configuration", variant="primary", id="load-config")
|
|
111
121
|
yield Button("➕ Create New Configuration", id="new-config")
|
|
112
|
-
|
|
122
|
+
|
|
113
123
|
yield Rule()
|
|
114
124
|
yield Button("❓ Help & Documentation", id="help")
|
|
115
|
-
|
|
125
|
+
|
|
116
126
|
# Quick stats section (if config loaded)
|
|
117
127
|
if self.config:
|
|
118
128
|
with Container(classes="stats-panel"):
|
|
119
129
|
yield Label("Quick Information", classes="section-title")
|
|
120
130
|
stats = self._get_quick_stats()
|
|
121
131
|
yield Static(stats, id="quick-stats")
|
|
122
|
-
|
|
132
|
+
|
|
123
133
|
yield Footer()
|
|
124
|
-
|
|
134
|
+
|
|
125
135
|
def _get_config_summary(self) -> str:
|
|
126
136
|
"""
|
|
127
137
|
Generate configuration summary for display.
|
|
128
|
-
|
|
138
|
+
|
|
129
139
|
WHY: Provides users with immediate visibility into their current
|
|
130
140
|
configuration without needing to navigate to a separate screen.
|
|
131
141
|
"""
|
|
132
142
|
if not self.config:
|
|
133
143
|
return ""
|
|
134
|
-
|
|
144
|
+
|
|
135
145
|
lines = []
|
|
136
|
-
|
|
146
|
+
|
|
137
147
|
# Repository count
|
|
138
148
|
repo_count = len(self.config.repositories) if self.config.repositories else 0
|
|
139
149
|
if self.config.github.organization and not self.config.repositories:
|
|
140
150
|
lines.append(f"• Organization: {self.config.github.organization} (auto-discovery)")
|
|
141
151
|
else:
|
|
142
152
|
lines.append(f"• Repositories: {repo_count} configured")
|
|
143
|
-
|
|
153
|
+
|
|
144
154
|
# GitHub integration
|
|
145
155
|
if self.config.github.token:
|
|
146
156
|
lines.append("✅ GitHub API configured")
|
|
147
157
|
else:
|
|
148
158
|
lines.append("⚠️ GitHub API not configured")
|
|
149
|
-
|
|
159
|
+
|
|
150
160
|
# Qualitative analysis
|
|
151
|
-
if
|
|
161
|
+
if (
|
|
162
|
+
hasattr(self.config, "qualitative")
|
|
163
|
+
and self.config.qualitative
|
|
164
|
+
and self.config.qualitative.enabled
|
|
165
|
+
):
|
|
152
166
|
lines.append("✅ Qualitative analysis enabled")
|
|
153
167
|
else:
|
|
154
168
|
lines.append("⚠️ Qualitative analysis disabled")
|
|
155
|
-
|
|
169
|
+
|
|
156
170
|
# JIRA integration
|
|
157
|
-
if hasattr(self.config,
|
|
171
|
+
if hasattr(self.config, "jira") and self.config.jira and self.config.jira.base_url:
|
|
158
172
|
lines.append("✅ JIRA integration configured")
|
|
159
173
|
else:
|
|
160
174
|
lines.append("⚠️ JIRA integration not configured")
|
|
161
|
-
|
|
175
|
+
|
|
162
176
|
# Cache configuration
|
|
163
177
|
lines.append(f"• Cache TTL: {self.config.cache.ttl_hours}h")
|
|
164
|
-
|
|
178
|
+
|
|
165
179
|
return "\n".join(lines)
|
|
166
|
-
|
|
180
|
+
|
|
167
181
|
def _get_quick_stats(self) -> str:
|
|
168
182
|
"""
|
|
169
183
|
Generate quick statistics if cache data is available.
|
|
170
|
-
|
|
184
|
+
|
|
171
185
|
WHY: Shows users what data is already available from previous runs,
|
|
172
186
|
helping them understand if they need to run a new analysis.
|
|
173
187
|
"""
|
|
174
188
|
try:
|
|
175
189
|
from ....core.cache import GitAnalysisCache
|
|
176
|
-
|
|
190
|
+
|
|
177
191
|
cache = GitAnalysisCache(self.config.cache.directory)
|
|
178
192
|
stats = cache.get_cache_stats()
|
|
179
|
-
|
|
193
|
+
|
|
180
194
|
lines = []
|
|
181
195
|
lines.append(f"• Cached commits: {stats['cached_commits']:,}")
|
|
182
196
|
lines.append(f"• Cached PRs: {stats['cached_prs']:,}")
|
|
183
197
|
lines.append(f"• Cached issues: {stats['cached_issues']:,}")
|
|
184
|
-
|
|
185
|
-
if stats[
|
|
198
|
+
|
|
199
|
+
if stats["stale_commits"] > 0:
|
|
186
200
|
lines.append(f"• Stale entries: {stats['stale_commits']:,}")
|
|
187
|
-
|
|
201
|
+
|
|
188
202
|
# Cache size
|
|
189
203
|
import os
|
|
204
|
+
|
|
190
205
|
cache_size = 0
|
|
191
206
|
try:
|
|
192
207
|
for root, _dirs, files in os.walk(self.config.cache.directory):
|
|
@@ -194,14 +209,14 @@ class MainScreen(Screen):
|
|
|
194
209
|
cache_size += os.path.getsize(os.path.join(root, f))
|
|
195
210
|
cache_size_mb = cache_size / 1024 / 1024
|
|
196
211
|
lines.append(f"• Cache size: {cache_size_mb:.1f} MB")
|
|
197
|
-
except:
|
|
212
|
+
except Exception:
|
|
198
213
|
pass
|
|
199
|
-
|
|
214
|
+
|
|
200
215
|
return "\n".join(lines)
|
|
201
|
-
|
|
216
|
+
|
|
202
217
|
except Exception:
|
|
203
218
|
return "Cache statistics unavailable"
|
|
204
|
-
|
|
219
|
+
|
|
205
220
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
206
221
|
"""Handle button press events."""
|
|
207
222
|
button_actions = {
|
|
@@ -213,92 +228,94 @@ class MainScreen(Screen):
|
|
|
213
228
|
"manage-identities": self._manage_identities,
|
|
214
229
|
"help": self._show_help,
|
|
215
230
|
}
|
|
216
|
-
|
|
231
|
+
|
|
217
232
|
action = button_actions.get(event.button.id)
|
|
218
233
|
if action:
|
|
219
234
|
action()
|
|
220
|
-
|
|
235
|
+
|
|
221
236
|
def _run_analysis(self) -> None:
|
|
222
237
|
"""Request new analysis."""
|
|
223
238
|
if not self.config:
|
|
224
239
|
self.notify("Please load or create a configuration first", severity="error")
|
|
225
240
|
return
|
|
226
241
|
self.post_message(self.NewAnalysisRequested())
|
|
227
|
-
|
|
242
|
+
|
|
228
243
|
def _load_config(self) -> None:
|
|
229
244
|
"""Request configuration loading."""
|
|
230
245
|
self.post_message(self.ConfigurationRequested())
|
|
231
|
-
|
|
246
|
+
|
|
232
247
|
def _new_config(self) -> None:
|
|
233
248
|
"""Request new configuration creation."""
|
|
234
249
|
self.post_message(self.ConfigurationRequested())
|
|
235
|
-
|
|
250
|
+
|
|
236
251
|
def _edit_config(self) -> None:
|
|
237
252
|
"""Request configuration editing."""
|
|
238
253
|
self.post_message(self.ConfigurationRequested())
|
|
239
|
-
|
|
254
|
+
|
|
240
255
|
def _cache_status(self) -> None:
|
|
241
256
|
"""Request cache status display."""
|
|
242
257
|
self.post_message(self.CacheStatusRequested())
|
|
243
|
-
|
|
258
|
+
|
|
244
259
|
def _manage_identities(self) -> None:
|
|
245
260
|
"""Request identity management."""
|
|
246
261
|
self.post_message(self.IdentityManagementRequested())
|
|
247
|
-
|
|
262
|
+
|
|
248
263
|
def _show_help(self) -> None:
|
|
249
264
|
"""Request help display."""
|
|
250
265
|
self.post_message(self.HelpRequested())
|
|
251
|
-
|
|
266
|
+
|
|
252
267
|
# Action handlers for key bindings
|
|
253
268
|
def action_quit(self) -> None:
|
|
254
269
|
"""Quit the application."""
|
|
255
270
|
self.app.exit()
|
|
256
|
-
|
|
271
|
+
|
|
257
272
|
def action_new_analysis(self) -> None:
|
|
258
273
|
"""Start new analysis via keyboard shortcut."""
|
|
259
274
|
self._run_analysis()
|
|
260
|
-
|
|
275
|
+
|
|
261
276
|
def action_open_config(self) -> None:
|
|
262
277
|
"""Open configuration via keyboard shortcut."""
|
|
263
278
|
self._load_config()
|
|
264
|
-
|
|
279
|
+
|
|
265
280
|
def action_help(self) -> None:
|
|
266
281
|
"""Show help via keyboard shortcut."""
|
|
267
282
|
self._show_help()
|
|
268
|
-
|
|
283
|
+
|
|
269
284
|
def action_cache_status(self) -> None:
|
|
270
285
|
"""Show cache status via keyboard shortcut."""
|
|
271
286
|
self._cache_status()
|
|
272
|
-
|
|
287
|
+
|
|
273
288
|
def action_manage_identities(self) -> None:
|
|
274
289
|
"""Manage identities via keyboard shortcut."""
|
|
275
290
|
self._manage_identities()
|
|
276
|
-
|
|
291
|
+
|
|
277
292
|
def update_config(self, config: Config, config_path: Optional[Path] = None) -> None:
|
|
278
293
|
"""
|
|
279
294
|
Update the configuration and refresh the display.
|
|
280
|
-
|
|
295
|
+
|
|
281
296
|
WHY: Allows the main screen to be updated when configuration changes
|
|
282
297
|
without requiring a full screen rebuild, maintaining user context.
|
|
283
298
|
"""
|
|
284
299
|
self.config = config
|
|
285
300
|
self.config_path = config_path
|
|
286
|
-
|
|
301
|
+
|
|
287
302
|
# Update configuration status
|
|
288
303
|
if self.config:
|
|
289
|
-
config_status =
|
|
304
|
+
config_status = (
|
|
305
|
+
f"✅ Loaded: {self.config_path}" if self.config_path else "✅ Configuration loaded"
|
|
306
|
+
)
|
|
290
307
|
self.query_one("#config-status", Static).update(config_status)
|
|
291
|
-
|
|
308
|
+
|
|
292
309
|
# Update configuration details
|
|
293
310
|
details = self._get_config_summary()
|
|
294
311
|
self.query_one("#config-details", Static).update(details)
|
|
295
|
-
|
|
312
|
+
|
|
296
313
|
# Update quick stats if available
|
|
297
314
|
try:
|
|
298
315
|
stats = self._get_quick_stats()
|
|
299
316
|
self.query_one("#quick-stats", Static).update(stats)
|
|
300
|
-
except:
|
|
317
|
+
except Exception:
|
|
301
318
|
pass
|
|
302
|
-
|
|
319
|
+
|
|
303
320
|
# Refresh to rebuild buttons with new state
|
|
304
|
-
self.refresh(recompose=True)
|
|
321
|
+
self.refresh(recompose=True)
|