hadith-detector 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hadith_detector/__init__.py +33 -0
- hadith_detector/_annotation.py +49 -0
- hadith_detector/_bundle.json +45 -0
- hadith_detector/_bundled.py +16 -0
- hadith_detector/_convenience.py +84 -0
- hadith_detector/_data/bundle.json +1 -0
- hadith_detector/_data/manifest.json +1 -0
- hadith_detector/_data/notices/LICENSES.md +17 -0
- hadith_detector/_data/notices/PROVENANCE.md +46 -0
- hadith_detector/_data/notices/README.md +866 -0
- hadith_detector/_data/notices/SOURCES.md +28 -0
- hadith_detector/_data/notices/UPSTREAM-UNLICENSE.txt +24 -0
- hadith_detector/_data/notices/collections.csv +14 -0
- hadith_detector/_data/notices/configs.csv +139 -0
- hadith_detector/_data/record-offsets.bin +0 -0
- hadith_detector/_data/records.jsonl +14940 -0
- hadith_detector/_data/tokens.bin +0 -0
- hadith_detector/_data/trigram-offsets.bin +0 -0
- hadith_detector/_data/trigram-postings.bin +0 -0
- hadith_detector/_data/trigrams.marisa +0 -0
- hadith_detector/_data/unigram-offsets.bin +0 -0
- hadith_detector/_data/unigram-postings.bin +0 -0
- hadith_detector/_data/vocabulary.marisa +0 -0
- hadith_detector/_fuzzy.py +272 -0
- hadith_detector/_marisa.py +102 -0
- hadith_detector/_resources.py +365 -0
- hadith_detector/_serialization.py +210 -0
- hadith_detector/_text.py +120 -0
- hadith_detector/config.py +31 -0
- hadith_detector/detector.py +296 -0
- hadith_detector/models.py +161 -0
- hadith_detector/py.typed +0 -0
- hadith_detector-0.1.0.dist-info/METADATA +170 -0
- hadith_detector-0.1.0.dist-info/RECORD +37 -0
- hadith_detector-0.1.0.dist-info/WHEEL +5 -0
- hadith_detector-0.1.0.dist-info/licenses/LICENSE +21 -0
- hadith_detector-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Offline Arabic Hadith detection with bundled or explicitly supplied resources."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import version
|
|
4
|
+
|
|
5
|
+
from ._convenience import annotate, clear_cache, detect, get_hadith
|
|
6
|
+
from .config import DetectorConfig
|
|
7
|
+
from .detector import HadithDetector
|
|
8
|
+
from .models import (
|
|
9
|
+
Detection,
|
|
10
|
+
HadithRecord,
|
|
11
|
+
HadithReference,
|
|
12
|
+
OmittedSourceToken,
|
|
13
|
+
SourceOccurrence,
|
|
14
|
+
WordAlignment,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__version__ = version("hadith-detector")
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"__version__",
|
|
21
|
+
"HadithDetector",
|
|
22
|
+
"DetectorConfig",
|
|
23
|
+
"detect",
|
|
24
|
+
"annotate",
|
|
25
|
+
"get_hadith",
|
|
26
|
+
"clear_cache",
|
|
27
|
+
"HadithRecord",
|
|
28
|
+
"SourceOccurrence",
|
|
29
|
+
"HadithReference",
|
|
30
|
+
"Detection",
|
|
31
|
+
"WordAlignment",
|
|
32
|
+
"OmittedSourceToken",
|
|
33
|
+
]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Lossless plain-text escaping and deterministic HTML fragments."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
from html import escape
|
|
5
|
+
|
|
6
|
+
from .models import Detection
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def validate(text: str, format: str) -> None:
|
|
10
|
+
"""Reject unsupported formats and characters HTML cannot preserve losslessly."""
|
|
11
|
+
if not isinstance(text, str):
|
|
12
|
+
raise TypeError("text must be a string")
|
|
13
|
+
if not isinstance(format, str):
|
|
14
|
+
raise TypeError("format must be a string")
|
|
15
|
+
if format != "html":
|
|
16
|
+
raise ValueError("Only format='html' is supported")
|
|
17
|
+
if any(char == "\x00" or 0xD800 <= ord(char) <= 0xDFFF for char in text):
|
|
18
|
+
raise ValueError("HTML annotation cannot preserve NUL or lone surrogate characters")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _escape(text: str) -> str:
|
|
22
|
+
# Character references keep CR intact through HTML's newline preprocessing.
|
|
23
|
+
return escape(text, quote=True).replace("\r", " ")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def render(text: str, detections: Sequence[Detection]) -> str:
|
|
27
|
+
"""Render detector-selected spans without repeating matching or overlap selection."""
|
|
28
|
+
parts = []
|
|
29
|
+
cursor = 0
|
|
30
|
+
for index, match in enumerate(detections):
|
|
31
|
+
for value in (match.start, match.end, match.errors, match.missing_words):
|
|
32
|
+
if isinstance(value, bool) or not isinstance(value, int) or value < 0:
|
|
33
|
+
raise ValueError("Annotation offsets and costs must be nonnegative integers")
|
|
34
|
+
if not cursor <= match.start < match.end <= len(text):
|
|
35
|
+
raise ValueError("Annotation spans must be in bounds, ordered and non-overlapping")
|
|
36
|
+
if text[match.start : match.end] != match.text:
|
|
37
|
+
raise ValueError("Annotation span differs from original input text")
|
|
38
|
+
parts.append(_escape(text[cursor : match.start]))
|
|
39
|
+
parts.append(
|
|
40
|
+
f'<mark class="hadith-match" data-match-index="{index}" '
|
|
41
|
+
f'data-start="{match.start}" data-end="{match.end}" '
|
|
42
|
+
f'data-errors="{match.errors}" data-missing-words="{match.missing_words}" '
|
|
43
|
+
f'data-reference-count="{len(match.references)}">'
|
|
44
|
+
)
|
|
45
|
+
parts.append(_escape(text[match.start : match.end]))
|
|
46
|
+
parts.append("</mark>")
|
|
47
|
+
cursor = match.end
|
|
48
|
+
parts.append(_escape(text[cursor:]))
|
|
49
|
+
return "".join(parts)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"artifacts": {
|
|
3
|
+
"record-offsets.bin": "5ff1228d1d7cc82df5baa6853b510dca8d0897e165217d4df9bf3a26f16f32d1",
|
|
4
|
+
"records.jsonl": "9ec8e19aaab80ffbc01aa162122060099bc0a5de3841b544b4afa1c381414246",
|
|
5
|
+
"tokens.bin": "3488e5e47e5a91d42c2f84ba14560cf673e134081aea52a6ab28f08fdda2d317",
|
|
6
|
+
"trigram-offsets.bin": "c7edf613d74f00c806eafea7f0a6fd3f7dcd916c094f2c210d88b8380d6cbf6b",
|
|
7
|
+
"trigram-postings.bin": "0a246cc840d7fad97945ca2164545c72ad59a35245d87cb6aa7359fd6bea21cf",
|
|
8
|
+
"trigrams.marisa": "9bb9eae9fc858f7a5ed9a6675035f407b77f9c561e9cfd441cbcf0b14b49518e",
|
|
9
|
+
"unigram-offsets.bin": "3e8f5dc282137fc6f418f6bc33e2b0dbe987d860b4ffc5761944432a5f3f5d01",
|
|
10
|
+
"unigram-postings.bin": "752a095ec3f92dddbc90a751c6ed295f5b363035f76ba6479098d13750381b8a",
|
|
11
|
+
"vocabulary.marisa": "faf3d4b7d5d9952e5ea3823d305d29e46d4fcc1a6f52751cdada81a529c655cc"
|
|
12
|
+
},
|
|
13
|
+
"counts": {
|
|
14
|
+
"records": 14940,
|
|
15
|
+
"tokens": 1039600,
|
|
16
|
+
"trigram_keys": 382866,
|
|
17
|
+
"trigram_postings": 1009649,
|
|
18
|
+
"vocabulary": 40949
|
|
19
|
+
},
|
|
20
|
+
"notices": {
|
|
21
|
+
"LICENSES.md": "211ab9b99234f8b4c99f4e295cd29f0524d1a8508b10916c2ce84aa28ee4fde3",
|
|
22
|
+
"PROVENANCE.md": "f565245adb555fbe5b4d4a838369fd3c50e37de3f0302c5a0291c6a36c9517c7",
|
|
23
|
+
"README.md": "fc207de3c37457f4cd02075cdd1f76562905f7b5efe7cdaf3c0e2ae0a51bc9fc",
|
|
24
|
+
"SOURCES.md": "a395c79251f54b1b780a475c32480f968d277b71a04017af18227df3e9545e53",
|
|
25
|
+
"UPSTREAM-UNLICENSE.txt": "6b0382b16279f26ff69014300541967a356a666eb0b91b422f6862f6b7dad17e",
|
|
26
|
+
"collections.csv": "806aef24f330464c3b9bb0f2526ed1f962d12c6edecc8d0047633c324189f11b",
|
|
27
|
+
"configs.csv": "5c397a92a7449a568104ff2e6d6e60bdea68d7d0f19556591871be38693c210c"
|
|
28
|
+
},
|
|
29
|
+
"source_dataset": {
|
|
30
|
+
"configurations": [
|
|
31
|
+
"bukhari-ar",
|
|
32
|
+
"muslim-ar"
|
|
33
|
+
],
|
|
34
|
+
"repository": "quranlab/hadith",
|
|
35
|
+
"revision": "cc6972dae1f46f5f5e2b65e877d24c4539bc4391"
|
|
36
|
+
},
|
|
37
|
+
"version": 1,
|
|
38
|
+
"versions": {
|
|
39
|
+
"index": 2,
|
|
40
|
+
"manifest": 1,
|
|
41
|
+
"normalization": 1,
|
|
42
|
+
"records": 1,
|
|
43
|
+
"tokens": 1
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Resolve preinstalled resources without extraction, downloads or construction."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def directory(index_dir: str | Path | None) -> Path:
|
|
7
|
+
if index_dir is not None:
|
|
8
|
+
return Path(index_dir).resolve()
|
|
9
|
+
bundled = Path(__file__).resolve().parent / "_data"
|
|
10
|
+
if not (bundled / "manifest.json").is_file():
|
|
11
|
+
raise FileNotFoundError(
|
|
12
|
+
"Bundled Hadith resources are missing. Reinstall the complete hadith-detector "
|
|
13
|
+
"distribution, or pass index_dir explicitly. Development checkouts must run "
|
|
14
|
+
"python -m tools.stage_bundle before using bundled resources."
|
|
15
|
+
)
|
|
16
|
+
return bundled
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""One synchronized, lazily populated convenience detector cache."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from threading import RLock
|
|
5
|
+
|
|
6
|
+
from ._annotation import validate
|
|
7
|
+
from ._bundled import directory
|
|
8
|
+
from .config import DetectorConfig
|
|
9
|
+
from .detector import HadithDetector
|
|
10
|
+
from .models import Detection, HadithRecord
|
|
11
|
+
|
|
12
|
+
_lock = RLock()
|
|
13
|
+
_entry: tuple[Path, HadithDetector] | None = None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _detector(index_dir: str | Path | None) -> HadithDetector:
|
|
17
|
+
global _entry
|
|
18
|
+
resolved = directory(index_dir)
|
|
19
|
+
with _lock:
|
|
20
|
+
if _entry is None or _entry[0] != resolved:
|
|
21
|
+
detector = HadithDetector(index_dir)
|
|
22
|
+
_entry = resolved, detector
|
|
23
|
+
return _entry[1]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def clear_cache() -> None:
|
|
27
|
+
"""Forget the cached detector without interrupting any in-flight call."""
|
|
28
|
+
global _entry
|
|
29
|
+
with _lock:
|
|
30
|
+
_entry = None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def detect(
|
|
34
|
+
text: str,
|
|
35
|
+
*,
|
|
36
|
+
index_dir: str | Path | None = None,
|
|
37
|
+
config: DetectorConfig | None = None,
|
|
38
|
+
min_match_words: int | None = None,
|
|
39
|
+
max_errors: int | None = None,
|
|
40
|
+
max_missing_words: int | None = None,
|
|
41
|
+
) -> list[Detection]:
|
|
42
|
+
"""Detect using cached bundled/local resources and independent per-call defaults."""
|
|
43
|
+
if config is not None and not isinstance(config, DetectorConfig):
|
|
44
|
+
raise TypeError("config must be a DetectorConfig or None")
|
|
45
|
+
effective = (config if config is not None else DetectorConfig())._override(
|
|
46
|
+
min_match_words, max_errors, max_missing_words
|
|
47
|
+
)
|
|
48
|
+
return _detector(index_dir).detect(
|
|
49
|
+
text,
|
|
50
|
+
min_match_words=effective.min_match_words,
|
|
51
|
+
max_errors=effective.max_errors,
|
|
52
|
+
max_missing_words=effective.max_missing_words,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def annotate(
|
|
57
|
+
text: str,
|
|
58
|
+
*,
|
|
59
|
+
index_dir: str | Path | None = None,
|
|
60
|
+
format: str = "html",
|
|
61
|
+
config: DetectorConfig | None = None,
|
|
62
|
+
min_match_words: int | None = None,
|
|
63
|
+
max_errors: int | None = None,
|
|
64
|
+
max_missing_words: int | None = None,
|
|
65
|
+
) -> str:
|
|
66
|
+
"""Annotate plain text using the single cached detector and per-call settings."""
|
|
67
|
+
validate(text, format)
|
|
68
|
+
if config is not None and not isinstance(config, DetectorConfig):
|
|
69
|
+
raise TypeError("config must be a DetectorConfig or None")
|
|
70
|
+
effective = (config if config is not None else DetectorConfig())._override(
|
|
71
|
+
min_match_words, max_errors, max_missing_words
|
|
72
|
+
)
|
|
73
|
+
return _detector(index_dir).annotate(
|
|
74
|
+
text,
|
|
75
|
+
format=format,
|
|
76
|
+
min_match_words=effective.min_match_words,
|
|
77
|
+
max_errors=effective.max_errors,
|
|
78
|
+
max_missing_words=effective.max_missing_words,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def get_hadith(hadith_key: str, *, index_dir: str | Path | None = None) -> HadithRecord:
|
|
83
|
+
"""Retrieve an isolated source snapshot using the single cached detector."""
|
|
84
|
+
return _detector(index_dir).get_hadith(hadith_key)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"files":{"manifest.json":"a34ebdd099389594b039a4ac55e667081110c2c192f43eee08095fe1f7d5dcbd","notices/LICENSES.md":"211ab9b99234f8b4c99f4e295cd29f0524d1a8508b10916c2ce84aa28ee4fde3","notices/PROVENANCE.md":"f565245adb555fbe5b4d4a838369fd3c50e37de3f0302c5a0291c6a36c9517c7","notices/README.md":"fc207de3c37457f4cd02075cdd1f76562905f7b5efe7cdaf3c0e2ae0a51bc9fc","notices/SOURCES.md":"a395c79251f54b1b780a475c32480f968d277b71a04017af18227df3e9545e53","notices/UPSTREAM-UNLICENSE.txt":"6b0382b16279f26ff69014300541967a356a666eb0b91b422f6862f6b7dad17e","notices/collections.csv":"806aef24f330464c3b9bb0f2526ed1f962d12c6edecc8d0047633c324189f11b","notices/configs.csv":"5c397a92a7449a568104ff2e6d6e60bdea68d7d0f19556591871be38693c210c","record-offsets.bin":"5ff1228d1d7cc82df5baa6853b510dca8d0897e165217d4df9bf3a26f16f32d1","records.jsonl":"9ec8e19aaab80ffbc01aa162122060099bc0a5de3841b544b4afa1c381414246","tokens.bin":"3488e5e47e5a91d42c2f84ba14560cf673e134081aea52a6ab28f08fdda2d317","trigram-offsets.bin":"c7edf613d74f00c806eafea7f0a6fd3f7dcd916c094f2c210d88b8380d6cbf6b","trigram-postings.bin":"0a246cc840d7fad97945ca2164545c72ad59a35245d87cb6aa7359fd6bea21cf","trigrams.marisa":"9bb9eae9fc858f7a5ed9a6675035f407b77f9c561e9cfd441cbcf0b14b49518e","unigram-offsets.bin":"3e8f5dc282137fc6f418f6bc33e2b0dbe987d860b4ffc5761944432a5f3f5d01","unigram-postings.bin":"752a095ec3f92dddbc90a751c6ed295f5b363035f76ba6479098d13750381b8a","vocabulary.marisa":"faf3d4b7d5d9952e5ea3823d305d29e46d4fcc1a6f52751cdada81a529c655cc"},"lock_sha256":"1f4711afa95bda7cd5ce69d54ca804553e53d0b51ef64611dd796f2b219f593d","version":1}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"algorithm":"trigram+unigram","artifacts":{"record-offsets.bin":"5ff1228d1d7cc82df5baa6853b510dca8d0897e165217d4df9bf3a26f16f32d1","records.jsonl":"9ec8e19aaab80ffbc01aa162122060099bc0a5de3841b544b4afa1c381414246","tokens.bin":"3488e5e47e5a91d42c2f84ba14560cf673e134081aea52a6ab28f08fdda2d317","trigram-offsets.bin":"c7edf613d74f00c806eafea7f0a6fd3f7dcd916c094f2c210d88b8380d6cbf6b","trigram-postings.bin":"0a246cc840d7fad97945ca2164545c72ad59a35245d87cb6aa7359fd6bea21cf","trigrams.marisa":"9bb9eae9fc858f7a5ed9a6675035f407b77f9c561e9cfd441cbcf0b14b49518e","unigram-offsets.bin":"3e8f5dc282137fc6f418f6bc33e2b0dbe987d860b4ffc5761944432a5f3f5d01","unigram-postings.bin":"752a095ec3f92dddbc90a751c6ed295f5b363035f76ba6479098d13750381b8a","vocabulary.marisa":"faf3d4b7d5d9952e5ea3823d305d29e46d4fcc1a6f52751cdada81a529c655cc"},"build_fingerprint":"e92ba58375816eb02ebb26c9f66a9cb15add35e990037e1b6444864ba7ce344b","builder":{"dependency_lock_sha256":"8ef4b3b8de07c227551b845eb52ef46d47ff7b618f153c3cc9ebd157aa1666ce","marisa_trie":"1.4.1","native_settings":{"binary":false,"cache_size":512,"num_tries":3,"order":131072},"python":"3.14.6","sources":{"src/hadith_detector/_marisa.py":"a3b7800da1923af9aef7a16127ae92a8aa4a6506bfc89540a9cc8b93f5a9a6b1","src/hadith_detector/_resources.py":"f7e9c2e124ba1fec9c255fbf89fe1f0590e82f827237f939d3bb854e0ca92a59","src/hadith_detector/_text.py":"825013da3b680c286c824d9915668618a9fa2aab3afab93f9eea85ae813a1571","tools/build_index.py":"3752db36fcd43a107edec828e253ffd76925b282a103d077ec638b2f5b6dfec5","tools/exact_benchmark/corpus.py":"5ccd73de6eb35a41e2df0f95ddc37550f9a7649282a928b5b5489902a472bc09","tools/exact_benchmark/indexes.py":"38266cdffc92f383b6d728cabb6c38699fae5b0177a1b2e9f60f4abb9d9c2a1c"},"unicode":"16.0.0"},"byte_order":"little","counts":{"records":14940,"tokens":1039600,"trigram_keys":382866,"trigram_postings":1009649,"vocabulary":40949},"format":"hadith-detector-index","prepared_manifest_sha256":"f0ee8509b911b7835771de94fe65a97a17098ebba53de57f9c274dd077a5a0e2","source_artifacts":{"records.jsonl":"9ec8e19aaab80ffbc01aa162122060099bc0a5de3841b544b4afa1c381414246","tokens.jsonl":"ee07fce76d7862854e069ef81d72255cf3cb616daad9d0c57977e287a4e11f33","validation.json":"0e2187d3b4e7abb19de4648de54ff5ec79c595893fc5a8ef659dd8ed5baadbb1"},"source_dataset":{"configurations":["bukhari-ar","muslim-ar"],"repository":"quranlab/hadith","revision":"cc6972dae1f46f5f5e2b65e877d24c4539bc4391"},"source_fingerprint":"87fd7b7824fe60063026a582a845082f7fc6fe88587cb1d0805dbae372ea01ed","versions":{"index":2,"manifest":1,"normalization":1,"records":1,"tokens":1}}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Licenses, Attribution & Removal (NOTICE) — quranlab/hadith
|
|
2
|
+
|
|
3
|
+
Binding terms are **per row** (`license`, `source`, `attribution`). Summary below.
|
|
4
|
+
|
|
5
|
+
## Theology gate
|
|
6
|
+
Strictly **Ahl al-Sunnah wa'l-Jamāʿah**. Excluded by design: the Shia Four Books (al-Kāfī, Man lā yaḥḍuruhu al-Faqīh, Tahdhīb al-Aḥkām, al-Istibṣār) and other Shia/Ismāʿīlī collections; Ibāḍī (Musnad al-Rabīʿ b. Ḥabīb); Ahmadiyya/Qādiānī; Quranist/hadith-rejecter sources; hostile-orientalist grade framings.
|
|
7
|
+
|
|
8
|
+
## Sources & tiers
|
|
9
|
+
- **Arabic matn (65,834 hadith across 13 collections):** public domain; digitisation from `mhashim6/Open-Hadith-Data`, ODbL-1.0 + DbCL-1.0. Attribution required.
|
|
10
|
+
- **Graded multilingual layer (72 languages):** HadeethEnc.com (IslamHouse / Saudi MoIA) — verbatim, version-pinned, with grade + attribution; reproduced under their open-with-attribution terms; removal on request.
|
|
11
|
+
- **Copyrighted full-collection translations (roadmap):** by reference + full attribution under **notice-and-takedown** (removal on rights-holder request via the HF Community tab).
|
|
12
|
+
|
|
13
|
+
## Grade integrity
|
|
14
|
+
Grades are **grader-attributed, never asserted by QuranLab**; the matn layer is ungraded; collection-level status lives in the `collections` config, never auto-stamped per row.
|
|
15
|
+
|
|
16
|
+
## Removal
|
|
17
|
+
Open the dataset's **Community (Discussions)** tab to request removal of any row.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Bundled corpus provenance and redistribution record
|
|
2
|
+
|
|
3
|
+
This package includes the Arabic `bukhari-ar` and `muslim-ar` configurations from
|
|
4
|
+
QuranLab Hadith revision `cc6972dae1f46f5f5e2b65e877d24c4539bc4391`: 7,580 and
|
|
5
|
+
7,360 records respectively. Original fields, identities, duplicate narrations,
|
|
6
|
+
nullable URLs and source text are preserved. Tokens and the prebuilt MARISA v2
|
|
7
|
+
index are derived resources; normalization version is 1.
|
|
8
|
+
|
|
9
|
+
Credit **QuranLab** (<https://huggingface.co/datasets/quranlab/hadith>) and
|
|
10
|
+
**fawazahmed0/hadith-api** (<https://github.com/fawazahmed0/hadith-api>).
|
|
11
|
+
The pinned `configs.csv` attributes both selected configurations to hadith-api,
|
|
12
|
+
describes classical Arabic text as public domain, and marks commercial use as
|
|
13
|
+
allowed. Upstream's Unlicense permits copying, modification and redistribution.
|
|
14
|
+
Its preserved text was retrieved at commit
|
|
15
|
+
`df57907be35291c91ad6a6691180e22ca9920784`; this pins the licensing evidence, not
|
|
16
|
+
the unknown upstream data revision used by QuranLab.
|
|
17
|
+
|
|
18
|
+
## Distinguishing the available evidence
|
|
19
|
+
|
|
20
|
+
The broad QuranLab `LICENSES.md` summary attributes Arabic material to
|
|
21
|
+
Open-Hadith-Data and mentions ODbL/DbCL. The selected configuration rows instead
|
|
22
|
+
identify hadith-api. `SOURCES.md` identifies Open-Hadith-Data with Ahmad/Darimi,
|
|
23
|
+
which are not bundled. Both notices are preserved verbatim, alongside the full
|
|
24
|
+
pinned configuration and collection metadata. This package follows the selected
|
|
25
|
+
configurations' attribution and the maintainer's approved Phase 2 provenance
|
|
26
|
+
decision; the broad summary is not rewritten or presented as consistent.
|
|
27
|
+
|
|
28
|
+
Only the selected Arabic records are redistributed: no translations, HadeethEnc
|
|
29
|
+
text, Ahmad/Darimi records, or text scraped from sunnah.com. Source links and
|
|
30
|
+
numbering are retained as supplied; links do not establish rights to linked pages.
|
|
31
|
+
No independent authentication or grader endorsement is asserted.
|
|
32
|
+
|
|
33
|
+
The project code is MIT licensed. This does not relicense third-party data.
|
|
34
|
+
`UPSTREAM-UNLICENSE.txt`, QuranLab `README.md`, `LICENSES.md`, `SOURCES.md`,
|
|
35
|
+
`configs.csv` and `collections.csv` accompany the data. Preserve these notices
|
|
36
|
+
when redistributing this bundle. Consult the original notices for attribution,
|
|
37
|
+
correction and removal contact information.
|
|
38
|
+
|
|
39
|
+
## Integrity and build provenance
|
|
40
|
+
|
|
41
|
+
`_bundle.json` records approved resource payload hashes, counts, versions and
|
|
42
|
+
source revision. `_data/manifest.json` records source preparation and builder
|
|
43
|
+
fingerprints; `_data/bundle.json` records every staged file and its checksum.
|
|
44
|
+
The builder manifest may differ when code or environment provenance changes,
|
|
45
|
+
while approved resource payloads must remain byte-identical. No runtime resource
|
|
46
|
+
construction or download is performed.
|