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.
Files changed (116) hide show
  1. gitflow_analytics/_version.py +1 -1
  2. gitflow_analytics/classification/__init__.py +31 -0
  3. gitflow_analytics/classification/batch_classifier.py +752 -0
  4. gitflow_analytics/classification/classifier.py +464 -0
  5. gitflow_analytics/classification/feature_extractor.py +725 -0
  6. gitflow_analytics/classification/linguist_analyzer.py +574 -0
  7. gitflow_analytics/classification/model.py +455 -0
  8. gitflow_analytics/cli.py +4108 -350
  9. gitflow_analytics/cli_rich.py +198 -48
  10. gitflow_analytics/config/__init__.py +43 -0
  11. gitflow_analytics/config/errors.py +261 -0
  12. gitflow_analytics/config/loader.py +904 -0
  13. gitflow_analytics/config/profiles.py +264 -0
  14. gitflow_analytics/config/repository.py +124 -0
  15. gitflow_analytics/config/schema.py +441 -0
  16. gitflow_analytics/config/validator.py +154 -0
  17. gitflow_analytics/config.py +44 -508
  18. gitflow_analytics/core/analyzer.py +1209 -98
  19. gitflow_analytics/core/cache.py +1337 -29
  20. gitflow_analytics/core/data_fetcher.py +1193 -0
  21. gitflow_analytics/core/identity.py +363 -14
  22. gitflow_analytics/core/metrics_storage.py +526 -0
  23. gitflow_analytics/core/progress.py +372 -0
  24. gitflow_analytics/core/schema_version.py +269 -0
  25. gitflow_analytics/extractors/ml_tickets.py +1100 -0
  26. gitflow_analytics/extractors/story_points.py +8 -1
  27. gitflow_analytics/extractors/tickets.py +749 -11
  28. gitflow_analytics/identity_llm/__init__.py +6 -0
  29. gitflow_analytics/identity_llm/analysis_pass.py +231 -0
  30. gitflow_analytics/identity_llm/analyzer.py +464 -0
  31. gitflow_analytics/identity_llm/models.py +76 -0
  32. gitflow_analytics/integrations/github_integration.py +175 -11
  33. gitflow_analytics/integrations/jira_integration.py +461 -24
  34. gitflow_analytics/integrations/orchestrator.py +124 -1
  35. gitflow_analytics/metrics/activity_scoring.py +322 -0
  36. gitflow_analytics/metrics/branch_health.py +470 -0
  37. gitflow_analytics/metrics/dora.py +379 -20
  38. gitflow_analytics/models/database.py +843 -53
  39. gitflow_analytics/pm_framework/__init__.py +115 -0
  40. gitflow_analytics/pm_framework/adapters/__init__.py +50 -0
  41. gitflow_analytics/pm_framework/adapters/jira_adapter.py +1845 -0
  42. gitflow_analytics/pm_framework/base.py +406 -0
  43. gitflow_analytics/pm_framework/models.py +211 -0
  44. gitflow_analytics/pm_framework/orchestrator.py +652 -0
  45. gitflow_analytics/pm_framework/registry.py +333 -0
  46. gitflow_analytics/qualitative/__init__.py +9 -10
  47. gitflow_analytics/qualitative/chatgpt_analyzer.py +259 -0
  48. gitflow_analytics/qualitative/classifiers/__init__.py +3 -3
  49. gitflow_analytics/qualitative/classifiers/change_type.py +518 -244
  50. gitflow_analytics/qualitative/classifiers/domain_classifier.py +272 -165
  51. gitflow_analytics/qualitative/classifiers/intent_analyzer.py +321 -222
  52. gitflow_analytics/qualitative/classifiers/llm/__init__.py +35 -0
  53. gitflow_analytics/qualitative/classifiers/llm/base.py +193 -0
  54. gitflow_analytics/qualitative/classifiers/llm/batch_processor.py +383 -0
  55. gitflow_analytics/qualitative/classifiers/llm/cache.py +479 -0
  56. gitflow_analytics/qualitative/classifiers/llm/cost_tracker.py +435 -0
  57. gitflow_analytics/qualitative/classifiers/llm/openai_client.py +403 -0
  58. gitflow_analytics/qualitative/classifiers/llm/prompts.py +373 -0
  59. gitflow_analytics/qualitative/classifiers/llm/response_parser.py +287 -0
  60. gitflow_analytics/qualitative/classifiers/llm_commit_classifier.py +607 -0
  61. gitflow_analytics/qualitative/classifiers/risk_analyzer.py +215 -189
  62. gitflow_analytics/qualitative/core/__init__.py +4 -4
  63. gitflow_analytics/qualitative/core/llm_fallback.py +239 -235
  64. gitflow_analytics/qualitative/core/nlp_engine.py +157 -148
  65. gitflow_analytics/qualitative/core/pattern_cache.py +214 -192
  66. gitflow_analytics/qualitative/core/processor.py +381 -248
  67. gitflow_analytics/qualitative/enhanced_analyzer.py +2236 -0
  68. gitflow_analytics/qualitative/example_enhanced_usage.py +420 -0
  69. gitflow_analytics/qualitative/models/__init__.py +7 -7
  70. gitflow_analytics/qualitative/models/schemas.py +155 -121
  71. gitflow_analytics/qualitative/utils/__init__.py +4 -4
  72. gitflow_analytics/qualitative/utils/batch_processor.py +136 -123
  73. gitflow_analytics/qualitative/utils/cost_tracker.py +142 -140
  74. gitflow_analytics/qualitative/utils/metrics.py +172 -158
  75. gitflow_analytics/qualitative/utils/text_processing.py +146 -104
  76. gitflow_analytics/reports/__init__.py +100 -0
  77. gitflow_analytics/reports/analytics_writer.py +539 -14
  78. gitflow_analytics/reports/base.py +648 -0
  79. gitflow_analytics/reports/branch_health_writer.py +322 -0
  80. gitflow_analytics/reports/classification_writer.py +924 -0
  81. gitflow_analytics/reports/cli_integration.py +427 -0
  82. gitflow_analytics/reports/csv_writer.py +1676 -212
  83. gitflow_analytics/reports/data_models.py +504 -0
  84. gitflow_analytics/reports/database_report_generator.py +427 -0
  85. gitflow_analytics/reports/example_usage.py +344 -0
  86. gitflow_analytics/reports/factory.py +499 -0
  87. gitflow_analytics/reports/formatters.py +698 -0
  88. gitflow_analytics/reports/html_generator.py +1116 -0
  89. gitflow_analytics/reports/interfaces.py +489 -0
  90. gitflow_analytics/reports/json_exporter.py +2770 -0
  91. gitflow_analytics/reports/narrative_writer.py +2287 -158
  92. gitflow_analytics/reports/story_point_correlation.py +1144 -0
  93. gitflow_analytics/reports/weekly_trends_writer.py +389 -0
  94. gitflow_analytics/training/__init__.py +5 -0
  95. gitflow_analytics/training/model_loader.py +377 -0
  96. gitflow_analytics/training/pipeline.py +550 -0
  97. gitflow_analytics/tui/__init__.py +1 -1
  98. gitflow_analytics/tui/app.py +129 -126
  99. gitflow_analytics/tui/screens/__init__.py +3 -3
  100. gitflow_analytics/tui/screens/analysis_progress_screen.py +188 -179
  101. gitflow_analytics/tui/screens/configuration_screen.py +154 -178
  102. gitflow_analytics/tui/screens/loading_screen.py +100 -110
  103. gitflow_analytics/tui/screens/main_screen.py +89 -72
  104. gitflow_analytics/tui/screens/results_screen.py +305 -281
  105. gitflow_analytics/tui/widgets/__init__.py +2 -2
  106. gitflow_analytics/tui/widgets/data_table.py +67 -69
  107. gitflow_analytics/tui/widgets/export_modal.py +76 -76
  108. gitflow_analytics/tui/widgets/progress_widget.py +41 -46
  109. gitflow_analytics-1.3.6.dist-info/METADATA +1015 -0
  110. gitflow_analytics-1.3.6.dist-info/RECORD +122 -0
  111. gitflow_analytics-1.0.3.dist-info/METADATA +0 -490
  112. gitflow_analytics-1.0.3.dist-info/RECORD +0 -62
  113. {gitflow_analytics-1.0.3.dist-info → gitflow_analytics-1.3.6.dist-info}/WHEEL +0 -0
  114. {gitflow_analytics-1.0.3.dist-info → gitflow_analytics-1.3.6.dist-info}/entry_points.txt +0 -0
  115. {gitflow_analytics-1.0.3.dist-info → gitflow_analytics-1.3.6.dist-info}/licenses/LICENSE +0 -0
  116. {gitflow_analytics-1.0.3.dist-info → gitflow_analytics-1.3.6.dist-info}/top_level.txt +0 -0
@@ -2,15 +2,13 @@
2
2
 
3
3
  import asyncio
4
4
  import time
5
- from typing import Optional, Dict, Any
5
+ from typing import Any, Optional
6
6
 
7
- from textual.widgets import Header, Footer, Label, Static, LoadingIndicator
8
- from textual.containers import Container, Vertical, Horizontal, Center
9
- from textual.screen import Screen
10
7
  from textual.binding import Binding
8
+ from textual.containers import Center, Container, Vertical
11
9
  from textual.message import Message
12
- from rich.text import Text
13
- from rich.align import Align
10
+ from textual.screen import Screen
11
+ from textual.widgets import Footer, Header, Label, LoadingIndicator, Static
14
12
 
15
13
  from ..widgets.progress_widget import AnalysisProgressWidget
16
14
 
@@ -18,146 +16,141 @@ from ..widgets.progress_widget import AnalysisProgressWidget
18
16
  class LoadingScreen(Screen):
19
17
  """
20
18
  Loading screen displayed during application startup and heavy initialization.
21
-
19
+
22
20
  WHY: The TUI application needs to load configurations, spaCy models, and other
23
21
  heavy resources during startup. This screen provides user feedback about the
24
22
  loading process instead of showing a black screen, improving user experience.
25
-
23
+
26
24
  DESIGN DECISION: Uses a combination of progress indicators and status messages
27
25
  to show both overall progress and specific loading steps. This keeps users
28
26
  informed about what's happening and that the application is responsive.
29
27
  """
30
-
28
+
31
29
  BINDINGS = [
32
30
  Binding("ctrl+c", "cancel", "Cancel Loading"),
33
31
  Binding("escape", "cancel", "Cancel Loading"),
34
32
  ]
35
-
33
+
36
34
  def __init__(
37
35
  self,
38
36
  loading_message: str = "Initializing GitFlow Analytics...",
39
37
  *,
40
38
  name: Optional[str] = None,
41
- id: Optional[str] = None
39
+ id: Optional[str] = None,
42
40
  ) -> None:
43
41
  super().__init__(name=name, id=id)
44
42
  self.loading_message = loading_message
45
43
  self.loading_task: Optional[asyncio.Task] = None
46
44
  self.start_time = time.time()
47
45
  self.cancelled = False
48
-
46
+
49
47
  def compose(self):
50
48
  """Compose the loading screen with progress indicators."""
51
49
  yield Header()
52
-
50
+
53
51
  with Container(id="loading-container"):
54
52
  # Main loading title
55
53
  yield Label("GitFlow Analytics", classes="screen-title")
56
54
  yield Label("Developer Productivity Analysis", classes="help-text center")
57
-
55
+
58
56
  # Loading animation and message
59
- with Center():
60
- with Vertical(id="loading-content"):
61
- yield LoadingIndicator(id="main-spinner")
62
- yield Label(self.loading_message, classes="center", id="loading-message")
63
-
64
- # Overall progress bar
65
- yield AnalysisProgressWidget(
66
- "Startup Progress",
67
- total=100.0,
68
- id="startup-progress"
69
- )
70
-
71
- # Status messages
72
- with Container(classes="status-panel"):
73
- yield Label("Status", classes="panel-title")
74
- yield Static("Starting up...", id="status-message")
75
-
76
- # Loading steps indicators
77
- with Container(classes="stats-panel"):
78
- yield Label("Loading Steps", classes="panel-title")
79
- with Vertical(id="loading-steps"):
80
- yield Static("⏳ Initializing application...", id="step-init")
81
- yield Static("⏳ Loading configuration...", id="step-config")
82
- yield Static("⏳ Preparing analysis engine...", id="step-engine")
83
- yield Static("⏳ Loading NLP models...", id="step-nlp")
84
- yield Static("⏳ Finalizing setup...", id="step-finalize")
85
-
57
+ with Center(), Vertical(id="loading-content"):
58
+ yield LoadingIndicator(id="main-spinner")
59
+ yield Label(self.loading_message, classes="center", id="loading-message")
60
+
61
+ # Overall progress bar
62
+ yield AnalysisProgressWidget("Startup Progress", total=100.0, id="startup-progress")
63
+
64
+ # Status messages
65
+ with Container(classes="status-panel"):
66
+ yield Label("Status", classes="panel-title")
67
+ yield Static("Starting up...", id="status-message")
68
+
69
+ # Loading steps indicators
70
+ with Container(classes="stats-panel"):
71
+ yield Label("Loading Steps", classes="panel-title")
72
+ with Vertical(id="loading-steps"):
73
+ yield Static("⏳ Initializing application...", id="step-init")
74
+ yield Static("⏳ Loading configuration...", id="step-config")
75
+ yield Static("⏳ Preparing analysis engine...", id="step-engine")
76
+ yield Static("Loading NLP models...", id="step-nlp")
77
+ yield Static("⏳ Finalizing setup...", id="step-finalize")
78
+
86
79
  yield Footer()
87
-
80
+
88
81
  def on_mount(self) -> None:
89
82
  """Start loading process when screen mounts."""
90
83
  self.loading_task = asyncio.create_task(self._simulate_loading())
91
-
84
+
92
85
  async def _simulate_loading(self) -> None:
93
86
  """
94
87
  Simulate the loading process with progress updates.
95
-
88
+
96
89
  WHY: This provides visual feedback during startup initialization.
97
90
  In practice, this would be replaced by actual initialization calls.
98
91
  """
99
92
  progress_widget = self.query_one("#startup-progress", AnalysisProgressWidget)
100
93
  status_message = self.query_one("#status-message", Static)
101
-
94
+
102
95
  try:
103
96
  # Step 1: Initialize application (20%)
104
97
  await self._update_step("step-init", "✅ Application initialized", "success")
105
98
  status_message.update("Initializing core components...")
106
99
  progress_widget.update_progress(20, "Core components ready")
107
100
  await asyncio.sleep(0.3)
108
-
101
+
109
102
  if self.cancelled:
110
103
  return
111
-
104
+
112
105
  # Step 2: Load configuration (40%)
113
106
  await self._update_step("step-config", "✅ Configuration loaded", "success")
114
107
  status_message.update("Loading configuration files...")
115
108
  progress_widget.update_progress(40, "Configuration loaded")
116
109
  await asyncio.sleep(0.5)
117
-
110
+
118
111
  if self.cancelled:
119
112
  return
120
-
113
+
121
114
  # Step 3: Prepare analysis engine (60%)
122
115
  await self._update_step("step-engine", "✅ Analysis engine ready", "success")
123
116
  status_message.update("Preparing Git analysis engine...")
124
117
  progress_widget.update_progress(60, "Analysis engine initialized")
125
118
  await asyncio.sleep(0.4)
126
-
119
+
127
120
  if self.cancelled:
128
121
  return
129
-
122
+
130
123
  # Step 4: Load NLP models (85%) - This is the heavy operation
131
124
  await self._update_step("step-nlp", "⏳ Loading spaCy models...", "warning")
132
125
  status_message.update("Loading natural language processing models...")
133
126
  progress_widget.update_progress(70, "Loading spaCy models...")
134
127
  await asyncio.sleep(1.2) # Simulate spaCy model loading time
135
-
128
+
136
129
  if self.cancelled:
137
130
  return
138
-
131
+
139
132
  await self._update_step("step-nlp", "✅ NLP models loaded", "success")
140
133
  progress_widget.update_progress(85, "NLP models ready")
141
-
134
+
142
135
  # Step 5: Finalize setup (100%)
143
136
  await self._update_step("step-finalize", "✅ Setup complete", "success")
144
137
  status_message.update("Finalizing application setup...")
145
138
  progress_widget.update_progress(100, "GitFlow Analytics ready!")
146
139
  await asyncio.sleep(0.3)
147
-
140
+
148
141
  # Show completion message briefly
149
142
  loading_message = self.query_one("#loading-message", Label)
150
143
  loading_message.update("Loading complete! Starting application...")
151
-
144
+
152
145
  elapsed_time = time.time() - self.start_time
153
146
  status_message.update(f"Ready! Loaded in {elapsed_time:.1f} seconds")
154
-
147
+
155
148
  # Wait a moment to show completion
156
149
  await asyncio.sleep(0.8)
157
-
150
+
158
151
  # Signal that loading is complete
159
152
  self.app.post_message(self.LoadingComplete())
160
-
153
+
161
154
  except asyncio.CancelledError:
162
155
  progress_widget.update_progress(0, "Loading cancelled")
163
156
  status_message.update("Loading cancelled by user")
@@ -165,73 +158,75 @@ class LoadingScreen(Screen):
165
158
  progress_widget.update_progress(0, f"Error: {str(e)[:50]}...")
166
159
  status_message.update(f"Loading failed: {e}")
167
160
  self.app.notify(f"Loading failed: {e}", severity="error")
168
-
161
+
169
162
  async def _update_step(self, step_id: str, message: str, status: str) -> None:
170
163
  """
171
164
  Update a loading step with status.
172
-
165
+
173
166
  @param step_id: ID of the step element to update
174
167
  @param message: Status message to display
175
168
  @param status: Status type (success, warning, error)
176
169
  """
177
170
  step_element = self.query_one(f"#{step_id}", Static)
178
171
  step_element.update(message)
179
-
172
+
180
173
  # Apply appropriate styling based on status
181
174
  step_element.remove_class("success", "warning", "error")
182
175
  step_element.add_class(status)
183
-
176
+
184
177
  def update_loading_message(self, message: str) -> None:
185
178
  """
186
179
  Update the main loading message.
187
-
180
+
188
181
  @param message: New loading message to display
189
182
  """
190
183
  try:
191
184
  loading_message = self.query_one("#loading-message", Label)
192
185
  loading_message.update(message)
193
- except:
186
+ except Exception:
194
187
  pass # Ignore if element not found
195
-
188
+
196
189
  def update_progress(self, percentage: float, status: str) -> None:
197
190
  """
198
191
  Update the overall progress bar.
199
-
192
+
200
193
  @param percentage: Progress percentage (0-100)
201
194
  @param status: Status message to display
202
195
  """
203
196
  try:
204
197
  progress_widget = self.query_one("#startup-progress", AnalysisProgressWidget)
205
198
  progress_widget.update_progress(percentage, status)
206
- except:
199
+ except Exception:
207
200
  pass # Ignore if element not found
208
-
201
+
209
202
  def update_status(self, status: str) -> None:
210
203
  """
211
204
  Update the status message.
212
-
205
+
213
206
  @param status: Status message to display
214
207
  """
215
208
  try:
216
209
  status_message = self.query_one("#status-message", Static)
217
210
  status_message.update(status)
218
- except:
211
+ except Exception:
219
212
  pass # Ignore if element not found
220
-
213
+
221
214
  def action_cancel(self) -> None:
222
215
  """Cancel the loading process."""
223
216
  self.cancelled = True
224
217
  if self.loading_task and not self.loading_task.done():
225
218
  self.loading_task.cancel()
226
219
  self.app.post_message(self.LoadingCancelled())
227
-
220
+
228
221
  class LoadingComplete(Message):
229
222
  """Message sent when loading is complete."""
223
+
230
224
  def __init__(self) -> None:
231
225
  super().__init__()
232
-
226
+
233
227
  class LoadingCancelled(Message):
234
228
  """Message sent when loading is cancelled."""
229
+
235
230
  def __init__(self) -> None:
236
231
  super().__init__()
237
232
 
@@ -239,110 +234,104 @@ class LoadingScreen(Screen):
239
234
  class InitializationLoadingScreen(LoadingScreen):
240
235
  """
241
236
  Specialized loading screen for real application initialization.
242
-
237
+
243
238
  WHY: This version of the loading screen performs actual initialization
244
239
  tasks instead of just simulating them, providing real progress feedback
245
240
  during startup.
246
241
  """
247
-
248
- def __init__(
249
- self,
250
- config_loader_func=None,
251
- nlp_init_func=None,
252
- *args,
253
- **kwargs
254
- ) -> None:
242
+
243
+ def __init__(self, config_loader_func=None, nlp_init_func=None, *args, **kwargs) -> None:
255
244
  super().__init__(*args, **kwargs)
256
245
  self.config_loader_func = config_loader_func
257
246
  self.nlp_init_func = nlp_init_func
258
247
  self.initialization_data = {}
259
-
248
+
260
249
  async def _simulate_loading(self) -> None:
261
250
  """
262
251
  Perform actual initialization tasks with progress updates.
263
-
252
+
264
253
  WHY: This replaces the simulation with real initialization work,
265
254
  allowing the loading screen to show progress of actual operations.
266
255
  """
267
256
  progress_widget = self.query_one("#startup-progress", AnalysisProgressWidget)
268
257
  status_message = self.query_one("#status-message", Static)
269
-
258
+
270
259
  try:
271
260
  # Step 1: Initialize application (20%)
272
261
  await self._update_step("step-init", "✅ Application initialized", "success")
273
262
  status_message.update("Initializing core components...")
274
263
  progress_widget.update_progress(20, "Core components ready")
275
264
  await asyncio.sleep(0.1)
276
-
265
+
277
266
  if self.cancelled:
278
267
  return
279
-
268
+
280
269
  # Step 2: Load configuration (40%)
281
270
  status_message.update("Discovering configuration files...")
282
271
  await self._update_step("step-config", "⏳ Loading configuration...", "warning")
283
-
272
+
284
273
  if self.config_loader_func:
285
274
  config_result = await asyncio.get_event_loop().run_in_executor(
286
275
  None, self.config_loader_func
287
276
  )
288
- self.initialization_data['config'] = config_result
289
-
277
+ self.initialization_data["config"] = config_result
278
+
290
279
  await self._update_step("step-config", "✅ Configuration loaded", "success")
291
280
  progress_widget.update_progress(40, "Configuration ready")
292
281
  await asyncio.sleep(0.1)
293
-
282
+
294
283
  if self.cancelled:
295
284
  return
296
-
285
+
297
286
  # Step 3: Prepare analysis engine (60%)
298
287
  await self._update_step("step-engine", "✅ Analysis engine ready", "success")
299
288
  status_message.update("Preparing Git analysis components...")
300
289
  progress_widget.update_progress(60, "Analysis engine initialized")
301
290
  await asyncio.sleep(0.2)
302
-
291
+
303
292
  if self.cancelled:
304
293
  return
305
-
294
+
306
295
  # Step 4: Load NLP models (85%) - Heavy operation
307
- config = self.initialization_data.get('config')
308
- if config and getattr(config, 'qualitative', None) and config.qualitative.enabled:
296
+ config = self.initialization_data.get("config")
297
+ if config and getattr(config, "qualitative", None) and config.qualitative.enabled:
309
298
  await self._update_step("step-nlp", "⏳ Loading spaCy models...", "warning")
310
299
  status_message.update("Loading natural language processing models...")
311
300
  progress_widget.update_progress(70, "Loading spaCy models...")
312
-
301
+
313
302
  if self.nlp_init_func:
314
303
  nlp_result = await asyncio.get_event_loop().run_in_executor(
315
304
  None, self.nlp_init_func, config
316
305
  )
317
- self.initialization_data['nlp'] = nlp_result
318
-
306
+ self.initialization_data["nlp"] = nlp_result
307
+
319
308
  await self._update_step("step-nlp", "✅ NLP models loaded", "success")
320
309
  progress_widget.update_progress(85, "NLP models ready")
321
310
  else:
322
311
  await self._update_step("step-nlp", "⏸️ NLP models skipped", "warning")
323
312
  progress_widget.update_progress(85, "NLP models skipped (qualitative disabled)")
324
-
313
+
325
314
  if self.cancelled:
326
315
  return
327
-
316
+
328
317
  # Step 5: Finalize setup (100%)
329
318
  await self._update_step("step-finalize", "✅ Setup complete", "success")
330
319
  status_message.update("Finalizing application setup...")
331
320
  progress_widget.update_progress(100, "GitFlow Analytics ready!")
332
-
321
+
333
322
  # Show completion message briefly
334
323
  loading_message = self.query_one("#loading-message", Label)
335
324
  loading_message.update("Initialization complete! Starting application...")
336
-
325
+
337
326
  elapsed_time = time.time() - self.start_time
338
327
  status_message.update(f"Ready! Initialized in {elapsed_time:.1f} seconds")
339
-
328
+
340
329
  # Wait a moment to show completion
341
330
  await asyncio.sleep(0.5)
342
-
331
+
343
332
  # Signal that loading is complete with initialization data
344
333
  self.app.post_message(self.InitializationComplete(self.initialization_data))
345
-
334
+
346
335
  except asyncio.CancelledError:
347
336
  progress_widget.update_progress(0, "Initialization cancelled")
348
337
  status_message.update("Initialization cancelled by user")
@@ -350,9 +339,10 @@ class InitializationLoadingScreen(LoadingScreen):
350
339
  progress_widget.update_progress(0, f"Error: {str(e)[:50]}...")
351
340
  status_message.update(f"Initialization failed: {e}")
352
341
  self.app.notify(f"Initialization failed: {e}", severity="error")
353
-
342
+
354
343
  class InitializationComplete(Message):
355
344
  """Message sent when initialization is complete with data."""
356
- def __init__(self, data: Dict[str, Any]) -> None:
345
+
346
+ def __init__(self, data: dict[str, Any]) -> None:
357
347
  super().__init__()
358
- self.data = data
348
+ self.data = data