Spaces:
Running
Running
| /** | |
| * Medical Datasets Manager JavaScript | |
| * Handles medical datasets functionality | |
| */ | |
| class MedicalDatasetsManager { | |
| constructor() { | |
| this.datasets = []; | |
| this.loadedDatasets = new Set(); | |
| this.systemInfo = {}; | |
| this.init(); | |
| } | |
| init() { | |
| this.loadDatasets(); | |
| this.loadSystemInfo(); | |
| this.setupEventListeners(); | |
| this.setupDatabaseManagement(); | |
| // Refresh system info every 30 seconds | |
| setInterval(() => this.loadSystemInfo(), 30000); | |
| } | |
| setupEventListeners() { | |
| // Dataset loading modal events | |
| document.getElementById('load-dataset-btn').addEventListener('click', () => { | |
| this.loadSelectedDataset(); | |
| }); | |
| } | |
| async loadDatasets() { | |
| try { | |
| const response = await fetch('/api/medical-datasets'); | |
| const data = await response.json(); | |
| if (response.ok) { | |
| this.datasets = data.datasets; | |
| this.renderDatasets(); | |
| } else { | |
| this.showError('فشل في تحميل قواعد البيانات'); | |
| } | |
| } catch (error) { | |
| console.error('Error loading datasets:', error); | |
| this.showError('خطأ في الاتصال بالخادم'); | |
| } | |
| } | |
| async loadSystemInfo() { | |
| try { | |
| const response = await fetch('/api/system/performance'); | |
| const data = await response.json(); | |
| if (response.ok) { | |
| this.systemInfo = data; | |
| this.updateSystemInfo(); | |
| } | |
| } catch (error) { | |
| console.error('Error loading system info:', error); | |
| } | |
| } | |
| updateSystemInfo() { | |
| const memoryElement = document.getElementById('memory-usage'); | |
| const cpuElement = document.getElementById('cpu-cores'); | |
| const datasetsElement = document.getElementById('loaded-datasets'); | |
| const tokenElement = document.getElementById('active-token'); | |
| if (this.systemInfo.memory) { | |
| const memoryPercent = this.systemInfo.memory.process_memory_percent || 0; | |
| memoryElement.textContent = `${memoryPercent.toFixed(1)}%`; | |
| // Update color based on usage | |
| memoryElement.className = memoryPercent > 80 ? 'h5 text-danger' : | |
| memoryPercent > 60 ? 'h5 text-warning' : 'h5 text-primary'; | |
| } | |
| if (this.systemInfo.cpu_cores) { | |
| cpuElement.textContent = `${this.systemInfo.cpu_cores} نواة`; | |
| } | |
| datasetsElement.textContent = this.loadedDatasets.size; | |
| // Update token information | |
| this.updateTokenInfo(); | |
| } | |
| async updateTokenInfo() { | |
| try { | |
| const response = await fetch('/api/tokens/for-task/medical'); | |
| if (response.ok) { | |
| const data = await response.json(); | |
| const tokenElement = document.getElementById('active-token'); | |
| if (data.success) { | |
| tokenElement.textContent = data.token_info.type_name; | |
| tokenElement.className = 'h6 text-success'; | |
| tokenElement.title = `${data.token_info.description} - مستوى الأمان: ${data.token_info.security_level}`; | |
| } else { | |
| tokenElement.textContent = 'غير متوفر'; | |
| tokenElement.className = 'h6 text-danger'; | |
| } | |
| } | |
| } catch (error) { | |
| console.error('Error getting token info:', error); | |
| const tokenElement = document.getElementById('active-token'); | |
| tokenElement.textContent = 'خطأ'; | |
| tokenElement.className = 'h6 text-warning'; | |
| } | |
| } | |
| renderDatasets() { | |
| const container = document.getElementById('datasets-grid'); | |
| if (this.datasets.length === 0) { | |
| container.innerHTML = ` | |
| <div class="col-12 text-center text-muted py-5"> | |
| <i class="fas fa-database fa-4x mb-3"></i> | |
| <h4>لا توجد قواعد بيانات متاحة</h4> | |
| <p>تحقق من الاتصال بالإنترنت أو إعدادات الرموز المميزة</p> | |
| </div> | |
| `; | |
| return; | |
| } | |
| const datasetsHtml = this.datasets.map(dataset => this.renderDatasetCard(dataset)).join(''); | |
| container.innerHTML = `<div class="row">${datasetsHtml}</div>`; | |
| } | |
| renderDatasetCard(dataset) { | |
| const modalitiesBadges = dataset.modalities.map(modality => | |
| `<span class="modality-badge badge bg-primary">${this.getModalityText(modality)}</span>` | |
| ).join(''); | |
| const specialtiesBadges = dataset.medical_specialties.map(specialty => | |
| `<span class="specialty-badge">${this.getSpecialtyText(specialty)}</span>` | |
| ).join(''); | |
| const languageFlags = dataset.languages.map(lang => | |
| `<span class="badge bg-secondary me-1">${this.getLanguageText(lang)}</span>` | |
| ).join(''); | |
| const isLoaded = this.loadedDatasets.has(dataset.key); | |
| const statusClass = isLoaded ? 'status-loaded' : 'status-available'; | |
| const statusText = isLoaded ? 'محمل' : 'متاح'; | |
| return ` | |
| <div class="col-lg-6 col-xl-4"> | |
| <div class="dataset-card position-relative"> | |
| <div class="dataset-status ${statusClass}">${statusText}</div> | |
| <div class="text-center"> | |
| <i class="fas ${this.getDatasetIcon(dataset.modalities)} medical-icon"></i> | |
| <h5 class="mb-2">${dataset.name}</h5> | |
| <p class="text-muted mb-3">${dataset.description}</p> | |
| </div> | |
| <div class="mb-3"> | |
| <div class="d-flex justify-content-between align-items-center mb-2"> | |
| <span class="size-indicator"> | |
| <i class="fas fa-hdd me-1"></i> | |
| ${dataset.size_gb} جيجابايت | |
| </span> | |
| <span class="samples-indicator"> | |
| <i class="fas fa-images me-1"></i> | |
| ${this.formatNumber(dataset.num_samples)} عينة | |
| </span> | |
| </div> | |
| </div> | |
| <div class="mb-3"> | |
| <h6 class="mb-2">الوسائط:</h6> | |
| <div>${modalitiesBadges}</div> | |
| </div> | |
| <div class="mb-3"> | |
| <h6 class="mb-2">التخصصات الطبية:</h6> | |
| <div>${specialtiesBadges}</div> | |
| </div> | |
| <div class="mb-3"> | |
| <h6 class="mb-2">اللغات:</h6> | |
| <div>${languageFlags}</div> | |
| </div> | |
| <div class="dataset-actions"> | |
| <button class="btn btn-outline-info btn-sm flex-fill" | |
| onclick="medicalDatasets.showDatasetDetails('${dataset.key}')"> | |
| <i class="fas fa-info-circle me-1"></i> | |
| التفاصيل | |
| </button> | |
| ${!isLoaded ? ` | |
| <button class="btn btn-primary btn-sm flex-fill" | |
| onclick="medicalDatasets.loadDataset('${dataset.key}')"> | |
| <i class="fas fa-download me-1"></i> | |
| تحميل | |
| </button> | |
| ` : ` | |
| <button class="btn btn-success btn-sm flex-fill" disabled> | |
| <i class="fas fa-check me-1"></i> | |
| محمل | |
| </button> | |
| `} | |
| </div> | |
| </div> | |
| </div> | |
| `; | |
| } | |
| getDatasetIcon(modalities) { | |
| if (modalities.includes('radiology') || modalities.includes('ct_scan')) { | |
| return 'fa-x-ray'; | |
| } else if (modalities.includes('multimodal')) { | |
| return 'fa-layer-group'; | |
| } else if (modalities.includes('imaging')) { | |
| return 'fa-image'; | |
| } | |
| return 'fa-database'; | |
| } | |
| getModalityText(modality) { | |
| const modalityTexts = { | |
| 'radiology': 'أشعة', | |
| 'ct_scan': 'أشعة مقطعية', | |
| 'text': 'نص', | |
| 'multimodal': 'متعدد الوسائط', | |
| 'imaging': 'تصوير طبي', | |
| 'vision': 'رؤية حاسوبية' | |
| }; | |
| return modalityTexts[modality] || modality; | |
| } | |
| getSpecialtyText(specialty) { | |
| const specialtyTexts = { | |
| 'radiology': 'الأشعة', | |
| 'general': 'عام', | |
| 'emergency': 'طوارئ', | |
| 'internal_medicine': 'باطنة', | |
| 'cardiology': 'قلب', | |
| 'neurology': 'أعصاب', | |
| 'oncology': 'أورام' | |
| }; | |
| return specialtyTexts[specialty] || specialty; | |
| } | |
| getLanguageText(language) { | |
| const languageTexts = { | |
| 'en': 'إنجليزي', | |
| 'ar': 'عربي', | |
| 'fr': 'فرنسي' | |
| }; | |
| return languageTexts[language] || language; | |
| } | |
| formatNumber(num) { | |
| if (num >= 1000000) { | |
| return (num / 1000000).toFixed(1) + 'م'; | |
| } else if (num >= 1000) { | |
| return (num / 1000).toFixed(1) + 'ك'; | |
| } | |
| return num.toString(); | |
| } | |
| showDatasetDetails(datasetKey) { | |
| const dataset = this.datasets.find(d => d.key === datasetKey); | |
| if (!dataset) return; | |
| document.getElementById('dataset-details-title').innerHTML = | |
| `<i class="fas fa-info-circle me-2"></i>${dataset.name}`; | |
| const detailsContent = ` | |
| <div class="row"> | |
| <div class="col-md-6"> | |
| <h6>معلومات أساسية</h6> | |
| <table class="table table-sm"> | |
| <tr><td><strong>المعرف:</strong></td><td>${dataset.repo_id}</td></tr> | |
| <tr><td><strong>الحجم:</strong></td><td>${dataset.size_gb} جيجابايت</td></tr> | |
| <tr><td><strong>عدد العينات:</strong></td><td>${this.formatNumber(dataset.num_samples)}</td></tr> | |
| <tr><td><strong>دعم التدفق:</strong></td><td>${dataset.streaming_supported ? 'نعم' : 'لا'}</td></tr> | |
| </table> | |
| </div> | |
| <div class="col-md-6"> | |
| <h6>التفاصيل التقنية</h6> | |
| <table class="table table-sm"> | |
| <tr><td><strong>تنسيق البيانات:</strong></td><td>${dataset.data_format}</td></tr> | |
| <tr><td><strong>الوسائط:</strong></td><td>${dataset.modalities.join(', ')}</td></tr> | |
| <tr><td><strong>التخصصات:</strong></td><td>${dataset.medical_specialties.join(', ')}</td></tr> | |
| <tr><td><strong>اللغات:</strong></td><td>${dataset.languages.join(', ')}</td></tr> | |
| </table> | |
| </div> | |
| </div> | |
| <div class="mt-3"> | |
| <h6>الوصف</h6> | |
| <p class="text-muted">${dataset.description}</p> | |
| </div> | |
| <div class="mt-3"> | |
| <h6>متطلبات النظام</h6> | |
| <div class="alert alert-info"> | |
| <i class="fas fa-info-circle me-2"></i> | |
| يتطلب هذا المجموعة ذاكرة تقديرية ${Math.ceil(dataset.size_gb * 1.5)} جيجابايت للمعالجة | |
| </div> | |
| </div> | |
| `; | |
| document.getElementById('dataset-details-content').innerHTML = detailsContent; | |
| // Set up load button | |
| const loadBtn = document.getElementById('load-dataset-btn'); | |
| loadBtn.onclick = () => this.loadDataset(datasetKey); | |
| const modal = new bootstrap.Modal(document.getElementById('datasetDetailsModal')); | |
| modal.show(); | |
| } | |
| async loadDataset(datasetKey) { | |
| const dataset = this.datasets.find(d => d.key === datasetKey); | |
| if (!dataset) return; | |
| // Close details modal if open | |
| const detailsModal = bootstrap.Modal.getInstance(document.getElementById('datasetDetailsModal')); | |
| if (detailsModal) { | |
| detailsModal.hide(); | |
| } | |
| // Show loading modal | |
| document.getElementById('loading-dataset-name').textContent = dataset.name; | |
| document.getElementById('loading-status').textContent = 'جاري تحضير التحميل...'; | |
| const loadingModal = new bootstrap.Modal(document.getElementById('loadingModal')); | |
| loadingModal.show(); | |
| try { | |
| const formData = new FormData(); | |
| formData.append('dataset_name', datasetKey); | |
| formData.append('streaming', 'true'); | |
| formData.append('split', 'train'); | |
| document.getElementById('loading-status').textContent = 'جاري تحميل البيانات...'; | |
| const response = await fetch('/api/medical-datasets/load', { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| const data = await response.json(); | |
| if (response.ok) { | |
| this.loadedDatasets.add(datasetKey); | |
| this.renderDatasets(); | |
| this.updateSystemInfo(); | |
| loadingModal.hide(); | |
| this.showSuccess(`تم تحميل ${dataset.name} بنجاح`); | |
| } else { | |
| loadingModal.hide(); | |
| this.showError(data.detail || 'فشل في تحميل قاعدة البيانات'); | |
| } | |
| } catch (error) { | |
| console.error('Error loading dataset:', error); | |
| loadingModal.hide(); | |
| this.showError('خطأ في الاتصال بالخادم'); | |
| } | |
| } | |
| async refreshDatasets() { | |
| await this.loadDatasets(); | |
| await this.loadSystemInfo(); | |
| this.showSuccess('تم تحديث البيانات'); | |
| } | |
| showSuccess(message) { | |
| document.getElementById('success-message').textContent = message; | |
| const toast = new bootstrap.Toast(document.getElementById('success-toast')); | |
| toast.show(); | |
| } | |
| showError(message) { | |
| document.getElementById('error-message').textContent = message; | |
| const toast = new bootstrap.Toast(document.getElementById('error-toast')); | |
| toast.show(); | |
| } | |
| setupDatabaseManagement() { | |
| // Search datasets | |
| const searchButton = document.getElementById('search-datasets'); | |
| if (searchButton) { | |
| searchButton.addEventListener('click', () => { | |
| this.searchDatabases(); | |
| }); | |
| } | |
| // Search on Enter key | |
| const searchQuery = document.getElementById('search-query'); | |
| if (searchQuery) { | |
| searchQuery.addEventListener('keypress', (e) => { | |
| if (e.key === 'Enter') { | |
| this.searchDatabases(); | |
| } | |
| }); | |
| } | |
| // Add dataset form | |
| const addForm = document.getElementById('add-dataset-form'); | |
| if (addForm) { | |
| addForm.addEventListener('submit', (e) => { | |
| e.preventDefault(); | |
| this.addDatabase(); | |
| }); | |
| } | |
| // Validate dataset | |
| const validateButton = document.getElementById('validate-dataset'); | |
| if (validateButton) { | |
| validateButton.addEventListener('click', () => { | |
| this.validateDataset(); | |
| }); | |
| } | |
| // Refresh databases | |
| const refreshButton = document.getElementById('refresh-databases'); | |
| if (refreshButton) { | |
| refreshButton.addEventListener('click', () => { | |
| this.loadConfiguredDatabases(); | |
| }); | |
| } | |
| // Load configured databases on startup | |
| this.loadConfiguredDatabases(); | |
| } | |
| async searchDatabases() { | |
| const queryElement = document.getElementById('search-query'); | |
| const categoryElement = document.getElementById('search-category'); | |
| if (!queryElement) return; | |
| const query = queryElement.value.trim(); | |
| const category = categoryElement ? categoryElement.value : ''; | |
| if (!query) { | |
| this.showError('يرجى إدخال كلمة البحث'); | |
| return; | |
| } | |
| const searchButton = document.getElementById('search-datasets'); | |
| const originalText = searchButton.innerHTML; | |
| searchButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i> جاري البحث...'; | |
| searchButton.disabled = true; | |
| try { | |
| const response = await fetch('/api/databases/search', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ | |
| query: query, | |
| limit: 20, | |
| category: category || null | |
| }) | |
| }); | |
| const data = await response.json(); | |
| if (data.success) { | |
| this.displaySearchResults(data.results); | |
| } else { | |
| this.showError('فشل في البحث عن قواعد البيانات'); | |
| } | |
| } catch (error) { | |
| console.error('Error searching databases:', error); | |
| this.showError('خطأ في البحث عن قواعد البيانات'); | |
| } finally { | |
| searchButton.innerHTML = originalText; | |
| searchButton.disabled = false; | |
| } | |
| } | |
| displaySearchResults(results) { | |
| const resultsContainer = document.getElementById('search-results-list'); | |
| const searchResults = document.getElementById('search-results'); | |
| if (!resultsContainer || !searchResults) return; | |
| if (results.length === 0) { | |
| resultsContainer.innerHTML = '<p class="text-muted">لم يتم العثور على نتائج</p>'; | |
| } else { | |
| resultsContainer.innerHTML = results.map(result => ` | |
| <div class="card mb-2"> | |
| <div class="card-body"> | |
| <div class="d-flex justify-content-between align-items-start"> | |
| <div> | |
| <h6 class="card-title">${result.name}</h6> | |
| <p class="card-text text-muted small">${result.description || 'لا يوجد وصف'}</p> | |
| <div class="d-flex gap-2"> | |
| <span class="badge bg-primary">${result.author}</span> | |
| <span class="badge bg-secondary">${result.downloads || 0} تحميل</span> | |
| <span class="badge bg-success">${result.likes || 0} إعجاب</span> | |
| </div> | |
| </div> | |
| <button class="btn btn-sm btn-outline-primary" onclick="medicalDatasets.addDatabaseFromSearch('${result.id}', '${result.name}', '${result.description || ''}')"> | |
| <i class="fas fa-plus"></i> إضافة | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| `).join(''); | |
| } | |
| searchResults.style.display = 'block'; | |
| } | |
| async addDatabaseFromSearch(datasetId, name, description) { | |
| try { | |
| const databaseInfo = { | |
| name: name, | |
| dataset_id: datasetId, | |
| category: 'medical', | |
| description: description, | |
| language: 'English', | |
| modality: 'text' | |
| }; | |
| const success = await this.submitDatabase(databaseInfo); | |
| if (success) { | |
| this.showSuccess(`تم إضافة قاعدة البيانات: ${name}`); | |
| this.loadConfiguredDatabases(); | |
| } | |
| } catch (error) { | |
| console.error('Error adding database from search:', error); | |
| this.showError('فشل في إضافة قاعدة البيانات'); | |
| } | |
| } | |
| async loadConfiguredDatabases() { | |
| try { | |
| const response = await fetch('/api/databases'); | |
| const data = await response.json(); | |
| if (data.success) { | |
| this.displayConfiguredDatabases(data.databases, data.selected); | |
| } | |
| } catch (error) { | |
| console.error('Error loading configured databases:', error); | |
| } | |
| } | |
| displayConfiguredDatabases(databases, selected) { | |
| const container = document.getElementById('configured-databases'); | |
| if (!container) return; | |
| if (Object.keys(databases).length === 0) { | |
| container.innerHTML = '<p class="text-muted">لا توجد قواعد بيانات مُعدة</p>'; | |
| return; | |
| } | |
| container.innerHTML = Object.entries(databases).map(([id, db]) => ` | |
| <div class="card mb-2"> | |
| <div class="card-body"> | |
| <div class="d-flex justify-content-between align-items-start"> | |
| <div class="flex-grow-1"> | |
| <div class="form-check"> | |
| <input class="form-check-input" type="checkbox" | |
| id="db-${id}" ${selected.includes(id) ? 'checked' : ''} | |
| onchange="medicalDatasets.toggleDatabaseSelection('${id}', this.checked)"> | |
| <label class="form-check-label" for="db-${id}"> | |
| <h6 class="mb-1">${db.name}</h6> | |
| </label> | |
| </div> | |
| <p class="text-muted small mb-1">${db.description || 'لا يوجد وصف'}</p> | |
| <div class="d-flex gap-2"> | |
| <span class="badge bg-primary">${db.category}</span> | |
| <span class="badge bg-secondary">${db.language}</span> | |
| <span class="badge bg-info">${db.modality}</span> | |
| </div> | |
| </div> | |
| <div class="d-flex gap-1"> | |
| <button class="btn btn-sm btn-outline-danger" onclick="medicalDatasets.removeDatabase('${id}')"> | |
| <i class="fas fa-trash"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| `).join(''); | |
| } | |
| async toggleDatabaseSelection(databaseId, selected) { | |
| try { | |
| if (selected) { | |
| const response = await fetch('/api/databases/select', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ | |
| database_ids: [databaseId] | |
| }) | |
| }); | |
| if (response.ok) { | |
| this.showSuccess('تم تحديد قاعدة البيانات'); | |
| } | |
| } else { | |
| this.showInfo('تم إلغاء تحديد قاعدة البيانات'); | |
| } | |
| } catch (error) { | |
| console.error('Error toggling database selection:', error); | |
| this.showError('فشل في تحديث اختيار قاعدة البيانات'); | |
| } | |
| } | |
| async removeDatabase(databaseId) { | |
| if (!confirm('هل أنت متأكد من حذف قاعدة البيانات؟')) { | |
| return; | |
| } | |
| try { | |
| const response = await fetch(`/api/databases/${encodeURIComponent(databaseId)}`, { | |
| method: 'DELETE' | |
| }); | |
| const data = await response.json(); | |
| if (data.success) { | |
| this.showSuccess('تم حذف قاعدة البيانات'); | |
| this.loadConfiguredDatabases(); | |
| } else { | |
| this.showError('فشل في حذف قاعدة البيانات'); | |
| } | |
| } catch (error) { | |
| console.error('Error removing database:', error); | |
| this.showError('خطأ في حذف قاعدة البيانات'); | |
| } | |
| } | |
| async submitDatabase(databaseInfo) { | |
| try { | |
| const response = await fetch('/api/databases/add', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify(databaseInfo) | |
| }); | |
| const data = await response.json(); | |
| return data.success; | |
| } catch (error) { | |
| console.error('Error submitting database:', error); | |
| this.showError('فشل في إضافة قاعدة البيانات'); | |
| return false; | |
| } | |
| } | |
| } | |
| // Initialize medical datasets manager when page loads | |
| document.addEventListener('DOMContentLoaded', () => { | |
| window.medicalDatasets = new MedicalDatasetsManager(); | |
| }); | |