Commit ·
fa8b3c4
1
Parent(s): e383e71
chore: remove large db file from git (HF 10MB limit)
Browse files- quran.py +0 -350
- quran_master.db +0 -3
quran.py
DELETED
|
@@ -1,350 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""Quran.py
|
| 3 |
-
|
| 4 |
-
Automatically generated by Colab.
|
| 5 |
-
|
| 6 |
-
Original file is located at
|
| 7 |
-
https://colab.research.google.com/drive/1WwaR-xsFnY5iffCndJV5metzB0RS4_GP
|
| 8 |
-
"""
|
| 9 |
-
|
| 10 |
-
import sqlite3
|
| 11 |
-
import re
|
| 12 |
-
from rapidfuzz.distance import Levenshtein
|
| 13 |
-
|
| 14 |
-
# ==========================================
|
| 15 |
-
# طبقة التطبيع الموسعة
|
| 16 |
-
# ==========================================
|
| 17 |
-
|
| 18 |
-
# مجموعات الحروف المتشابهة بصرياً أو صوتياً — كلها ترجع لشكل واحد
|
| 19 |
-
_SIMILAR_GROUPS = [
|
| 20 |
-
('اأإآٱ', 'ا'),
|
| 21 |
-
('ةه', 'ه'),
|
| 22 |
-
('يىئ', 'ي'),
|
| 23 |
-
('وؤ', 'و'),
|
| 24 |
-
('ذد', 'د'), # متقاربة بصرياً للمبتدئين
|
| 25 |
-
('زرذ', 'ر'), # أحياناً يخلط المستخدم بينها
|
| 26 |
-
('طت', 'ت'),
|
| 27 |
-
('ضظ', 'ض'),
|
| 28 |
-
('سص', 'س'),
|
| 29 |
-
('ثت', 'ت'),
|
| 30 |
-
('خح', 'ح'),
|
| 31 |
-
('غع', 'ع'),
|
| 32 |
-
]
|
| 33 |
-
|
| 34 |
-
def _build_similarity_table():
|
| 35 |
-
table = {}
|
| 36 |
-
for group, canonical in _SIMILAR_GROUPS:
|
| 37 |
-
for ch in group:
|
| 38 |
-
table[ch] = canonical
|
| 39 |
-
return table
|
| 40 |
-
|
| 41 |
-
_SIM_TABLE = _build_similarity_table()
|
| 42 |
-
|
| 43 |
-
def normalize_arabic(text: str, *, deep: bool = False) -> str:
|
| 44 |
-
"""
|
| 45 |
-
تطبيع النص العربي.
|
| 46 |
-
deep=False → تطبيع خفيف (للتخزين في DB أو المقارنة الدقيقة)
|
| 47 |
-
deep=True → تطبيع عميق يساوي بين الحروف المتشابهة (للبحث الضبابي)
|
| 48 |
-
"""
|
| 49 |
-
if not text:
|
| 50 |
-
return ""
|
| 51 |
-
# إزالة التشكيل والعلامات القرآنية
|
| 52 |
-
text = re.sub(
|
| 53 |
-
r'[\u064B-\u065F\u0670\u0671\u0656'
|
| 54 |
-
r'\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED]',
|
| 55 |
-
'', text
|
| 56 |
-
)
|
| 57 |
-
# همزات
|
| 58 |
-
text = re.sub(r'[أإآٱ]', 'ا', text)
|
| 59 |
-
# تاء مربوطة
|
| 60 |
-
text = re.sub(r'ة', 'ه', text)
|
| 61 |
-
# ألف مقصورة
|
| 62 |
-
text = re.sub(r'ى', 'ي', text)
|
| 63 |
-
|
| 64 |
-
if deep:
|
| 65 |
-
text = ''.join(_SIM_TABLE.get(ch, ch) for ch in text)
|
| 66 |
-
|
| 67 |
-
return ' '.join(text.strip().split())
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
# ==========================================
|
| 71 |
-
# مطابقة ضبابية على مستوى الكلمات
|
| 72 |
-
# ==========================================
|
| 73 |
-
|
| 74 |
-
def _word_similarity(a: str, b: str) -> float:
|
| 75 |
-
"""نسبة التشابه بين كلمتين (0.0 → 1.0)."""
|
| 76 |
-
max_len = max(len(a), len(b), 1)
|
| 77 |
-
dist = Levenshtein.distance(a, b)
|
| 78 |
-
return 1.0 - dist / max_len
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
def _score_window(query_words: list[str], window_words: list[str],
|
| 82 |
-
query_deep: list[str], window_deep: list[str]) -> float:
|
| 83 |
-
"""
|
| 84 |
-
احسب نسبة تطابق نافذة كلمات مع استعلام المستخدم.
|
| 85 |
-
نستخدم نسختين: خفيفة (أولوية) وعميقة (احتياط).
|
| 86 |
-
"""
|
| 87 |
-
if len(window_words) != len(query_words):
|
| 88 |
-
return 0.0
|
| 89 |
-
|
| 90 |
-
total = 0.0
|
| 91 |
-
for qw, ww, qd, wd in zip(query_words, window_words, query_deep, window_deep):
|
| 92 |
-
# نأخذ أعلى نتيجة بين المقارنة الخفيفة والعميقة
|
| 93 |
-
s_light = _word_similarity(qw, ww)
|
| 94 |
-
s_deep = _word_similarity(qd, wd)
|
| 95 |
-
total += max(s_light, s_deep)
|
| 96 |
-
|
| 97 |
-
return total / len(query_words)
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
# ==========================================
|
| 101 |
-
# الدالة الرئيسية
|
| 102 |
-
# ==========================================
|
| 103 |
-
|
| 104 |
-
def search_bayan(query_text: str,
|
| 105 |
-
target_type: str = "تدقيق الايات",
|
| 106 |
-
fuzzy_threshold: float = 0.72) -> dict:
|
| 107 |
-
"""
|
| 108 |
-
البحث عن آية قرآنية مع دعم الأخطاء الإملائية.
|
| 109 |
-
|
| 110 |
-
المعاملات:
|
| 111 |
-
query_text : النص المُدخَل من المستخدم (قد يحتوي أخطاء)
|
| 112 |
-
target_type : لغة الإخراج (uthmani / english / french / ...)
|
| 113 |
-
fuzzy_threshold : الحد الأدنى لقبول التطابق (0→1)، افتراضياً 0.72
|
| 114 |
-
|
| 115 |
-
المُخرج:
|
| 116 |
-
dict يحتوي على:
|
| 117 |
-
matched_segment : النص المُصحَّح بالرسم العثماني أو الترجمة
|
| 118 |
-
full_verse : الآيات كاملة مع التوثيق
|
| 119 |
-
similarity_score : درجة التشابه
|
| 120 |
-
metadata : تفاصيل الآيات
|
| 121 |
-
أو:
|
| 122 |
-
error : رسالة الخطأ
|
| 123 |
-
"""
|
| 124 |
-
conn = sqlite3.connect('quran_master.db')
|
| 125 |
-
cursor = conn.cursor()
|
| 126 |
-
|
| 127 |
-
language_mapping = {
|
| 128 |
-
"تدقيق الايات": "uthmani",
|
| 129 |
-
"bengali": "bn", "bosnian": "bs", "english": "en", "french": "fr",
|
| 130 |
-
"german": "de", "indonesian": "id", "malay": "ms", "persian": "fa",
|
| 131 |
-
"portuguese": "pt", "russian": "ru", "spanish": "es",
|
| 132 |
-
"turkish": "tr", "uzbek": "uz"
|
| 133 |
-
}
|
| 134 |
-
|
| 135 |
-
clean_target = str(target_type).lower().strip()
|
| 136 |
-
lang_code = language_mapping.get(clean_target, "uthmani")
|
| 137 |
-
|
| 138 |
-
verse_column = "v.text_uthmani" if lang_code == "uthmani" else f"v.lang_{lang_code}"
|
| 139 |
-
sura_column = "s.ar" if lang_code == "uthmani" else f"s.lang_{lang_code}"
|
| 140 |
-
|
| 141 |
-
# ── تطبيع الاستعلام بنسختين ──
|
| 142 |
-
query_light = normalize_arabic(query_text, deep=False).split()
|
| 143 |
-
query_deep = normalize_arabic(query_text, deep=True).split()
|
| 144 |
-
|
| 145 |
-
if not query_light:
|
| 146 |
-
conn.close()
|
| 147 |
-
return {"error": "النص المُدخل فارغ"}
|
| 148 |
-
|
| 149 |
-
n = len(query_light)
|
| 150 |
-
|
| 151 |
-
# ==========================================
|
| 152 |
-
# البحث بالمرساة الديناميكية (من الأطول للأقصر)
|
| 153 |
-
# ==========================================
|
| 154 |
-
candidate_starts: list[tuple[int, int]] = []
|
| 155 |
-
|
| 156 |
-
for i in range(n, 0, -1):
|
| 157 |
-
anchor = ' '.join(query_light[:i])
|
| 158 |
-
# بحث LIKE عادي أولاً (سريع)
|
| 159 |
-
cursor.execute("""
|
| 160 |
-
SELECT v.sura_num, v.aya_num
|
| 161 |
-
FROM verses v
|
| 162 |
-
WHERE v.text_clean LIKE ?
|
| 163 |
-
ORDER BY v.sura_num, v.aya_num
|
| 164 |
-
""", ('%' + anchor + '%',))
|
| 165 |
-
candidate_starts = cursor.fetchall()
|
| 166 |
-
if candidate_starts:
|
| 167 |
-
break
|
| 168 |
-
|
| 169 |
-
# إذا لم تجد شيئاً بالمرساة الخفيفة → جرّب المرساة العميقة
|
| 170 |
-
# (يستخدم text_deep إذا كان موجوداً، وإلا يعود لـ text_clean)
|
| 171 |
-
if not candidate_starts:
|
| 172 |
-
# اكتشف أعمدة الجدول
|
| 173 |
-
cursor.execute("PRAGMA table_info(verses)")
|
| 174 |
-
cols = {row[1] for row in cursor.fetchall()}
|
| 175 |
-
deep_col = "v.text_deep" if "text_deep" in cols else "v.text_clean"
|
| 176 |
-
|
| 177 |
-
for i in range(n, 0, -1):
|
| 178 |
-
anchor_deep = ' '.join(query_deep[:i])
|
| 179 |
-
cursor.execute(f"""
|
| 180 |
-
SELECT v.sura_num, v.aya_num
|
| 181 |
-
FROM verses v
|
| 182 |
-
WHERE {deep_col} LIKE ?
|
| 183 |
-
ORDER BY v.sura_num, v.aya_num
|
| 184 |
-
""", ('%' + anchor_deep + '%',))
|
| 185 |
-
rows = cursor.fetchall()
|
| 186 |
-
if rows:
|
| 187 |
-
candidate_starts = rows
|
| 188 |
-
break
|
| 189 |
-
|
| 190 |
-
# الملاذ الأخير: ابحث بكل كلمة على حدة وخذ الآيات الأكثر تكراراً
|
| 191 |
-
if not candidate_starts:
|
| 192 |
-
counts: dict[tuple, int] = {}
|
| 193 |
-
for word in query_light:
|
| 194 |
-
if len(word) < 3:
|
| 195 |
-
continue
|
| 196 |
-
cursor.execute("""
|
| 197 |
-
SELECT v.sura_num, v.aya_num
|
| 198 |
-
FROM verses v
|
| 199 |
-
WHERE v.text_clean LIKE ?
|
| 200 |
-
""", ('%' + word + '%',))
|
| 201 |
-
for row in cursor.fetchall():
|
| 202 |
-
counts[row] = counts.get(row, 0) + 1
|
| 203 |
-
if counts:
|
| 204 |
-
candidate_starts = sorted(counts, key=counts.get, reverse=True)[:15]
|
| 205 |
-
|
| 206 |
-
if not candidate_starts:
|
| 207 |
-
conn.close()
|
| 208 |
-
return {
|
| 209 |
-
"matched_segment": "",
|
| 210 |
-
"full_verse": "لم يُعثر على تطابق — تحقق من النص المُدخل"
|
| 211 |
-
}
|
| 212 |
-
|
| 213 |
-
# ==========================================
|
| 214 |
-
# النافذة المنزلقة + التقييم الضبابي
|
| 215 |
-
# ==========================================
|
| 216 |
-
best_score = -1.0
|
| 217 |
-
best_match_idx = -1
|
| 218 |
-
best_rows = None
|
| 219 |
-
|
| 220 |
-
for start_sura, start_aya in candidate_starts:
|
| 221 |
-
cursor.execute(f"""
|
| 222 |
-
SELECT v.sura_num, v.aya_num, v.text_clean,
|
| 223 |
-
v.text_uthmani, {verse_column}, {sura_column}
|
| 224 |
-
FROM verses v
|
| 225 |
-
JOIN suras_translated s ON v.sura_num = s.sura_number
|
| 226 |
-
WHERE (v.sura_num = ? AND v.aya_num >= ?) OR (v.sura_num > ?)
|
| 227 |
-
ORDER BY v.sura_num, v.aya_num
|
| 228 |
-
LIMIT 12
|
| 229 |
-
""", (start_sura, start_aya, start_sura))
|
| 230 |
-
fetched = cursor.fetchall()
|
| 231 |
-
|
| 232 |
-
QURAN_MARKS = {
|
| 233 |
-
'ۖ', 'ۗ', 'ۘ', 'ۙ', 'ۚ', 'ۛ', 'ۜ', '',
|
| 234 |
-
'۞', '۩'
|
| 235 |
-
}
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
# بناء خريطة الكلمات
|
| 239 |
-
combined_light, combined_deep, word_map = [], [], []
|
| 240 |
-
for row in fetched:
|
| 241 |
-
s_num, a_num, t_clean, t_uthmani, t_target, s_name = row
|
| 242 |
-
clean_w = t_clean.split()
|
| 243 |
-
uthmani_w = [
|
| 244 |
-
token
|
| 245 |
-
for token in t_uthmani.split()
|
| 246 |
-
if token not in QURAN_MARKS
|
| 247 |
-
]
|
| 248 |
-
deep_w = normalize_arabic(t_clean, deep=True).split()
|
| 249 |
-
|
| 250 |
-
for j, cw in enumerate(clean_w):
|
| 251 |
-
combined_light.append(cw)
|
| 252 |
-
combined_deep.append(deep_w[j] if j < len(deep_w) else cw)
|
| 253 |
-
word_map.append({
|
| 254 |
-
"clean": cw,
|
| 255 |
-
"uthmani": uthmani_w[j] if j < len(uthmani_w) else cw,
|
| 256 |
-
"sura_num": s_num,
|
| 257 |
-
"aya_num": a_num,
|
| 258 |
-
"target_text": t_target,
|
| 259 |
-
"sura_name": s_name,
|
| 260 |
-
})
|
| 261 |
-
|
| 262 |
-
total_words = len(combined_light)
|
| 263 |
-
if total_words < n:
|
| 264 |
-
continue
|
| 265 |
-
|
| 266 |
-
# نافذة منزلقة — ابحث عن أعلى نتيجة
|
| 267 |
-
for j in range(total_words - n + 1):
|
| 268 |
-
score = _score_window(
|
| 269 |
-
query_light, combined_light[j:j+n],
|
| 270 |
-
query_deep, combined_deep[j:j+n]
|
| 271 |
-
)
|
| 272 |
-
if score > best_score:
|
| 273 |
-
best_score = score
|
| 274 |
-
best_match_idx = j
|
| 275 |
-
best_rows = word_map
|
| 276 |
-
|
| 277 |
-
# إذا وجدنا تطابقاً كاملاً، لا داعي للاستمرار
|
| 278 |
-
if best_score >= 0.999:
|
| 279 |
-
break
|
| 280 |
-
|
| 281 |
-
conn.close()
|
| 282 |
-
|
| 283 |
-
# if best_score < fuzzy_threshold or best_match_idx == -1:
|
| 284 |
-
# return {
|
| 285 |
-
# "error": (
|
| 286 |
-
# f"أقرب تطابق وجدناه بدرجة {best_score:.0%} وهي أقل من الحد المقبول "
|
| 287 |
-
# f"({fuzzy_threshold:.0%}). تحقق من النص المُدخل."
|
| 288 |
-
# )
|
| 289 |
-
# }
|
| 290 |
-
|
| 291 |
-
# ==========================================
|
| 292 |
-
# تشكيل المخرجات
|
| 293 |
-
# ==========================================
|
| 294 |
-
matched_words = best_rows[best_match_idx: best_match_idx + n]
|
| 295 |
-
|
| 296 |
-
# بناء matched_segment مع رقم كل آية
|
| 297 |
-
aya_words: dict[tuple, list] = {}
|
| 298 |
-
for w in matched_words:
|
| 299 |
-
key = (w["sura_num"], w["aya_num"])
|
| 300 |
-
if key not in aya_words:
|
| 301 |
-
aya_words[key] = []
|
| 302 |
-
aya_words[key].append(w["uthmani"] if lang_code == "uthmani" else w["target_text"])
|
| 303 |
-
|
| 304 |
-
if lang_code == "uthmani":
|
| 305 |
-
seg_parts = [
|
| 306 |
-
" ".join(words) + f" ({a_num})"
|
| 307 |
-
for (_, a_num), words in aya_words.items()
|
| 308 |
-
]
|
| 309 |
-
matched_segment = " ".join(seg_parts)
|
| 310 |
-
else:
|
| 311 |
-
# للترجمات: نص الآية كامل + رقمها (بدون تكرار)
|
| 312 |
-
seen_texts, seg_parts = set(), []
|
| 313 |
-
for (_, a_num), words in aya_words.items():
|
| 314 |
-
txt = words[0] # target_text مكرر لكل كلمة، نأخذ الأول
|
| 315 |
-
if txt not in seen_texts:
|
| 316 |
-
seen_texts.add(txt)
|
| 317 |
-
seg_parts.append(f"{txt} ({a_num})")
|
| 318 |
-
matched_segment = " ".join(seg_parts)
|
| 319 |
-
|
| 320 |
-
# الآيات المشمولة — مرتبة بالترتيب
|
| 321 |
-
involved: dict[tuple, dict] = {}
|
| 322 |
-
for w in matched_words:
|
| 323 |
-
key = (w["sura_num"], w["aya_num"])
|
| 324 |
-
if key not in involved:
|
| 325 |
-
involved[key] = {"sura_name": w["sura_name"], "target_text": w["target_text"]}
|
| 326 |
-
|
| 327 |
-
ayah_nums = [a_num for (_, a_num) in involved]
|
| 328 |
-
sura_name = next(iter(involved.values()))["sura_name"]
|
| 329 |
-
|
| 330 |
-
verse_body_parts = []
|
| 331 |
-
for (s_num, a_num), data in involved.items():
|
| 332 |
-
verse_body_parts.append(f"{data['target_text']} ({a_num})")
|
| 333 |
-
|
| 334 |
-
combined_body = " ".join(verse_body_parts)
|
| 335 |
-
|
| 336 |
-
if len(ayah_nums) == 1:
|
| 337 |
-
ref = f"{sura_name}: {ayah_nums[0]}"
|
| 338 |
-
else:
|
| 339 |
-
nums_str = "،".join(str(x) for x in ayah_nums)
|
| 340 |
-
ref = f"{sura_name}: {nums_str}"
|
| 341 |
-
|
| 342 |
-
full_verse_formatted = f"({combined_body}) [{ref}]"
|
| 343 |
-
matched_segment = f"({matched_segment}) [{ref}]"
|
| 344 |
-
|
| 345 |
-
is_exact = best_score >= 0.999
|
| 346 |
-
return {
|
| 347 |
-
"matched_segment": matched_segment,
|
| 348 |
-
"full_verse": full_verse_formatted,
|
| 349 |
-
|
| 350 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
quran_master.db
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:8249c308936ddc26e70e46580405f7751d2c96501d1185f617ae9b0436e987a9
|
| 3 |
-
size 23064576
|
|
|
|
|
|
|
|
|
|
|
|