353 lines
19 KiB
Python
353 lines
19 KiB
Python
from pydantic import BaseModel, Field
|
|
from typing import Optional
|
|
|
|
|
|
# ── Common ─────────────────────────────────────────────────────────────────
|
|
|
|
class PaginationMeta(BaseModel):
|
|
total: int
|
|
page: int
|
|
per_page: int
|
|
pages: int
|
|
|
|
model_config = {
|
|
"json_schema_extra": {
|
|
"examples": [{"total": 6986, "page": 1, "per_page": 20, "pages": 350}]
|
|
}
|
|
}
|
|
|
|
|
|
class PaginatedResponse(BaseModel):
|
|
meta: PaginationMeta
|
|
data: list
|
|
|
|
|
|
# ── Hadith ─────────────────────────────────────────────────────────────────
|
|
|
|
class HadithSummary(BaseModel):
|
|
id: str = Field(description="Unique hadith UUID")
|
|
collection: str = Field(description="Collection name in English")
|
|
hadith_number: int = Field(description="Hadith number within collection")
|
|
grade: Optional[str] = Field(None, description="Grading: Sahih, Hasan, Da'if, etc.")
|
|
arabic_text: Optional[str] = Field(None, description="Full Arabic text (may be truncated in list views)")
|
|
matn_text: Optional[str] = Field(None, description="Body text only (without isnad)")
|
|
sanad_text: Optional[str] = Field(None, description="Chain of narration text only")
|
|
|
|
model_config = {
|
|
"json_schema_extra": {
|
|
"examples": [{
|
|
"id": "dcf8df41-3185-4e20-a9af-db3696a48c79",
|
|
"collection": "Sahih Bukhari",
|
|
"hadith_number": 1,
|
|
"grade": "Sahih",
|
|
"arabic_text": "حَدَّثَنَا الْحُمَيْدِيُّ عَبْدُ اللَّهِ بْنُ الزُّبَيْرِ قَالَ حَدَّثَنَا سُفْيَانُ...",
|
|
"matn_text": "إِنَّمَا الأَعْمَالُ بِالنِّيَّاتِ وَإِنَّمَا لِكُلِّ امْرِئٍ مَا نَوَى...",
|
|
"sanad_text": "حَدَّثَنَا الْحُمَيْدِيُّ عَبْدُ اللَّهِ بْنُ الزُّبَيْرِ قَالَ حَدَّثَنَا سُفْيَانُ قَالَ حَدَّثَنَا يَحْيَى بْنُ سَعِيدٍ الأَنْصَارِيُّ"
|
|
}]
|
|
}
|
|
}
|
|
|
|
|
|
class TopicTag(BaseModel):
|
|
topic_arabic: str = Field(description="Topic name in Arabic, e.g. الصلاة")
|
|
topic_english: str = Field(description="Topic name in English, e.g. Prayer")
|
|
category: str = Field(description="Broad Islamic category: عقيدة، فقه، سيرة، أخلاق، تفسير")
|
|
|
|
model_config = {
|
|
"json_schema_extra": {
|
|
"examples": [{
|
|
"topic_arabic": "النية",
|
|
"topic_english": "Intention",
|
|
"category": "فقه"
|
|
}]
|
|
}
|
|
}
|
|
|
|
|
|
class NarratorInChain(BaseModel):
|
|
order: int = Field(description="Position in chain: 1=closest to compiler, last=closest to Prophet ﷺ")
|
|
name_arabic: str = Field(description="Narrator's Arabic name as it appears in the hadith text")
|
|
name_transliterated: str = Field("", description="Latin transliteration of the name")
|
|
entity_type: str = Field("", description="PERSON, KUNYA (أبو/أم), NISBA (attributional), or TITLE (رسول الله)")
|
|
transmission_verb: Optional[str] = Field(None, description="Exact Arabic transmission verb: حدثنا، أخبرنا، عن، سمعت")
|
|
|
|
model_config = {
|
|
"json_schema_extra": {
|
|
"examples": [{
|
|
"order": 1,
|
|
"name_arabic": "الْحُمَيْدِيُّ",
|
|
"name_transliterated": "al-Humaydi",
|
|
"entity_type": "NISBA",
|
|
"transmission_verb": "حَدَّثَنَا"
|
|
}]
|
|
}
|
|
}
|
|
|
|
|
|
class HadithDetail(BaseModel):
|
|
id: str = Field(description="Unique hadith UUID")
|
|
collection: str = Field(description="Collection English name")
|
|
hadith_number: int = Field(description="Number within collection")
|
|
grade: Optional[str] = Field(None, description="Hadith grade")
|
|
arabic_text: Optional[str] = Field(None, description="Complete Arabic text")
|
|
sanad_text: Optional[str] = Field(None, description="Isnad (chain) text only")
|
|
matn_text: Optional[str] = Field(None, description="Matn (body) text only")
|
|
narrator_chain: list[NarratorInChain] = Field(default_factory=list, description="Ordered narrator chain from Neo4j graph")
|
|
topics: list[TopicTag] = Field(default_factory=list, description="Topic tags for searchability")
|
|
|
|
model_config = {
|
|
"json_schema_extra": {
|
|
"examples": [{
|
|
"id": "dcf8df41-3185-4e20-a9af-db3696a48c79",
|
|
"collection": "Sahih Bukhari",
|
|
"hadith_number": 1,
|
|
"grade": "Sahih",
|
|
"arabic_text": "حَدَّثَنَا الْحُمَيْدِيُّ عَبْدُ اللَّهِ بْنُ الزُّبَيْرِ...",
|
|
"sanad_text": "حَدَّثَنَا الْحُمَيْدِيُّ...",
|
|
"matn_text": "إِنَّمَا الأَعْمَالُ بِالنِّيَّاتِ...",
|
|
"narrator_chain": [
|
|
{"order": 1, "name_arabic": "الْحُمَيْدِيُّ", "name_transliterated": "al-Humaydi", "entity_type": "NISBA", "transmission_verb": "حَدَّثَنَا"},
|
|
{"order": 2, "name_arabic": "سُفْيَانُ", "name_transliterated": "Sufyan", "entity_type": "PERSON", "transmission_verb": "حَدَّثَنَا"},
|
|
],
|
|
"topics": [
|
|
{"topic_arabic": "النية", "topic_english": "Intention", "category": "فقه"},
|
|
]
|
|
}]
|
|
}
|
|
}
|
|
|
|
|
|
# ── Narrator ───────────────────────────────────────────────────────────────
|
|
|
|
class NarratorSummary(BaseModel):
|
|
name_arabic: str = Field(description="Primary Arabic name")
|
|
name_transliterated: str = Field("", description="Latin transliteration")
|
|
entity_type: str = Field("", description="PERSON, KUNYA, NISBA, TITLE")
|
|
generation: Optional[str] = Field(None, description="طبقة: صحابي، تابعي، تابع التابعين")
|
|
reliability_grade: Optional[str] = Field(None, description="جرح وتعديل: ثقة، صدوق، ضعيف، متروك")
|
|
hadith_count: int = Field(0, description="Number of hadiths this narrator appears in")
|
|
|
|
model_config = {
|
|
"json_schema_extra": {
|
|
"examples": [{
|
|
"name_arabic": "أَبُو هُرَيْرَةَ",
|
|
"name_transliterated": "Abu Hurayrah",
|
|
"entity_type": "KUNYA",
|
|
"generation": "صحابي",
|
|
"reliability_grade": "ثقة",
|
|
"hadith_count": 5374
|
|
}]
|
|
}
|
|
}
|
|
|
|
|
|
class NameForm(BaseModel):
|
|
name: str = Field(description="Alternative name form")
|
|
type: str = Field(description="Name type: PERSON, KUNYA, NISBA, TITLE")
|
|
|
|
|
|
class FamilyInfo(BaseModel):
|
|
father: Optional[str] = None
|
|
mother: Optional[str] = None
|
|
spouse: Optional[str] = None
|
|
children: list[str] = Field(default_factory=list)
|
|
|
|
|
|
class PlaceRelation(BaseModel):
|
|
place: str = Field(description="Place name in Arabic")
|
|
relation: str = Field(description="BORN_IN, LIVED_IN, DIED_IN, or TRAVELED_TO")
|
|
|
|
model_config = {
|
|
"json_schema_extra": {
|
|
"examples": [{"place": "المدينة", "relation": "LIVED_IN"}]
|
|
}
|
|
}
|
|
|
|
|
|
class NarratorProfile(BaseModel):
|
|
name_arabic: str = Field(description="Primary Arabic name")
|
|
name_transliterated: str = Field("", description="Latin transliteration")
|
|
entity_type: str = Field("", description="PERSON, KUNYA, NISBA, TITLE")
|
|
full_nasab: Optional[str] = Field(None, description="Full lineage: فلان بن فلان بن فلان")
|
|
kunya: Optional[str] = Field(None, description="أبو/أم name (e.g. أبو هريرة)")
|
|
nisba: Optional[str] = Field(None, description="Attributional name (e.g. البخاري، المدني، الزهري)")
|
|
laqab: Optional[str] = Field(None, description="Title or epithet (e.g. أمير المؤمنين في الحديث)")
|
|
generation: Optional[str] = Field(None, description="طبقة: صحابي، تابعي، تابع التابعين، أتباع تابع التابعين")
|
|
reliability_grade: Optional[str] = Field(None, description="جرح وتعديل: ثقة، ثقة حافظ، صدوق، ضعيف، متروك")
|
|
reliability_detail: Optional[str] = Field(None, description="Extended grading explanation from scholars")
|
|
birth_year_hijri: Optional[int] = Field(None, description="Birth year (Hijri calendar)")
|
|
death_year_hijri: Optional[int] = Field(None, description="Death year (Hijri calendar)")
|
|
birth_year_ce: Optional[int] = Field(None, description="Birth year (CE)")
|
|
death_year_ce: Optional[int] = Field(None, description="Death year (CE)")
|
|
biography_summary_arabic: Optional[str] = Field(None, description="2-3 sentence biography in Arabic")
|
|
biography_summary_english: Optional[str] = Field(None, description="2-3 sentence biography in English")
|
|
total_hadiths_narrated_approx: Optional[int] = Field(None, description="Approximate total hadiths narrated across all collections")
|
|
hadith_count: int = Field(0, description="Hadiths in current database")
|
|
hadiths: list[HadithSummary] = Field(default_factory=list, description="Sample hadiths narrated (max 50)")
|
|
teachers: list[NarratorSummary] = Field(default_factory=list, description="Known teachers / شيوخ")
|
|
students: list[NarratorSummary] = Field(default_factory=list, description="Known students / تلاميذ")
|
|
places: list[PlaceRelation] = Field(default_factory=list, description="Associated places (born, lived, died, traveled)")
|
|
tribes: list[str] = Field(default_factory=list, description="Tribal affiliations (e.g. قريش، دوس، الأنصار)")
|
|
bio_verified: bool = Field(False, description="Whether biography has been manually verified against classical sources")
|
|
|
|
model_config = {
|
|
"json_schema_extra": {
|
|
"examples": [{
|
|
"name_arabic": "أَبُو هُرَيْرَةَ",
|
|
"name_transliterated": "Abu Hurayrah",
|
|
"entity_type": "KUNYA",
|
|
"full_nasab": "عبد الرحمن بن صخر الدوسي",
|
|
"kunya": "أبو هريرة",
|
|
"nisba": "الدوسي",
|
|
"laqab": None,
|
|
"generation": "صحابي",
|
|
"reliability_grade": "ثقة",
|
|
"reliability_detail": "صحابي جليل، أكثر الصحابة رواية للحديث",
|
|
"birth_year_hijri": None,
|
|
"death_year_hijri": 57,
|
|
"birth_year_ce": None,
|
|
"death_year_ce": 676,
|
|
"biography_summary_arabic": "أبو هريرة الدوسي، صحابي جليل، أكثر الصحابة رواية للحديث النبوي. أسلم عام خيبر ولازم النبي ﷺ.",
|
|
"biography_summary_english": "Abu Hurayrah al-Dawsi, a prominent Companion and the most prolific narrator of hadith. He accepted Islam during Khaybar and remained close to the Prophet ﷺ.",
|
|
"total_hadiths_narrated_approx": 5374,
|
|
"hadith_count": 142,
|
|
"hadiths": [],
|
|
"teachers": [{"name_arabic": "رسول الله ﷺ", "name_transliterated": "Prophet Muhammad", "entity_type": "TITLE", "generation": None, "reliability_grade": None, "hadith_count": 0}],
|
|
"students": [{"name_arabic": "الزهري", "name_transliterated": "al-Zuhri", "entity_type": "NISBA", "generation": "تابعي", "reliability_grade": "ثقة", "hadith_count": 89}],
|
|
"places": [{"place": "المدينة", "relation": "LIVED_IN"}],
|
|
"tribes": ["دوس"],
|
|
"bio_verified": False,
|
|
}]
|
|
}
|
|
}
|
|
|
|
|
|
# ── Isnad Chain ────────────────────────────────────────────────────────────
|
|
|
|
class IsnadNode(BaseModel):
|
|
name_arabic: str = Field(description="Narrator Arabic name")
|
|
name_transliterated: str = Field("", description="Latin transliteration")
|
|
entity_type: str = Field("", description="PERSON, KUNYA, NISBA, TITLE")
|
|
generation: Optional[str] = Field(None, description="طبقة")
|
|
reliability_grade: Optional[str] = Field(None, description="جرح وتعديل grade")
|
|
|
|
|
|
class IsnadLink(BaseModel):
|
|
source: str = Field(description="name_arabic of narrator who received the hadith")
|
|
target: str = Field(description="name_arabic of narrator they received it from")
|
|
transmission_verb: Optional[str] = Field(None, description="Exact verb: حدثنا، أخبرنا، عن، سمعت، أنبأنا")
|
|
|
|
|
|
class IsnadChain(BaseModel):
|
|
hadith_id: str = Field(description="UUID of the hadith")
|
|
collection: str = Field(description="Collection name")
|
|
hadith_number: int = Field(description="Hadith number")
|
|
nodes: list[IsnadNode] = Field(default_factory=list, description="Narrator nodes for graph visualization")
|
|
links: list[IsnadLink] = Field(default_factory=list, description="Directed edges: source heard from target")
|
|
|
|
model_config = {
|
|
"json_schema_extra": {
|
|
"examples": [{
|
|
"hadith_id": "dcf8df41-3185-4e20-a9af-db3696a48c79",
|
|
"collection": "Sahih Bukhari",
|
|
"hadith_number": 1,
|
|
"nodes": [
|
|
{"name_arabic": "الْحُمَيْدِيُّ", "name_transliterated": "al-Humaydi", "entity_type": "NISBA", "generation": "تابع التابعين", "reliability_grade": "ثقة"},
|
|
{"name_arabic": "سُفْيَانُ بْنُ عُيَيْنَةَ", "name_transliterated": "Sufyan ibn Uyaynah", "entity_type": "PERSON", "generation": "تابع التابعين", "reliability_grade": "ثقة"},
|
|
{"name_arabic": "يَحْيَى بْنُ سَعِيدٍ", "name_transliterated": "Yahya ibn Sa'id al-Ansari", "entity_type": "PERSON", "generation": "تابعي", "reliability_grade": "ثقة"},
|
|
{"name_arabic": "عُمَرُ بْنُ الْخَطَّابِ", "name_transliterated": "Umar ibn al-Khattab", "entity_type": "PERSON", "generation": "صحابي", "reliability_grade": "ثقة"},
|
|
],
|
|
"links": [
|
|
{"source": "الْحُمَيْدِيُّ", "target": "سُفْيَانُ بْنُ عُيَيْنَةَ", "transmission_verb": "حَدَّثَنَا"},
|
|
{"source": "سُفْيَانُ بْنُ عُيَيْنَةَ", "target": "يَحْيَى بْنُ سَعِيدٍ", "transmission_verb": "حَدَّثَنَا"},
|
|
{"source": "يَحْيَى بْنُ سَعِيدٍ", "target": "عُمَرُ بْنُ الْخَطَّابِ", "transmission_verb": "عن"},
|
|
]
|
|
}]
|
|
}
|
|
}
|
|
|
|
|
|
# ── Relationships / Who Met Who ────────────────────────────────────────────
|
|
|
|
class NarratorInteraction(BaseModel):
|
|
narrator_a: str = Field(description="First narrator Arabic name")
|
|
narrator_a_transliterated: str = Field("", description="First narrator transliteration")
|
|
narrator_b: str = Field(description="Second narrator Arabic name")
|
|
narrator_b_transliterated: str = Field("", description="Second narrator transliteration")
|
|
relationship_type: str = Field(description="NARRATED_FROM, TEACHER_OF, HEARD_BY, STUDENT_OF")
|
|
shared_hadith_count: int = Field(0, description="Number of hadiths connecting them")
|
|
hadith_ids: list[str] = Field(default_factory=list, description="IDs of connecting hadiths (max 20)")
|
|
|
|
model_config = {
|
|
"json_schema_extra": {
|
|
"examples": [{
|
|
"narrator_a": "الزهري",
|
|
"narrator_a_transliterated": "al-Zuhri",
|
|
"narrator_b": "أنس بن مالك",
|
|
"narrator_b_transliterated": "Anas ibn Malik",
|
|
"relationship_type": "NARRATED_FROM",
|
|
"shared_hadith_count": 23,
|
|
"hadith_ids": ["abc-123", "def-456"]
|
|
}]
|
|
}
|
|
}
|
|
|
|
|
|
class NarratorConnection(BaseModel):
|
|
narrator: str = Field(description="Connected narrator Arabic name")
|
|
narrator_transliterated: str = Field("", description="Transliteration")
|
|
connection_type: str = Field(description="Relationship type")
|
|
direction: str = Field(description="'incoming' (they → this) or 'outgoing' (this → them)")
|
|
|
|
|
|
class NarratorNetwork(BaseModel):
|
|
center: NarratorSummary
|
|
connections: list[NarratorConnection] = Field(default_factory=list)
|
|
total_connections: int = 0
|
|
|
|
|
|
# ── Search ─────────────────────────────────────────────────────────────────
|
|
|
|
class SemanticSearchResult(BaseModel):
|
|
hadith: HadithSummary = Field(description="Matching hadith")
|
|
score: float = Field(description="Cosine similarity score (0-1, higher = more relevant)")
|
|
collection: str = Field("", description="Collection name")
|
|
|
|
model_config = {
|
|
"json_schema_extra": {
|
|
"examples": [{
|
|
"hadith": {
|
|
"id": "abc-123",
|
|
"collection": "Sahih Bukhari",
|
|
"hadith_number": 1,
|
|
"grade": "Sahih",
|
|
"arabic_text": "إِنَّمَا الأَعْمَالُ بِالنِّيَّاتِ..."
|
|
},
|
|
"score": 0.9234,
|
|
"collection": "Sahih Bukhari"
|
|
}]
|
|
}
|
|
}
|
|
|
|
|
|
class FullTextSearchResult(BaseModel):
|
|
hadith: HadithSummary = Field(description="Matching hadith")
|
|
score: float = Field(description="Elasticsearch relevance score (higher = more relevant)")
|
|
highlights: list[str] = Field(default_factory=list, description="Text fragments with <em>highlighted</em> matches")
|
|
|
|
model_config = {
|
|
"json_schema_extra": {
|
|
"examples": [{
|
|
"hadith": {
|
|
"id": "abc-123",
|
|
"collection": "Sahih Muslim",
|
|
"hadith_number": 1599,
|
|
"grade": "Sahih",
|
|
"arabic_text": "..."
|
|
},
|
|
"score": 12.45,
|
|
"highlights": ["...عن <em>الصلاة</em> في المسجد الحرام..."]
|
|
}]
|
|
}
|
|
}
|