bteodoru commited on
Commit
122c156
·
verified ·
1 Parent(s): 534577a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +182 -765
app.py CHANGED
@@ -1,813 +1,230 @@
1
- # ==============================================================================
2
- # APLICAȚIA FASTAPI CU SISTEM DE INCERTITUDINE INTEGRATĂ
3
- # ==============================================================================
4
- # Această aplicație înlocuiește sistemul simplu de predicție cu un framework
5
- # avansat de cuantificare a incertitudinii, oferind inginierilor nu doar predicții
6
- # ci și intervale de încredere calibrate pentru luarea deciziilor informate
7
-
8
- from fastapi import FastAPI, HTTPException, Request, Depends, Query
9
- from fastapi.responses import JSONResponse, HTMLResponse
10
- from fastapi.middleware.cors import CORSMiddleware
11
- from fastapi.staticfiles import StaticFiles
12
- import pandas as pd
13
- import joblib
14
- import numpy as np
15
  import os
16
- import time
 
 
17
  import pickle
18
  from datetime import datetime
19
- from sklearn.ensemble import RandomForestRegressor # Necesar pentru deserializare
20
- from pydantic import BaseModel, ValidationError, Field, field_validator, model_validator
21
- from typing import Any, Dict, List, Optional, Union
22
- from scipy import stats
23
- import json
24
-
25
- # ==============================================================================
26
- # CONFIGURAȚIA APLICAȚIEI FASTAPI
27
- # ==============================================================================
28
-
29
- app = FastAPI(
30
- title="UCS Prediction API with Uncertainty Quantification",
31
- description="""
32
- **API avansat pentru predicția rezistenței la compresiune simplă (UCS) a solurilor stabilizate cu cement**
33
-
34
- Această aplicație implementează sistemul de cuantificare a incertitudinii dezvoltat în cercetarea
35
- "Prediction of Unconfined Compressive Strength in Cement-Treated Soil: A Machine Learning Approach".
36
-
37
- **Caracteristici principale:**
38
- - Predicții precise ale UCS folosind Random Forest optimizat
39
- - Cuantificare completă a incertitudinii cu intervale de încredere calibrate
40
- - Analiză de sensibilitate pentru optimizarea parametrilor
41
- - Interpretabilitate prin analiza importanței features-urilor
42
-
43
- **Dezvoltat de:** Echipa de cercetare - Universitatea Tehnică Gheorghe Asachi din Iași
44
- """,
45
- version="2.0.0",
46
- contact={
47
- "name": "Echipa de Dezvoltare UCS",
48
- "email": "iancu-bogdan.teodoru@academic.tuiasi.ro",
49
- }
50
- )
51
-
52
- # Configurarea CORS pentru interfața web
53
- app.add_middleware(
54
- CORSMiddleware,
55
- allow_origins=[
56
- "http://www.bi4e-at.tuiasi.ro",
57
- "https://www.bi4e-at.tuiasi.ro",
58
- "http://localhost:3000", # Pentru dezvoltarea locală
59
- "http://localhost:8000" # Pentru testarea locală
60
- ],
61
- allow_credentials=True,
62
- allow_methods=["GET", "POST", "OPTIONS"],
63
- allow_headers=["*"],
64
- )
65
-
66
- # ==============================================================================
67
- # CONFIGURAȚIA MODELELOR ȘI ÎNCĂRCAREA SISTEMULUI
68
- # ==============================================================================
69
-
70
- # Căile către modelele serializate
71
- MODELS_DIR = "./models_for_deployment"
72
- PRIMARY_MODEL_PATH = os.path.join(MODELS_DIR, "rf_primary_model.joblib")
73
- UNCERTAINTY_MODEL_PATH = os.path.join(MODELS_DIR, "rf_uncertainty_model.joblib")
74
- METADATA_PATH = os.path.join(MODELS_DIR, "system_metadata.pkl")
75
-
76
- # Ordinea features-urilor (critică pentru compatibilitate)
77
- DEFAULT_FEATURE_ORDER = ['cement_perecent', 'curing_period', 'compaction_rate']
78
-
79
- # Variabile globale pentru sistem
80
- primary_model = None
81
- uncertainty_model = None
82
- system_metadata = None
83
- FEATURE_ORDER = None
84
-
85
- def load_uncertainty_system():
86
- """
87
- Încarcă și validează întregul sistem de incertitudine.
88
-
89
- Această funcție orchestrează încărcarea tuturor componentelor sistemului
90
- și efectuează validări de bază pentru a asigura funcționarea corectă.
91
- Procesul este proiectat să fie robust și să ofere informații detaliate
92
- despre orice probleme întâlnite în timpul încărcării.
93
- """
94
- global primary_model, uncertainty_model, system_metadata, FEATURE_ORDER
95
-
96
- print("🚀 Încărcarea sistemului de incertitudine...")
97
- start_time = time.time()
98
-
99
- try:
100
- # Încărcăm modelul principal
101
- if os.path.exists(PRIMARY_MODEL_PATH):
102
- primary_model = joblib.load(PRIMARY_MODEL_PATH)
103
- print(f"✅ Model principal încărcat: {type(primary_model).__name__}")
104
- else:
105
- raise FileNotFoundError(f"Modelul principal nu a fost găsit la: {PRIMARY_MODEL_PATH}")
106
-
107
- # Încărcăm modelul de incertitudine
108
- if os.path.exists(UNCERTAINTY_MODEL_PATH):
109
- uncertainty_model = joblib.load(UNCERTAINTY_MODEL_PATH)
110
- print(f"✅ Model incertitudine încărcat: {type(uncertainty_model).__name__}")
111
- else:
112
- raise FileNotFoundError(f"Modelul de incertitudine nu a fost găsit la: {UNCERTAINTY_MODEL_PATH}")
113
-
114
- # Încărcăm metadata-ul sistemului
115
- if os.path.exists(METADATA_PATH):
116
- with open(METADATA_PATH, 'rb') as f:
117
- system_metadata = pickle.load(f)
118
- print(f"✅ Metadata sistem încărcată: {len(system_metadata)} chei")
119
- else:
120
- print("⚠️ Metadata-ul sistemului nu a fost găsit, se folosesc valorile default")
121
- system_metadata = {"feature_names": DEFAULT_FEATURE_ORDER}
122
-
123
- # Determinăm ordinea features-urilor
124
- if hasattr(primary_model, 'feature_names_in_'):
125
- FEATURE_ORDER = primary_model.feature_names_in_
126
- elif system_metadata and 'feature_names' in system_metadata:
127
- FEATURE_ORDER = np.array(system_metadata['feature_names'])
128
- else:
129
- FEATURE_ORDER = np.array(DEFAULT_FEATURE_ORDER)
130
-
131
- # Validăm compatibilitatea modelelor
132
- validation_result = validate_models_compatibility()
133
- if not validation_result:
134
- raise ValueError("Modelele nu sunt compatibile între ele")
135
-
136
- load_time = time.time() - start_time
137
- print(f"🎉 Sistem de incertitudine încărcat cu succes în {load_time:.2f} secunde!")
138
- print(f"📊 Features: {FEATURE_ORDER.tolist()}")
139
-
140
- return True
141
-
142
- except Exception as e:
143
- print(f"❌ Eroare la încărcarea sistemului: {str(e)}")
144
- import traceback
145
- print(traceback.format_exc())
146
- return False
147
-
148
- def validate_models_compatibility():
149
- """
150
- Validează că modelele sunt compatibile și funcționează împreună.
151
-
152
- Această validare include teste de compatibilitate dimensională,
153
- verificarea tipurilor de date și un test funcțional complet.
154
- """
155
- try:
156
- # Test cu date sintetice
157
- test_input = np.array([[5.0, 14.0, 1.0]]) # cement, curing, compaction
158
-
159
- # Testăm modelul principal
160
- primary_pred = primary_model.predict(test_input)[0]
161
-
162
- # Testăm modelul de incertitudine cu feature augmentation
163
- uncertainty_input = np.column_stack([test_input, [[primary_pred]]])
164
- uncertainty_pred = uncertainty_model.predict(uncertainty_input)[0]
165
-
166
- # Verificăm că rezultatele sunt numerice și rezonabile
167
- assert isinstance(primary_pred, (int, float, np.number))
168
- assert isinstance(uncertainty_pred, (int, float, np.number))
169
- assert primary_pred > 0
170
- assert uncertainty_pred > 0
171
-
172
- print(f"✅ Test compatibilitate: UCS={primary_pred:.1f} kPa, σ={uncertainty_pred:.1f} kPa")
173
- return True
174
-
175
- except Exception as e:
176
- print(f"❌ Test compatibilitate eșuat: {str(e)}")
177
- return False
178
-
179
- # Încărcăm sistemul la pornirea aplicației
180
- system_loaded = load_uncertainty_system()
181
-
182
- # ==============================================================================
183
- # MODELELE PYDANTIC PENTRU INPUT ȘI OUTPUT
184
- # ==============================================================================
185
-
186
- class SoilInput(BaseModel):
187
- """
188
- Modelul pentru datele de input ale solului.
189
-
190
- Această clasă definește și validează parametrii de intrare,
191
- asigurându-se că valorile sunt în intervalele experimentale validate.
192
- """
193
- cement_perecent: float = Field(
194
- ...,
195
- description="Procentajul de cement în amestec",
196
- ge=0, le=15,
197
- example=5.0
198
- )
199
- curing_period: float = Field(
200
- ...,
201
- description="Perioada de maturare în zile",
202
- ge=0, le=90,
203
- example=28.0
204
- )
205
- compaction_rate: float = Field(
206
- ...,
207
- description="Rata de compactare în mm/min",
208
- ge=0.5, le=2.0,
209
- example=1.0
210
- )
211
 
212
- @model_validator(mode="after")
213
- def validate_cement_curing_relationship(self):
214
- """
215
- Validează relația dintre conținutul de cement și perioada de maturare.
216
-
217
- Pentru solul netratat (0% cement), perioada de maturare este forțată la 0
218
- deoarece nu există proces de hidratare a cimentului.
219
- """
220
- if self.cement_perecent == 0:
221
- self.curing_period = 0
222
- elif self.cement_perecent > 0 and self.curing_period < 1:
223
- raise ValueError("Pentru sol tratat cu cement, perioada de maturare trebuie să fie ≥ 1 zi")
224
- return self
225
-
226
- class Config:
227
- json_schema_extra = {
228
- "example": {
229
- "cement_perecent": 5.0,
230
- "curing_period": 28.0,
231
- "compaction_rate": 1.0
232
- }
233
- }
234
-
235
- class ConfidenceInterval(BaseModel):
236
- """Modelul pentru un interval de încredere."""
237
- lower: float = Field(..., description="Limita inferioară a intervalului")
238
- upper: float = Field(..., description="Limita superioară a intervalului")
239
- width: float = Field(..., description="Lățimea intervalului")
240
-
241
- class UncertaintyPredictionResponse(BaseModel):
242
  """
243
- Răspunsul complet cu cuantificare de incertitudine.
244
-
245
- Această structură extinsă oferă inginerului o imagine completă
246
- a predicției, incluzând nu doar valoarea estimată ci și încrederea
247
- în acea estimare prin intervale calibrate.
248
  """
249
- success: bool = Field(..., description="Statusul procesării cererii")
250
-
251
- # Predicția centrală
252
- central_prediction: float = Field(..., description="Predicția UCS cea mai probabilă")
253
- units: str = Field(default="kPa", description="Unitățile de măsură")
254
-
255
- # Informații despre incertitudine
256
- uncertainty_estimate: float = Field(..., description="Estimarea incertitudinii absolute (1-sigma)")
257
- relative_uncertainty: float = Field(..., description="Incertitudinea relativă ca procent")
258
-
259
- # Intervale de încredere
260
- confidence_intervals: Dict[str, ConfidenceInterval] = Field(
261
- ...,
262
- description="Intervale de încredere pentru multiple nivele de probabilitate"
263
- )
264
 
265
- # Interpretarea pentru utilizator
266
- interpretation: Dict[str, str] = Field(..., description="Ghid de interpretare pentru rezultate")
267
-
268
- # Metadata
269
- input_parameters: Dict[str, float] = Field(..., description="Parametrii de intrare folosiți")
270
- prediction_time_ms: Optional[float] = Field(None, description="Timpul de procesare în milisecunde")
271
- model_info: Optional[Dict[str, Any]] = Field(None, description="Informații despre modelele folosite")
272
-
273
- class SensitivityAnalysisRequest(BaseModel):
274
- """Cererea pentru analiza de sensibilitate."""
275
- base_parameters: SoilInput
276
- parameter_to_vary: str = Field(..., regex="^(cement_perecent|curing_period|compaction_rate)$")
277
- variation_range: float = Field(default=10.0, ge=1.0, le=50.0, description="Intervalul de variație în procente")
278
- num_points: int = Field(default=11, ge=5, le=21, description="Numărul de puncte pentru analiză")
279
 
280
- # ==============================================================================
281
- # FUNCȚIILE CORE PENTRU PREDICȚIA CU INCERTITUDINE
282
- # ==============================================================================
283
 
284
- def predict_with_uncertainty(input_data: np.ndarray,
285
- confidence_levels: List[float] = [0.68, 0.80, 0.90, 0.95]) -> Dict[str, Any]:
286
- """
287
- Realizează predicția completă cu cuantificare de incertitudine.
288
-
289
- Această funcție implementează algoritmul în două etape dezvoltat în cercetare:
290
- 1. Modelul principal generează predicția centrală UCS
291
- 2. Modelul de incertitudine estimează magnitudinea erorii probabile
292
- 3. Se construiesc intervale de încredere presupunând distribuție normală
293
-
294
- Args:
295
- input_data: Array numpy cu features-urile [cement%, curing_days, compaction_rate]
296
- confidence_levels: Lista nivelelor de încredere pentru care să calculăm intervale
297
-
298
- Returns:
299
- Dicționar cu predicția centrală, estimarea incertitudinii și intervalele de încredere
300
- """
301
-
302
- # Etapa 1: Predicția centrală cu modelul principal
303
- central_prediction = primary_model.predict(input_data)[0]
304
-
305
- # Etapa 2: Pregătirea input-ului pentru modelul de incertitudine
306
- # Modelul de incertitudine folosește feature augmentation:
307
- # features originale + predicția centrală
308
- uncertainty_input = np.column_stack([input_data, [[central_prediction]]])
309
-
310
- # Etapa 3: Predicția incertitudinii (magnitudinea erorii așteptate)
311
- uncertainty_estimate = uncertainty_model.predict(uncertainty_input)[0]
312
 
313
- # Etapa 4: Calcularea intervalelor de încredere
314
- confidence_intervals = {}
 
 
315
 
316
- for conf_level in confidence_levels:
317
- # Z-score corespunzător nivelului de încredere
318
- # Pentru distribuția normală: 68% → z≈1.0, 90% → z≈1.645, 95% → z≈1.96
319
- z_score = stats.norm.ppf((1 + conf_level) / 2)
320
-
321
- # Marginea de eroare = z-score × estimarea incertitudinii
322
- margin = z_score * uncertainty_estimate
323
-
324
- confidence_intervals[f'{conf_level:.0%}'] = ConfidenceInterval(
325
- lower=float(central_prediction - margin),
326
- upper=float(central_prediction + margin),
327
- width=float(2 * margin)
328
- )
329
-
330
- # Calcularea incertitudinii relative
331
- relative_uncertainty = (uncertainty_estimate / central_prediction) * 100 if central_prediction != 0 else 0
332
-
333
- return {
334
- 'central_prediction': float(central_prediction),
335
- 'uncertainty_estimate': float(uncertainty_estimate),
336
- 'relative_uncertainty': float(relative_uncertainty),
337
- 'confidence_intervals': confidence_intervals
338
- }
339
-
340
- def generate_interpretation_guide(central_prediction: float, uncertainty_estimate: float,
341
- confidence_intervals: Dict[str, ConfidenceInterval]) -> Dict[str, str]:
342
- """
343
- Generează un ghid de interpretare personalizat pentru rezultatele predicției.
344
 
345
- Această funcție traduce rezultatele statistice în limbaj practic pentru ingineri,
346
- oferind contextul necesar pentru luarea deciziilor informate în proiecte.
347
- """
348
 
349
- # Calculăm intervalul 95% pentru interpretare
350
- interval_95 = confidence_intervals.get('95%')
 
 
351
 
352
- # Clasificarea încrederii bazată pe incertitudinea relativă
353
- relative_unc = (uncertainty_estimate / central_prediction) * 100
 
 
354
 
355
- if relative_unc <= 10:
356
- confidence_level = "foarte mare"
357
- reliability_desc = "Predicția este foarte fiabilă pentru luarea deciziilor de proiectare."
358
- elif relative_unc <= 20:
359
- confidence_level = "mare"
360
- reliability_desc = "Predicția este fiabilă, recomandăm validare prin teste limitate."
361
- elif relative_unc <= 30:
362
- confidence_level = "moderată"
363
- reliability_desc = "Predicția oferă o estimare utilă, dar se recomandă testare suplimentară."
364
  else:
365
- confidence_level = "limitată"
366
- reliability_desc = "Predicția este indicativă, se recomandă testare extinsă pentru validare."
367
-
368
- interpretation = {
369
- "central_prediction": f"Valoarea UCS cea mai probabilă este {central_prediction:.0f} kPa, bazată pe parametrii introduși.",
370
-
371
- "uncertainty": f"Incertitudinea estimată este ±{uncertainty_estimate:.0f} kPa ({relative_unc:.1f}%), "
372
- f"indicând o încredere {confidence_level} în predicție.",
373
-
374
- "confidence_95": f"Avem 95% încredere că valoarea reală UCS se află între "
375
- f"{interval_95.lower:.0f} și {interval_95.upper:.0f} kPa." if interval_95 else "",
376
 
377
- "reliability": reliability_desc,
378
-
379
- "practical_guidance": f"Pentru aplicații cu cerințe UCS > {central_prediction + uncertainty_estimate:.0f} kPa, "
380
- f"considerați mărirea conținutului de cement sau extinderea perioadei de maturare."
381
- }
382
-
383
- return interpretation
384
-
385
- async def validate_models_loaded():
386
- """Dependency function pentru validarea încărcării modelelor."""
387
- if not system_loaded or primary_model is None or uncertainty_model is None:
388
- raise HTTPException(
389
- status_code=503,
390
- detail="Sistemul de modele nu este încărcat corect. Contactați administratorul."
391
- )
392
- return True
393
 
394
- # ==============================================================================
395
- # ENDPOINT-URILE API
396
- # ==============================================================================
 
 
 
 
 
397
 
398
- @app.get("/", response_class=HTMLResponse, summary="Pagina principală")
399
- async def root():
400
- """
401
- Returnează pagina principală cu informații despre API.
402
- """
403
- return """
404
- <!DOCTYPE html>
405
- <html>
406
- <head>
407
- <title>UCS Prediction API</title>
408
- <style>
409
- body { font-family: Arial, sans-serif; margin: 40px; }
410
- .header { color: #2c3e50; }
411
- .endpoint { background: #f8f9fa; padding: 15px; margin: 10px 0; border-left: 4px solid #007bff; }
412
- </style>
413
- </head>
414
- <body>
415
- <h1 class="header">🏗️ UCS Prediction API with Uncertainty Quantification</h1>
416
- <p>API avansat pentru predicția rezistenței la compresiune simplă a solurilor stabilizate cu cement.</p>
417
-
418
- <h2>📋 Endpoint-uri disponibile:</h2>
419
- <div class="endpoint">
420
- <strong>POST /predict</strong> - Predicție UCS cu cuantificare de incertitudine
421
- </div>
422
- <div class="endpoint">
423
- <strong>POST /sensitivity-analysis</strong> - Analiza sensibilității parametrilor
424
- </div>
425
- <div class="endpoint">
426
- <strong>GET /status</strong> - Statusul sistemului
427
- </div>
428
- <div class="endpoint">
429
- <strong>GET /model-info</strong> - Informații detaliate despre modele
430
- </div>
431
-
432
- <h2>📖 Documentație:</h2>
433
- <p><a href="/docs">Swagger UI - Documentație interactivă</a></p>
434
- <p><a href="/redoc">ReDoc - Documentație alternativă</a></p>
435
-
436
- <footer style="margin-top: 40px; color: #666;">
437
- <p>Dezvoltat de echipa de cercetare - Universitatea Tehnică Gheorghe Asachi din Iași</p>
438
- </footer>
439
- </body>
440
- </html>
441
  """
442
-
443
- @app.post("/predict", response_model=UncertaintyPredictionResponse,
444
- summary="Predicție UCS cu Cuantificare de Incertitudine")
445
- async def predict_ucs_with_uncertainty(
446
- soil_data: SoilInput,
447
- include_model_info: bool = Query(False, description="Include informații detaliate despre modele"),
448
- _: bool = Depends(validate_models_loaded)
449
- ):
450
  """
451
- **Realizează predicția UCS cu cuantificare completă a incertitudinii.**
452
-
453
- Acest endpoint implementează sistemul avansat de incertitudine dezvoltat în cercetarea noastră,
454
- oferind nu doar predicția centrală ci și intervale de încredere calibrate la multiple nivele.
455
-
456
- **Parametrii de intrare:**
457
- - **cement_perecent**: Conținutul de cement (0-15%)
458
- - **curing_period**: Perioada de maturare (0-90 zile)
459
- - **compaction_rate**: Rata de compactare (0.5-2.0 mm/min)
460
 
461
- **Rezultatele includ:**
462
- - Predicția centrală UCS în kPa
463
- - Estimarea incertitudinii absolute și relative
464
- - Intervale de încredere la 68%, 80%, 90% și 95%
465
- - Ghid de interpretare personalizat pentru rezultate
466
-
467
- **Utilizare tipică:**
468
- ```json
469
- {
470
- "cement_perecent": 7.5,
471
- "curing_period": 28,
472
- "compaction_rate": 1.0
473
- }
474
- ```
475
- """
476
 
477
  try:
478
- start_time = time.time()
479
-
480
- # Pregătirea datelor de input în formatul așteptat de model
481
- input_data = soil_data.dict()
482
- input_df = pd.DataFrame([input_data])
483
-
484
- # Asigurarea ordinii corecte a features-urilor
485
- prediction_df = pd.DataFrame()
486
- for feature in FEATURE_ORDER:
487
- if feature in input_df.columns:
488
- prediction_df[feature] = input_df[feature]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
489
  else:
490
- raise ValueError(f"Feature-ul '{feature}' lipsește din datele de intrare")
491
-
492
- # Convertirea la numpy array pentru modelele scikit-learn
493
- input_array = prediction_df.values
494
-
495
- # Realizarea predicției cu incertitudine
496
- prediction_result = predict_with_uncertainty(input_array)
497
 
498
- # Generarea ghidului de interpretare
499
- interpretation = generate_interpretation_guide(
500
- prediction_result['central_prediction'],
501
- prediction_result['uncertainty_estimate'],
502
- prediction_result['confidence_intervals']
503
- )
504
 
505
- # Informații opționale despre modele
506
- model_info = None
507
- if include_model_info:
508
- model_info = {
509
- "primary_model": type(primary_model).__name__,
510
- "uncertainty_model": type(uncertainty_model).__name__,
511
- "feature_order": FEATURE_ORDER.tolist(),
512
- "system_metadata": system_metadata if system_metadata else "Not available"
513
- }
514
-
515
- # Calcularea timpului de procesare
516
- processing_time = (time.time() - start_time) * 1000
517
-
518
- # Construirea răspunsului complet
519
- return UncertaintyPredictionResponse(
520
- success=True,
521
- central_prediction=prediction_result['central_prediction'],
522
- units="kPa",
523
- uncertainty_estimate=prediction_result['uncertainty_estimate'],
524
- relative_uncertainty=prediction_result['relative_uncertainty'],
525
- confidence_intervals=prediction_result['confidence_intervals'],
526
- interpretation=interpretation,
527
- input_parameters=input_data,
528
- prediction_time_ms=processing_time,
529
- model_info=model_info
530
- )
531
 
532
- except ValueError as ve:
533
- raise HTTPException(status_code=400, detail=f"Eroare de validare: {str(ve)}")
534
  except Exception as e:
535
- raise HTTPException(status_code=500, detail=f"Eroare de procesare: {str(e)}")
 
 
536
 
537
- @app.post("/sensitivity-analysis", summary="Analiza Sensibilității Parametrilor")
538
- async def perform_sensitivity_analysis(
539
- request: SensitivityAnalysisRequest,
540
- _: bool = Depends(validate_models_loaded)
541
- ):
542
  """
543
- **Efectuează analiza sensibilității pentru un parametru specific.**
544
-
545
- Această analiză arată cum variația unui parametru de intrare afectează
546
- atât predicția centrală cât și incertitudinea asociată, oferind insight-uri
547
- valoroase pentru optimizarea mix design-ului.
548
  """
 
549
 
550
  try:
551
- base_params = request.base_parameters.dict()
552
- param_to_vary = request.parameter_to_vary
553
- variation_range = request.variation_range / 100 # Convertim din procente
554
- num_points = request.num_points
555
-
556
- # Valorile de bază
557
- base_value = base_params[param_to_vary]
558
-
559
- # Calculăm intervalul de variație
560
- min_variation = base_value * (1 - variation_range)
561
- max_variation = base_value * (1 + variation_range)
562
-
563
- # Respectăm limitele fizice ale parametrilor
564
- if param_to_vary == "cement_perecent":
565
- min_variation = max(0, min_variation)
566
- max_variation = min(15, max_variation)
567
- elif param_to_vary == "curing_period":
568
- min_variation = max(0 if base_params["cement_perecent"] == 0 else 1, min_variation)
569
- max_variation = min(90, max_variation)
570
- elif param_to_vary == "compaction_rate":
571
- min_variation = max(0.5, min_variation)
572
- max_variation = min(2.0, max_variation)
573
-
574
- # Generăm punctele pentru analiză
575
- variation_values = np.linspace(min_variation, max_variation, num_points)
576
-
577
- results = []
578
-
579
- for value in variation_values:
580
- # Creăm parametrii modificați
581
- modified_params = base_params.copy()
582
- modified_params[param_to_vary] = float(value)
583
-
584
- # Validăm relația cement-curing pentru fiecare punct
585
- if modified_params["cement_perecent"] == 0:
586
- modified_params["curing_period"] = 0
587
-
588
- # Realizăm predicția
589
- input_df = pd.DataFrame([modified_params])
590
- prediction_df = pd.DataFrame()
591
- for feature in FEATURE_ORDER:
592
- prediction_df[feature] = input_df[feature]
593
-
594
- input_array = prediction_df.values
595
- prediction_result = predict_with_uncertainty(input_array)
596
-
597
- results.append({
598
- param_to_vary: float(value),
599
- "central_prediction": prediction_result['central_prediction'],
600
- "uncertainty_estimate": prediction_result['uncertainty_estimate'],
601
- "relative_uncertainty": prediction_result['relative_uncertainty'],
602
- "confidence_95_lower": prediction_result['confidence_intervals']['95%'].lower,
603
- "confidence_95_upper": prediction_result['confidence_intervals']['95%'].upper
604
- })
605
-
606
- # Calculăm statisticile sensibilității
607
- predictions = [r["central_prediction"] for r in results]
608
- uncertainties = [r["uncertainty_estimate"] for r in results]
609
-
610
- sensitivity_stats = {
611
- "parameter_range": {
612
- "min": float(min_variation),
613
- "max": float(max_variation),
614
- "base_value": float(base_value)
615
- },
616
- "prediction_sensitivity": {
617
- "min_prediction": float(min(predictions)),
618
- "max_prediction": float(max(predictions)),
619
- "range": float(max(predictions) - min(predictions)),
620
- "relative_change": float((max(predictions) - min(predictions)) / base_params.get("central_prediction", predictions[num_points//2]) * 100)
621
- },
622
- "uncertainty_sensitivity": {
623
- "min_uncertainty": float(min(uncertainties)),
624
- "max_uncertainty": float(max(uncertainties)),
625
- "range": float(max(uncertainties) - min(uncertainties))
626
- }
627
- }
628
 
629
- return {
630
- "success": True,
631
- "parameter_analyzed": param_to_vary,
632
- "base_parameters": base_params,
633
- "sensitivity_data": results,
634
- "sensitivity_statistics": sensitivity_stats,
635
- "interpretation": {
636
- "parameter_impact": f"Variația de {variation_range*100:.1f}% în {param_to_vary} "
637
- f"produce o schimbare de {sensitivity_stats['prediction_sensitivity']['range']:.1f} kPa în UCS",
638
- "recommendation": "Parametrul cu cel mai mare impact ar trebui controlat cu atenție în teren"
639
- if sensitivity_stats['prediction_sensitivity']['relative_change'] > 10
640
- else "Parametrul are impact moderat, variațiile mici sunt acceptabile"
641
- }
642
- }
643
 
644
  except Exception as e:
645
- raise HTTPException(status_code=500, detail=f"Eroare în analiza sensibilității: {str(e)}")
 
646
 
647
- @app.get("/status", summary="Status Sistem")
648
- async def get_system_status():
649
- """
650
- **Returnează statusul complet al sistemului de incertitudine.**
651
-
652
- Useful pentru monitorizarea sănătății aplicației și diagnosticarea problemelor.
653
- """
654
-
655
- status_info = {
656
- "api_status": "running",
657
- "timestamp": datetime.now().isoformat(),
658
- "system_loaded": system_loaded,
659
- "models_status": {
660
- "primary_model": primary_model is not None,
661
- "uncertainty_model": uncertainty_model is not None,
662
- "metadata_available": system_metadata is not None
663
- },
664
- "feature_configuration": {
665
- "feature_order": FEATURE_ORDER.tolist() if FEATURE_ORDER is not None else [],
666
- "num_features": len(FEATURE_ORDER) if FEATURE_ORDER is not None else 0
667
- }
668
- }
669
-
670
- # Testul rapid de funcționalitate dacă modelele sunt încărcate
671
- if system_loaded:
672
- try:
673
- test_result = validate_models_compatibility()
674
- status_info["functionality_test"] = "passed" if test_result else "failed"
675
- except Exception as e:
676
- status_info["functionality_test"] = f"error: {str(e)}"
677
 
678
- return status_info
 
 
679
 
680
- @app.get("/model-info", summary="Informații Modele")
681
- async def get_model_information(_: bool = Depends(validate_models_loaded)):
 
682
  """
683
- **Returnează informații detaliate despre modelele utilizate.**
684
-
685
- Include parametrii modelelor, performanțele istorice și limitele de aplicabilitate.
686
  """
 
687
 
688
  try:
689
- model_info = {
690
- "system_type": "Two-stage Random Forest Uncertainty Quantification",
691
- "models": {
692
- "primary_model": {
693
- "type": type(primary_model).__name__,
694
- "parameters": primary_model.get_params(),
695
- "purpose": "Central UCS prediction"
696
- },
697
- "uncertainty_model": {
698
- "type": type(uncertainty_model).__name__,
699
- "parameters": uncertainty_model.get_params(),
700
- "purpose": "Prediction error magnitude estimation"
701
- }
702
- },
703
- "features": {
704
- "input_features": FEATURE_ORDER.tolist(),
705
- "feature_engineering": "Feature augmentation for uncertainty model (original features + central prediction)"
706
- },
707
- "valid_ranges": {
708
- "cement_perecent": {"min": 0, "max": 15, "units": "%", "note": "Based on experimental data"},
709
- "curing_period": {"min": 0, "max": 90, "units": "days", "note": "0 only valid for 0% cement"},
710
- "compaction_rate": {"min": 0.5, "max": 2.0, "units": "mm/min", "note": "Within experimental range"}
711
- },
712
- "confidence_levels": ["68%", "80%", "90%", "95%"],
713
- "target_variable": {
714
- "name": "UCS",
715
- "description": "Unconfined Compressive Strength",
716
- "units": "kPa",
717
- "typical_range": "150-5500 kPa based on experimental data"
718
- }
719
  }
720
-
721
- # Adăugăm metadata-ul dacă este disponibil
722
- if system_metadata:
723
- model_info["training_metadata"] = {
724
- "training_samples": system_metadata.get("n_training_samples", "Unknown"),
725
- "training_timestamp": system_metadata.get("training_timestamp", "Unknown"),
726
- "model_version": "2.0.0"
727
- }
728
-
729
- return model_info
730
-
731
  except Exception as e:
732
- raise HTTPException(status_code=500, detail=f"Eroare la obținerea informațiilor: {str(e)}")
733
-
734
- # ==============================================================================
735
- # HANDLER-E PENTRU EXCEPȚII
736
- # ==============================================================================
737
-
738
- @app.exception_handler(ValidationError)
739
- async def validation_exception_handler(request: Request, exc: ValidationError):
740
- """
741
- Handler personalizat pentru erorile de validare Pydantic.
742
- Oferă mesaje de eroare mai prietenoase pentru utilizatori.
743
- """
744
-
745
- friendly_errors = []
746
- for error in exc.errors():
747
- field = " -> ".join(str(loc) for loc in error.get('loc', []))
748
- message = error.get('msg', '')
749
-
750
- # Personalizăm mesajele pentru cazuri comune
751
- if "greater than or equal" in message:
752
- message = f"Valoarea pentru {field} este prea mică"
753
- elif "less than or equal" in message:
754
- message = f"Valoarea pentru {field} este prea mare"
755
- elif "string does not match regex" in message:
756
- message = f"Valoarea pentru {field} nu este validă"
757
-
758
- friendly_errors.append({
759
- "field": field,
760
- "message": message,
761
- "error_type": error.get('type', '')
762
- })
763
-
764
- return JSONResponse(
765
- status_code=422,
766
- content={
767
- "success": False,
768
- "error": "Eroare de validare a datelor de intrare",
769
- "details": friendly_errors,
770
- "help": "Verificați că toate valorile sunt în intervalele specificate și încercați din nou"
771
- }
772
- )
773
-
774
- @app.exception_handler(Exception)
775
- async def general_exception_handler(request: Request, exc: Exception):
776
- """
777
- Handler general pentru excepții neașteptate.
778
- """
779
- return JSONResponse(
780
- status_code=500,
781
- content={
782
- "success": False,
783
- "error": "Eroare internă a serverului",
784
- "message": "A apărut o eroare neașteptată. Contactați administratorul dacă problema persistă.",
785
- "request_id": str(time.time()) # Pentru tracking în loguri
786
- }
787
- )
788
 
789
- # ==============================================================================
790
- # CONFIGURAȚIE FINALĂ ȘI STARTUP
791
- # ==============================================================================
792
-
793
- @app.on_event("startup")
794
- async def startup_event():
795
- """
796
- Eveniment executat la pornirea aplicației.
797
- Efectuează verificări finale și pregătește sistemul pentru producție.
798
- """
799
- print("🚀 Pornirea aplicației UCS Prediction API v2.0...")
800
-
801
- if system_loaded:
802
- print("✅ Sistem de incertitudine încărcat și funcțional")
803
- print(f"📊 Features configurate: {FEATURE_ORDER.tolist()}")
804
- else:
805
- print("❌ ATENȚIE: Sistemul nu a fost încărcat corect!")
806
- print(" Verificați că fișierele de modele sunt prezente în directorul models_for_deployment/")
807
-
808
- print("🌐 API disponibil pentru cereri")
809
 
810
- if __name__ == "__main__":
811
- # Pentru rularea în dezvoltare
812
- import uvicorn
813
- uvicorn.run(app, host="0.0.0.0", port=8000, reload=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
2
+ import sys
3
+ import traceback
4
+ from pathlib import Path
5
  import pickle
6
  from datetime import datetime
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
+ # Funcție de logging foarte verbos pentru debugging
9
+ def debug_log(message, level="INFO"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  """
11
+ Funcție de logging care afișează informații detaliate despre starea aplicației.
12
+ Aceasta ne ajută să vedem exact unde se oprește procesul de inițializare.
 
 
 
13
  """
14
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
15
+ print(f"[{timestamp}] [{level}] {message}", flush=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
+ # Forțăm flush-ul pentru ca mesajele să apară imediat în loguri
18
+ sys.stdout.flush()
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
+ # Începem logging-ul imediat la încărcarea modulului
21
+ debug_log("🚀 PORNIREA APLICAȚIEI UCS - Început încărcare modul app.py")
 
22
 
23
+ try:
24
+ # Importurile necesare cu logging pentru fiecare pas major
25
+ debug_log("📚 Încărcare biblioteci Python...")
26
+ from fastapi import FastAPI, HTTPException, Request, Depends, Query
27
+ from fastapi.responses import JSONResponse, HTMLResponse
28
+ from fastapi.middleware.cors import CORSMiddleware
29
+ debug_log("✅ FastAPI și dependențele încărcate cu succes")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
+ import pandas as pd
32
+ import joblib
33
+ import numpy as np
34
+ debug_log("✅ Biblioteci științifice (pandas, numpy, joblib) încărcate")
35
 
36
+ from pydantic import BaseModel, ValidationError, Field, field_validator, model_validator
37
+ debug_log("✅ Pydantic încărcat pentru validarea datelor")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
+ debug_log("📊 Verificare directoare și fișiere disponibile...")
 
 
40
 
41
+ # Verificăm ce fișiere sunt disponibile în directorul de lucru
42
+ current_dir = Path("/code")
43
+ debug_log(f"📁 Directorul de lucru: {current_dir}")
44
+ debug_log(f"📁 Conținutul directorului /code: {list(current_dir.iterdir())}")
45
 
46
+ # Verificăm specific directorul cu modelele
47
+ models_dir = current_dir / "models_for_deployment"
48
+ debug_log(f"📁 Calea către modele: {models_dir}")
49
+ debug_log(f"📁 Directorul modele există: {models_dir.exists()}")
50
 
51
+ if models_dir.exists():
52
+ debug_log(f"📁 Conținutul directorului modele: {list(models_dir.iterdir())}")
 
 
 
 
 
 
 
53
  else:
54
+ debug_log("❌ PROBLEMĂ: Directorul models_for_deployment nu există!")
 
 
 
 
 
 
 
 
 
 
55
 
56
+ # Verificăm și modelele vechi pentru compatibilitate
57
+ old_model_files = ["rf_model.joblib", "rf_model_optim.joblib"]
58
+ for filename in old_model_files:
59
+ file_path = current_dir / filename
60
+ debug_log(f"📄 {filename} există: {file_path.exists()}")
 
 
 
 
 
 
 
 
 
 
 
61
 
62
+ except Exception as e:
63
+ debug_log(f"❌ EROARE la importuri inițiale: {str(e)}", "ERROR")
64
+ debug_log(f"❌ Traceback complet: {traceback.format_exc()}", "ERROR")
65
+ # Nu facem exit aici - lăsăm aplicația să încerce să continue
66
+
67
+ # Variabile globale pentru sistemul de modele
68
+ sistem_incertitudine = None
69
+ system_loaded = False
70
 
71
+ def load_uncertainty_system_with_detailed_logging():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  """
73
+ Funcție de încărcare a sistemului de incertitudine cu logging foarte detaliat.
74
+ Aceasta ne va arăta exact unde se oprește procesul dacă apar probleme.
 
 
 
 
 
 
75
  """
76
+ global sistem_incertitudine, system_loaded
 
 
 
 
 
 
 
 
77
 
78
+ debug_log("🔍 ÎNCEPEREA ÎNCĂRCĂRII SISTEMULUI DE INCERTITUDINE")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
  try:
81
+ # Strategia de încărcare cu mai multe opțiuni de fallback
82
+ models_dir = Path("/code/models_for_deployment")
83
+ possible_files = [
84
+ "sistem_incertitudine_final_sistem_complet.pkl",
85
+ "sistem_final_ncv_sistem_complet.pkl", # Numele posibil diferit
86
+ ]
87
+
88
+ sistem_loaded_successfully = False
89
+
90
+ for filename in possible_files:
91
+ file_path = models_dir / filename
92
+ debug_log(f"🔍 Verific fișierul: {file_path}")
93
+
94
+ if file_path.exists():
95
+ debug_log(f"✅ Fișierul găsit: {filename}")
96
+ debug_log(f"📏 Dimensiunea fișierului: {file_path.stat().st_size / (1024*1024):.2f} MB")
97
+
98
+ try:
99
+ debug_log(f"📖 Încep încărcarea fișierului: {filename}")
100
+
101
+ with open(file_path, 'rb') as f:
102
+ sistem_incertitudine = pickle.load(f)
103
+
104
+ debug_log(f"✅ Fișierul {filename} încărcat cu succes!")
105
+
106
+ # Verificăm că sistemul are componentele necesare
107
+ required_attrs = ['model_principal', 'model_incertitudine', 'predict_cu_incertitudine']
108
+ missing_attrs = [attr for attr in required_attrs if not hasattr(sistem_incertitudine, attr)]
109
+
110
+ if missing_attrs:
111
+ debug_log(f"⚠️ Sistemul nu are atributele: {missing_attrs}", "WARNING")
112
+ else:
113
+ debug_log("✅ Sistemul are toate atributele necesare")
114
+ sistem_loaded_successfully = True
115
+ break
116
+
117
+ except Exception as load_error:
118
+ debug_log(f"❌ Eroare la încărcarea {filename}: {str(load_error)}", "ERROR")
119
+ debug_log(f"❌ Traceback: {traceback.format_exc()}", "ERROR")
120
+ continue
121
  else:
122
+ debug_log(f" Fișierul nu există: {filename}")
 
 
 
 
 
 
123
 
124
+ if not sistem_loaded_successfully:
125
+ debug_log("🔄 Încerc încărcarea modelelor individuale ca fallback...")
126
+ return load_individual_models_fallback()
 
 
 
127
 
128
+ system_loaded = True
129
+ debug_log("🎉 SISTEM DE INCERTITUDINE ÎNCĂRCAT CU SUCCES!")
130
+ return True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
 
 
 
132
  except Exception as e:
133
+ debug_log(f" EROARE GENERALĂ la încărcarea sistemului: {str(e)}", "ERROR")
134
+ debug_log(f"❌ Traceback complet: {traceback.format_exc()}", "ERROR")
135
+ return False
136
 
137
+ def load_individual_models_fallback():
 
 
 
 
138
  """
139
+ Funcție de fallback care încearcă încarce modelele vechi dacă sistemul nou nu funcționează.
 
 
 
 
140
  """
141
+ debug_log("🔄 ÎNCĂRCARE FALLBACK - modele individuale")
142
 
143
  try:
144
+ # Încercăm să încărcăm modelele vechi pentru compatibilitate
145
+ model_files = ["/code/rf_model_optim.joblib", "/code/rf_model.joblib"]
146
+
147
+ for model_file in model_files:
148
+ debug_log(f"🔍 Verific modelul: {model_file}")
149
+ if Path(model_file).exists():
150
+ debug_log(f"✅ Modelul {model_file} există")
151
+ try:
152
+ model = joblib.load(model_file)
153
+ debug_log(f"✅ Modelul {model_file} încărcat cu succes")
154
+ # Pentru moment, doar confirmăm modelul se încarcă
155
+ # Puteți implementa logica de compatibilitate aici
156
+ except Exception as e:
157
+ debug_log(f"❌ Eroare la încărcarea {model_file}: {str(e)}", "ERROR")
158
+ else:
159
+ debug_log(f"❌ Modelul {model_file} nu există")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
+ debug_log("⚠️ Sistem funcționând în modul de compatibilitate limitată", "WARNING")
162
+ return False
 
 
 
 
 
 
 
 
 
 
 
 
163
 
164
  except Exception as e:
165
+ debug_log(f" EROARE în funcția de fallback: {str(e)}", "ERROR")
166
+ return False
167
 
168
+ # Încărcăm sistemul imediat după definirea funcțiilor
169
+ debug_log("🎯 Pornesc încărcarea sistemului de modele...")
170
+ model_loading_success = load_uncertainty_system_with_detailed_logging()
171
+ debug_log(f"📊 Rezultatul încărcării modelelor: {model_loading_success}")
172
+
173
+ # Creăm aplicația FastAPI cu logging
174
+ debug_log("🌐 Creez aplicația FastAPI...")
175
+
176
+ try:
177
+ app = FastAPI(
178
+ title="UCS Prediction API with Uncertainty Quantification - DEBUG VERSION",
179
+ description="Versiune de debugging pentru diagnoticarea problemelor de pornire",
180
+ version="2.0-debug"
181
+ )
182
+ debug_log(" Aplicația FastAPI creată cu succes")
183
+
184
+ # Adăugăm middleware CORS cu logging
185
+ debug_log("🔧 Configurez CORS middleware...")
186
+ app.add_middleware(
187
+ CORSMiddleware,
188
+ allow_origins=["*"], # Pentru debugging, permittem toate originile
189
+ allow_credentials=True,
190
+ allow_methods=["GET", "POST", "OPTIONS"],
191
+ allow_headers=["*"],
192
+ )
193
+ debug_log("✅ CORS middleware configurat")
 
 
 
 
194
 
195
+ except Exception as e:
196
+ debug_log(f"❌ EROARE la crearea aplicației FastAPI: {str(e)}", "ERROR")
197
+ debug_log(f"❌ Traceback: {traceback.format_exc()}", "ERROR")
198
 
199
+ # Endpoint simplu de debugging pentru testarea stării sistemului
200
+ @app.get("/debug-status")
201
+ async def debug_status():
202
  """
203
+ Endpoint special pentru debugging care returnează informații complete despre starea sistemului.
 
 
204
  """
205
+ debug_log("🔍 Cerere pentru debug-status")
206
 
207
  try:
208
+ return {
209
+ "status": "API is running",
210
+ "timestamp": datetime.now().isoformat(),
211
+ "system_loaded": system_loaded,
212
+ "models_directory_exists": Path("/code/models_for_deployment").exists(),
213
+ "files_in_models_dir": list(Path("/code/models_for_deployment").iterdir()) if Path("/code/models_for_deployment").exists() else [],
214
+ "python_version": sys.version,
215
+ "current_directory": str(Path.cwd()),
216
+ "environment_variables": dict(os.environ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  }
 
 
 
 
 
 
 
 
 
 
 
218
  except Exception as e:
219
+ debug_log(f" EROARE în debug-status: {str(e)}", "ERROR")
220
+ return {"error": str(e), "traceback": traceback.format_exc()}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
 
222
+ # Endpoint simplu pentru verificarea că aplicația răspunde
223
+ @app.get("/")
224
+ async def root():
225
+ debug_log("🏠 Cerere pentru endpoint root")
226
+ return {"message": "UCS API Debug Version - System Running", "system_loaded": system_loaded}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
 
228
+ # La sfârșit, confirmăm că modulul s-a încărcat complet
229
+ debug_log("🎉 MODULUL app.py S-A ÎNCĂRCAT COMPLET!")
230
+ debug_log(f"📊 Starea finală: system_loaded = {system_loaded}")