SPB Git forge

spb/uqo-chat

Public
14commits 1branches 0releases
1.4 MBsize
maindefault branch
17 days agolast push
Python 64.6% TypeScript 33.7% CSS 0.8%
2.8 KB · 51 lines python
Raw Blame History
1from app.rag.chunking import chunk_text, tokenize2from app.rag.ingest import parse_seance_html3from app.rag.retriever import BM25Index45HTML = """<html><body><main data-title="Séance 5 — Coût de reproduction et coût de remplacement">6<article>7<h2 id="s-5-1" class="h-section"><span class="h-num">5.1</span><span class="h-text">Cadrage</span><a class="h-anchor" href="#">#</a></h2>8<p>La formule fondamentale <span class="katex"><span class="katex-mathml"><math><semantics><mrow></mrow><annotation encoding="application/x-tex">V = V_T + C_N - D</annotation></semantics></math></span></span> structure la méthode du coût. Le terrain ne se déprécie pas.</p>9<div class="box box-definition"><div class="box-head"><span class="box-label">Définition</span><span class="box-title">Coût de reproduction</span></div><div class="box-body"><p>Coût de construire une réplique exacte du bâtiment avec les mêmes matériaux.</p></div></div>10<h2 id="s-5-2" class="h-section"><span class="h-num">5.2</span><span class="h-text">Le coût de remplacement</span></h2>11<p>Le coût de remplacement est le coût d'un bâtiment d'utilité équivalente construit selon les normes actuelles. Il est généralement inférieur au coût de reproduction pour un bâtiment ancien.</p>12</article></main></body></html>"""131415def test_tokenize_normalises_accents_and_plurals() -> None:16    assert tokenize("Dépréciations physiques") == tokenize("dépréciation physique")17    assert "le" not in tokenize("le coût de la valeur")181920def test_chunk_text_splits_long_text() -> None:21    text = "\n\n".join(f"Paragraphe {i} " + "mot " * 200 for i in range(20))22    chunks = chunk_text(text, module="M", section="S")23    assert len(chunks) > 124    assert all(c.module == "M" for c in chunks)252627def test_parse_seance_html_sections_and_katex() -> None:28    module, chunks = parse_seance_html(HTML, "IMM1033", "05")29    assert module.startswith("Séance 5")30    assert len(chunks) == 231    first = chunks[0]32    assert first.section.startswith("5.1")33    assert "$V = V_T + C_N - D$" in first.content34    assert "**Définition — Coût de reproduction**" in first.content35    assert first.url.endswith("/seance/05/#s-5-1")363738def test_bm25_search_ranks_relevant_section() -> None:39    _, chunks = parse_seance_html(HTML, "IMM1033", "05")4041    class Row:42        def __init__(self, i, c):  # noqa: ANN00143            self.id, self.course_code, self.module, self.section = str(i), "IMM1033", c.module, c.section44            self.page, self.url, self.content, self.visibility, self.embedding = c.page, c.url, c.content, "students", None4546    idx = BM25Index()47    idx.build([Row(i, c) for i, c in enumerate(chunks)])48    hits = idx.search("coût de remplacement normes actuelles", top_k=2)49    assert hits and hits[0].section.startswith("5.2")50    assert idx.search("coût", courses={"IMM1003"}) == []51