Test_AI_writing / tests /test_tools_tabs.py
unknown
Claude Opus 5.5
Journal Finder: title-only search, journals or conferences in one step, conference location, result cards
346458d
Raw History Blame Contribute Delete
16.2 kB
"""Journal Formatter and Journal Finder tabs (no network: the journal lookup and the search itself are not run)."""
import json
import os
import sys
import zipfile
from pathlib import Path
import pytest
import finder_tab as FND
import formatter_tab as FMT
ROOT = Path(__file__).resolve().parent.parent
progress = lambda *a, **k: None
def _manuscript(tmp_path):
from docx import Document
d = Document()
d.add_heading("A Hybrid Deep Learning Framework for Intrusion Detection", 0)
d.add_paragraph("Ravi Kumar, Priya Sharma")
d.add_paragraph("Abstract: This study proposes a hybrid model that reaches 98.4% accuracy on a public dataset.")
d.add_heading("Introduction", 1)
d.add_paragraph("Medical devices are increasingly connected (Smith, 2020). Prior work used shallow models.")
d.add_heading("References", 1)
d.add_paragraph("Smith, J. (2020). Deep learning for intrusion detection. Journal of Network Security, 12(3), "
"45-67.")
p = tmp_path / "my paper.docx"
d.save(str(p))
return str(p)
# ---------------------------------------------------------------- formatter
def test_formatter_engine_is_available():
assert FMT.AVAILABLE, FMT.WHY
assert len(FMT._preset_choices()) > 10 and FMT._preset_key(FMT.AUTO) is None
def test_format_gives_word_latex_report_and_an_overleaf_button(tmp_path):
style = FMT.PRESETS["ieee_journal"]["name"]
status, dl, docx, zip_, md, overleaf, report = FMT.format_doc(_manuscript(tmp_path), "", style, None, "", "", True,
True, False, FMT.REF_STYLES[0], FMT.CITE_MODES[0],
progress)
assert "Formatted as" in status and "checks passed" in status
assert docx.endswith("my_paper_formatted.docx") and Path(docx).stat().st_size > 5000
names = zipfile.ZipFile(zip_).namelist()
assert "main.tex" in names and not any(n.startswith("/") for n in names) # main.tex at the top level
main = zipfile.ZipFile(zip_).read("main.tex").decode("utf-8")
assert "\\documentclass" in main and "IEEEtran" in main and "\\begin{document}" in main
assert Path(md).read_text(encoding="utf-8").startswith("# Formatting report") and "Compliance" in report
# Overleaf: instructions only; the customer downloads the zip and uploads it himself
assert "Upload Project" in overleaf and "<form" not in overleaf and "overleaf.com/docs" not in overleaf
# download buttons that carry the files (the file box's link is blocked on a custom domain)
assert dl.count('class="cs-dl"') == 3 and 'download="my_paper_formatted.docx"' in dl
assert 'download="my_paper_latex.zip" href="data:application/zip;base64,' in dl and "Download report" in dl
def test_format_needs_a_manuscript_and_a_target(tmp_path):
import gradio as gr
with pytest.raises(gr.Error):
FMT.format_doc(None, "IEEE Access", FMT.AUTO, None, "", "", True, True, False, "", "", progress)
with pytest.raises(gr.Error):
FMT.format_doc(_manuscript(tmp_path), "", FMT.AUTO, None, "", "", True, True, False, "", "", progress)
def test_no_open_in_overleaf_button_only_instructions(tmp_path):
p = tmp_path / "paper_latex.zip"
with zipfile.ZipFile(p, "w") as z:
z.writestr("main.tex", "x")
h = FMT.overleaf_html(str(p))
assert "New Project → Upload Project" in h and "main.tex" in h
assert "<form" not in h and "<a " not in h and "Open in Overleaf" not in h and "snip_uri" not in h
assert FMT.overleaf_html(None) == ""
assert not hasattr(FMT, "public_url") # nothing hands files to Overleaf any more
# ---------------------------------------------------------------- finder
def test_finder_files_and_data_are_in_place():
assert FND.AVAILABLE
data = {f.name for f in (ROOT / "finder" / "data").iterdir()}
assert {"ext_list_Jul_2026.xlsx", "scimagojr_2025.csv", "J_Medline.txt", "core_conference_ranks.csv"} <= data
assert any("ABDC" in n for n in data)
def test_shipped_parsed_lists_are_used_not_rebuilt():
data = ROOT / "finder" / "data"
assert FND.freshen_data_caches() >= 4
for src, cache in ((data / "ext_list_Jul_2026.xlsx", data / "ext_list_Jul_2026.parsed.pkl"),
(data / "scimagojr_2025.csv", data / "scimagojr_2025.parsed.pkl"),
(data / "J_Medline.txt", data / ".pubmed_J_Medline.pkl")):
assert cache.exists() and cache.stat().st_mtime >= src.stat().st_mtime, cache.name
def test_both_tabs_take_the_suites_status_panel_and_log_their_runs():
import inspect
for mod, tool in ((FMT, "Journal Formatter"), (FND, "Journal Finder")):
src = inspect.getsource(mod.build)
assert 'ui["run_live"]' in src and 'ui["wait"]' in src and 'ui["job"](status)' in src
assert 'ui["log"]' in src and tool in src and 'ui["user"]' in src
def test_job_options_map_to_the_pipeline_flags():
sys.path.insert(0, str(ROOT / "finder"))
import run_job
a = run_job.Args(title="A study of things", terms=["x"], limit=20, top=5, acceptance=False, turnaround=True,
semantic=False, apc=True, quartiles=[1, 2], require_scopus=True, abdc_ranks=["A*", "A"],
fast_days=60, sort_by="speed", special_issues=False)
assert a.title == ["A", "study", "of", "things"] and a.term == ["x"] and a.limit == 20 and a.top == 5
assert a.no_scrape is True and a.no_turnaround is False and a.no_semantic is True and a.no_apc is False
assert a.fast is True and a.quartile == [1, 2] and a.abdc_ranks == ["A*", "A"] and a.fast_days == 60
assert a.sort_by == "speed" and callable(a.progress)
def test_shortlist_table_is_readable(tmp_path):
csv = tmp_path / "shortlist.csv"
csv.write_text("display_name,publisher,quartile,quartile_is_proxy,abdc_rank,in_pubmed,acceptance_rate_used,"
"acceptance_days,acceptance_speed,apc_status,apc_usd_est,term_matches,opportunity_score,homepage,"
"openalex_id\n"
"Computers & Security,Elsevier,1,False,,False,0.21,112,Moderate,paid,3200,2,71.234,"
"https://example.org/cs,S1\n"
"Journal of Things,Springer,2,True,A,True,,,,no APC,,1,55.5,https://example.org/jt,S2\n",
encoding="utf-8")
view, n = FND._table(csv)
assert n == 2 and list(view.columns)[:3] == ["Journal", "Publisher", "Quartile"] and "openalex_id" not in view
r1, r2 = view.iloc[0], view.iloc[1]
assert r1["Quartile"] == "Q1" and r1["Acceptance rate"] == "21%" and r1["APC"] == "$3,200" and r1["PubMed"] == "—"
assert r2["Quartile"] == "Q2 (estimate)" and r2["APC"] == "no APC" and r2["PubMed"] == "yes" and r2["ABDC"] == "A"
def test_pubmed_column_with_gaps_is_not_all_yes(tmp_path):
csv = tmp_path / "shortlist.csv"
csv.write_text(chr(10).join(["display_name,in_pubmed", "A,False", "B,True", "C,", ""]), encoding="utf-8")
view, _ = FND._table(csv)
assert list(view["PubMed"]) == ["—", "yes", "—"]
def test_download_buttons_hold_the_file(tmp_path):
import base64 as b64
from tools_common import download_link, download_row
f = tmp_path / "shortlist.csv"
f.write_text("a,b", encoding="utf-8")
h = download_link(str(f), "Download shortlist (.csv)")
assert 'download="shortlist.csv"' in h and "Download shortlist (.csv)" in h
assert b64.b64decode(h.split("base64,", 1)[1].split('"', 1)[0]) == b"a,b"
assert download_link(None) == "" and download_link(str(tmp_path / "missing.csv")) == ""
assert download_row((str(f), "One"), (None, "Two")).count("cs-dl") == 1 and download_row((None, "x")) == ""
def test_find_asks_for_a_title_and_a_subject():
import gradio as gr
blank = ["", [], "", ["Scopus"], [], [], 40, 10, 3, False, 90, "Best fit", True, True, True, True, 0, False]
with pytest.raises(gr.Error):
FND.find_journals(*blank, progress=progress)
blank[0] = "A hybrid deep learning framework for intrusion detection"
with pytest.raises(gr.Error):
FND.find_journals(*blank, progress=progress) # title but no subject kept
with pytest.raises(gr.Error):
FND.detect_terms("too short")
def test_both_tabs_are_part_of_the_app():
app = pytest.importorskip("app")
labels = json.dumps(app.demo.get_config_file(), default=str)
assert "Format my manuscript" in labels and "Find the best journals" in labels and "Where should the conference be?" in labels
assert "cs-go-formatter" in labels and "cs-go-finder" in labels
def test_a_reference_that_is_misread_keeps_the_authors_wording():
from formatmatch import refs as R
text = lambda raw, style: "".join(x.text for x in R.format_reference(R.parse_reference(raw), style, 1))
book = "Mehta, P. K., Monteiro, P. J. M. Concrete: Microstructure, Properties, and Materials. McGraw-Hill, 2014."
for raw in (book, "Neville, A. M. Properties of Concrete. Pearson, 2011.",
"de Larrard, F. Concrete Mixture Proportioning: A Scientific Approach. E & FN Spon, London, 1999.",
"Bureau of Indian Standards. IS 516: Method of tests for strength of concrete. New Delhi, 1959."):
assert not R.parse_reference(raw).confident
assert raw in text(raw, "apa") and raw in text("[7] " + raw, "ieee") # no surname or title word lost
assert R.lost_words(R.parse_reference(book)) == {"mehta", "monteiro"}
good = "Kumar, A., Rao, B., & Singh, C. (2019). Durability of blended cements. Cement and Concrete Research, 120, 45-56."
assert R.parse_reference(good).confident # a clean entry is still restyled
assert text(good, "ieee").startswith("[1]\tA. Kumar, B. Rao, and C. Singh, “Durability of blended cements,”")
# ---------------------------------------------------------------- LaTeX for Overleaf
def test_latex_checker_finds_what_stops_a_compile():
from formatmatch.texlint import lint
good = ("\\begin{document}\\section{A} Text \\cite{r1} $x$ \\begin{figure}\\includegraphics{figures/f.png}"
"\\caption{c}\\end{figure}\\begin{thebibliography}{1}\\bibitem{r1} X.\\end{thebibliography}\\end{document}")
assert lint(good, {"figures/f.png": b""}) == []
bad = lint("\\begin{document}\\begin{table}\\begin{tabular}{l}a\\end{table} {x \\cite{r9} "
"\\includegraphics{figures/missing.png}\\begin{thebibliography}{1}\\bibitem{r1} X."
"\\end{thebibliography}\\end{document}", {})
joined = " | ".join(bad)
for part in ("tabular", "opening brace", "missing.png is not in the project", "r9", "no caption"):
assert part in joined, (part, bad)
def test_two_column_pdf_lines_are_not_joined_across_the_gutter():
from formatmatch import read_pdf as RP
line = lambda x0, x1, y: {"bbox": (x0, y, x1, y + 10), "spans": [{"text": "word " * 8, "size": 10, "flags": 0}]}
blk = lambda x0, x1, ys: {"bbox": (x0, ys[0], x1, ys[-1] + 10), "lines": [line(x0, x1, y) for y in ys]}
ys = list(range(100, 300, 12))
left, right = blk(50, 295, ys), blk(310, 555, ys) # an IEEE page: 15 pt between the columns
g = RP._gutter([left, right], 612)
assert g and 295 <= g <= 310
merged = RP._merge_baselines([left, right], 10, g)
assert len(merged) == 2 and len(merged[0]["lines"]) == len(ys) # still two columns
assert RP._order_blocks(merged, 612, g)[0] is merged[0]
assert RP._gutter([blk(50, 555, ys)], 612) is None # a one-column page has no gutter
# without the gutter the same lines were glued together (the old behaviour this guards against)
assert len(RP._merge_baselines([blk(50, 295, ys), blk(310, 555, ys)], 10, None)) == 1
def test_pdf_pieces_cut_by_a_column_are_put_back_together():
from formatmatch import model as M, read_pdf as RP
B = lambda role, t: M.Block(role, runs=[M.Run(t)], orig=t)
assert RP._continues(B(M.REFERENCE, "[2] W. Tushar, T.K. Saha, Peer-to-peer trading"), B(M.REFERENCE, "in electricity networks, 2020."))
assert not RP._continues(B(M.REFERENCE, "[2] W. Tushar, Title, 2020."), B(M.REFERENCE, "[3] T. Morstyn, Title."))
assert RP._continues(B(M.PARA, "This paper fills these gaps with a personalised federated"), B(M.PARA, "MARL mechanism".lower()))
assert not RP._continues(B(M.PARA, "A finished sentence."), B(M.PARA, "another one"))
assert RP._continues(B(M.CAPTION, "LEARNING HYPERPARAMETERS (IDENTICAL FOR ALL"), B(M.PARA, "LEARNING METHODS)"))
assert RP._soft_hyphen("heteroge-", "neous") and not RP._soft_hyphen("peer-to-", "peer")
def test_sections_one_level_too_low_become_sections_and_floats_stay_in_the_column():
from formatmatch import model as M, pipeline as PL
H = lambda t, lvl: M.Block(M.HEADING, runs=[M.Run(t)], level=lvl, orig=t)
blocks = [M.Block(M.TITLE, runs=[M.Run("A Paper")]), H("Introduction", 2), H("Background", 3), H("Results", 2),
H("References", 1)]
rep = PL.Report()
PL._promote_headings(blocks, rep)
assert [b.level for b in blocks[1:]] == [1, 2, 1, 1] and any("moved up" in c for c in rep.changes)
ok = [H("Introduction", 1), H("Method", 2)]
PL._promote_headings(ok, PL.Report())
assert [b.level for b in ok] == [1, 2] # a sound outline is left alone
# figure, caption, figure, caption: each caption takes the figure before it
F = lambda n: M.Block(M.FIGURE, image=b"x" + bytes([n]), image_ext="png")
C = lambda n: M.Block(M.CAPTION, runs=[M.Run(f"cap {n}")], cap_kind="figure", cap_num=n, orig=f"Fig. {n}. cap {n}")
f1, c1, f2, c2 = F(1), C(1), F(2), C(2)
out = PL._pair_floats([f1, c1, f2, c2], {"captions": {}}, PL.Report())
assert [id(x) for x in out] == [id(f1), id(c1), id(f2), id(c2)]
def test_latex_keeps_equation_pictures_and_tables_inside_the_line():
from formatmatch import refs as R
src = open(os.path.join(os.path.dirname(FMT.__file__), "formatmatch", "render_latex.py"), encoding="utf-8").read()
assert "newcommand{" + chr(92) * 2 + "fmimg}" in src and "newcommand{" + chr(92) * 2 + "fmfit}" in src
assert "includegraphics[height={h" not in src # the fixed-height picture that overflowed the column
r = R.parse_reference("[2] W. Tushar, T.K. Saha, H.V. Poor, Peer-to-peer trading in electricity networks: An "
"overview, IEEE Transactions on Smart Grid 11 (4) (2020) 3185–3200.")
assert r.confident and r.authors[0] == ("Tushar", "W.") and r.container == "IEEE Transactions on Smart Grid"
assert (r.volume, r.issue, r.year, r.pages) == ("11", "4", "2020", "3185–3200")
def test_long_equations_are_fitted_to_the_column_in_latex_and_word(tmp_path):
import io
from PIL import Image
from formatmatch.render_latex import LatexRenderer as LR
assert LR.fit_math("a+b", False) == "\\fmmath{a+b}"
assert LR.fit_math("a+b", True).startswith("\\fmmath[\\dimexpr\\linewidth-3.2em\\relax]{a+b")
pic = "\\vcenter{\\hbox{\\fmimg{9.0pt}{equations/eq1.png}}}"
assert LR.fit_math(pic, True) == pic and LR.fit_math("", False) == "" # pictures fit themselves
# Word: a picture 10 in wide is shrunk to the column; a small one keeps its size
from formatmatch import render_docx as RD
import docx
buf = io.BytesIO(); Image.new("RGB", (2000, 100), "white").save(buf, "PNG")
class Fake:
_col_width_cm = lambda self: 8.0
_picture = RD.DocxRenderer._picture if hasattr(RD, "DocxRenderer") else None
cls = next(v for v in vars(RD).values() if isinstance(v, type) and hasattr(v, "_picture"))
Fake._picture = cls._picture
d = docx.Document(); p = d.add_paragraph()
Fake()._picture(p, {"blob": buf.getvalue(), "h": 0.5}) # 0.5 in tall -> 10 in wide
Fake()._picture(p, {"blob": buf.getvalue(), "h": 0.05}) # 1 in wide: fits
wide, small = d.inline_shapes[0], d.inline_shapes[1]
assert abs(wide.width.inches - 8.0 / 2.54 * 0.82) < 0.02 and wide.height.inches < 0.2
assert abs(small.height.inches - 0.05) < 0.005