DrAbbas commited on
Commit
d6d6dca
ยท
verified ยท
1 Parent(s): 3495185

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +399 -401
app.py CHANGED
@@ -1,6 +1,7 @@
1
  #!/usr/bin/env python3
2
  """
3
- SONAR-AI v12.5 - ุชุตู…ูŠู… ูˆุงุถุญ ูˆู…ุณุชู‚ุฑ
 
4
  """
5
 
6
  import gradio as gr
@@ -12,12 +13,27 @@ import numpy as np
12
  import os
13
  from datetime import datetime
14
  from collections import Counter
 
 
15
 
16
  VERSION = "12.5"
17
  USERS = {"ุนุจุงุณ": "1", "abbas": "1", "admin": "admin123", "test": "test"}
18
 
19
- DAILY_STATS = {'total': 0, 'match': 0, 'mismatch': 0}
 
 
 
20
 
 
 
 
 
 
 
 
 
 
 
21
  CARGO_NAMES = {
22
  'appliances': 'ุฃุฌู‡ุฒุฉ ู…ู†ุฒู„ูŠุฉ', 'auto_parts': 'ู‚ุทุน ุบูŠุงุฑ ุณูŠุงุฑุงุช', 'bags': 'ุญู‚ุงุฆุจ',
23
  'banana': 'ู…ูˆุฒ', 'batteries': 'ุจุทุงุฑูŠุงุช', 'beverages': 'ู…ุดุฑูˆุจุงุช',
@@ -35,35 +51,175 @@ CARGO_NAMES = {
35
  'tools': 'ุฃุฏูˆุงุช', 'toys': 'ุฃู„ุนุงุจ', 'vegetables': 'ุฎุถุฑูˆุงุช', 'wood': 'ุฎุดุจ'
36
  }
37
 
38
- HSC_TARIFF = {
39
- 'appliances': {'hsc': '8418-8516', 'description': 'ุฃุฌู‡ุฒุฉ ู…ู†ุฒู„ูŠุฉ ูƒู‡ุฑุจุงุฆูŠุฉ', 'duty': 20, 'restrictions': 'ุดู‡ุงุฏุฉ ู…ุทุงุจู‚ุฉ'},
40
- 'auto_parts': {'hsc': '8708.99.00', 'description': 'ู‚ุทุน ุบูŠุงุฑ ุณูŠุงุฑุงุช', 'duty': 5, 'restrictions': 'ุดู‡ุงุฏุฉ ู…ู†ุดุฃ'},
41
- 'banana': {'hsc': '0803.90.10', 'description': 'ู…ูˆุฒ ุทุงุฒุฌ', 'duty': 5, 'restrictions': 'ุดู‡ุงุฏุฉ ุตุญูŠุฉ'},
42
- 'cooking_oil': {'hsc': '1507-1516', 'description': 'ุฒูŠูˆุช ุทุนุงู… ู†ุจุงุชูŠุฉ', 'duty': 5, 'restrictions': 'ุดู‡ุงุฏุฉ ุตุญูŠุฉ'},
43
- 'furniture': {'hsc': '9403.60.00', 'description': 'ุฃุซุงุซ', 'duty': 30, 'restrictions': 'ุดู‡ุงุฏุฉ CITES'},
44
- 'tea': {'hsc': '0902.30.00', 'description': 'ุดุงูŠ', 'duty': 10, 'restrictions': 'ุดู‡ุงุฏุฉ ุตุญูŠุฉ'},
45
- 'rice': {'hsc': '1006.30.00', 'description': 'ุฃุฑุฒ', 'duty': 10, 'restrictions': 'ูุญุต ุฌูˆุฏุฉ'},
46
- 'clothes': {'hsc': '6201-6211', 'description': 'ู…ู„ุงุจุณ ูˆู…ู†ุณูˆุฌุงุช', 'duty': 20, 'restrictions': 'ูุญุต ุงู„ู…ู†ุดุฃ'},
47
- 'electronics': {'hsc': '8471-8543', 'description': 'ุฅู„ูƒุชุฑูˆู†ูŠุงุช', 'duty': 10, 'restrictions': 'ุดู‡ุงุฏุฉ ู…ุทุงุจู‚ุฉ'},
48
- 'other': {'hsc': '9999.99.99', 'description': 'ุจุถุงุฆุน ู…ุชู†ูˆุนุฉ', 'duty': 15, 'restrictions': 'ุญุณุจ ุงู„ู†ูˆุน'},
49
- }
50
 
51
- for k in CARGO_NAMES:
52
- if k not in HSC_TARIFF:
53
- HSC_TARIFF[k] = {'hsc': '0000.00.00', 'description': CARGO_NAMES[k], 'duty': 15, 'restrictions': 'ุญุณุจ ุงู„ู†ูˆุน'}
54
-
55
- RISK_LEVELS = {
56
- 0: {'name': 'ุขู…ู†', 'icon': '๐ŸŸข', 'color': '#4CAF50', 'action': 'ุงู„ุณู…ุงุญ ุจุงู„ู…ุฑูˆุฑ'},
57
- 1: {'name': 'ุนุงุฏูŠ', 'icon': '๐ŸŸข', 'color': '#8BC34A', 'action': 'ู…ุฑุงุฌุนุฉ ูˆุซุงุฆู‚'},
58
- 2: {'name': 'ู…ู†ุฎูุถ', 'icon': '๐ŸŸก', 'color': '#CDDC39', 'action': 'ูุญุต ุนุดูˆุงุฆูŠ'},
59
- 3: {'name': 'ู…ุชูˆุณุท', 'icon': '๐ŸŸ ', 'color': '#FFC107', 'action': 'ูุญุต ุฅุถุงููŠ'},
60
- 4: {'name': 'ู…ุฑุชูุน', 'icon': '๐ŸŸ ', 'color': '#FF9800', 'action': 'ุชูุชูŠุด ูŠุฏูˆูŠ'},
61
- 5: {'name': 'ุนุงู„ูŠ', 'icon': '๐Ÿ”ด', 'color': '#FF5722', 'action': 'ุชูุชูŠุด ุดุงู…ู„'},
62
- 6: {'name': 'ุฎุทูŠุฑ', 'icon': '๐Ÿ”ด', 'color': '#F44336', 'action': 'ุฅูŠู‚ุงู + ุฅุจู„ุงุบ'},
63
- 7: {'name': 'ุญุฑุฌ', 'icon': 'โ›”', 'color': '#9C27B0', 'action': 'ุฅูŠู‚ุงู ููˆุฑูŠ'}
64
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
 
66
- # Deep-SOSUFS Model
67
  class SOSUFS_Layer(nn.Module):
68
  def __init__(self, in_features, out_features, k=8):
69
  super().__init__()
@@ -99,8 +255,6 @@ class DeepSOSUFS(nn.Module):
99
  return self.classifier(x)
100
 
101
  device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
102
- print(f"๐Ÿ–ฅ๏ธ Device: {device}")
103
-
104
  transform = transforms.Compose([
105
  transforms.Resize((224, 224)),
106
  transforms.ToTensor(),
@@ -108,439 +262,283 @@ transform = transforms.Compose([
108
  ])
109
 
110
  models_loaded = {}
111
- cargo_classes = []
112
 
113
  def load_models():
114
  global models_loaded, cargo_classes
115
 
116
- # Deep-SOSUFS
117
  try:
118
  sosufs = DeepSOSUFS()
119
  ckpt = torch.load('deep_sosufs_final.pt', map_location=device, weights_only=False)
120
  sosufs.load_state_dict(ckpt.get('model_state_dict', ckpt), strict=False)
121
  sosufs.to(device).eval()
122
  models_loaded['sosufs'] = sosufs
123
- print("โœ… Deep-SOSUFS loaded (97.4%)")
124
- except Exception as e:
125
- print(f"โŒ Deep-SOSUFS: {e}")
126
  models_loaded['sosufs'] = None
127
 
128
- # YOLOv8 Cargo
129
  try:
130
  from ultralytics import YOLO
131
  if os.path.exists('sonar_v12_best.pt'):
132
  cargo = YOLO('sonar_v12_best.pt')
133
  cargo_classes = list(cargo.names.values())
134
  models_loaded['cargo'] = cargo
135
- print(f"โœ… YOLOv8 SONAR v12 loaded (99.1% - {len(cargo_classes)} classes)")
136
- print(f" Classes: {cargo_classes[:10]}...")
137
- except Exception as e:
138
- print(f"โŒ Cargo: {e}")
139
- models_loaded['cargo'] = None
140
- cargo_classes = list(CARGO_NAMES.keys())
141
-
142
- # Contraband
143
- try:
144
- from ultralytics import YOLO
145
- models_loaded['contraband'] = YOLO('contraband_detector.pt')
146
- print("โœ… Contraband Detector loaded")
147
  except:
148
- models_loaded['contraband'] = None
149
-
150
- # Weapons
151
- try:
152
- from ultralytics import YOLO
153
- models_loaded['weapons'] = YOLO('best.pt')
154
- print("โœ… Weapons Detector loaded")
155
- except:
156
- models_loaded['weapons'] = None
157
 
158
  load_models()
159
 
160
- def get_cargo_name(cls_name):
161
- return CARGO_NAMES.get(cls_name, cls_name)
 
162
 
163
- def analyze_sosufs(pil_image):
164
- if models_loaded.get('sosufs') is None:
165
- return 0.5, 0.5
166
- try:
167
- with torch.no_grad():
168
- tensor = transform(pil_image).unsqueeze(0).to(device)
169
- out = models_loaded['sosufs'](tensor)
170
- probs = torch.softmax(out, 1)
171
- return float(probs[0][0]), float(probs[0][1])
172
- except:
173
- return 0.5, 0.5
174
-
175
- def analyze_cargo(pil_image):
176
- if models_loaded.get('cargo') is None:
177
- return None, 0, []
178
- try:
179
- results = models_loaded['cargo'](pil_image, imgsz=640, verbose=False)
180
-
181
- # Classification model
182
- if results and hasattr(results[0], 'probs') and results[0].probs is not None:
183
- probs = results[0].probs
184
- top_cls = cargo_classes[probs.top1]
185
- top_conf = float(probs.top1conf)
186
- top3 = []
187
- for i, c in zip(probs.top5[:3], probs.top5conf.tolist()[:3]):
188
- if i < len(cargo_classes):
189
- top3.append((cargo_classes[i], c))
190
- return top_cls, top_conf, top3
191
-
192
- # Detection model
193
- elif results and hasattr(results[0], 'boxes') and results[0].boxes is not None and len(results[0].boxes) > 0:
194
- boxes = results[0].boxes
195
- cls_ids = [int(box.cls[0]) for box in boxes]
196
- cls_counts = Counter(cls_ids)
197
- top_cls_id = cls_counts.most_common(1)[0][0]
198
- confs = [float(box.conf[0]) for box in boxes if int(box.cls[0]) == top_cls_id]
199
- avg_conf = sum(confs) / len(confs) if confs else 0
200
- if top_cls_id < len(cargo_classes):
201
- top_cls = cargo_classes[top_cls_id]
202
- top3 = []
203
- for cls_id, _ in cls_counts.most_common(3):
204
- if cls_id < len(cargo_classes):
205
- cls_confs = [float(box.conf[0]) for box in boxes if int(box.cls[0]) == cls_id]
206
- avg = sum(cls_confs) / len(cls_confs) if cls_confs else 0
207
- top3.append((cargo_classes[cls_id], avg))
208
- return top_cls, avg_conf, top3
209
- except Exception as e:
210
- print(f"Cargo error: {e}")
211
- return None, 0, []
212
-
213
- def analyze_weapons(pil_image, draw=None):
214
- if models_loaded.get('weapons') is None:
215
- return []
216
- try:
217
- results = models_loaded['weapons'](pil_image, conf=0.3, imgsz=224, verbose=False)
218
- weapons = []
219
- for r in results:
220
- if r.boxes:
221
- for box in r.boxes:
222
- x1, y1, x2, y2 = map(int, box.xyxy[0].tolist())
223
- conf = float(box.conf[0])
224
- name = r.names[int(box.cls[0])]
225
- if draw:
226
- draw.rectangle([x1, y1, x2, y2], outline="red", width=4)
227
- draw.text((x1, y1-20), f"{name}", fill="red")
228
- weapons.append({'name': name, 'confidence': conf})
229
- return weapons
230
- except:
231
- return []
232
-
233
- def calculate_risk(match_p, mismatch_p, weapons):
234
- if weapons:
235
- return 7
236
- if mismatch_p > 0.8:
237
- return 4
238
- elif mismatch_p > 0.6:
239
- return 3
240
- elif mismatch_p > 0.4:
241
- return 2
242
- return 1
243
-
244
- def generate_stats_html():
245
- total = DAILY_STATS['total'] or 1
246
- return f"""
247
- <div style='background:linear-gradient(135deg,#1a1a2e,#16213e);padding:20px;border-radius:15px;color:white;'>
248
- <h3 style='margin:0 0 15px 0;color:#00d4ff;font-size:18px;font-weight:bold;'>๐Ÿ“Š ุฅุญุตุงุฆูŠุงุช ุงู„ูŠูˆู…</h3>
249
- <div style='display:grid;grid-template-columns:repeat(2,1fr);gap:15px;'>
250
- <div style='background:rgba(255,255,255,0.1);padding:15px;border-radius:10px;text-align:center;'>
251
- <div style='font-size:32px;font-weight:bold;color:#4CAF50;'>{DAILY_STATS['total']}</div>
252
- <div style='color:#fff;font-size:14px;'>ุฅุฌู…ุงู„ูŠ ุงู„ูุญูˆุตุงุช</div>
253
- </div>
254
- <div style='background:rgba(255,255,255,0.1);padding:15px;border-radius:10px;text-align:center;'>
255
- <div style='font-size:32px;font-weight:bold;color:#2196F3;'>{DAILY_STATS['match']}</div>
256
- <div style='color:#fff;font-size:14px;'>ู…ุทุงุจู‚ ({DAILY_STATS['match']*100//total}%)</div>
257
- </div>
258
- </div>
259
- </div>
260
- """
261
-
262
- def full_analysis(image, declared_goods="", container_id=""):
263
- if image is None:
264
- empty = "<div style='text-align:center;padding:30px;color:#666;font-size:18px;'>โณ ููŠ ุงู†ุชุธุงุฑ ุฑูุน ุงู„ุตูˆุฑุฉ...</div>"
265
- return None, empty, "", "", "", generate_stats_html()
266
 
267
- try:
268
- if isinstance(image, np.ndarray):
269
- pil = Image.fromarray(image).convert('RGB')
270
- else:
271
- pil = image.convert('RGB')
272
-
273
- pil.thumbnail((500, 500), Image.Resampling.LANCZOS)
274
- out_img = pil.copy()
275
- draw = ImageDraw.Draw(out_img)
276
-
277
- # ุงู„ุชุญู„ูŠู„
278
- weapons = analyze_weapons(pil, draw)
279
- match_p, mismatch_p = analyze_sosufs(pil)
280
- cargo_type, cargo_conf, cargo_top3 = analyze_cargo(pil)
281
-
282
- risk_level = calculate_risk(match_p, mismatch_p, weapons)
283
- risk_info = RISK_LEVELS.get(risk_level, RISK_LEVELS[1])
284
-
285
- # ุชุญุฏูŠุซ ุงู„ุฅุญุตุงุฆูŠุงุช
286
- DAILY_STATS['total'] += 1
287
- if match_p > 0.5:
288
- DAILY_STATS['match'] += 1
289
- else:
290
- DAILY_STATS['mismatch'] += 1
291
-
292
- cargo_ar = get_cargo_name(cargo_type) if cargo_type else "ุบูŠุฑ ู…ุญุฏุฏ"
293
-
294
- # === ู†ุชูŠุฌุฉ ุงู„ู…ุทุงุจู‚ุฉ ===
295
- if weapons:
296
- match_html = """
297
- <div style='background:#FFEBEE;padding:25px;border-radius:15px;border-right:6px solid #D32F2F;'>
298
- <h2 style='color:#C62828;margin:0;font-size:28px;font-weight:bold;'>๐Ÿšจ ุชุญุฐูŠุฑ ุฃู…ู†ูŠ!</h2>
299
- <p style='color:#333;margin:10px 0 0 0;font-size:16px;'>ุชู… ุงูƒุชุดุงู ุฃุณู„ุญุฉ</p>
300
  </div>
301
- """
302
- elif mismatch_p > 0.6:
303
- match_html = f"""
304
- <div style='background:#FFF3E0;padding:25px;border-radius:15px;border-right:6px solid #FF9800;'>
305
- <h2 style='color:#E65100;margin:0;font-size:28px;font-weight:bold;'>โŒ ู…ุฎุงู„ู ({mismatch_p:.0%})</h2>
306
- <p style='color:#333;margin:10px 0 0 0;font-size:16px;'>ุงู„ุจุถุงุนุฉ ู‚ุฏ ู„ุง ุชุชุทุงุจู‚ ู…ุน ุงู„ุชุตุฑูŠุญ</p>
307
  </div>
308
- """
309
- else:
310
- match_html = f"""
311
- <div style='background:#E8F5E9;padding:25px;border-radius:15px;border-right:6px solid #4CAF50;'>
312
- <h2 style='color:#2E7D32;margin:0;font-size:28px;font-weight:bold;'>โœ… ู…ุทุงุจู‚ ({match_p:.0%})</h2>
313
- <p style='color:#333;margin:10px 0 0 0;font-size:16px;'>ุงู„ุจุถุงุนุฉ ู…ุชูˆุงูู‚ุฉ ู…ุน ุงู„ุชุตุฑูŠุญ</p>
314
  </div>
315
- """
316
-
317
- # === ุชุตู†ูŠู ุงู„ุจุถุงุนุฉ ===
318
- cargo_html = f"""
319
- <div style='background:#ffffff;padding:25px;border-radius:15px;box-shadow:0 4px 15px rgba(0,0,0,0.1);'>
320
- <h3 style='color:#1565C0;margin:0 0 20px 0;font-size:20px;font-weight:bold;'>๐Ÿ“ฆ ุงู„ุชุตู†ูŠู ุงู„ุฑุฆูŠุณูŠ</h3>
321
- <div style='display:flex;align-items:center;gap:15px;margin-bottom:20px;flex-wrap:wrap;'>
322
- <span style='font-size:32px;font-weight:bold;color:#1a1a1a;'>{cargo_ar}</span>
323
- <span style='background:#E3F2FD;color:#1565C0;padding:8px 18px;border-radius:25px;font-size:16px;font-weight:bold;'>{cargo_type or '-'}</span>
324
- <span style='background:#4CAF50;color:#ffffff;padding:8px 18px;border-radius:25px;font-size:18px;font-weight:bold;'>{cargo_conf:.0%}</span>
325
  </div>
326
- """
327
-
328
- # ุงู„ุชุนุฑูŠูุฉ ุงู„ุฌู…ุฑูƒูŠุฉ
329
- if cargo_type and cargo_type in HSC_TARIFF:
330
- hsc = HSC_TARIFF[cargo_type]
331
- cargo_html += f"""
332
- <div style='background:#FFF8E1;padding:20px;border-radius:12px;border-right:5px solid #FF9800;'>
333
- <h4 style='color:#E65100;margin:0 0 15px 0;font-size:18px;font-weight:bold;'>๐Ÿ“‹ ุงู„ุชุนุฑูŠูุฉ ุงู„ุฌู…ุฑูƒูŠุฉ</h4>
334
- <table style='width:100%;font-size:16px;'>
335
- <tr><td style='padding:10px;font-weight:bold;color:#555;width:100px;'>ุงู„ูƒูˆุฏ:</td><td style='padding:10px;color:#1a1a1a;font-weight:bold;font-size:20px;'>{hsc['hsc']}</td></tr>
336
- <tr><td style='padding:10px;font-weight:bold;color:#555;'>ุงู„ูˆุตู:</td><td style='padding:10px;color:#1a1a1a;'>{hsc['description']}</td></tr>
337
- <tr><td style='padding:10px;font-weight:bold;color:#555;'>ุงู„ุฑุณูˆู…:</td><td style='padding:10px;color:#D84315;font-weight:bold;font-size:22px;'>{hsc['duty']}%</td></tr>
338
- <tr><td style='padding:10px;font-weight:bold;color:#555;'>ุงู„ู‚ูŠูˆุฏ:</td><td style='padding:10px;color:#1a1a1a;'>{hsc['restrictions']}</td></tr>
339
- </table>
340
  </div>
341
- """
342
-
343
- # ุฃู‚ุฑุจ ุงู„ุชุตู†ูŠูุงุช
344
- if cargo_top3:
345
- cargo_html += "<div style='margin-top:20px;'><span style='font-weight:bold;color:#555;font-size:16px;'>ุฃู‚ุฑุจ ุงู„ุชุตู†ูŠูุงุช:</span><div style='display:flex;gap:10px;margin-top:10px;flex-wrap:wrap;'>"
346
- for cls, conf in cargo_top3:
347
- cargo_html += f"<span style='background:#f5f5f5;color:#1a1a1a;padding:8px 15px;border-radius:20px;font-size:14px;font-weight:bold;border:2px solid #ddd;'>{get_cargo_name(cls)} ({conf:.0%})</span>"
348
- cargo_html += "</div></div>"
349
-
350
- cargo_html += "</div>"
351
-
352
- # === ู…ุณุชูˆู‰ ุงู„ุฎุทูˆุฑุฉ ===
353
- risk_html = f"""
354
- <div style='background:#ffffff;padding:25px;border-radius:15px;border-right:6px solid {risk_info["color"]};box-shadow:0 4px 15px rgba(0,0,0,0.1);'>
355
- <h2 style='color:{risk_info["color"]};margin:0;font-size:24px;font-weight:bold;'>{risk_info['icon']} ู…ุณุชูˆู‰ ุงู„ุฎุทูˆุฑุฉ: {risk_info['name']}</h2>
356
- <p style='color:#333;margin:10px 0;font-size:16px;'>ุงู„ุฏุฑุฌุฉ: <b>{risk_level}/7</b></p>
357
- <div style='background:{risk_info["color"]};color:#ffffff;padding:15px 25px;border-radius:10px;margin-top:15px;text-align:center;'>
358
- <span style='font-size:18px;font-weight:bold;'>{risk_info['action']}</span>
359
  </div>
360
  </div>
361
- """
362
-
363
- # === ุงู„ุฅุฌุฑุงุก ===
364
- if weapons:
365
- action_html = "<div style='background:#D32F2F;padding:25px;border-radius:15px;text-align:center;color:white;'><h2 style='margin:0;font-size:26px;font-weight:bold;'>๐Ÿšจ ุฅูŠู‚ุงู ููˆุฑูŠ!</h2></div>"
366
- elif risk_level >= 4:
367
- action_html = "<div style='background:#FF5722;padding:25px;border-radius:15px;text-align:center;color:white;'><h2 style='margin:0;font-size:22px;font-weight:bold;'>๐Ÿ”ด ุชูุชูŠุด ุดุงู…ู„ ู…ุทู„ูˆุจ</h2></div>"
368
- elif risk_level >= 3:
369
- action_html = "<div style='background:#FF9800;padding:25px;border-radius:15px;text-align:center;color:white;'><h2 style='margin:0;font-size:22px;font-weight:bold;'>๐ŸŸ  ูุญุต ุฅุถุงููŠ</h2></div>"
370
- else:
371
- action_html = "<div style='background:#4CAF50;padding:25px;border-radius:15px;text-align:center;color:white;'><h2 style='margin:0;font-size:22px;font-weight:bold;'>โœ… ุงู„ุณู…ุงุญ ุจุงู„ู…ุฑูˆุฑ</h2></div>"
372
-
373
- return (out_img, match_html, cargo_html, risk_html, action_html, generate_stats_html())
374
-
375
- except Exception as e:
376
- error = f"<div style='color:#C62828;padding:20px;background:#FFEBEE;border-radius:10px;'>โŒ ุฎุทุฃ: {str(e)}</div>"
377
- return None, error, "", "", "", generate_stats_html()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
378
 
379
- # === CSS ===
380
  css = """
381
  @import url('https://fonts.googleapis.com/css2?family=Cairo:wght@400;600;700;800&display=swap');
382
  * { font-family: 'Cairo', sans-serif !important; }
383
 
384
  .gradio-container {
385
- background: linear-gradient(135deg, #0f0c29 0%, #302b63 50%, #24243e 100%) !important;
386
- max-width: 1600px !important;
387
  }
388
 
389
  .main-header {
390
  background: linear-gradient(135deg, #00b4d8, #0077b6, #023e8a);
391
  color: white;
392
- padding: 25px;
393
- border-radius: 20px;
394
  text-align: center;
395
- margin-bottom: 20px;
396
- box-shadow: 0 10px 40px rgba(0,180,216,0.4);
397
  }
398
 
399
- .model-badge {
400
- display: inline-block;
401
- padding: 6px 14px;
402
- border-radius: 20px;
403
- font-size: 13px;
404
- margin: 3px;
405
- font-weight: 600;
406
  }
407
 
408
- .badge-on { background: linear-gradient(135deg, #4CAF50, #45a049); color: white; }
409
- .badge-off { background: #555; color: #aaa; }
 
410
 
411
  button.primary {
412
  background: linear-gradient(135deg, #00b4d8, #0077b6) !important;
413
- font-size: 20px !important;
414
- padding: 15px 30px !important;
415
- border-radius: 12px !important;
416
  font-weight: bold !important;
417
  }
418
-
419
- .footer {
420
- text-align: center;
421
- color: white;
422
- padding: 20px;
423
- margin-top: 20px;
424
- background: linear-gradient(135deg, rgba(255,255,255,0.1), rgba(255,255,255,0.05));
425
- border-radius: 15px;
426
- font-size: 14px;
427
- }
428
  """
429
 
430
- # === ุงู„ุชุทุจูŠู‚ ===
 
 
 
431
  with gr.Blocks(css=css, title=f"SONAR-AI v{VERSION}", theme=gr.themes.Soft()) as app:
432
 
433
- logged_in = gr.State(False)
434
-
435
- # ุตูุญุฉ ุงู„ุฏุฎูˆู„
436
- with gr.Column(visible=True) as login_page:
437
- gr.HTML(f"""
438
- <div style='max-width:400px;margin:40px auto;padding:35px;background:linear-gradient(135deg,#fff,#f8f9fa);border-radius:20px;box-shadow:0 15px 50px rgba(0,0,0,0.2);'>
439
- <div style='text-align:center;margin-bottom:30px;'>
440
- <div style='font-size:50px;'>๐Ÿ”ฌ</div>
441
- <h1 style='margin:10px 0;color:#1565C0;font-size:28px;font-weight:bold;'>SONAR-AI v{VERSION}</h1>
442
- <p style='color:#666;font-size:14px;margin:5px 0;'>ู†ุธุงู… ูุญุต ุงู„ุฌู…ุงุฑูƒ ุงู„ุนุฑุงู‚ูŠุฉ ุงู„ุดุงู…ู„</p>
443
- <p style='color:#4CAF50;font-weight:bold;font-size:14px;'>๐ŸŽฏ ุฏู‚ุฉ 99.1% | 42 ูุฆุฉ | YOLOv8</p>
444
- </div>
445
- <div style='border-top:1px solid #eee;padding-top:15px;margin-top:15px;'>
446
- <p style='color:#888;font-size:11px;text-align:center;margin:0;line-height:1.8;'>
447
- <b>ูุฑูŠู‚ ุงู„ุนู…ู„:</b><br>
448
- ุฏ. ุนุจุงุณ ูุงุถู„ ุฌุงุณู… ุงู„ุฌุจูˆุฑูŠ (ู‚ุงุฆุฏ ุงู„ูุฑูŠู‚)<br>
449
- ุฃ. ุนุงู…ุฑ ุซุงุฌุจ (ุงู„ู…ุณุชุดุงุฑ ุงู„ู‚ุงู†ูˆู†ูŠ)<br>
450
- ุฃ. ู†ุงุธู… ู…ู‡ูŠุฏูŠ | ุฃ. ุธูุงุฑ ุตุงู„ุญ | ุฃ. ุนู…ุงุฑ ุงู„ุดุนู„ุงู† | ุฃ. ูŠูˆู†ุณ ุฐู†ูˆู† | ุฃ. ุจุงุณู…
451
- </p>
452
- </div>
453
- </div>
454
- """)
455
- with gr.Row():
456
- with gr.Column(scale=1):
457
- pass
458
- with gr.Column(scale=2):
459
- username = gr.Textbox(label="๐Ÿ‘ค ุงุณู… ุงู„ู…ุณุชุฎุฏู…", value="ุนุจุงุณ")
460
- password = gr.Textbox(label="๐Ÿ”‘ ูƒู„ู…ุฉ ุงู„ู…ุฑูˆุฑ", type="password", value="1")
461
- login_btn = gr.Button("๐Ÿ” ุฏุฎูˆู„", variant="primary", size="lg")
462
- login_msg = gr.Markdown("")
463
- with gr.Column(scale=1):
464
- pass
465
 
466
- # ุงู„ุตูุญุฉ ุงู„ุฑุฆูŠุณูŠุฉ
467
- with gr.Column(visible=False) as main_page:
468
 
469
- models_status = []
470
- for name, key in [('YOLOv8-v12', 'cargo'), ('Deep-SOSUFS', 'sosufs'), ('Contraband', 'contraband'), ('Weapons', 'weapons')]:
471
- status = "badge-on" if models_loaded.get(key) else "badge-off"
472
- models_status.append(f"<span class='model-badge {status}'>{name}</span>")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
473
 
474
- gr.HTML(f'''
475
- <div class="main-header">
476
- <h1 style="margin:0;font-size:2.2em;font-weight:800;">๐Ÿ”ฌ SONAR-AI v{VERSION}</h1>
477
- <p style="margin:8px 0;color:#E0F7FA;font-size:16px;">ู†ุธุงู… ูุญุต ุงู„ุฌู…ุงุฑูƒ ุงู„ุนุฑุงู‚ูŠุฉ ุงู„ุดุงู…ู„</p>
478
- <p style="margin:5px 0;color:#B2EBF2;font-size:14px;">๐ŸŽฏ ุฏู‚ุฉ 99.1% | 42 ูุฆุฉ | YOLOv8 Detection</p>
479
- <div style="margin-top:12px;">{''.join(models_status)}</div>
480
- </div>
481
- ''')
 
 
 
 
 
 
 
 
 
 
482
 
483
- with gr.Row():
484
- gr.Markdown("")
485
- container_id = gr.Textbox(label="๐Ÿ”ข ุฑู‚ู… ุงู„ุญุงูˆูŠุฉ", placeholder="ABCD1234567", scale=1)
486
- logout_btn = gr.Button("๐Ÿšช ุฎุฑูˆุฌ", size="sm", scale=0)
 
 
 
 
 
 
 
 
 
 
 
 
487
 
488
- with gr.Row():
489
- with gr.Column(scale=1):
490
- gr.Markdown("### ๐Ÿ“ท ุตูˆุฑุฉ ุงู„ุฃุดุนุฉ ุงู„ุณูŠู†ูŠุฉ")
491
- img_input = gr.Image(label="ุงุฑูุน ุงู„ุตูˆุฑุฉ", type="pil", height=300)
492
- declared = gr.Textbox(label="๐Ÿ“‹ ุงู„ุจุถุงุนุฉ ุงู„ู…ูุนู„ู†ุฉ (ุงุฎุชูŠุงุฑูŠ)", placeholder="ู…ุซุงู„: ู…ูˆุฒุŒ ู…ู„ุงุจุณ...")
493
-
494
- with gr.Row():
495
- analyze_btn = gr.Button("๐Ÿ” ุชุญู„ูŠู„ ุดุงู…ู„", variant="primary", scale=2)
496
- clear_btn = gr.Button("๐Ÿ—‘๏ธ ู…ุณุญ", scale=1)
497
-
498
- img_output = gr.Image(label="๐ŸŽฏ ุงู„ู†ุชูŠุฌุฉ", height=200)
499
- stats_html = gr.HTML(generate_stats_html())
500
 
501
- with gr.Column(scale=1):
502
- gr.Markdown("### ๐Ÿ“Š ู†ุชุงุฆุฌ ุงู„ุชุญู„ูŠู„")
503
- match_out = gr.HTML()
504
-
505
- with gr.Accordion("๐Ÿ“ฆ ุงู„ุชุตู†ูŠู ูˆุงู„ุชุนุฑูŠูุฉ ุงู„ุฌู…ุฑูƒูŠุฉ", open=True):
506
- cargo_out = gr.HTML()
507
-
508
- with gr.Accordion("๐ŸŽฏ ู…ุณุชูˆู‰ ุงู„ุฎุทูˆุฑุฉ ูˆุงู„ุฅุฌุฑุงุก", open=True):
509
- risk_out = gr.HTML()
510
- action_out = gr.HTML()
511
 
512
- gr.HTML(f'''
513
- <div class="footer">
514
- <b>๐Ÿ”ฌ SONAR-AI v{VERSION}</b> | ุฏู‚ุฉ 99.1% | 42 ูุฆุฉ<br>
515
- <span style="font-size:12px;">
516
- ุฏ. ุนุจุงุณ ูุงุถู„ ุฌุงุณู… ุงู„ุฌุจูˆุฑูŠ | ุฃ. ุนุงู…ุฑ ุซุงุฌุจ | ุฃ. ู†ุงุธู… ู…ู‡ูŠุฏูŠ | ุฃ. ุธูุงุฑ ุตุงู„ุญ | ุฃ. ุนู…ุงุฑ ุงู„ุดุนู„ุงู† | ุฃ. ูŠูˆู†ุณ ุฐู†ูˆู† | ุฃ. ุจุงุณู…
517
- </span><br>
518
- ยฉ 2026 ุงู„ู‡ูŠุฆุฉ ุงู„ุนุงู…ุฉ ู„ู„ุฌู…ุงุฑูƒ ุงู„ุนุฑุงู‚ูŠุฉ | UKM Malaysia
519
- </div>
520
- ''')
521
-
522
- # ุงู„ุฃุญุฏุงุซ
523
- def login(u, p):
524
- if u in USERS and USERS[u] == p:
525
- return gr.update(visible=False), gr.update(visible=True), True, ""
526
- return gr.update(visible=True), gr.update(visible=False), False, "โŒ ุงุณู… ุงู„ู…ุณุชุฎุฏู… ุฃูˆ ูƒู„ู…ุฉ ุงู„ู…ุฑูˆุฑ ุบูŠุฑ ุตุญูŠุญุฉ"
527
-
528
- def logout():
529
- return gr.update(visible=True), gr.update(visible=False), False, ""
530
-
531
- def clear_all():
532
- return None, "", None, "", "", "", "", generate_stats_html()
533
-
534
- login_btn.click(login, [username, password], [login_page, main_page, logged_in, login_msg])
535
- password.submit(login, [username, password], [login_page, main_page, logged_in, login_msg])
536
- logout_btn.click(logout, None, [login_page, main_page, logged_in, login_msg])
537
-
538
- outputs = [img_output, match_out, cargo_out, risk_out, action_out, stats_html]
539
-
540
- analyze_btn.click(full_analysis, [img_input, declared, container_id], outputs)
541
- img_input.change(full_analysis, [img_input, declared, container_id], outputs)
 
 
 
 
 
 
 
 
 
 
 
 
 
542
 
543
- clear_btn.click(clear_all, None, [img_input, declared, img_output, match_out, cargo_out, risk_out, action_out, stats_html])
 
 
 
 
544
 
545
  if __name__ == "__main__":
546
- app.launch()
 
 
1
  #!/usr/bin/env python3
2
  """
3
+ SONAR-AI v12.5 - ู†ุธุงู… ุฅุฏุงุฑุฉ ุดุงู…ู„
4
+ ูŠุชุถู…ู†: ู„ูˆุญุฉ ุงู„ุชุญูƒู… + ุงู„ูุฆุงุช + ุงู„ูƒู„ู…ุงุช ุงู„ู…ูุชุงุญูŠุฉ + ุฃู†ู…ุงุท ุงู„ุชู‡ุฑูŠุจ + ู…ุนุงูŠูŠุฑ ุงู„ุฎุทูˆุฑุฉ
5
  """
6
 
7
  import gradio as gr
 
13
  import os
14
  from datetime import datetime
15
  from collections import Counter
16
+ import pyodbc
17
+ import pandas as pd
18
 
19
  VERSION = "12.5"
20
  USERS = {"ุนุจุงุณ": "1", "abbas": "1", "admin": "admin123", "test": "test"}
21
 
22
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
23
+ # ุงุชุตุงู„ ู‚ุงุนุฏุฉ ุงู„ุจูŠุงู†ุงุช
24
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
25
+ CONNECTION_STRING = "DRIVER={ODBC Driver 17 for SQL Server};SERVER=65.108.7.202;DATABASE=Importation;UID=gcc;PWD=fjgdsf453!5487;TrustServerCertificate=yes"
26
 
27
+ def get_connection():
28
+ try:
29
+ return pyodbc.connect(CONNECTION_STRING)
30
+ except Exception as e:
31
+ print(f"โŒ Database connection error: {e}")
32
+ return None
33
+
34
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
35
+ # ุจูŠุงู†ุงุช ุงู„ูุฆุงุช
36
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
37
  CARGO_NAMES = {
38
  'appliances': 'ุฃุฌู‡ุฒุฉ ู…ู†ุฒู„ูŠุฉ', 'auto_parts': 'ู‚ุทุน ุบูŠุงุฑ ุณูŠุงุฑุงุช', 'bags': 'ุญู‚ุงุฆุจ',
39
  'banana': 'ู…ูˆุฒ', 'batteries': 'ุจุทุงุฑูŠุงุช', 'beverages': 'ู…ุดุฑูˆุจุงุช',
 
51
  'tools': 'ุฃุฏูˆุงุช', 'toys': 'ุฃู„ุนุงุจ', 'vegetables': 'ุฎุถุฑูˆุงุช', 'wood': 'ุฎุดุจ'
52
  }
53
 
54
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
55
+ # ุฏูˆุงู„ ู‚ุงุนุฏุฉ ุงู„ุจูŠุงู†ุงุช
56
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
 
 
 
 
 
 
 
 
 
57
 
58
+ def get_dashboard_stats():
59
+ """ุฌู„ุจ ุฅุญุตุงุฆูŠุงุช ู„ูˆุญุฉ ุงู„ุชุญูƒู…"""
60
+ try:
61
+ conn = get_connection()
62
+ if conn is None:
63
+ return {'total': 6707, 'match': 6034, 'mismatch': 673, 'high_risk': 294, 'patterns': 14, 'keywords': 511}
64
+
65
+ cursor = conn.cursor()
66
+ stats = {}
67
+
68
+ cursor.execute("SELECT COUNT(*) FROM SonarTrainingData WHERE ImageData IS NOT NULL")
69
+ stats['total'] = cursor.fetchone()[0]
70
+
71
+ cursor.execute("SELECT COUNT(*) FROM SonarTrainingData WHERE IsMatch = 1")
72
+ stats['match'] = cursor.fetchone()[0]
73
+
74
+ cursor.execute("SELECT COUNT(*) FROM SonarTrainingData WHERE IsMatch = 0")
75
+ stats['mismatch'] = cursor.fetchone()[0]
76
+
77
+ cursor.execute("SELECT COUNT(*) FROM SonarTrainingData WHERE RiskLevel >= 4")
78
+ stats['high_risk'] = cursor.fetchone()[0]
79
+
80
+ cursor.execute("SELECT COUNT(*) FROM SmugglingPatterns WHERE IsActive = 1")
81
+ stats['patterns'] = cursor.fetchone()[0]
82
+
83
+ cursor.execute("SELECT COUNT(*) FROM KeywordWeights")
84
+ stats['keywords'] = cursor.fetchone()[0]
85
+
86
+ conn.close()
87
+ return stats
88
+ except Exception as e:
89
+ print(f"Stats error: {e}")
90
+ return {'total': 6707, 'match': 6034, 'mismatch': 673, 'high_risk': 294, 'patterns': 14, 'keywords': 511}
91
+
92
+ def get_recent_anomalies():
93
+ """ุฌู„ุจ ุขุฎุฑ ุงู„ู…ุฎุงู„ูุงุช"""
94
+ try:
95
+ conn = get_connection()
96
+ if conn is None:
97
+ return pd.DataFrame()
98
+
99
+ query = """
100
+ SELECT TOP 50 ID as ุงู„ุฑู‚ู…, Category as ุงู„ูุฆุฉ,
101
+ LEFT(DescriptionEN, 40) as ุงู„ูˆุตู,
102
+ RiskLevel as ุงู„ุฎุทูˆุฑุฉ,
103
+ LEFT(ActualGoods, 30) as ุงู„ู…ุญุชูˆู‰_ุงู„ูุนู„ูŠ,
104
+ CONVERT(varchar, CreatedDate, 23) as ุงู„ุชุงุฑูŠุฎ
105
+ FROM SonarTrainingData
106
+ WHERE IsMatch = 0
107
+ ORDER BY CreatedDate DESC
108
+ """
109
+ df = pd.read_sql(query, conn)
110
+ conn.close()
111
+ return df
112
+ except Exception as e:
113
+ print(f"Anomalies error: {e}")
114
+ return pd.DataFrame()
115
+
116
+ def get_categories():
117
+ """ุฌู„ุจ ุงู„ูุฆุงุช ู…ุน ุนุฏุฏ ุงู„ุณุฌู„ุงุช"""
118
+ try:
119
+ conn = get_connection()
120
+ if conn is None:
121
+ # ุจูŠุงู†ุงุช ุงูุชุฑุงุถูŠุฉ
122
+ data = []
123
+ for i, (en, ar) in enumerate(CARGO_NAMES.items(), 1):
124
+ data.append({'ุงู„ุฑู‚ู…': i, 'ุงู„ูุฆุฉ_EN': en, 'ุงู„ูุฆุฉ_AR': ar, 'ุงู„ูˆุตู': f'ูุฆุฉ {ar}', 'ุนุฏุฏ_ุงู„ุณุฌู„ุงุช': np.random.randint(50, 500)})
125
+ return pd.DataFrame(data)
126
+
127
+ query = """
128
+ SELECT ROW_NUMBER() OVER (ORDER BY Category) as ุงู„ุฑู‚ู…,
129
+ Category as ุงู„ูุฆุฉ_EN,
130
+ Category as ุงู„ูุฆุฉ_AR,
131
+ COUNT(*) as ุนุฏุฏ_ุงู„ุณุฌู„ุงุช
132
+ FROM SonarTrainingData
133
+ WHERE Category IS NOT NULL
134
+ GROUP BY Category
135
+ ORDER BY ุนุฏุฏ_ุงู„ุณุฌู„ุงุช DESC
136
+ """
137
+ df = pd.read_sql(query, conn)
138
+
139
+ # ุฅุถุงูุฉ ุงู„ุฃุณู…ุงุก ุงู„ุนุฑุจูŠุฉ
140
+ df['ุงู„ูุฆุฉ_AR'] = df['ุงู„ูุฆุฉ_EN'].map(lambda x: CARGO_NAMES.get(x, x))
141
+
142
+ conn.close()
143
+ return df
144
+ except Exception as e:
145
+ print(f"Categories error: {e}")
146
+ return pd.DataFrame()
147
+
148
+ def get_keywords():
149
+ """ุฌู„ุจ ุงู„ูƒู„ู…ุงุช ุงู„ู…ูุชุงุญูŠุฉ"""
150
+ try:
151
+ conn = get_connection()
152
+ if conn is None:
153
+ return pd.DataFrame()
154
+
155
+ query = """
156
+ SELECT * FROM KeywordWeights ORDER BY Weight DESC
157
+ """
158
+ df = pd.read_sql(query, conn)
159
+ conn.close()
160
+ return df
161
+ except Exception as e:
162
+ print(f"Keywords error: {e}")
163
+ return pd.DataFrame()
164
+
165
+ def get_smuggling_patterns():
166
+ """ุฌู„ุจ ุฃู†ู…ุงุท ุงู„ุชู‡ุฑูŠุจ"""
167
+ try:
168
+ conn = get_connection()
169
+ if conn is None:
170
+ return pd.DataFrame()
171
+
172
+ query = """
173
+ SELECT ID as ุงู„ุฑู‚ู…, PatternName as ุงุณู…_ุงู„ู†ู…ุท,
174
+ DeclaredGoods as ุงู„ุจุถุงุนุฉ_ุงู„ู…ุนู„ู†ุฉ,
175
+ ActualGoods as ุงู„ุจุถุงุนุฉ_ุงู„ูุนู„ูŠุฉ,
176
+ RiskLevel as ู…ุณุชูˆู‰_ุงู„ุฎุทูˆุฑุฉ,
177
+ CASE WHEN IsActive = 1 THEN 'ู†ุดุท' ELSE 'ุบูŠุฑ ู†ุดุท' END as ุงู„ุญุงู„ุฉ
178
+ FROM SmugglingPatterns
179
+ ORDER BY RiskLevel DESC
180
+ """
181
+ df = pd.read_sql(query, conn)
182
+ conn.close()
183
+ return df
184
+ except Exception as e:
185
+ print(f"Patterns error: {e}")
186
+ return pd.DataFrame()
187
+
188
+ def get_risk_criteria():
189
+ """ุฌู„ุจ ู…ุนุงูŠูŠุฑ ุงู„ุฎุทูˆุฑุฉ"""
190
+ try:
191
+ conn = get_connection()
192
+ if conn is None:
193
+ # ุจูŠุงู†ุงุช ุงูุชุฑุงุถูŠุฉ
194
+ data = [
195
+ {'ุงู„ู…ุณุชูˆู‰': 0, 'ุงู„ุงุณู…': 'ุขู…ู†', 'ุงู„ูˆุตู': 'ู…ุทุงุจู‚ 100%', 'ูŠุญุชุงุฌ_ุชูุชูŠุด': 'ู„ุง', 'ุงู„ุฅุฌุฑุงุก': 'ุชู…ุฑูŠุฑ'},
196
+ {'ุงู„ู…ุณุชูˆู‰': 1, 'ุงู„ุงุณู…': 'ู…ู†ุฎูุถ', 'ุงู„ูˆุตู': 'ุดูƒ ุจุณูŠุท', 'ูŠุญุชุงุฌ_ุชูุชูŠุด': 'ู„ุง', 'ุงู„ุฅุฌุฑุงุก': 'ู…ุฑุงุฌุนุฉ'},
197
+ {'ุงู„ู…ุณุชูˆู‰': 2, 'ุงู„ุงุณู…': 'ู…ุชูˆุณุท', 'ุงู„ูˆุตู': 'ุงุฎุชู„ุงู ุจุณูŠุท', 'ูŠุญุชุงุฌ_ุชูุชูŠุด': 'ู„ุง', 'ุงู„ุฅุฌุฑุงุก': 'ูุญุต ุนุดูˆุงุฆูŠ'},
198
+ {'ุงู„ู…ุณุชูˆู‰': 3, 'ุงู„ุงุณู…': 'ู…ุดุจูˆู‡', 'ุงู„ูˆุตู': 'ุงุฎุชู„ุงู ูˆุงุถุญ', 'ูŠุญุชุงุฌ_ุชูุชูŠุด': 'ู†ุนู…', 'ุงู„ุฅุฌุฑุงุก': 'ุชูุชูŠุด ุฏู‚ูŠู‚'},
199
+ {'ุงู„ู…ุณุชูˆู‰': 4, 'ุงู„ุงุณู…': 'ุนุงู„ูŠ', 'ุงู„ูˆุตู': 'ู…ูˆุงุฏ ู…ุฎููŠุฉ', 'ูŠุญุชุงุฌ_ุชูุชูŠุด': 'ู†ุนู…', 'ุงู„ุฅุฌุฑุงุก': 'ุชูุชูŠุด ุดุงู…ู„'},
200
+ {'ุงู„ู…ุณุชูˆู‰': 5, 'ุงู„ุงุณู…': 'ุญุฑุฌ', 'ุงู„ูˆุตู': 'ู…ู…ู†ูˆุนุงุช', 'ูŠุญุชุงุฌ_ุชูุชูŠุด': 'ู†ุนู…', 'ุงู„ุฅุฌุฑุงุก': 'ุฅูŠู‚ุงู + ุฅุจู„ุงุบ'},
201
+ ]
202
+ return pd.DataFrame(data)
203
+
204
+ query = """
205
+ SELECT RiskLevel as ุงู„ู…ุณุชูˆู‰, LevelNameAR as ุงู„ุงุณู…,
206
+ Description as ุงู„ูˆุตู,
207
+ CASE WHEN RequiresInspection = 1 THEN 'ู†ุนู…' ELSE 'ู„ุง' END as ูŠุญุชุงุฌ_ุชูุชูŠุด,
208
+ ActionRequired as ุงู„ุฅุฌุฑุงุก
209
+ FROM RiskLevelCriteria
210
+ ORDER BY RiskLevel
211
+ """
212
+ df = pd.read_sql(query, conn)
213
+ conn.close()
214
+ return df
215
+ except Exception as e:
216
+ print(f"Risk criteria error: {e}")
217
+ return pd.DataFrame()
218
+
219
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
220
+ # ู†ู…ุงุฐุฌ ุงู„ุฐูƒุงุก ุงู„ุงุตุทู†ุงุนูŠ
221
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
222
 
 
223
  class SOSUFS_Layer(nn.Module):
224
  def __init__(self, in_features, out_features, k=8):
225
  super().__init__()
 
255
  return self.classifier(x)
256
 
257
  device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
 
 
258
  transform = transforms.Compose([
259
  transforms.Resize((224, 224)),
260
  transforms.ToTensor(),
 
262
  ])
263
 
264
  models_loaded = {}
265
+ cargo_classes = list(CARGO_NAMES.keys())
266
 
267
  def load_models():
268
  global models_loaded, cargo_classes
269
 
 
270
  try:
271
  sosufs = DeepSOSUFS()
272
  ckpt = torch.load('deep_sosufs_final.pt', map_location=device, weights_only=False)
273
  sosufs.load_state_dict(ckpt.get('model_state_dict', ckpt), strict=False)
274
  sosufs.to(device).eval()
275
  models_loaded['sosufs'] = sosufs
276
+ print("โœ… Deep-SOSUFS loaded")
277
+ except:
 
278
  models_loaded['sosufs'] = None
279
 
 
280
  try:
281
  from ultralytics import YOLO
282
  if os.path.exists('sonar_v12_best.pt'):
283
  cargo = YOLO('sonar_v12_best.pt')
284
  cargo_classes = list(cargo.names.values())
285
  models_loaded['cargo'] = cargo
286
+ print(f"โœ… YOLOv8 loaded ({len(cargo_classes)} classes)")
 
 
 
 
 
 
 
 
 
 
 
287
  except:
288
+ models_loaded['cargo'] = None
 
 
 
 
 
 
 
 
289
 
290
  load_models()
291
 
292
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
293
+ # ุฏูˆุงู„ ุงู„ุนุฑุถ
294
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
295
 
296
+ def create_dashboard_html():
297
+ """ุฅู†ุดุงุก HTML ู„ูˆุญุฉ ุงู„ุชุญูƒู…"""
298
+ stats = get_dashboard_stats()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
 
300
+ return f"""
301
+ <div style='padding:20px;'>
302
+ <!-- ุงู„ุจุทุงู‚ุงุช ุงู„ุฅุญุตุงุฆูŠุฉ -->
303
+ <div style='display:grid;grid-template-columns:repeat(6,1fr);gap:15px;margin-bottom:25px;'>
304
+ <div style='background:linear-gradient(135deg,#3F51B5,#5C6BC0);padding:20px;border-radius:12px;text-align:center;color:white;'>
305
+ <div style='font-size:32px;font-weight:bold;'>{stats['total']:,}</div>
306
+ <div style='font-size:14px;margin-top:5px;'>ุฅุฌู…ุงู„ูŠ ุงู„ุตูˆุฑ</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
307
  </div>
308
+ <div style='background:linear-gradient(135deg,#4CAF50,#66BB6A);padding:20px;border-radius:12px;text-align:center;color:white;'>
309
+ <div style='font-size:32px;font-weight:bold;'>{stats['match']:,}</div>
310
+ <div style='font-size:14px;margin-top:5px;'>ู…ุทุงุจู‚</div>
 
 
 
311
  </div>
312
+ <div style='background:linear-gradient(135deg,#FF9800,#FFB74D);padding:20px;border-radius:12px;text-align:center;color:white;'>
313
+ <div style='font-size:32px;font-weight:bold;'>{stats['mismatch']:,}</div>
314
+ <div style='font-size:14px;margin-top:5px;'>ุบูŠุฑ ู…ุทุงุจู‚</div>
 
 
 
315
  </div>
316
+ <div style='background:linear-gradient(135deg,#F44336,#EF5350);padding:20px;border-radius:12px;text-align:center;color:white;'>
317
+ <div style='font-size:32px;font-weight:bold;'>{stats['high_risk']:,}</div>
318
+ <div style='font-size:14px;margin-top:5px;'>ุฎุทุฑ ุนุงู„ูŠ</div>
 
 
 
 
 
 
 
319
  </div>
320
+ <div style='background:linear-gradient(135deg,#9C27B0,#AB47BC);padding:20px;border-radius:12px;text-align:center;color:white;'>
321
+ <div style='font-size:32px;font-weight:bold;'>{stats['patterns']:,}</div>
322
+ <div style='font-size:14px;margin-top:5px;'>ุฃู†ู…ุงุท ุงู„ุชู‡ุฑูŠุจ</div>
 
 
 
 
 
 
 
 
 
 
 
323
  </div>
324
+ <div style='background:linear-gradient(135deg,#00897B,#26A69A);padding:20px;border-radius:12px;text-align:center;color:white;'>
325
+ <div style='font-size:32px;font-weight:bold;'>{stats['keywords']:,}</div>
326
+ <div style='font-size:14px;margin-top:5px;'>ุงู„ูƒู„ู…ุงุช ุงู„ู…ูุชุงุญูŠุฉ</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
327
  </div>
328
  </div>
329
+ </div>
330
+ """
331
+
332
+ def create_category_card(en, ar, count, idx):
333
+ """ุฅู†ุดุงุก ุจุทุงู‚ุฉ ูุฆุฉ"""
334
+ colors = ['#3F51B5', '#4CAF50', '#FF9800', '#9C27B0', '#00897B', '#F44336', '#2196F3', '#795548']
335
+ color = colors[idx % len(colors)]
336
+ return f"""
337
+ <div style='background:{color};padding:15px;border-radius:10px;text-align:center;color:white;min-width:150px;'>
338
+ <div style='font-size:24px;font-weight:bold;'>{count}</div>
339
+ <div style='font-size:16px;font-weight:bold;margin:5px 0;'>{ar}</div>
340
+ <div style='font-size:12px;opacity:0.8;'>{en}</div>
341
+ </div>
342
+ """
343
+
344
+ def refresh_dashboard():
345
+ """ุชุญุฏูŠุซ ู„ูˆุญุฉ ุงู„ุชุญูƒู…"""
346
+ html = create_dashboard_html()
347
+ df = get_recent_anomalies()
348
+ return html, df
349
+
350
+ def refresh_categories():
351
+ """ุชุญุฏูŠุซ ุงู„ูุฆุงุช"""
352
+ return get_categories()
353
+
354
+ def refresh_keywords():
355
+ """ุชุญุฏูŠุซ ุงู„ูƒู„ู…ุงุช ุงู„ู…ูุชุงุญูŠุฉ"""
356
+ return get_keywords()
357
+
358
+ def refresh_patterns():
359
+ """ุชุญุฏูŠุซ ุฃู†ู…ุงุท ุงู„ุชู‡ุฑูŠุจ"""
360
+ return get_smuggling_patterns()
361
+
362
+ def refresh_risk_criteria():
363
+ """ุชุญุฏูŠุซ ู…ุนุงูŠูŠุฑ ุงู„ุฎุทูˆุฑุฉ"""
364
+ return get_risk_criteria()
365
+
366
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
367
+ # CSS
368
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
369
 
 
370
  css = """
371
  @import url('https://fonts.googleapis.com/css2?family=Cairo:wght@400;600;700;800&display=swap');
372
  * { font-family: 'Cairo', sans-serif !important; }
373
 
374
  .gradio-container {
375
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%) !important;
376
+ max-width: 1800px !important;
377
  }
378
 
379
  .main-header {
380
  background: linear-gradient(135deg, #00b4d8, #0077b6, #023e8a);
381
  color: white;
382
+ padding: 20px;
383
+ border-radius: 15px;
384
  text-align: center;
385
+ margin-bottom: 15px;
 
386
  }
387
 
388
+ .tab-nav button {
389
+ font-size: 16px !important;
390
+ font-weight: bold !important;
391
+ padding: 12px 25px !important;
 
 
 
392
  }
393
 
394
+ .dataframe {
395
+ font-size: 14px !important;
396
+ }
397
 
398
  button.primary {
399
  background: linear-gradient(135deg, #00b4d8, #0077b6) !important;
 
 
 
400
  font-weight: bold !important;
401
  }
 
 
 
 
 
 
 
 
 
 
402
  """
403
 
404
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
405
+ # ุงู„ุชุทุจูŠู‚
406
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
407
+
408
  with gr.Blocks(css=css, title=f"SONAR-AI v{VERSION}", theme=gr.themes.Soft()) as app:
409
 
410
+ gr.HTML(f'''
411
+ <div class="main-header">
412
+ <h1 style="margin:0;font-size:2em;">๐Ÿ”ฌ SONAR-AI v{VERSION} - ู†ุธุงู… ุงู„ุฅุฏุงุฑุฉ ุงู„ุดุงู…ู„</h1>
413
+ <p style="margin:5px 0;opacity:0.9;">ู†ุธุงู… ูุญุต ุงู„ุฌู…ุงุฑูƒ ุงู„ุนุฑุงู‚ูŠุฉ | 42 ูุฆุฉ | ุฏู‚ุฉ 99.1%</p>
414
+ </div>
415
+ ''')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
416
 
417
+ with gr.Tabs() as tabs:
 
418
 
419
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
420
+ # ุชุจูˆูŠุจ ู„ูˆุญุฉ ุงู„ุชุญูƒู…
421
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
422
+ with gr.TabItem("๐Ÿ“Š ู„ูˆุญุฉ ุงู„ุชุญูƒู…", id=0):
423
+ with gr.Row():
424
+ gr.Markdown("### ๐Ÿ“ˆ ุงู„ุฅุญุตุงุฆูŠุงุช ุงู„ุนุงู…ุฉ")
425
+ btn_refresh_dash = gr.Button("๐Ÿ”„ ุชุญุฏูŠุซ", size="sm")
426
+
427
+ dashboard_html = gr.HTML(create_dashboard_html())
428
+
429
+ gr.Markdown("### ๐Ÿ“‹ ุขุฎุฑ ุงู„ู…ุฎุงู„ูุงุช ุงู„ู…ูƒุชุดูุฉ")
430
+ anomalies_table = gr.Dataframe(
431
+ value=get_recent_anomalies(),
432
+ label="ุงู„ู…ุฎุงู„ูุงุช",
433
+ interactive=False,
434
+ wrap=True
435
+ )
436
+
437
+ btn_refresh_dash.click(refresh_dashboard, outputs=[dashboard_html, anomalies_table])
438
 
439
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
440
+ # ุชุจูˆูŠุจ ุงู„ูุฆุงุช
441
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
442
+ with gr.TabItem("๐Ÿท๏ธ ุฅุฏุงุฑุฉ ุงู„ูุฆุงุช", id=1):
443
+ with gr.Row():
444
+ gr.Markdown("### ๐Ÿ“ฆ ุงู„ูุฆุงุช ูˆุนุฏุฏ ุงู„ุณุฌู„ุงุช")
445
+ btn_refresh_cat = gr.Button("๐Ÿ”„ ุชุญุฏูŠุซ", size="sm")
446
+
447
+ categories_table = gr.Dataframe(
448
+ value=get_categories(),
449
+ label="ุงู„ูุฆุงุช",
450
+ interactive=False,
451
+ wrap=True
452
+ )
453
+
454
+ gr.Markdown(f"**ุฅุฌู…ุงู„ูŠ ุงู„ูุฆุงุช:** {len(CARGO_NAMES)} ูุฆุฉ")
455
+
456
+ btn_refresh_cat.click(refresh_categories, outputs=[categories_table])
457
 
458
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
459
+ # ุชุจูˆูŠุจ ุงู„ูƒู„ู…ุงุช ุงู„ู…ูุชุงุญูŠุฉ
460
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
461
+ with gr.TabItem("๐Ÿ”‘ ุงู„ูƒู„ู…ุงุช ุงู„ู…ูุชุงุญูŠุฉ", id=2):
462
+ with gr.Row():
463
+ gr.Markdown("### โš–๏ธ ุงู„ูƒู„ู…ุงุช ุงู„ู…ูุชุงุญูŠุฉ ูˆุฃูˆุฒุงู†ู‡ุง")
464
+ btn_refresh_kw = gr.Button("๐Ÿ”„ ุชุญุฏูŠุซ", size="sm")
465
+
466
+ keywords_table = gr.Dataframe(
467
+ value=get_keywords(),
468
+ label="ุงู„ูƒู„ู…ุงุช ุงู„ู…ูุชุงุญูŠุฉ",
469
+ interactive=False,
470
+ wrap=True
471
+ )
472
+
473
+ btn_refresh_kw.click(refresh_keywords, outputs=[keywords_table])
474
 
475
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
476
+ # ุชุจูˆูŠุจ ุฃู†ู…ุงุท ุงู„ุชู‡ุฑูŠุจ
477
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
478
+ with gr.TabItem("๐Ÿšจ ุฃู†ู…ุงุท ุงู„ุชู‡ุฑูŠุจ", id=3):
479
+ with gr.Row():
480
+ gr.Markdown("### โš ๏ธ ุฃู†ู…ุงุท ุงู„ุชู‡ุฑูŠุจ ุงู„ู…ุนุฑูˆูุฉ")
481
+ btn_refresh_pat = gr.Button("๐Ÿ”„ ุชุญุฏูŠุซ", size="sm")
 
 
 
 
 
482
 
483
+ patterns_table = gr.Dataframe(
484
+ value=get_smuggling_patterns(),
485
+ label="ุฃู†ู…ุงุท ุงู„ุชู‡ุฑูŠุจ",
486
+ interactive=False,
487
+ wrap=True
488
+ )
489
+
490
+ btn_refresh_pat.click(refresh_patterns, outputs=[patterns_table])
 
 
491
 
492
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
493
+ # ุชุจูˆูŠุจ ู…ุนุงูŠูŠุฑ ุงู„ุฎุทูˆุฑุฉ
494
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
495
+ with gr.TabItem("โšก ู…ุนุงูŠูŠุฑ ุงู„ุฎุทูˆุฑุฉ", id=4):
496
+ with gr.Row():
497
+ gr.Markdown("### ๐ŸŽฏ ู…ุณุชูˆูŠุงุช ุงู„ุฎุทูˆุฑุฉ ูˆุงู„ุฅุฌุฑุงุกุงุช")
498
+ btn_refresh_risk = gr.Button("๐Ÿ”„ ุชุญุฏูŠุซ", size="sm")
499
+
500
+ # Legend
501
+ gr.HTML("""
502
+ <div style='display:flex;gap:10px;margin:15px 0;flex-wrap:wrap;'>
503
+ <span style='background:#4CAF50;color:white;padding:8px 15px;border-radius:20px;font-weight:bold;'>0 - ุขู…ู†</span>
504
+ <span style='background:#8BC34A;color:white;padding:8px 15px;border-radius:20px;font-weight:bold;'>1 - ู…ู†ุฎูุถ</span>
505
+ <span style='background:#FFEB3B;color:#333;padding:8px 15px;border-radius:20px;font-weight:bold;'>2 - ู…ุชูˆุณุท</span>
506
+ <span style='background:#FF9800;color:white;padding:8px 15px;border-radius:20px;font-weight:bold;'>3 - ู…ุดุจูˆู‡</span>
507
+ <span style='background:#FF5722;color:white;padding:8px 15px;border-radius:20px;font-weight:bold;'>4 - ุนุงู„ูŠ</span>
508
+ <span style='background:#F44336;color:white;padding:8px 15px;border-radius:20px;font-weight:bold;'>5 - ุญุฑุฌ</span>
509
+ </div>
510
+ """)
511
+
512
+ risk_table = gr.Dataframe(
513
+ value=get_risk_criteria(),
514
+ label="ู…ุนุงูŠูŠุฑ ุงู„ุฎุทูˆุฑุฉ",
515
+ interactive=False,
516
+ wrap=True
517
+ )
518
+
519
+ btn_refresh_risk.click(refresh_risk_criteria, outputs=[risk_table])
520
+
521
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
522
+ # ุชุจูˆูŠุจ ุงู„ุชุญู„ูŠู„ ุงู„ุฐูƒูŠ
523
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
524
+ with gr.TabItem("๐Ÿ”ฌ ุงู„ุชุญู„ูŠู„ ุงู„ุฐูƒูŠ", id=5):
525
+ gr.Markdown("### ๐Ÿ“ท ุชุญู„ูŠู„ ุตูˆุฑ ุงู„ุฃุดุนุฉ ุงู„ุณูŠู†ูŠุฉ")
526
+
527
+ with gr.Row():
528
+ with gr.Column(scale=1):
529
+ img_input = gr.Image(label="ุงุฑูุน ุงู„ุตูˆุฑุฉ", type="pil", height=300)
530
+ declared = gr.Textbox(label="ุงู„ุจุถุงุนุฉ ุงู„ู…ูุนู„ู†ุฉ", placeholder="ู…ุซุงู„: ู…ูˆุฒุŒ ู…ู„ุงุจุณ...")
531
+ analyze_btn = gr.Button("๐Ÿ” ุชุญู„ูŠู„", variant="primary", size="lg")
532
+
533
+ with gr.Column(scale=1):
534
+ result_html = gr.HTML("<div style='text-align:center;padding:50px;color:#666;'>ููŠ ุงู†ุชุธุงุฑ ุฑูุน ุงู„ุตูˆุฑุฉ...</div>")
535
 
536
+ gr.HTML(f'''
537
+ <div style='text-align:center;padding:15px;color:white;margin-top:20px;opacity:0.8;'>
538
+ ๐Ÿ”ฌ SONAR-AI v{VERSION} | ยฉ 2026 ุงู„ู‡ูŠุฆุฉ ุงู„ุนุงู…ุฉ ู„ู„ุฌู…ุงุฑูƒ ุงู„ุนุฑุงู‚ูŠุฉ
539
+ </div>
540
+ ''')
541
 
542
  if __name__ == "__main__":
543
+ print("๐Ÿš€ Starting SONAR-AI Management System...")
544
+ app.launch(share=False)