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(); | |
}); | |