(cleaned_pages: list[dict[str, Any]])
| 173 | |
| 174 | |
| 175 | def build_sections(cleaned_pages: list[dict[str, Any]]) -> list[dict[str, Any]]: |
| 176 | sections: list[dict[str, Any]] = [] |
| 177 | |
| 178 | part_title = "" |
| 179 | chapter_title = "" |
| 180 | section_title = "" |
| 181 | |
| 182 | curr_lines: list[str] = [] |
| 183 | curr_start_page: int | None = None |
| 184 | curr_end_page: int | None = None |
| 185 | |
| 186 | def flush_section() -> None: |
| 187 | nonlocal curr_lines, curr_start_page, curr_end_page |
| 188 | text = normalize_text("\n".join(curr_lines)) |
| 189 | if not text: |
| 190 | curr_lines = [] |
| 191 | curr_start_page = None |
| 192 | curr_end_page = None |
| 193 | return |
| 194 | |
| 195 | sec_idx = len(sections) + 1 |
| 196 | sections.append( |
| 197 | { |
| 198 | "section_id": f"S{sec_idx:05d}", |
| 199 | "part": part_title, |
| 200 | "chapter": chapter_title, |
| 201 | "section": section_title, |
| 202 | "page_start": curr_start_page, |
| 203 | "page_end": curr_end_page, |
| 204 | "text": text, |
| 205 | } |
| 206 | ) |
| 207 | curr_lines = [] |
| 208 | curr_start_page = None |
| 209 | curr_end_page = None |
| 210 | |
| 211 | for p in cleaned_pages: |
| 212 | page_num = int(p["page"]) |
| 213 | for line in p["lines"]: |
| 214 | s = normalize_line(line) |
| 215 | if not s: |
| 216 | curr_lines.append("") |
| 217 | continue |
| 218 | |
| 219 | heading = classify_heading(s) |
| 220 | if heading is not None: |
| 221 | # Start a new semantic section when encountering structural headings. |
| 222 | flush_section() |
| 223 | if heading["level"] == "part": |
| 224 | part_title = heading["title"] |
| 225 | chapter_title = "" |
| 226 | section_title = "" |
| 227 | elif heading["level"] == "chapter": |
| 228 | chapter_title = heading["title"] |
| 229 | section_title = "" |
| 230 | elif heading["level"] in {"section", "snippet", "heading"}: |
| 231 | section_title = heading["title"] |
| 232 |
no test coverage detected