# ============================================================ # Project: EDGAR Research Database # Description: SEC EDGAR Research Infrastructure # # Authors: # - Manel Kammoun — manel.kammoun@uqo.ca # - Djerrycharli Tandjambianda — djerrycharli.tandjambianda@uqo.ca # - Simon-Pierre Boucher — simon-pierre.boucher@uqo.ca # # Institution: # Université du Québec en Outaouais (UQO) # # License: # Research / Academic Use # ============================================================ """Word-window chunker (re-created 2026-10-27; the original module was lost). Produces the same record layout as the existing ``filing_chunks`` table: ``chunk_id`` (uuid4), ``section_id``, ``chunk_text``, ``chunk_order``, ``word_count``, ``char_count``, ``start_char``, ``end_char``. Chunks are ``chunk_size`` words long with ``overlap`` words shared between consecutive chunks; a trailing chunk shorter than ``min_chunk_size`` words is merged into the previous one. """ from __future__ import annotations import uuid class TextChunker: def __init__( self, chunk_size: int = 512, overlap: int = 64, min_chunk_size: int = 50, ) -> None: if overlap >= chunk_size: raise ValueError("overlap must be smaller than chunk_size") self.chunk_size = chunk_size self.overlap = overlap self.min_chunk_size = min_chunk_size def chunk_text(self, text: str, section_id: str | None = None) -> list[dict]: words = text.split() if not words: return [] step = self.chunk_size - self.overlap windows: list[list[str]] = [] start = 0 while start < len(words): window = words[start:start + self.chunk_size] if windows and len(window) < self.min_chunk_size: # fold a tiny tail into the previous window tail = window[self.overlap:] if len(window) > self.overlap else [] windows[-1].extend(tail) break windows.append(window) if start + self.chunk_size >= len(words): break start += step chunks: list[dict] = [] cursor = 0 for order, window in enumerate(windows): chunk_text = " ".join(window) chunks.append({ "chunk_id": str(uuid.uuid4()), "section_id": section_id, "chunk_text": chunk_text, "chunk_order": order, "word_count": len(window), "char_count": len(chunk_text), "start_char": cursor, "end_char": cursor + len(chunk_text), }) cursor += len(chunk_text) + 1 return chunks def chunk_section(self, section: dict) -> list[dict]: text = section.get("text") or "" return self.chunk_text(text, section_id=section.get("section_id"))