""" نقشه‌ی فیزیکیِ فایل‌های PDF کتاب‌های درسی در data/textbooks/. چرا این فایل لازم است: `ingest_pdf` یک PDF را یک‌تکه و با یک نامِ فصل ingest می‌کند. کتابِ کاملِ درسی چند فصل دارد، پس بدونِ نگاشتِ «فصل → بازه‌ی صفحه» همه‌ی نقاطِ کتاب زیرِ یک فصلِ اشتباه ذخیره می‌شدند و فیلترِ `chapter` در retrieval هیچ‌وقت درست جواب نمی‌داد. دو نکته که موقعِ افزودنِ کتابِ جدید باید حواستان باشد: - `page_offset`: شماره‌ی صفحه‌ای که روی کاغذ چاپ شده با شماره‌ی صفحه در PDF یکی نیست (جلد، شناسنامه، فهرست و سخنی با معلم جلوترند). فهرستِ کتاب شماره‌ی کاغذی می‌دهد، pdf2image شماره‌ی PDF می‌خواهد: pdf_page = book_page + offset. - بازه‌ها بر اساسِ فهرستِ خودِ کتاب‌اند و شاملِ صفحه‌ی جداکننده‌ی فصل هم می‌شوند؛ صفحه‌ی آخرِ هر فصل = یکی قبل از شروعِ فصلِ بعد. مقادیرِ زیر با خواندنِ فهرست و بازبینیِ چشمیِ صفحه‌های مرزی از همان فایل‌ها درآمده‌اند (۱۴۰۵/۰۶/۲۱). """ from dataclasses import dataclass @dataclass(frozen=True) class Section: """یک «درس» داخلِ فصل، همان‌طور که در فهرستِ کتاب آمده.""" number: int title: str first_page: int # شماره‌ی چاپ‌شده روی کاغذ last_page: int = 0 # از روی درسِ بعدی پر می‌شود؛ صفر یعنی «تا آخرِ فصل» @dataclass(frozen=True) class Chapter: number: int title: str # عنوانِ کاملِ فصل، دقیقاً همان رشته‌ای که در payload می‌نشیند first_page: int # شماره‌ی چاپ‌شده روی کاغذ last_page: int sections: tuple[Section, ...] = () def resolved_sections(self) -> tuple[Section, ...]: """درس‌ها با last_page پرشده (هر درس تا یکی قبل از درسِ بعدی).""" out = [] for index, section in enumerate(self.sections): end = ( self.sections[index + 1].first_page - 1 if index + 1 < len(self.sections) else self.last_page ) out.append(Section(section.number, section.title, section.first_page, end)) return tuple(out) @dataclass(frozen=True) class Textbook: subject: str grade: str # همان رشته‌ای که در نامِ collection می‌آید («نهم») grade_label: str # همان رشته‌ای که در payloadِ `grade` می‌نشیند filename: str page_offset: int chapters: tuple[Chapter, ...] @property def key(self) -> str: return f'{self.subject} {self.grade}'.strip() def pdf_pages(self, chapter: Chapter) -> tuple[int, int]: return chapter.first_page + self.page_offset, chapter.last_page + self.page_offset def chapter(self, number: int) -> Chapter | None: return next((c for c in self.chapters if c.number == number), None) def _ch(*rows) -> tuple[Chapter, ...]: """rows: (number, title, first_page, last_page, [(sec_no, sec_title, sec_page), ...])""" chapters = [] for row in rows: number, title, first, last = row[:4] sections = tuple(Section(*s) for s in (row[4] if len(row) > 4 else ())) chapters.append(Chapter(number, title, first, last, sections)) return tuple(chapters) MATH_3 = Textbook( subject='ریاضی', grade='سوم', grade_label='سوم دبستان', filename='', page_offset=0, chapters=_ch( (1, 'فصل ۱: الگوها', 0, 0), (2, 'فصل ۲: عددهای چهار رقمی', 0, 0), (3, 'فصل ۳: عددهای کسری', 0, 0), (4, 'فصل ۴: ضرب و تقسیم', 0, 0), (5, 'فصل ۵: محیط و مساحت', 0, 0), (6, 'فصل ۶: جمع و تفریق', 0, 0), (7, 'فصل ۷: آمار و احتمال', 0, 0), (8, 'فصل ۸: ضرب عددها', 0, 0), ), ) # چهارم و پنجم: درخواستِ دکتر (۲۰۲۶-۱۰-۰۳) که «از سوم به بعد» همه‌ی پایه‌ها باشند. # فهرست و offset از خودِ PDFها خوانده شد: در چهارم PDF ۸ = صفحه‌ی چاپیِ ۲، در # پنجم (چاپِ تازه‌ی ۱۴۰۵) شماره‌ی PDF همان شماره‌ی چاپی است. «حلِ مسئله»ی آغازِ # هر فصل جزءِ درسِ اول حساب می‌شود (`_sectioned` درسِ اول را تا ابتدای فصل # عقب می‌کشد) و «مرور فصل» جزءِ درسِ آخر. MATH_4 = Textbook( subject='ریاضی', grade='چهارم', grade_label='چهارم دبستان', filename='ریاضی چهارم.pdf', page_offset=6, chapters=_ch( (1, 'فصل ۱: اعداد و الگوها', 1, 23, ( (1, 'عددنویسی', 4), (2, 'الگوها', 8), (3, 'ماشین ورودی ـ خروجی', 12), (4, 'معرفی میلیون', 16), )), (2, 'فصل ۲: کسر', 24, 45, ( (1, 'شناخت کسرها', 26), (2, 'جمع و تفریق کسرها', 30), (3, 'تساوی کسرها', 34), (4, 'ضرب عدد در کسر', 38), )), (3, 'فصل ۳: ضرب و تقسیم', 46, 75, ( (1, 'ضرب دو عدد دورقمی', 48), (2, 'محاسبهٔ حاصل‌ضرب', 52), (3, 'محاسبه‌های تقریبی', 56), (4, 'تقسیم و بخش‌پذیری', 60), (5, 'تقسیم بر عددهای یک‌رقمی', 64), (6, 'تقسیم بر عددهای دورقمی', 68), )), (4, 'فصل ۴: اندازه‌گیری', 76, 97, ( (1, 'زاویه', 78), (2, 'اندازه‌گیری زاویه', 82), (3, 'اندازه‌گیری زمان', 86), (4, 'اندازه‌گیری طول', 90), )), (5, 'فصل ۵: عدد مخلوط و عدد اعشاری', 98, 119, ( (1, 'عدد مخلوط', 100), (2, 'عدد اعشاری', 104), (3, 'جمع و تفریق اعشاری', 108), (4, 'ارزش مکانی عددهای اعشاری', 112), )), (6, 'فصل ۶: شکل‌های هندسی', 120, 141, ( (1, 'عمود و موازی', 122), (2, 'چهارضلعی‌ها', 126), (3, 'مساحت متوازی‌الاضلاع و مثلث', 130), (4, 'محیط و مساحت', 134), )), (7, 'فصل ۷: آمار و احتمال', 142, 154, ( (1, 'نمودار خط شکسته', 144), (2, 'احتمال', 148), )), ), ) # شروعِ هر فصل یک صفحه پیش از درسِ اولش گرفته شد (صفحه‌ی جداکننده)؛ فقط برای # فصل ۱ با چشم دیده شد (PDF ۱۱). جداکننده‌ی بقیه اگر دو صفحه باشد، یک صفحه‌ی # تصویریِ بی‌مسئله به درسِ آخرِ فصلِ قبل می‌افتد. ۲۰۷–۲۰۸ پشتِ جلد است. MATH_5 = Textbook( subject='ریاضی', grade='پنجم', grade_label='پنجم دبستان', filename='ریاضی پنجم.pdf', page_offset=0, chapters=_ch( (1, 'فصل ۱: عددنویسی و الگوها', 11, 38, ( (1, 'یادآوری عددنویسی و محاسبات عددی', 12), (2, 'معرفی میلیارد', 18), (3, 'جمع و تفریق عددهای مرکب', 25), (4, 'الگوها', 30), )), (2, 'فصل ۲: کسر', 39, 76, ( (1, 'کسرهای بزرگ‌تر از واحد', 40), (2, 'جمع و تفریق عددهای مخلوط', 46), (3, 'ضرب کسرها', 53), (4, 'تقسیم کسرها', 59), (5, 'ضرب عددهای مخلوط', 66), )), (3, 'فصل ۳: نسبت، تناسب و درصد', 77, 102, ( (1, 'نسبت', 78), (2, 'نسبت‌های مساوی', 83), (3, 'تناسب', 88), (4, 'درصد', 93), )), (4, 'فصل ۴: تقارن و چندضلعی‌ها', 103, 128, ( (1, 'تقارن محوری', 104), (2, 'تقارن مرکزی', 110), (3, 'زاویه و نیمساز', 113), (4, 'چندضلعی‌ها و مجموع زاویه‌های آن‌ها', 118), )), (5, 'فصل ۵: عددهای اعشاری', 129, 156, ( (1, 'عددهای اعشاری', 130), (2, 'جمع عددهای اعشاری', 139), (3, 'تفریق عددهای اعشاری', 143), (4, 'ضرب عددهای اعشاری', 147), )), (6, 'فصل ۶: اندازه‌گیری', 157, 184, ( (1, 'مساحت لوزی و ذوزنقه', 158), (2, 'محیط دایره', 165), (3, 'حجم', 170), (4, 'گنجایش', 177), )), (7, 'فصل ۷: آمار و احتمال', 185, 206, ( (1, 'جمع‌آوری و نمایش داده‌ها', 186), (2, 'میانگین', 193), (3, 'احتمال', 197), )), ), ) # هشتم تنها کتابی‌ست که هم PDF دارد و هم روی Qdrant نیست — یعنی فصل‌بندی و # درس‌هایش کاملاً از فهرستِ خودِ کتاب می‌آید و ingest دقیقاً همین رشته‌ها را # می‌نویسد. «مرور فصل» جزء آخرین درسِ هر فصل حساب شده است. MATH_8 = Textbook( subject='ریاضی', grade='هشتم', grade_label='هشتم', filename='ریاضی هشتم.pdf', page_offset=8, chapters=_ch( (1, 'فصل ۱: عددهای صحیح و گویا', 1, 18, ( (1, 'یادآوری عددهای صحیح', 2), (2, 'معرفی عددهای گویا', 6), (3, 'جمع و تفریق عددهای گویا', 10), (4, 'ضرب و تقسیم عددهای گویا', 14), )), (2, 'فصل ۲: عددهای اول', 19, 28, ( (1, 'یادآوری عددهای اول', 20), (2, 'تعیین عددهای اول', 24), )), (3, 'فصل ۳: چندضلعی‌ها', 29, 50, ( (1, 'چندضلعی‌ها و تقارن', 30), (2, 'توازی و تعامد', 34), (3, 'چهارضلعی‌ها', 38), (4, 'زاویه‌های داخلی', 42), (5, 'زاویه‌های خارجی', 46), )), (4, 'فصل ۴: جبر و معادله', 51, 68, ( (1, 'ساده کردن عبارت‌های جبری', 52), (2, 'پیدا کردن مقدار یک عبارت جبری', 56), (3, 'تجزیه عبارت‌های جبری', 60), (4, 'معادله', 64), )), (5, 'فصل ۵: بردار و مختصات', 69, 82, ( (1, 'جمع بردارها', 70), (2, 'ضرب عدد در بردار', 74), (3, 'بردارهای واحد مختصات', 78), )), (6, 'فصل ۶: مثلث', 83, 100, ( (1, 'رابطهٔ فیثاغورس', 84), (2, 'شکل‌های هم‌نهشت', 88), (3, 'مثلث‌های هم‌نهشت', 92), (4, 'هم‌نهشتی مثلث‌های قائم‌الزاویه', 96), )), (7, 'فصل ۷: توان و جذر', 101, 118, ( (1, 'توان', 102), (2, 'تقسیم اعداد توان‌دار', 106), (3, 'جذر تقریبی', 110), (4, 'نمایش اعداد رادیکالی روی محور اعداد', 114), (5, 'خواص ضرب و تقسیم رادیکال‌ها', 115), )), (8, 'فصل ۸: آمار و احتمال', 119, 136, ( (1, 'دسته‌بندی داده‌ها', 120), (2, 'میانگین داده‌ها', 124), (3, 'احتمال یا اندازه‌گیری شانس', 128), (4, 'بررسی حالت‌های ممکن', 132), )), (9, 'فصل ۹: دایره', 137, 150, ( (1, 'خط و دایره', 138), (2, 'زاویه‌های مرکزی', 142), (3, 'زاویه‌های محاطی', 146), )), ), ) MATH_9 = Textbook( subject='ریاضی', grade='نهم', grade_label='نهم', filename='ریاضی نهم.pdf', page_offset=8, chapters=_ch( (1, 'فصل ۱: مجموعه‌ها', 1, 17, ( (1, 'معرفی مجموعه', 2), (2, 'مجموعه‌های برابر و نمایش مجموعه‌ها', 6), (3, 'اجتماع، اشتراک و تفاضلِ مجموعه‌ها', 11), (4, 'مجموعه‌ها و احتمال', 15), )), (2, 'فصل ۲: عددهای حقیقی', 18, 31, ( (1, 'عددهای گویا', 19), (2, 'عددهای حقیقی', 23), (3, 'قدر مطلق و محاسبهٔ تقریبی', 28), )), (3, 'فصل ۳: استدلال و اثبات در هندسه', 32, 58, ( (1, 'استدلال', 33), (2, 'آشنایی با اثبات در هندسه', 37), (3, 'هم‌نهشتی مثلث‌ها', 44), (4, 'حل مسئله در هندسه', 49), (5, 'شکل‌های متشابه', 53), )), (4, 'فصل ۴: توان و ریشه', 59, 77, ( (1, 'توان صحیح', 60), (2, 'نماد علمی', 65), (3, 'ریشه‌گیری', 68), (4, 'جمع و تفریق رادیکال‌ها', 73), )), (5, 'فصل ۵: عبارت‌های جبری', 78, 94, ( (1, 'عبارت‌های جبری و مفهوم اتحاد', 79), (2, 'چند اتحاد دیگر، تجزیه و کاربردها', 86), (3, 'نابرابری‌ها و نامعادله‌ها', 90), )), (6, 'فصل ۶: خط و معادله‌های خطی', 95, 112, ( (1, 'معادلهٔ خط', 96), (2, 'شیب خط و عرض از مبدأ', 102), (3, 'دستگاه معادله‌های خطی', 108), )), (7, 'فصل ۷: عبارت‌های گویا', 113, 129, ( (1, 'معرفی و ساده کردن عبارت‌های گویا', 114), (2, 'محاسبات عبارت‌های گویا', 119), (3, 'تقسیم چندجمله‌ای‌ها', 126), )), (8, 'فصل ۸: حجم و مساحت', 130, 143, ( (1, 'حجم و مساحت کره', 131), (2, 'حجم هرم و مخروط', 135), (3, 'سطح و حجم', 140), )), ), ) MATH_10 = Textbook( subject='ریاضی', grade='دهم', grade_label='دهم', filename='ریاضی دهم.pdf', page_offset=6, chapters=_ch( (1, 'فصل ۱: مجموعه، الگو و دنباله', 1, 27, ( (1, 'مجموعه‌های متناهی و نامتناهی', 2), (2, 'متمم یک مجموعه', 8), (3, 'الگو و دنباله', 14), (4, 'دنباله‌های حسابی و هندسی', 21), )), (2, 'فصل ۲: مثلثات', 28, 46, ( (1, 'نسبت‌های مثلثاتی', 29), (2, 'دایرهٔ مثلثاتی', 36), (3, 'روابط بین نسبت‌های مثلثاتی', 42), )), (3, 'فصل ۳: توان‌های گویا و عبارت‌های جبری', 47, 68, ( (1, 'ریشه و توان', 48), (2, 'ریشهٔ nام', 54), (3, 'توان‌های گویا', 59), (4, 'عبارت‌های جبری', 62), )), (4, 'فصل ۴: معادله‌ها و نامعادله‌ها', 69, 93, ( (1, 'معادلهٔ درجه دوم و روش‌های مختلف حل آن', 70), (2, 'سهمی', 78), (3, 'تعیین علامت', 83), )), (5, 'فصل ۵: تابع', 94, 117, ( (1, 'مفهوم تابع و بازنمایی‌های آن', 95), (2, 'دامنه و برد توابع', 101), (3, 'انواع تابع', 109), )), (6, 'فصل ۶: شمارش، بدون شمردن', 118, 140, ( (1, 'شمارش', 119), (2, 'جایگشت', 127), (3, 'ترکیب', 133), )), (7, 'فصل ۷: آمار و احتمال', 141, 170, ( (1, 'احتمال یا اندازه‌گیری شانس', 142), (2, 'مقدمه‌ای بر علم آمار، جامعه و نمونه', 152), (3, 'متغیر و انواع آن', 159), )), ), ) # پایه‌های ششم و هفتم را تیمِ دیگر ingest کرده و روی Qdrant هستند؛ فایلِ PDF # برایشان لازم نیست و بازه‌ی صفحه هم نداریم (نقاطشان شماره‌ی صفحه ندارند). فقط # فصل‌بندی ثبت می‌شود تا `seed_curriculum_from_textbook` بتواند برایشان برنامه‌ی # درسی بسازد؛ `filename` خالی یعنی «این کتاب از مسیرِ PDF ingest نمی‌شود». MATH_6 = Textbook( subject='ریاضی', grade='ششم', grade_label='ششم دبستان', filename='', page_offset=0, chapters=_ch( (1, 'فصل ۱: عدد و الگوهای عددی', 0, 0), (2, 'فصل ۲: کسر', 0, 0), (3, 'فصل ۳: اعداد اعشاری', 0, 0), (4, 'فصل ۴: تقارن و مختصات', 0, 0), (5, 'فصل ۵: اندازه گیری', 0, 0), (6, 'فصل ۶: تناسب و درصد', 0, 0), (7, 'فصل ۷: تقریب', 0, 0), ), ) MATH_7 = Textbook( subject='ریاضی', grade='هفتم', grade_label='هفتم', filename='', page_offset=0, chapters=_ch( (1, 'فصل ۱: راهبردهای حل مسئله', 0, 0), (2, 'فصل ۲: عددهای صحیح', 0, 0), (3, 'فصل ۳: جبر و معادله', 0, 0), (4, 'فصل ۴: هندسه و استدلال', 0, 0), (5, 'فصل ۵: شمارنده‌ها و اعداد اول', 0, 0), (6, 'فصل ۶: سطح و حجم', 0, 0), (7, 'فصل ۷: توان و جذر', 0, 0), (8, 'فصل ۸: بردار و مختصات', 0, 0), (9, 'فصل ۹: آمار و احتمال', 0, 0), ), ) # ── حسابان ───────────────────────────────────────────────────────────── # «حسابان» کتابِ رشته‌ی ریاضی‌فیزیک است، نه «ریاضی» عمومی. subject عمداً # «حسابان» است تا collection جدا بماند: هم‌نامیِ آن با «ریاضی یازدهم» # (کتابِ تجربی) دو کتابِ متفاوت را سرِ یک collection می‌انداخت. # ⚠️ هر subjectِ تازه‌ی ریاضی‌وار باید به known_collections.MATH_SUBJECTS هم # اضافه شود، وگرنه retrieval آن را «درسِ متنی» می‌بیند و به‌جای `chapter` # روی `lesson_number` فیلتر می‌کند — یعنی بی‌سروصدا هیچ‌وقت چیزی برنمی‌گرداند. # # بازه‌ها از فهرستِ خودِ کتاب (PDF صفحه‌ی ۵) درآمده‌اند و page_offset با # تطبیقِ صفحه‌ی ۱ کتاب روی صفحه‌ی ۷ PDF تأیید شده است. CALCULUS_11 = Textbook( subject='حسابان', grade='یازدهم', grade_label='یازدهم', filename='حسابان یازدهم.pdf', page_offset=6, chapters=_ch( (1, 'فصل ۱: جبر و معادله', 1, 36, ( (1, 'مجموع جمله‌های دنباله‌های حسابی و هندسی', 2), (2, 'معادله‌های درجه دوم', 7), (3, 'معادله‌های گویا و گنگ', 17), (4, 'قدر مطلق و ویژگی‌های آن', 23), (5, 'آشنایی با هندسه تحلیلی', 29), )), (2, 'فصل ۲: تابع', 37, 70, ( (1, 'آشنایی بیشتر با تابع', 38), (2, 'انواع توابع', 44), (3, 'وارون تابع', 54), (4, 'اعمال روی توابع', 63), )), (3, 'فصل ۳: توابع نمایی و لگاریتمی', 71, 90, ( (1, 'تابع نمایی', 72), (2, 'تابع لگاریتمی و لگاریتم', 80), (3, 'ویژگی‌های لگاریتم و حل معادله‌های لگاریتمی', 86), )), (4, 'فصل ۴: مثلثات', 91, 112, ( (1, 'رادیان', 92), (2, 'نسبت‌های مثلثاتی برخی زوایا', 98), (3, 'توابع مثلثاتی', 105), (4, 'روابط مثلثاتی مجموع و تفاضل زوایا', 110), )), (5, 'فصل ۵: حد و پیوستگی', 113, 151, ( (1, 'مفهوم حد و فرایندهای حدی', 114), (2, 'حدهای یک‌طرفه (حد چپ و حد راست)', 123), (3, 'قضایای حد', 130), (4, 'محاسبه حد توابع کسری', 141), (5, 'پیوستگی', 145), )), ), ) CALCULUS_12 = Textbook( subject='حسابان', grade='دوازدهم', grade_label='دوازدهم', filename='حسابان دوازدهم.pdf', page_offset=6, chapters=_ch( (1, 'فصل ۱: تابع', 1, 22, ( (1, 'تبدیل نمودار توابع', 2), (2, 'تابع درجه سوم، توابع یکنوا و بخش‌پذیری و تقسیم', 13), )), (2, 'فصل ۲: مثلثات', 23, 44, ( (1, 'تناوب و تانژانت', 24), (2, 'معادله‌های مثلثاتی', 35), )), (3, 'فصل ۳: حدهای نامتناهی ـ حد در بی‌نهایت', 45, 70, ( (1, 'حدهای نامتناهی', 46), (2, 'حد در بی‌نهایت', 59), )), (4, 'فصل ۴: مشتق', 71, 110, ( (1, 'آشنایی با مفهوم مشتق', 72), (2, 'مشتق‌پذیری و پیوستگی', 84), (3, 'آهنگ متوسط تغییر و آهنگ لحظه‌ای تغییر', 102), )), (5, 'فصل ۵: کاربردهای مشتق', 111, 146, ( (1, 'اکسترمم‌های یک تابع و توابع صعودی و نزولی', 112), (2, 'جهت تقعر نمودار یک تابع و نقطه‌ی عطف آن', 127), (3, 'رسم نمودار تابع', 137), )), ), ) # ── هندسه، آمار و احتمال، گسسته (۲۰۲۶-۱۰-۱۱؛ درخواستِ سینیور) ───────────── # کتاب‌های رشته‌ی ریاضی‌فیزیک که کنارِ ریاضی/حسابانِ همان پایه می‌آیند، پس هر # کدام subject و collectionِ خودش را دارد (همان دلیلِ «حسابان»). # فهرست و آفست از خودِ PDFها خوانده شد (فهرست چشمی، آفست با تطبیقِ صفحه‌ی بازکننده‌ی # فصل با متنِ همان صفحه‌ی PDF): هندسه‌ها و گسسته چاپی=PDF (۰) و (۸)؛ آمار صفحه‌ی ۱ # چاپی روی PDF ۷ است (آفست ۶، مثلِ حسابان). ⚠️ آخرین صفحه‌ی هر فصل «یکی قبل از فصل # بعد» است و آخرین فصل تا قبل از «منابع»؛ مرزها با ingest و شمارشِ chunk وارسی شوند. GEOMETRY_10 = Textbook( subject='هندسه', grade='دهم', grade_label='دهم', filename='هندسه دهم.pdf', page_offset=0, chapters=_ch( (1, 'فصل ۱: ترسیم‌های هندسی و استدلال', 9, 28, ( (1, 'ترسیم‌های هندسی', 10), (2, 'استدلال', 17), )), (2, 'فصل ۲: قضیه‌ی تالس، تشابه و کاربردهای آن', 29, 52, ( (1, 'نسبت و تناسب در هندسه', 30), (2, 'قضیه‌ی تالس', 34), (3, 'تشابه مثلث‌ها', 38), (4, 'کاربردهایی از قضیه‌ی تالس و تشابه مثلث‌ها', 45), )), (3, 'فصل ۳: چندضلعی‌ها', 53, 76, ( (1, 'چندضلعی‌ها و ویژگی‌هایی از آن‌ها', 54), (2, 'مساحت و کاربردهای آن', 65), )), (4, 'فصل ۴: تجسم فضایی', 77, 96, ( (1, 'خط، نقطه و صفحه', 78), (2, 'تفکر تجسمی', 87), )), ), ) GEOMETRY_11 = Textbook( subject='هندسه', grade='یازدهم', grade_label='یازدهم', filename='هندسه یازدهم.pdf', page_offset=0, chapters=_ch( (1, 'فصل ۱: دایره', 9, 30, ( (1, 'مفاهیم اولیه و زاویه‌ها در دایره', 10), (2, 'رابطه‌های طولی در دایره', 18), (3, 'چندضلعی‌های محاطی و محیطی', 24), )), (2, 'فصل ۲: تبدیل‌های هندسی و کاربردها', 31, 58, ( (1, 'تبدیل‌های هندسی', 32), (2, 'کاربرد تبدیل‌ها', 50), )), (3, 'فصل ۳: روابط طولی در مثلث', 59, 80, ( (1, 'قضیه‌ی سینوس‌ها', 60), (2, 'قضیه‌ی کسینوس‌ها', 64), (3, 'قضیه‌ی نیمسازهای زوایای داخلی و محاسبه‌ی طول نیمسازها', 68), (4, 'قضیه‌ی هرون (محاسبه‌ی ارتفاع‌ها و مساحت مثلث)', 71), )), ), ) GEOMETRY_12 = Textbook( subject='هندسه', grade='دوازدهم', grade_label='دوازدهم', filename='هندسه دوازدهم.pdf', page_offset=0, chapters=_ch( (1, 'فصل ۱: ماتریس و کاربردها', 9, 32, ( (1, 'ماتریس و اعمال روی ماتریس‌ها', 10), (2, 'وارون ماتریس و دترمینان', 22), )), (2, 'فصل ۲: آشنایی با مقاطع مخروطی', 33, 60, ( (1, 'آشنایی با مقاطع مخروطی و مکان هندسی', 34), (2, 'دایره', 40), (3, 'بیضی و سهمی', 47), )), (3, 'فصل ۳: بردارها', 61, 86, ( (1, 'معرفی فضای R³', 62), (2, 'ضرب داخلی و ضرب خارجی بردارها', 77), )), ), ) STATS_11 = Textbook( subject='آمار و احتمال', grade='یازدهم', grade_label='یازدهم', filename='آمار و احتمال یازدهم.pdf', page_offset=6, chapters=_ch( (1, 'فصل ۱: آشنایی با مبانی ریاضیات', 1, 34, ( (1, 'آشنایی با منطق ریاضی', 2), (2, 'جبر مجموعه‌ها', 16), )), (2, 'فصل ۲: احتمال', 35, 68, ( (1, 'مبانی احتمال', 36), (2, 'احتمال غیرهم‌شانس', 44), (3, 'احتمال شرطی', 48), (4, 'پیشامدهای مستقل و وابسته', 63), )), (3, 'فصل ۳: آمار توصیفی', 69, 96, ( (1, 'توصیف و نمایش داده‌ها', 70), (2, 'معیارهای گرایش به مرکز', 80), (3, 'معیارهای پراکندگی', 87), )), (4, 'فصل ۴: آمار استنباطی', 97, 121, ( (1, 'گردآوری داده‌ها', 98), (2, 'برآورد', 112), )), ), ) DISCRETE_12 = Textbook( subject='گسسته', grade='دوازدهم', grade_label='دوازدهم', filename='گسسته دوازدهم.pdf', page_offset=8, chapters=_ch( (1, 'فصل ۱: آشنایی با نظریه‌ی اعداد', 1, 30, ( (1, 'استدلال ریاضی', 2), (2, 'بخش‌پذیری در اعداد صحیح', 9), (3, 'هم‌نهشتی در اعداد صحیح و کاربردها', 18), )), (2, 'فصل ۲: گراف و مدل‌سازی', 31, 54, ( (1, 'معرفی گراف', 32), (2, 'مدل‌سازی با گراف', 43), )), (3, 'فصل ۳: ترکیبیات (شمارش)', 55, 84, ( (1, 'مباحثی در ترکیبیات', 56), (2, 'روش‌هایی برای شمارش', 73), )), ), ) TEXTBOOKS: dict[str, Textbook] = { b.key: b for b in (MATH_3, MATH_4, MATH_5, MATH_6, MATH_7, MATH_8, MATH_9, MATH_10, CALCULUS_11, CALCULUS_12, GEOMETRY_10, GEOMETRY_11, GEOMETRY_12, STATS_11, DISCRETE_12) } # کتاب‌هایی که واقعاً فایلِ PDF دارند و `rag_ingest_book` رویشان کار می‌کند. INGESTABLE = {b.key: b for b in TEXTBOOKS.values() if b.filename} def get_textbook(subject: str, grade: str) -> Textbook | None: return TEXTBOOKS.get(f'{(subject or "").strip()} {(grade or "").strip()}') def chapter_titles(book: Textbook) -> dict[int, str]: return {c.number: c.title for c in book.chapters}