OmniFile-Processor / docs /DEVELOPER_GUIDE.md
Dr. Abdulmalek
deploy: OmniFile AI Processor v4.3.0
900df0b

دليل المطور - OmniFile AI Processor v2.1

Developer Guide - OmniFile AI Processor v2.1

دليل شامل للمطورين لبناء وتوسيع النظام Comprehensive developer guide for building and extending the system


المحتويات / Table of Contents

  1. هيكل المشروع / Project Structure
  2. البنية المعمارية / Architecture
  3. إضافة ميزة جديدة / Adding a New Feature
  4. إضافة لغة جديدة / Adding a New Language
  5. إضافة محرك OCR جديد / Adding a New OCR Engine
  6. الاختبارات / Testing
  7. النشر / Deployment
  8. معايير الكود / Code Standards
  9. GitHub Actions CI/CD

1. هيكل المشروع / Project Structure

OmniFile_Processor/
|
|-- app.py                          # واجهة Streamlit الرئيسية / Main Streamlit UI
|-- main.py                         # نقطة الدخول / Entry point (CLI arguments)
|-- config.py                       # الإعدادات المركزية / Central config (OmniFileConfig dataclass)
|-- database.py                     # قاعدة بيانات SQLite / SQLite database (OmniFileDB)
|-- tasks.py                        # مهام Celery / Celery async tasks
|-- requirements.txt                # التبعيات / Dependencies
|-- Dockerfile                      # Docker للنشر / Docker for deployment
|-- LICENSE                         # الترخيص / License
|-- __init__.py                     # إصدار المشروع / Project version
|
|-- modules/                        # الوحدات الفرعية / Sub-modules
|   |-- __init__.py
|   |
|   |-- nlp/                        # معالجة اللغة الطبيعية / NLP
|   |   |-- __init__.py
|   |   |-- spell_corrector.py      # المصحح الإملائي / Spell corrector (EN/AR/DE)
|   |   |-- translator.py           # المترجم التقني / Technical translator
|   |   |-- summarizer.py           # ملخص النصوص / Text summarizer (BART)
|   |   |-- entity_extractor.py     # استخراج الكيانات / NER entity extraction
|   |   |-- text_classifier.py      # تصنيف النصوص / Text classification
|   |   |-- language_detector.py    # كشف اللغة / Language detection
|   |   |-- correction_dict.json    # قاموس التصحيحات المُتعلمة / Learned corrections
|   |
|   |-- vision/                     # الرؤية الحاسوبية / Computer Vision
|   |   |-- __init__.py
|   |   |-- ocr_engine.py           # محرك OCR المتكامل / Integrated OCR engine
|   |   |-- pdf_processor.py        # معالج PDF / PDF processor
|   |   |-- image_preprocessor.py   # معالجة الصور المسبقة / Image preprocessing
|   |   |-- text_reconstructor.py   # إعادة تجميع النصوص / Text reconstruction
|   |
|   |-- security/                   # الأمان والحماية / Security
|       |-- __init__.py
|       |-- sensitive_data_scanner.py # فحص البيانات الحساسة / Sensitive data scanner
|       |-- file_scanner.py         # فحص الملفات / File scanner
|       |-- file_organizer.py       # تنظيم الملفات / File organizer
|       |-- backup_manager.py       # النسخ الاحتياطي / Backup manager
|       |-- archive_handler.py      # معالجة الأرشيفات / Archive handler
|       |-- code_protector.py       # حماية الكود / Code protection
|
|-- src/                            # محرك HandwrittenOCR المتقدم / Advanced HandwrittenOCR
|   |-- __init__.py
|   |-- main.py                     # نقطة دخول المحرك / Engine entry point
|   |-- gradio_ui.py                # واجهة Gradio المتقدمة / Advanced Gradio UI
|   |-- recognition.py              # التعرف المتقدم / Advanced recognition
|   |-- preprocessing.py            # المعالجة المسبقة المتقدمة / Advanced preprocessing
|   |-- reconstruction.py           # إعادة البناء المتقدمة / Advanced reconstruction
|   |-- correction.py               # التصحيح المتقدم / Advanced correction
|   |-- export.py                   # التصدير / Export
|   |-- pdf_processor.py            # معالج PDF متقدم / Advanced PDF processor
|   |-- review_ui.py                # واجهة المراجعة / Review UI
|   |-- study_guide.py              # دليل الدراسة / Study guide
|   |-- finetuning.py               # التدريب الدقيق / Fine-tuning
|   |-- metrics.py                  # مقاييس الأداء / Performance metrics
|   |-- database.py                 # قاعدة بيانات المحرك / Engine database
|   |-- migration.py                # ترحيل البيانات / Data migration
|   |-- sync.py                     # المزامنة / Synchronization
|   |-- logger.py                   # نظام التسجيل / Logging system
|
|-- tests/                          # الاختبارات / Tests
|   |-- __init__.py
|   |-- conftest.py                 # Fixtures المشتركة / Shared fixtures
|   |-- test_spell_corrector.py     # اختبارات المصحح / Spell corrector tests
|   |-- test_summarizer.py          # اختبارات التلخيص / Summarizer tests
|   |-- test_sensitive_scanner.py   # اختبارات فحص البيانات / Sensitive data tests
|
|-- notebooks/                      | # دفاتر Jupyter / Jupyter notebooks
|   |-- OmniFile_Complete.ipynb     # دفتر شامل / Complete notebook
|   |-- HandwrittenOCR_Ultimate.ipynb
|   |-- HandwrittenOCR_Colab.ipynb
|
|-- data_seed/                      | # بيانات أولية / Seed data
|   |-- correction_dict_seed.json   # بذرة قاموس التصحيح / Corrections seed
|
|-- artifacts/                      | # ملفات مُنتجة / Generated artifacts
|   |-- correction_dict.json
|
|-- database/                       | # ملفات قاعدة البيانات / Database files
|-- data/                           | # بيانات المستخدم / User data
|   |-- raw/                        | # ملفات خام / Raw files
|   |   |-- pdfs/
|   |   |-- images/
|   |   |-- archives/
|   |-- processed/                  | # ملفات معالجة / Processed files
|   |-- exports/                    | # ملفات مُصدّرة / Exported files
|-- models_cache/                   | # تخزين النماذج / Model cache
|-- backups/                        | # نسخ احتياطية / Backups
|-- logs/                           | # سجلات / Logs
|-- docs/                           | # التوثيق / Documentation

2. البنية المعمارية / Architecture

2.1 نظرة عامة / Overview

يعتمد النظام على بنية وحدات (Modular) مع تحميل بطيء (Lazy Loading) وانحطاط سلس (Graceful Degradation):

┌─────────────────────────────────────────────────────────────┐
│                    واجهة المستخدم / UI                       │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────┐   │
│  │  Streamlit   │  │   Gradio     │  │      CLI         │   │
│  │  (app.py)    │  │ (gradio_ui)  │  │  (main.py)       │   │
│  └──────┬───────┘  └──────┬───────┘  └────────┬─────────┘   │
└─────────┼─────────────────┼───────────────────┼─────────────┘
          │                 │                   │
          v                 v                   v
┌─────────────────────────────────────────────────────────────┐
│                    الإعدادات / Config                        │
│              OmniFileConfig (config.py)                      │
│     إعدادات OCR | NLP | الأمان | النشر | اللغات             │
└──────────────────────────┬──────────────────────────────────┘
                           │
          ┌────────────────┼────────────────┐
          v                v                v
┌─────────────┐   ┌──────────────┐  ┌──────────────┐
│ modules/    │   │ modules/     │  │ modules/     │
│  vision/    │   │  nlp/        │  │  security/   │
│             │   │              │  │              │
│ OCR Engine  │   │ SpellCheck   │  │ SensitiveScan│
│ PDF Process │   │ Translator   │  │ FileOrganize │
│ ImgProcess  │   │ Summarizer   │  │ BackupMgr    │
│ TextRecon   │   │ NER          │  │ CodeProtect  │
│             │   │ Classify     │  │ FileScan     │
│             │   │ LangDetect   │  │ ArchiveHand  │
└──────┬──────┘   └──────┬───────┘  └──────┬───────┘
       │                 │                 │
       v                 v                 v
┌─────────────────────────────────────────────────────────────┐
│                    قاعدة البيانات / Database                  │
│                 OmniFileDB (database.py)                      │
│     documents | ocr_results | translations | entities        │
│     corrections_log | processing_history                     │
└─────────────────────────────────────────────────────────────┘

2.2 الوحدات الأساسية / Core Modules

config.py - الإعدادات المركزية / Central Configuration

فئة OmniFileConfig هي Dataclass مركزية تحتوي جميع إعدادات النظام: The OmniFileConfig class is a central dataclass containing all system settings:

from config import OmniFileConfig

# إنشاء إعدادات لبيئة مختلفة / Create config for different environments
cfg_local = OmniFileConfig.from_local(project_root="~/OmniFile_AI")
cfg_colab = OmniFileConfig.from_colab_drive()
cfg_custom = OmniFileConfig(
    use_gpu=True,
    enable_trocr=True,
    trocr_model_variant="small",
)

# حفظ وتحميل الإعدادات / Save and load config
cfg.save("config/settings.json")
cfg_loaded = OmniFileConfig.load("config/settings.json")

# إعداد البيئة / Setup environment
cfg.setup_environment()  # ينشئ المجلدات ويضبط المتغيرات

# خصائص مفيدة / Useful properties
cfg.db_path           # مسار قاعدة البيانات
cfg.data_raw_dir      # مجلد الملفات الخام
cfg.models_cache_dir  # مجلد تخزين النماذج
cfg.is_colab          # كشف بيئة Colab

database.py - قاعدة البيانات / Database

فئة OmniFileDB تدير قاعدة بيانات SQLite بوضع WAL: The OmniFileDB class manages an SQLite database in WAL mode:

from database import OmniFileDB

db = OmniFileDB("omnifile_data.db")
db.create_tables()

# إدراج مستند / Insert document
doc_id = db.insert_document({
    "file_name": "test.pdf",
    "file_type": "pdf",
    "raw_text": "Hello World",
    "language": "en",
})

# تحديث مستند / Update document
db.update_document(doc_id, {"corrected_text": "Hello World!", "is_reviewed": True})

# جلب مستند / Get document
doc = db.get_document(doc_id)

# بحث / Search
results = db.search_documents("hello")

# إحصائيات / Statistics
stats = db.get_stats()

# تصدير / Export
db.export_to_json("backup.json")

الجداول / Tables:

الجدول / Table الوصف / Description
documents المستندات الأساسية (file_name, raw_text, corrected_text, category, ...)
ocr_results نتائج OCR التفصيلية (word_text, confidence, model_source, x, y, w, h)
translations الترجمات (source_text, translated_text, source_lang, target_lang)
entities الكيانات المسماة (entity_text, entity_type, confidence)
corrections_log سجل التصحيحات (original_text, corrected_text, auto_or_manual)
processing_history سجل المعالجة (action, target, status, duration_sec)

app.py - واجهة Streamlit / Streamlit UI

الواجهة الرئيسية مبنية بتبويبات Streamlit: The main interface is built with Streamlit tabs:

# app.py يستخدم OmniFileConfig و OmniFileDB
# app.py uses OmniFileConfig and OmniFileDB

# الهيكل العام / General structure:
# - شريط جانبي للإعدادات / Sidebar for settings
# - تبويبات للميزات / Tabs for features
# - رفع الملفات / File upload
# - عرض النتائج / Results display

2.3 الوحدات الفرعية / Sub-modules

modules/nlp/ - معالجة اللغة الطبيعية / NLP

الملف / File الفئة / Class الوصف / Description
spell_corrector.py SpellCorrector تصحيح إملائي متعدد اللغات (EN/AR/DE) مع حماية المصطلحات
translator.py TechnicalTranslator ترجمة تقنية مع حماية الكود وقاموس مدمج
summarizer.py TextSummarizer تلخيص بـ BART مع كشف لغة تلقائي وكاش
entity_extractor.py - استخراج الكيانات المسماة (NER) بـ AraBERT
text_classifier.py TextClassifier تصنيف المستندات بـ AraBERTv2
language_detector.py LanguageDetector كشف لغة النص تلقائياً

modules/vision/ - الرؤية الحاسوبية / Computer Vision

الملف / File الفئة / Class الوصف / Description
ocr_engine.py OCREngine محرك متكامل (TrOCR + EasyOCR + Tesseract) مع كاش و ONNX
pdf_processor.py PDFProcessor معالجة PDF (تحويل صفحات لصور، استخراج نص)
image_preprocessor.py ImagePreprocessor معالجة مسبقة (CLAHE, denoise, deskew, binarize)
text_reconstructor.py TextReconstructor إعادة تجميع النص من مربعات الكلمات

modules/security/ - الأمان والحماية / Security

الملف / File الفئة / Class الوصف / Description
sensitive_data_scanner.py SensitiveDataScanner فحص بيانات حساسة (Presidio + Regex) وإخفائها
file_scanner.py - فحص أمني للملفات (امتدادات، أنماط)
file_organizer.py FileOrganizer فرز تلقائي حسب النوع
backup_manager.py BackupManager نسخ احتياطي واستعادة
archive_handler.py - معالجة الأرشيفات (zip, tar.gz)
code_protector.py - حماية مقاطع الكود من المعالجة

src/ - محرك HandwrittenOCR المتقدم / Advanced HandwrittenOCR Engine

محرك متقدم للخط اليدوي مع واجهة Gradio: Advanced handwriting engine with Gradio interface:

الملف / File الوصف / Description
main.py نقطة دخول المحرك
gradio_ui.py واجهة Gradio المتقدمة
recognition.py التعرف المتقدم بالدفعات
preprocessing.py معالجة مسبقة متقدمة
reconstruction.py إعادة بناء النص
correction.py التصحيح المتقدم
finetuning.py التدريب الدقيق بـ LoRA
metrics.py حساب CER/WER
study_guide.py إنشاء أدلة دراسة

3. إضافة ميزة جديدة / Adding a New Feature

مثال عملي: إضافة ميزة Question Answering / Practical Example: Adding QA Feature

الخطوة 1: إنشاء ملف الوحدة / Step 1: Create the Module File

# modules/nlp/qa_engine.py
"""
محرك الأسئلة والأجوبة (Question Answering Engine) v1.0
==========================================================
إجابة على الأسئلة بناءً على سياق نصي.

المؤلف: Your Name
الإصدار: 1.0.0
"""

import logging
from typing import Optional

logger = logging.getLogger(__name__)


class QAEngine:
    """
    محرك الأسئلة والأجوبة — يجيب على أسئلة بناءً على سياق نصي.

    الميزات:
    - تحميل بطيء (Lazy Loading)
    - كشف GPU تلقائي
    - دعم اللغتين العربية والإنجليزية
    - انحطاط سلس عند الفشل
    """

    # النماذج المدعومة حسب اللغة
    MODELS_BY_LANG = {
        "en": "deepset/roberta-base-squad2",
        "ar": "deepset/roberta-base-squad2",  # يمكن تغييره لنموذج عربي
    }

    def __init__(
        self,
        model_name: Optional[str] = None,
        device: Optional[str] = None,
        max_answer_length: int = 50,
    ) -> None:
        """
        تهيئة محرك QA.

        المعاملات:
            model_name: اسم النموذج (إذا None، يُختار حسب اللغة)
            device: الجهاز ('cuda' أو 'cpu' أو None لتلقائي)
            max_answer_length: أقصى طول للإجابة
        """
        self.model_name = model_name
        self._device = device or self._detect_device()
        self.max_answer_length = max_answer_length

        # النموذج - تُحمّل بشكل بطيء
        self._pipeline = None
        self._loaded_model_name = None

        # فحص توفر المكتبات
        self._has_transformers = self._check_library("transformers")
        self._has_torch = self._check_library("torch")

    @staticmethod
    def _detect_device() -> str:
        """كشف أفضل جهاز متاح."""
        try:
            import torch
            if torch.cuda.is_available():
                return "cuda"
        except (ImportError, Exception):
            pass
        return "cpu"

    @staticmethod
    def _check_library(import_name: str) -> bool:
        """التحقق من توفر مكتبة."""
        try:
            __import__(import_name)
            return True
        except ImportError:
            return False

    def _load_pipeline(self, model_name: str) -> bool:
        """تحميل نموذج QA (يتم مرة واحدة)."""
        if self._loaded_model_name == model_name and self._pipeline is not None:
            return True

        if not (self._has_transformers and self._has_torch):
            logger.warning(
                "مكتبات transformers/torch غير مثبتة. "
                "pip install transformers torch"
            )
            return False

        try:
            from transformers import pipeline

            logger.info("جارٍ تحميل نموذج QA: %s على %s...", model_name, self._device)
            self._pipeline = pipeline(
                "question-answering",
                model=model_name,
                device=self._device,
            )
            self._loaded_model_name = model_name
            logger.info("تم تحميل نموذج QA بنجاح")
            return True

        except Exception as e:
            logger.error("فشل تحميل نموذج QA '%s': %s", model_name, e)
            return False

    def answer(
        self,
        question: str,
        context: str,
        language: Optional[str] = None,
    ) -> dict:
        """
        الإجابة على سؤال بناءً على سياق.

        المعاملات:
            question: السؤال
            context: النص المرجعي
            language: لغة النص (إذا None، يُكشف تلقائياً)

        العائد:
            قاموس يحتوي:
                - answer: الإجابة
                - score: مستوى الثقة
                - start: بداية الإجابة في السياق
                - end: نهاية الإجابة في السياق
                - model: النموذج المستخدم
        """
        if not question or not context:
            return {
                "answer": "",
                "score": 0.0,
                "start": 0,
                "end": 0,
                "model": "none",
                "error": "question_or_context_empty",
            }

        # اختيار النموذج
        lang = language or "en"
        model_name = self.model_name or self.MODELS_BY_LANG.get(lang, self.MODELS_BY_LANG["en"])

        # تحميل النموذج
        if not self._load_pipeline(model_name):
            return {
                "answer": "النموذج غير متاح",
                "score": 0.0,
                "start": 0,
                "end": 0,
                "model": "none",
                "error": "model_not_loaded",
            }

        # الإجابة
        try:
            result = self._pipeline(
                question=question,
                context=context,
                max_answer_length=self.max_answer_length,
            )
            return {
                "answer": result.get("answer", ""),
                "score": result.get("score", 0.0),
                "start": result.get("start", 0),
                "end": result.get("end", 0),
                "model": self._loaded_model_name,
            }
        except Exception as e:
            logger.error("فشل الإجابة: %s", e)
            return {
                "answer": "",
                "score": 0.0,
                "start": 0,
                "end": 0,
                "model": self._loaded_model_name,
                "error": str(e),
            }

    def is_available(self) -> bool:
        """هل المحرك متاح؟"""
        return self._has_transformers and self._has_torch

الخطوة 2: التسجيل في __init__.py / Step 2: Register in __init__.py

# modules/nlp/__init__.py
from .qa_engine import QAEngine

__all__ = [
    "QAEngine",
    # ... existing exports
]

الخطوة 3: إضافة التبويب في app.py / Step 3: Add Tab in app.py

# app.py
import streamlit as st
from modules.nlp.qa_engine import QAEngine


def render_qa_tab(cfg, db):
    """تبويب الأسئلة والأجوبة."""
    st.header("❓ الأسئلة والأجوبة / Question Answering")

    # Initialize engine
    if "qa_engine" not in st.session_state:
        st.session_state.qa_engine = QAEngine(
            device="cuda" if cfg.use_gpu else "cpu"
        )

    qa = st.session_state.qa_engine

    # Input
    question = st.text_input("السؤال / Question", placeholder="e.g., What is machine learning?")
    context = st.text_area(
        "السياق / Context",
        placeholder="Paste the reference text here...",
        height=200,
    )

    if st.button("إجابة / Answer") and question and context:
        with st.spinner("جارٍ البحث عن الإجابة..."):
            result = qa.answer(question, context)

        if result.get("answer"):
            st.success(f"**الإجابة / Answer:** {result['answer']}")
            st.info(f"الثقة / Confidence: {result['score']:.1%}")
        else:
            st.error(f"لم يتم العثور على إجابة: {result.get('error', '')}")

الخطوة 4: تحديث requirements.txt / Step 4: Update Requirements

# NLP - Question Answering
datasets>=2.15.0
# (transformers و torch موجودان بالفعل)

الخطوة 5: كتابة الاختبارات / Step 5: Write Tests

# tests/test_qa_engine.py
"""
اختبارات محرك الأسئلة والأجوبة / QA Engine Tests
"""
import pytest
from modules.nlp.qa_engine import QAEngine


class TestQAEngine:
    """اختبارات فئة QAEngine."""

    def test_init(self):
        """اختبار التهيئة."""
        qa = QAEngine()
        assert qa._device in ("cpu", "cuda")
        assert qa._pipeline is None  # لا يُحمّل تلقائياً

    def test_init_with_model(self):
        """اختبار التهيئة بنموذج محدد."""
        qa = QAEngine(model_name="deepset/roberta-base-squad2")
        assert qa.model_name == "deepset/roberta-base-squad2"

    def test_answer_empty_question(self):
        """اختبار: سؤال فارغ."""
        qa = QAEngine()
        result = qa.answer("", "Some context")
        assert result["answer"] == ""
        assert "error" in result

    def test_answer_empty_context(self):
        """اختبار: سياق فارغ."""
        qa = QAEngine()
        result = qa.answer("What?", "")
        assert result["answer"] == ""
        assert "error" in result

    def test_answer_returns_expected_keys(self):
        """اختبار: مفاتيح النتيجة."""
        qa = QAEngine()
        result = qa.answer("Q", "C")
        expected_keys = {"answer", "score", "start", "end", "model"}
        assert expected_keys.issubset(result.keys())

    def test_is_available(self):
        """اختبار: توفر المحرك."""
        qa = QAEngine()
        # يجب أن يعيد True إذا كانت المكتبات مثبتة
        result = qa.is_available()
        assert isinstance(result, bool)

الخطوة 6: إضافة الإعداد في config.py / Step 6: Add Config Setting

# config.py - في OmniFileConfig dataclass
# QA Engine
enable_qa: bool = True
qa_model_name: str = "deepset/roberta-base-squad2"
qa_max_answer_length: int = 50

4. إضافة لغة جديدة / Adding a New Language

مثال: إضافة اللغة الفرنسية (FR) / Example: Adding French (FR)

الخطوة 1: تحديث config.py / Step 1: Update Config

# config.py
@dataclass
class OmniFileConfig:
    # ...
    supported_languages: list = field(
        default_factory=lambda: ["en", "ar", "de", "fr"]  # أضف "fr"
    )

الخطوة 2: إضافة مصحح في spell_corrector.py / Step 2: Add Corrector

# modules/nlp/spell_corrector.py

class SpellCorrector:
    def __init__(self, ...):
        # ...
        self.supported_languages = ["en", "ar", "de", "fr"]  # أضف "fr"

        # محاولة تحميل المصحح الفرنسي
        self._fr_corrector = None
        self._fr_available = False
        self._try_load_french_corrector()

    def _try_load_french_corrector(self) -> None:
        """محاولة تحميل مصحح الفرنسية (pyspellchecker)."""
        try:
            from spellchecker import SpellChecker
            self._fr_corrector = SpellChecker(language="fr")
            self._fr_available = True
            logger.info("تم تحميل مصحح الفرنسية (pyspellchecker) بنجاح")
        except ImportError:
            logger.warning("مكتبة pyspellchecker غير مثبتة. pip install pyspellchecker")
        except Exception as e:
            logger.warning("فشل تحميل مصحح الفرنسية: %s", e)

    @staticmethod
    def _is_french_word(word: str) -> bool:
        """هل الكلمة فرنسية؟"""
        french_chars = sum(1 for c in word if c in "àâäéèêëïîôùûüÿçœæÀÂÄÉÈÊËÏÎÔÙÛÜŸÇŒÆ")
        if french_chars > 0:
            return True
        latin_chars = sum(1 for c in word if c.isalpha() and c.isascii())
        arabic_chars = sum(1 for c in word if "\u0600" <= c <= "\u06FF")
        return latin_chars > len(word) * 0.5 and arabic_chars == 0

    def _correct_french_word(self, word: str) -> Optional[str]:
        """تصحيح كلمة فرنسية."""
        if word in self._protected_terms or word.lower() in self._protected_terms:
            return None
        learned = self._get_learned_correction(word)
        if learned:
            return learned
        if self._fr_available and self._fr_corrector:
            try:
                if len(word) <= 2:
                    return None
                if word.lower() in self._fr_corrector.word_frequency:
                    return None
                candidates = self._fr_corrector.correction(word)
                if candidates and candidates.lower() != word.lower():
                    if abs(len(candidates) - len(word)) <= 3:
                        return candidates
            except Exception as e:
                logger.debug("خطأ في تصحيح فرنسي '%s': %s", word, e)
        return None

    def correct_word(self, word: str) -> str:
        """تصحيح كلمة واحدة (محدّث لدعم الفرنسية)."""
        if self._should_skip_word(word):
            return word
        if self._is_arabic_word(word):
            correction = self._correct_arabic_word(word)
        elif self._is_french_word(word):      # <-- أضف هذا الشرط قبل الإنجليزية
            correction = self._correct_french_word(word)
        elif self._is_german_word(word):
            correction = self._correct_german_word(word)
        elif self._is_english_word(word):
            correction = self._correct_english_word(word)
        else:
            return word
        return correction if correction else word

    def is_available(self) -> dict:
        """فحص توفر المصححات (محدّث)."""
        return {
            "english": self._en_available,
            "arabic": self._ar_available,
            "german": self._de_available,
            "french": self._fr_available,  # <-- أضف هذا
            "learned": len(self._learned_corrections) > 0,
        }

الخطوة 3: إضافة نموذج ترجمة في translator.py / Step 3: Add Translation Model

# modules/nlp/translator.py

class TechnicalTranslator:
    TRANSLATION_MODELS: dict[str, str] = {
        # ... existing models ...
        # أضف الاتجاهات الجديدة:
        "en-fr": "Helsinki-NLP/opus-mt-en-fr",
        "fr-en": "Helsinki-NLP/opus-mt-fr-en",
        "fr-ar": "Helsinki-NLP/opus-mt-fr-ar",
        "ar-fr": "Helsinki-NLP/opus-mt-ar-fr",
        "fr-de": "Helsinki-NLP/opus-mt-fr-de",
        "de-fr": "Helsinki-NLP/opus-mt-de-fr",
    }

    SUPPORTED_LANGUAGES = ["en", "ar", "de", "fr"]  # أضف "fr"

الخطوة 4: إضافة نموذج تلخيص (اختياري) / Step 4: Add Summarization Model (Optional)

# modules/nlp/summarizer.py

class TextSummarizer:
    MODELS_BY_LANG = {
        # ... existing models ...
        "fr": [
            "mrm8488/camembert2camembert_shared-french-summarization",
        ],
    }

    FALLBACK_MODELS = {
        # ... existing models ...
        "fr": "mrm8488/camembert2camembert_shared-french-summarization",
    }

الخطوة 5: تحديث كشف اللغة / Step 5: Update Language Detection

# modules/nlp/summarizer.py - _detect_language()
@staticmethod
def _detect_language(text: str) -> str:
    """كشف لغة النص."""
    arabic_chars = sum(1 for c in text if "\u0600" <= c <= "\u06FF")
    german_chars = sum(1 for c in text if c in "äöüÄÖÜß")
    french_chars = sum(1 for c in text if c in "àâäéèêëïîôùûüÿçœæÀÂÄÉÈÊËÏÎÔÙÛÜŸÇŒÆ")

    total_alpha = sum(1 for c in text if c.isalpha())
    if total_alpha == 0:
        return "en"

    if arabic_chars / total_alpha > 0.3:
        return "ar"
    if french_chars / total_alpha > 0.05:   # <-- أضف هذا قبل الألمانية
        return "fr"
    if german_chars / total_alpha > 0.05:
        return "de"
    return "en"

الخطوة 6: اختبارات اللغة الجديدة / Step 6: Tests for New Language

# tests/test_spell_corrector.py
class TestFrenchCorrection:
    """اختبارات التصحيح الفرنسي."""

    def test_correct_french_word(self):
        """اختبار تصحيح كلمة فرنسية."""
        corrector = SpellCorrector()
        if corrector.is_available().get("french"):
            result = corrector.correct_word("bonjor")  # خطأ إملائي
            assert result == "bonjour"

    def test_protect_french_code(self):
        """اختبار حماية الكود مع فرنسية."""
        corrector = SpellCorrector()
        result = corrector.correct_word("numpy")  # لا يُصحح
        assert result == "numpy"

5. إضافة محرك OCR جديد / Adding a New OCR Engine

واجهة المحرك / Engine Interface

كل محرك OCR جديد يجب أن يوفر الواجهة التالية: Every new OCR engine must provide the following interface:

from typing import Optional, Union
import numpy as np
from PIL import Image


class NewOCREngine:
    """
    واجهة محرك OCR جديد.
    يجب أن يوفر: recognize, recognize_batch, recognize_pdf, get_available_engines, unload_models
    """

    def __init__(self, **kwargs):
        """تهيئة المحرك."""
        self._model = None
        self._loaded = False

    def recognize(
        self,
        image: Union[np.ndarray, Image.Image],
        languages: Optional[list[str]] = None,
    ) -> dict:
        """
        التعرف على النص في صورة واحدة.

        المعاملات:
            image: صورة PIL أو numpy array
            languages: لغات مطلوبة

        العائد:
            dict: {
                "text": str,           # النص المستخرج
                "confidence": float,   # مستوى الثقة (0-1)
                "source": str,         # اسم المحرك
                "word_count": int,     # عدد الكلمات
                "words": list[dict],   # تفاصيل الكلمات مع الإحداثيات
                "processing_time": float,
                "details": dict,
            }
        """
        raise NotImplementedError

    def recognize_batch(
        self,
        images: list[Union[np.ndarray, Image.Image]],
        languages: Optional[list[str]] = None,
    ) -> list[dict]:
        """التعرف على مجموعة صور."""
        return [self.recognize(img, languages) for img in images]

    def recognize_pdf(
        self,
        pdf_path: str,
        pages: Optional[list[int]] = None,
        languages: Optional[list[str]] = None,
    ) -> list[dict]:
        """استخراج النص من PDF."""
        raise NotImplementedError

    def get_available_engines(self) -> list[dict]:
        """قائمة المحركات المتاحة."""
        return [{"name": "NewOCR", "available": True, "enabled": True}]

    def unload_models(self) -> None:
        """تفريغ النماذج من الذاكرة."""
        self._model = None
        self._loaded = False

التسجيل في OCREngine / Register in OCREngine

# modules/vision/ocr_engine.py

class OCREngine:
    def __init__(self, ...):
        # ... existing engines ...
        self._new_ocr_reader = None
        self._new_ocr_loaded = False

    def _recognize_new_ocr(self, image):
        """التعرف باستخدام المحرك الجديد."""
        # ... implementation ...
        pass

    def recognize(self, image, languages=None):
        # ... existing code ...
        # أضف المحرك الجديد في سلسلة المحركات:
        if self.enable_new_ocr:
            new_result = self._recognize_new_ocr(pil_image)
            if new_result:
                results.append(new_result)
        # ... rest of method ...

6. الاختبارات / Testing

تشغيل الاختبارات / Running Tests

# تشغيل جميع الاختبارات / Run all tests
pytest tests/ -v

# تشغيل اختبار محدد / Run specific test
pytest tests/test_spell_corrector.py -v

# تشغيل مع تغطية الكود / Run with coverage
pytest tests/ --cov=modules --cov-report=html

# تشغيل اختبارات وحدة محددة / Run specific test class
pytest tests/test_spell_corrector.py::TestSpellCorrector::test_correct_word -v

# عرض الإخراج المطبوع / Show print output
pytest tests/ -v -s

كتابة اختبار جديد / Writing a New Test

# tests/test_qa_engine.py
"""
اختبارات محرك QA / QA Engine Tests

النمط المتبع: Arrange - Act - Assert
"""
import pytest


class TestQAEngine:
    """اختبارات فئة QAEngine."""

    def setup_method(self):
        """إعداد قبل كل اختبار (Arrange)."""
        from modules.nlp.qa_engine import QAEngine
        self.qa = QAEngine()

    # === اختبارات الحالة السعيدة / Happy Path Tests ===

    def test_answer_returns_dict(self):
        """اختبار: answer() يعيد dict."""
        result = self.qa.answer("What?", "Context text")
        assert isinstance(result, dict)

    def test_answer_has_required_keys(self):
        """اختبار: النتيجة تحتوي المفاتيح المطلوبة."""
        result = self.qa.answer("Q", "C")
        for key in ["answer", "score", "start", "end", "model"]:
            assert key in result, f"Missing key: {key}"

    def test_answer_score_is_float(self):
        """اختبار: score هو رقم بين 0 و 1."""
        result = self.qa.answer("Q", "C")
        assert isinstance(result["score"], (int, float))
        assert 0 <= result["score"] <= 1

    # === اختبارات الحالات الخطأ / Error Case Tests ===

    def test_answer_empty_question(self):
        """اختبار: سؤال فارغ يعيد خطأ."""
        result = self.qa.answer("", "Context")
        assert result["answer"] == ""
        assert "error" in result

    def test_answer_empty_context(self):
        """اختبار: سياق فارغ يعيد خطأ."""
        result = self.qa.answer("Question", "")
        assert result["answer"] == ""
        assert "error" in result

    def test_answer_none_inputs(self):
        """اختبار: مدخلات None لا تسبب crash."""
        result = self.qa.answer(None, None)
        assert isinstance(result, dict)

    # === اختبارات التهيئة / Initialization Tests ===

    def test_lazy_loading(self):
        """اختبار: النموذج لا يُحمّل تلقائياً."""
        qa = QAEngine()
        assert qa._pipeline is None

    def test_custom_model_name(self):
        """اختبار: تمرير اسم نموذج مخصص."""
        qa = QAEngine(model_name="custom-model")
        assert qa.model_name == "custom-model"

Fixtures المشتركة / Shared Fixtures

# tests/conftest.py
"""
Fixtures المشتركة لجميع الاختبارات.
"""
import pytest
import numpy as np
from PIL import Image


@pytest.fixture
def sample_text_en():
    """نص إنجليزي بسيط."""
    return "This is a sample text for testing."


@pytest.fixture
def sample_text_ar():
    """نص عربي بسيط."""
    return "هذا نص عربي للاختبار."


@pytest.fixture
def sample_image():
    """صورة اختبار بسيطة."""
    return Image.new("RGB", (200, 100), color="white")


@pytest.fixture
def sample_pdf_path(tmp_path):
    """مسار PDF اختبار مؤقت."""
    pdf_path = tmp_path / "test.pdf"
    return str(pdf_path)


@pytest.fixture
def sample_config():
    """إعدادات اختبار."""
    from config import OmniFileConfig
    return OmniFileConfig(
        use_gpu=False,
        enable_trocr=False,
        enable_easyocr=False,
        enable_tesseract=False,
    )


@pytest.fixture
def sample_db(tmp_path):
    """قاعدة بيانات اختبار مؤقتة."""
    from database import OmniFileDB
    db_path = str(tmp_path / "test.db")
    db = OmniFileDB(db_path)
    db.create_tables()
    yield db
    # cleanup

7. النشر / Deployment

7.1 HuggingFace Spaces (Docker) / HuggingFace Spaces

المشروع يحتوي Dockerfile جاهز. للنشر: The project includes a ready Dockerfile. To deploy:

# Dockerfile (موجود بالفعل / already exists)
FROM python:3.11-slim

WORKDIR /app

# Install system dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
    poppler-utils \
    tesseract-ocr \
    tesseract-ocr-ara \
    tesseract-ocr-eng \
    && rm -rf /var/lib/apt/lists/*

# Copy requirements and install Python packages
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy all project files
COPY . .

# Expose Streamlit default port
EXPOSE 7860

# Health check
HEALTHCHECK CMD curl -f http://localhost:7860/_stcore/health || exit 1

# Run Streamlit
CMD ["streamlit", "run", "app.py", "--server.port=7860", "--server.address=0.0.0.0"]

7.2 Docker Compose (مع Redis) / Docker Compose (with Redis)

# docker-compose.yml
version: '3.8'

services:
  omnifile:
    build: .
    ports:
      - "7860:7860"
    environment:
      - ENABLE_CELERY=true
      - CELERY_BROKER_URL=redis://redis:6379/0
    volumes:
      - ./data:/app/data
      - ./models_cache:/app/models_cache
      - ./database:/app/database
    depends_on:
      - redis
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data

  celery-worker:
    build: .
    command: celery -A tasks worker --loglevel=info
    environment:
      - ENABLE_CELERY=true
      - CELERY_BROKER_URL=redis://redis:6379/0
    volumes:
      - ./data:/app/data
      - ./models_cache:/app/models_cache
    depends_on:
      - redis

volumes:
  redis_data:
# تشغيل / Run
docker-compose up -d

# مشاهدة السجلات / View logs
docker-compose logs -f omnifile

# إيقاف / Stop
docker-compose down

7.3 Google Colab / Google Colab

# main.py --colab
# أو:
from config import OmniFileConfig
cfg = OmniFileConfig.from_colab_drive()
cfg.setup_environment()

# ثم:
!streamlit run app.py --server.port 7860

7.4 AWS / Azure / GCP

# باستخدام Docker / Using Docker
# AWS ECS:
docker build -t omnifile .
aws ecs register-task-definition --family omnifile --container-definitions ...

# Azure Container Apps:
az containerapp create --image omnifile ...

# GCP Cloud Run:
gcloud run deploy omnifile --source . --port 7860

7.5 Celery Workers (المعالجة غير المتزامنة) / Celery Workers

# 1. تشغيل Redis
redis-server &

# 2. تشغيل Worker
celery -A tasks worker --loglevel=info --concurrency=4

# 3. تشغيل Worker مع مراقبة
celery -A tasks worker --loglevel=info --concurrency=2 \
  --max-tasks-per-child=1000 \
  --time-limit=300 \
  --soft-time-limit=240

# 4. مراقبة المهام
celery -A tasks inspect active
celery -A tasks inspect reserved
celery -A tasks events --dump

8. معايير الكود / Code Standards

8.1 التنسيق / Formatting

# 1. Python 3.8+ type hints
def process_text(text: str, language: str = "en") -> dict:
    ...

# 2. توثيق عربي + إنجليزي
class SpellCorrector:
    """
    مصحح إملائي ذكي — يدعم العربية والإنجليزية مع حماية المصطلحات البرمجية.
    Smart spell corrector — supports Arabic and English with code term protection.

    الميزات / Features:
        - تصحيح متعدد اللغات
        - حماية المصطلحات التقنية
        - تعلم من المستخدم
    """

    def correct_word(self, word: str) -> str:
        """
        تصحيح كلمة واحدة.
        Correct a single word.

        المعاملات:
            word: الكلمة المراد تصحيحها / The word to correct

        العائد:
            الكلمة المصححة / The corrected word
        """
        ...

8.2 التسجيل / Logging

# استخدم logging بدلاً من print / Use logging instead of print

import logging
logger = logging.getLogger(__name__)

# الصحيح / Correct:
logger.info("تم تحميل النموذج: %s", model_name)
logger.warning("فشل التحميل: %s", error)
logger.error("خطأ حرج: %s", error, exc_info=True)
logger.debug("قيمة متغيرة: %s", value)

# الخاطئ / Wrong:
print("تم التحميل")           # لا تستخدم print في الإنتاج
print(f"Error: {error}")      # لا تستخدم print

8.3 معالجة الأخطاء / Error Handling

# 1. Graceful Degradation - انحطاط سلس
try:
    from presidio_analyzer import AnalyzerEngine
    self._analyzer = AnalyzerEngine()
    self._presidio_available = True
except ImportError:
    logger.warning("presidio غير مثبت. سيتم استخدام Regex فقط.")
    self._presidio_available = False
except Exception as e:
    logger.warning("فشل تحميل presidio: %s", e)
    self._presidio_available = False

# 2. لا تستخدم except: فارغ / Never use bare except:
try:
    ...
except:  # خاطئ / Wrong
    pass

# 3. حدد نوع الاستثناء / Specify exception type
try:
    ...
except (ValueError, TypeError) as e:  # صحيح / Correct
    logger.error("خطأ: %s", e)

8.4 تحميل بطيء / Lazy Loading

# النماذج الثقيلة لا تُحمّل في __init__ / Heavy models don't load in __init__

class MyEngine:
    def __init__(self):
        self._model = None        # لا تحميل هنا / Don't load here
        self._loaded = False

    def _load_model(self) -> bool:
        """تحميل النموذج عند أول استخدام / Load model on first use."""
        if self._loaded:
            return True
        try:
            self._model = load_heavy_model()
            self._loaded = True
            return True
        except Exception as e:
            logger.error("فشل التحميل: %s", e)
            return False

    def process(self, data):
        if not self._load_model():  # تحميل عند الحاجة / Load when needed
            return self._fallback(data)
        return self._model.process(data)

8.5 إدارة الذاكرة / Memory Management

# 1. تنظيف الذاكرة بعد المعالجة
import gc
import torch

def process_batch(images):
    results = []
    for img in images:
        result = process(img)
        results.append(result)
    
    # تنظيف / Cleanup
    torch.cuda.empty_cache()
    gc.collect()
    return results

# 2. تفريغ النماذج عند الحاجة
def unload_models(self):
    """تفريغ النماذج من الذاكرة."""
    self._model = None
    self._loaded = False
    torch.cuda.empty_cache()
    gc.collect()

8.6 دعم اللغات / Language Support

# كل نص مرئي للمستخدم يجب أن يكون ثنائي اللغة
# All user-visible text must be bilingual

# أسماء المتغيرات والدوال: إنجليزية / Variable and function names: English
def correct_text(text: str) -> dict:

# الرسائل والتوثيق: عربي + إنجليزي / Messages and docs: Arabic + English
logger.info("تم تحميل المصحح بنجاح / Spell corrector loaded successfully")
st.success("تمت المعالجة بنجاح / Processing completed successfully")

# أخطاء: عربي + إنجليزي / Errors: Arabic + English
raise ValueError("حقل file_name مطلوب / file_name field is required")

9. GitHub Actions CI/CD

9.1 اختبارات تلقائية / Automated Tests

# .github/workflows/tests.yml
name: Tests

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ['3.9', '3.10', '3.11']

    steps:
      - uses: actions/checkout@v4

      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}

      - name: Install system dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y tesseract-ocr tesseract-ocr-ara

      - name: Install Python dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
          pip install pytest pytest-cov

      - name: Run tests
        run: |
          pytest tests/ -v --cov=modules --cov-report=xml

      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          file: ./coverage.xml

  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - run: pip install ruff
      - run: ruff check modules/ src/ tests/

9.2 بناء Docker / Docker Build

# .github/workflows/docker.yml
name: Docker Build

on:
  push:
    tags: ['v*']
  workflow_dispatch:

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to DockerHub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            ${{ secrets.DOCKER_USERNAME }}/omnifile-processor:latest
            ${{ secrets.DOCKER_USERNAME }}/omnifile-processor:${{ github.ref_name }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

9.3 نشر على HuggingFace Spaces / Deploy to HuggingFace Spaces

# .github/workflows/deploy.yml
name: Deploy to HuggingFace

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Push to HuggingFace
        env:
          HF_TOKEN: ${{ secrets.HF_TOKEN }}
        run: |
          git clone https://DrAbdulmalek:$HF_TOKEN@huggingface.co/spaces/DrAbdulmalek/OmniFile_Processor hf_space
          rsync -av --delete --exclude='.git' ./ hf_space/
          cd hf_space
          git add .
          git commit -m "Deploy from GitHub Actions"
          git push

المؤلف / Author: Dr Abdulmalek Tamer Al-husseini 📍 الموقع / Location: Homs, Syria 📧 البريد / Email: Abdulmalek.husseini@gmail.com الإصدار / Version: 2.1 الترخيص / License: راجع ملف LICENSE المساهمة / Contributing: Pull Requests مرحب بها على branch develop