Spaces:
Paused
Paused
| """ | |
| وحدة مقارنة الملفات | |
| """ | |
| import streamlit as st | |
| import pandas as pd | |
| import random | |
| from datetime import datetime | |
| import difflib | |
| class FileComparisonApp: | |
| """ | |
| وحدة مقارنة الملفات للنظام | |
| """ | |
| def __init__(self): | |
| """ | |
| تهيئة وحدة مقارنة الملفات | |
| """ | |
| # تهيئة حالة الجلسة الخاصة بمقارنة الملفات إذا لم تكن موجودة | |
| if 'comparison_history' not in st.session_state: | |
| # إنشاء بيانات تجريبية لسجل المقارنات | |
| st.session_state.comparison_history = self._generate_sample_history() | |
| def run(self): | |
| """ | |
| تشغيل وحدة مقارنة الملفات | |
| """ | |
| st.markdown("<h2 class='module-title'>وحدة مقارنة الملفات</h2>", unsafe_allow_html=True) | |
| # إنشاء تبويبات لمقارنة الملفات المختلفة | |
| tabs = st.tabs(["مقارنة المستندات", "مقارنة جداول البيانات", "مقارنة النصوص", "سجل المقارنات"]) | |
| with tabs[0]: | |
| self._render_document_comparison() | |
| with tabs[1]: | |
| self._render_spreadsheet_comparison() | |
| with tabs[2]: | |
| self._render_text_comparison() | |
| with tabs[3]: | |
| self._render_comparison_history() | |
| def _render_document_comparison(self): | |
| """ | |
| عرض واجهة مقارنة المستندات | |
| """ | |
| st.markdown("### مقارنة المستندات") | |
| st.markdown("مقارنة مستندات PDF أو Word مع إظهار الاختلافات بشكل مرئي") | |
| # رفع الملفات | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.markdown("#### المستند الأول") | |
| file1 = st.file_uploader("اختر المستند الأول", type=["pdf", "docx"], key="doc_file1") | |
| with col2: | |
| st.markdown("#### المستند الثاني") | |
| file2 = st.file_uploader("اختر المستند الثاني", type=["pdf", "docx"], key="doc_file2") | |
| # خيارات المقارنة | |
| st.markdown("#### خيارات المقارنة") | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| st.checkbox("تجاهل التنسيق", value=False, key="ignore_formatting") | |
| with col2: | |
| st.checkbox("تجاهل الهوامش", value=True, key="ignore_headers_footers") | |
| with col3: | |
| st.checkbox("إظهار التغييرات الطفيفة", value=False, key="show_minor_changes") | |
| # زر المقارنة | |
| if st.button("مقارنة المستندات", key="compare_docs_btn"): | |
| if file1 is not None and file2 is not None: | |
| # محاكاة عملية المقارنة | |
| with st.spinner("جاري مقارنة المستندات..."): | |
| # محاكاة وقت المعالجة | |
| import time | |
| time.sleep(2) | |
| st.success("تمت مقارنة المستندات بنجاح!", icon="✅") | |
| # عرض ملخص المقارنة | |
| st.markdown("#### ملخص المقارنة") | |
| comparison_summary = { | |
| "عدد الصفحات المتطابقة": random.randint(3, 8), | |
| "عدد الصفحات المختلفة": random.randint(1, 5), | |
| "إجمالي التغييرات": random.randint(10, 50), | |
| "إضافات": random.randint(5, 20), | |
| "حذف": random.randint(5, 20), | |
| "تعديلات": random.randint(5, 20) | |
| } | |
| summary_df = pd.DataFrame({ | |
| "المعيار": list(comparison_summary.keys()), | |
| "القيمة": list(comparison_summary.values()) | |
| }) | |
| st.dataframe(summary_df, use_container_width=True, hide_index=True) | |
| # عرض صورة توضيحية للمقارنة | |
| st.markdown("#### معاينة المقارنة") | |
| st.info("سيتم عرض معاينة المقارنة هنا مع تمييز الاختلافات بألوان مختلفة", icon="ℹ️") | |
| # زر تنزيل تقرير المقارنة | |
| st.download_button( | |
| label="تنزيل تقرير المقارنة", | |
| data=b"محتوى وهمي لتقرير المقارنة", | |
| file_name="document_comparison_report.pdf", | |
| mime="application/pdf", | |
| key="download_doc_comparison" | |
| ) | |
| # إضافة المقارنة إلى السجل | |
| new_entry = { | |
| 'id': len(st.session_state.comparison_history) + 1, | |
| 'type': 'مستند', | |
| 'file1': file1.name, | |
| 'file2': file2.name, | |
| 'changes': comparison_summary["إجمالي التغييرات"], | |
| 'date': datetime.now().strftime("%Y-%m-%d %H:%M") | |
| } | |
| st.session_state.comparison_history.insert(0, new_entry) | |
| else: | |
| st.warning("يرجى رفع المستندين للمقارنة", icon="⚠️") | |
| def _render_spreadsheet_comparison(self): | |
| """ | |
| عرض واجهة مقارنة جداول البيانات | |
| """ | |
| st.markdown("### مقارنة جداول البيانات") | |
| st.markdown("مقارنة ملفات Excel أو CSV مع تحديد الاختلافات في البيانات") | |
| # رفع الملفات | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.markdown("#### الجدول الأول") | |
| sheet1 = st.file_uploader("اختر الجدول الأول", type=["xlsx", "csv"], key="sheet_file1") | |
| with col2: | |
| st.markdown("#### الجدول الثاني") | |
| sheet2 = st.file_uploader("اختر الجدول الثاني", type=["xlsx", "csv"], key="sheet_file2") | |
| # خيارات المقارنة | |
| st.markdown("#### خيارات المقارنة") | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.checkbox("مقارنة الصيغ", value=True, key="compare_formulas") | |
| st.checkbox("مقارنة التنسيق", value=False, key="compare_formatting") | |
| with col2: | |
| st.checkbox("تجاهل الأوراق المخفية", value=True, key="ignore_hidden_sheets") | |
| st.checkbox("مقارنة حسب القيمة فقط", value=False, key="compare_by_value") | |
| # زر المقارنة | |
| if st.button("مقارنة الجداول", key="compare_sheets_btn"): | |
| if sheet1 is not None and sheet2 is not None: | |
| # محاكاة عملية المقارنة | |
| with st.spinner("جاري مقارنة جداول البيانات..."): | |
| # محاكاة وقت المعالجة | |
| import time | |
| time.sleep(2) | |
| st.success("تمت مقارنة جداول البيانات بنجاح!", icon="✅") | |
| # عرض ملخص المقارنة | |
| st.markdown("#### ملخص المقارنة") | |
| comparison_summary = { | |
| "عدد الخلايا المتطابقة": random.randint(500, 2000), | |
| "عدد الخلايا المختلفة": random.randint(50, 200), | |
| "صفوف مضافة": random.randint(5, 20), | |
| "صفوف محذوفة": random.randint(5, 20), | |
| "أعمدة مضافة": random.randint(1, 5), | |
| "أعمدة محذوفة": random.randint(1, 5) | |
| } | |
| summary_df = pd.DataFrame({ | |
| "المعيار": list(comparison_summary.keys()), | |
| "القيمة": list(comparison_summary.values()) | |
| }) | |
| st.dataframe(summary_df, use_container_width=True, hide_index=True) | |
| # عرض جدول الاختلافات | |
| st.markdown("#### الاختلافات الرئيسية") | |
| # إنشاء بيانات تجريبية للاختلافات | |
| diff_data = [] | |
| for i in range(10): | |
| diff_data.append({ | |
| "الورقة": f"الورقة {random.randint(1, 3)}", | |
| "الخلية": f"{chr(65 + random.randint(0, 5))}{random.randint(1, 20)}", | |
| "القيمة الأولى": f"القيمة {i} (الأولى)", | |
| "القيمة الثانية": f"القيمة {i} (الثانية)", | |
| "نوع الاختلاف": random.choice(["تعديل", "إضافة", "حذف"]) | |
| }) | |
| diff_df = pd.DataFrame(diff_data) | |
| st.dataframe(diff_df, use_container_width=True) | |
| # زر تنزيل تقرير المقارنة | |
| st.download_button( | |
| label="تنزيل تقرير المقارنة", | |
| data=b"محتوى وهمي لتقرير المقارنة", | |
| file_name="spreadsheet_comparison_report.xlsx", | |
| mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", | |
| key="download_sheet_comparison" | |
| ) | |
| # إضافة المقارنة إلى السجل | |
| new_entry = { | |
| 'id': len(st.session_state.comparison_history) + 1, | |
| 'type': 'جدول بيانات', | |
| 'file1': sheet1.name, | |
| 'file2': sheet2.name, | |
| 'changes': comparison_summary["عدد الخلايا المختلفة"], | |
| 'date': datetime.now().strftime("%Y-%m-%d %H:%M") | |
| } | |
| st.session_state.comparison_history.insert(0, new_entry) | |
| else: | |
| st.warning("يرجى رفع جدولي البيانات للمقارنة", icon="⚠️") | |
| def _render_text_comparison(self): | |
| """ | |
| عرض واجهة مقارنة النصوص | |
| """ | |
| st.markdown("### مقارنة النصوص") | |
| st.markdown("مقارنة نصوص مباشرة أو ملفات نصية مع إظهار الاختلافات") | |
| # اختيار طريقة الإدخال | |
| input_method = st.radio( | |
| "طريقة الإدخال", | |
| options=["إدخال مباشر", "رفع ملفات"], | |
| horizontal=True, | |
| key="text_input_method" | |
| ) | |
| if input_method == "إدخال مباشر": | |
| # إدخال النصوص مباشرة | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.markdown("#### النص الأول") | |
| text1 = st.text_area("أدخل النص الأول", height=200, key="direct_text1") | |
| with col2: | |
| st.markdown("#### النص الثاني") | |
| text2 = st.text_area("أدخل النص الثاني", height=200, key="direct_text2") | |
| # زر المقارنة | |
| if st.button("مقارنة النصوص", key="compare_direct_text_btn"): | |
| if text1 and text2: | |
| self._show_text_comparison_results(text1, text2) | |
| else: | |
| st.warning("يرجى إدخال النصين للمقارنة", icon="⚠️") | |
| else: | |
| # رفع ملفات نصية | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.markdown("#### الملف الأول") | |
| text_file1 = st.file_uploader("اختر الملف الأول", type=["txt", "md", "json", "xml", "html", "css", "js", "py"], key="text_file1") | |
| with col2: | |
| st.markdown("#### الملف الثاني") | |
| text_file2 = st.file_uploader("اختر الملف الثاني", type=["txt", "md", "json", "xml", "html", "css", "js", "py"], key="text_file2") | |
| # زر المقارنة | |
| if st.button("مقارنة الملفات النصية", key="compare_text_files_btn"): | |
| if text_file1 is not None and text_file2 is not None: | |
| # قراءة محتوى الملفات | |
| text1 = text_file1.getvalue().decode("utf-8") | |
| text2 = text_file2.getvalue().decode("utf-8") | |
| self._show_text_comparison_results(text1, text2, text_file1.name, text_file2.name) | |
| else: | |
| st.warning("يرجى رفع الملفين النصيين للمقارنة", icon="⚠️") | |
| def _show_text_comparison_results(self, text1, text2, file1_name=None, file2_name=None): | |
| """ | |
| عرض نتائج مقارنة النصوص | |
| """ | |
| # محاكاة عملية المقارنة | |
| with st.spinner("جاري مقارنة النصوص..."): | |
| # استخدام difflib للمقارنة | |
| d = difflib.Differ() | |
| diff = list(d.compare(text1.splitlines(), text2.splitlines())) | |
| # عرض ملخص المقارنة | |
| st.markdown("#### ملخص المقارنة") | |
| # حساب الإحصائيات | |
| added = len([line for line in diff if line.startswith('+ ')]) | |
| removed = len([line for line in diff if line.startswith('- ')]) | |
| changed = len([line for line in diff if line.startswith('? ')]) | |
| unchanged = len([line for line in diff if line.startswith(' ')]) | |
| comparison_summary = { | |
| "الأسطر المتطابقة": unchanged, | |
| "الأسطر المضافة": added, | |
| "الأسطر المحذوفة": removed, | |
| "الأسطر المتغيرة": changed, | |
| "إجمالي الاختلافات": added + removed + changed | |
| } | |
| summary_df = pd.DataFrame({ | |
| "المعيار": list(comparison_summary.keys()), | |
| "القيمة": list(comparison_summary.values()) | |
| }) | |
| st.dataframe(summary_df, use_container_width=True, hide_index=True) | |
| # عرض الاختلافات | |
| st.markdown("#### عرض الاختلافات") | |
| # تنسيق الاختلافات بألوان مختلفة | |
| html_diff = [] | |
| for line in diff: | |
| if line.startswith('+ '): | |
| html_diff.append(f'<div style="background-color: #d4edda; color: #155724; padding: 2px 5px; margin: 2px 0; border-radius: 3px;">{line}</div>') | |
| elif line.startswith('- '): | |
| html_diff.append(f'<div style="background-color: #f8d7da; color: #721c24; padding: 2px 5px; margin: 2px 0; border-radius: 3px;">{line}</div>') | |
| elif line.startswith('? '): | |
| html_diff.append(f'<div style="background-color: #fff3cd; color: #856404; padding: 2px 5px; margin: 2px 0; border-radius: 3px;">{line}</div>') | |
| else: | |
| html_diff.append(f'<div style="padding: 2px 5px; margin: 2px 0;">{line}</div>') | |
| st.markdown(''.join(html_diff), unsafe_allow_html=True) | |
| # زر تنزيل تقرير المقارنة | |
| st.download_button( | |
| label="تنزيل تقرير المقارنة", | |
| data='\n'.join(diff), | |
| file_name="text_comparison_report.txt", | |
| mime="text/plain", | |
| key="download_text_comparison" | |
| ) | |
| # إضافة المقارنة إلى السجل | |
| new_entry = { | |
| 'id': len(st.session_state.comparison_history) + 1, | |
| 'type': 'نص', | |
| 'file1': file1_name or "نص مباشر 1", | |
| 'file2': file2_name or "نص مباشر 2", | |
| 'changes': comparison_summary["إجمالي الاختلافات"], | |
| 'date': datetime.now().strftime("%Y-%m-%d %H:%M") | |
| } | |
| st.session_state.comparison_history.insert(0, new_entry) | |
| def _render_comparison_history(self): | |
| """ | |
| عرض سجل المقارنات | |
| """ | |
| st.markdown("### سجل المقارنات") | |
| st.markdown("عرض سجل المقارنات السابقة مع إمكانية البحث والتصفية") | |
| # خيارات التصفية | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| filter_type = st.multiselect( | |
| "نوع المقارنة", | |
| options=["الكل", "مستند", "جدول بيانات", "نص"], | |
| default=["الكل"] | |
| ) | |
| with col2: | |
| date_range = st.selectbox( | |
| "النطاق الزمني", | |
| options=["الكل", "اليوم", "الأسبوع الماضي", "الشهر الماضي"] | |
| ) | |
| # تطبيق التصفية | |
| filtered_history = st.session_state.comparison_history | |
| if "الكل" not in filter_type: | |
| filtered_history = [h for h in filtered_history if h['type'] in filter_type] | |
| # تحويل البيانات إلى DataFrame | |
| if filtered_history: | |
| df = pd.DataFrame(filtered_history) | |
| df = df[['date', 'type', 'file1', 'file2', 'changes']] | |
| df.columns = ['التاريخ', 'النوع', 'الملف الأول', 'الملف الثاني', 'عدد الاختلافات'] | |
| # عرض الجدول | |
| st.dataframe(df, use_container_width=True) | |
| else: | |
| st.info("لا توجد مقارنات تطابق معايير التصفية", icon="ℹ️") | |
| def _generate_sample_history(self): | |
| """ | |
| إنشاء بيانات تجريبية لسجل المقارنات | |
| """ | |
| comparison_types = ['مستند', 'جدول بيانات', 'نص'] | |
| file_names = { | |
| 'مستند': [ | |
| 'مواصفات_المشروع_v1.pdf', 'مواصفات_المشروع_v2.pdf', | |
| 'العقد_النهائي.docx', 'العقد_المعدل.docx', | |
| 'تقرير_المشروع_2025.pdf', 'تقرير_المشروع_2025_مراجعة.pdf' | |
| ], | |
| 'جدول بيانات': [ | |
| 'جدول_الكميات_v1.xlsx', 'جدول_الكميات_v2.xlsx', | |
| 'تحليل_الأسعار_2025.xlsx', 'تحليل_الأسعار_2025_محدث.xlsx', | |
| 'الميزانية_التقديرية.xlsx', 'الميزانية_النهائية.xlsx' | |
| ], | |
| 'نص': [ | |
| 'ملاحظات_الاجتماع.txt', 'ملاحظات_الاجتماع_محدثة.txt', | |
| 'شروط_المناقصة.txt', 'شروط_المناقصة_معدلة.txt', | |
| 'config.json', 'config_new.json' | |
| ] | |
| } | |
| history = [] | |
| for i in range(15): | |
| comp_type = random.choice(comparison_types) | |
| # اختيار ملفين من نفس النوع | |
| file_index = random.randint(0, 2) * 2 | |
| file1 = file_names[comp_type][file_index] | |
| file2 = file_names[comp_type][file_index + 1] | |
| # تحديد تاريخ عشوائي خلال الشهر الماضي | |
| days_ago = random.randint(0, 30) | |
| entry_date = (datetime.now() - pd.Timedelta(days=days_ago)).strftime("%Y-%m-%d %H:%M") | |
| entry = { | |
| 'id': i + 1, | |
| 'type': comp_type, | |
| 'file1': file1, | |
| 'file2': file2, | |
| 'changes': random.randint(10, 100), | |
| 'date': entry_date | |
| } | |
| history.append(entry) | |
| # ترتيب السجل حسب التاريخ (الأحدث أولاً) | |
| history.sort(key=lambda x: x['date'], reverse=True) | |
| return history | |