Spaces:
Running
Running
unknown
Claude Opus 5.5
Journal Formatter and Journal Finder as native tabs of the suite (work in progress)
7ee2a84 Download formatmatch/engine.py from Rasheed/Test_AI_writing: direct link, hf CLI and curl.
- Browser
- Download file 7.88 kB
-
https://huggingface.co/spaces/Rasheed/Test_AI_writing/resolve/main/formatmatch/engine.py
- Command line
-
hf download hf://spaces/Rasheed/Test_AI_writing/formatmatch/engine.py
-
curl -L -o engine.py https://huggingface.co/spaces/Rasheed/Test_AI_writing/resolve/main/formatmatch/engine.py
7.88 kB
| """High-level API: build the target spec from all sources and format a manuscript.""" | |
| from __future__ import annotations | |
| import io | |
| import os | |
| import re | |
| import shutil | |
| import subprocess | |
| import tempfile | |
| import zipfile | |
| from dataclasses import dataclass, field | |
| from typing import Optional | |
| from .pipeline import Report, prepare | |
| from .read_any import read_any | |
| from .render_docx import render_docx | |
| from .render_latex import render_latex | |
| from .spec import DEFAULT_SPEC, deep_merge, get_preset, lookup_journal, parse_guidelines, parse_guidelines_llm | |
| from .template import analyze_docx_template, analyze_pdf_sample | |
| def dotx_to_docx(data: bytes) -> bytes: | |
| """Word templates (.dotx/.dotm) differ from .docx only in the main part's content type.""" | |
| src = zipfile.ZipFile(io.BytesIO(data)) | |
| buf = io.BytesIO() | |
| with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as out: | |
| for item in src.infolist(): | |
| content = src.read(item.filename) | |
| if item.filename == "[Content_Types].xml": | |
| content = re.sub(rb"application/vnd\.ms-word\.template\.macroEnabledTemplate\.main\+xml|" | |
| rb"application/vnd\.openxmlformats-officedocument\.wordprocessingml\.template\.main\+xml", | |
| b"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml", | |
| content) | |
| out.writestr(item, content) | |
| return buf.getvalue() | |
| def guidelines_text_from_file(name: str, data: bytes) -> str: | |
| ext = name.rsplit(".", 1)[-1].lower() | |
| if ext == "pdf": | |
| import pymupdf | |
| return "\n".join(p.get_text() for p in pymupdf.open(stream=data, filetype="pdf")) | |
| if ext in ("docx", "dotx"): | |
| import docx | |
| d = docx.Document(io.BytesIO(dotx_to_docx(data) if ext == "dotx" else data)) | |
| return "\n".join(p.text for p in d.paragraphs) | |
| if ext in ("html", "htm"): | |
| return html_to_text(data.decode("utf-8", "ignore")) | |
| return data.decode("utf-8", "ignore") | |
| def html_to_text(html: str) -> str: | |
| html = re.sub(r"(?is)<(script|style|nav|footer|header)[^>]*>.*?</\1>", " ", html) | |
| html = re.sub(r"(?i)<br\s*/?>|</p>|</li>|</h\d>|</div>", "\n", html) | |
| text = re.sub(r"<[^>]+>", " ", html) | |
| import html as h | |
| text = h.unescape(text) | |
| return re.sub(r"[ \t]+", " ", re.sub(r"\n\s*\n+", "\n\n", text)).strip() | |
| def fetch_guidelines_url(url: str, timeout=20) -> str: | |
| import requests | |
| r = requests.get(url, timeout=timeout, headers={"User-Agent": "Mozilla/5.0 (FormatMatch guideline reader)"}) | |
| r.raise_for_status() | |
| ctype = r.headers.get("content-type", "") | |
| if "pdf" in ctype or url.lower().endswith(".pdf"): | |
| return guidelines_text_from_file("g.pdf", r.content) | |
| return html_to_text(r.text) | |
| class FormatSource: | |
| preset: Optional[str] = None # preset key chosen/derived from journal name | |
| journal: Optional[dict] = None # lookup_journal() result | |
| guidelines: Optional[dict] = None # overrides from guidelines | |
| template_overrides: Optional[dict] = None # overrides from template/sample | |
| profile: object = None # TemplateProfile (docx templates only) | |
| manual: dict = field(default_factory=dict) # user edits in the UI | |
| found: list = field(default_factory=list) # human-readable detection notes | |
| def build_spec(src: FormatSource) -> dict: | |
| spec = get_preset(src.preset) if src.preset else DEFAULT_SPEC | |
| if src.guidelines: | |
| spec = deep_merge(spec, {k: v for k, v in src.guidelines.items() if not k.startswith("_")}) | |
| if src.template_overrides: | |
| spec = deep_merge(spec, {k: v for k, v in src.template_overrides.items() if not k.startswith("_")}) | |
| if src.manual: | |
| spec = deep_merge(spec, src.manual) | |
| if src.journal and src.journal.get("title"): | |
| spec["name"] = src.journal["title"] + (f" ({spec.get('name')})" if spec.get("name") else "") | |
| return spec | |
| def learn_template(name: str, data: bytes): | |
| """Returns (overrides, profile_or_None, found_notes).""" | |
| ext = name.rsplit(".", 1)[-1].lower() | |
| if ext in ("dotx", "dotm"): | |
| data = dotx_to_docx(data) | |
| ext = "docx" | |
| if ext == "docx": | |
| ov, prof = analyze_docx_template(data) | |
| return ov, prof, list(prof.found) | |
| if ext == "pdf": | |
| ov = analyze_pdf_sample(data) | |
| return ov, None, ov.pop("_found", []) | |
| if ext in ("doc", "odt", "rtf"): | |
| from .read_any import _office_to_docx | |
| d2 = _office_to_docx(data, ext) | |
| ov, prof = analyze_docx_template(d2) | |
| return ov, prof, list(prof.found) | |
| raise RuntimeError("Template/sample must be .docx, .dotx, .doc or a published-paper .pdf") | |
| class Result: | |
| docx: Optional[bytes] | |
| latex_zip: Optional[bytes] | |
| report: Report | |
| report_md: str | |
| spec: dict | |
| preview_pdf: Optional[bytes] = None | |
| def format_manuscript(filename: str, data: bytes, spec: dict, profile=None, want_docx=True, want_latex=True, | |
| crossref=False, progress=None, preview=False) -> Result: | |
| def prog(f, msg): | |
| if progress: | |
| progress(f, msg) | |
| prog(0.05, "Reading manuscript") | |
| doc = read_any(filename, data) | |
| prog(0.2, "Restructuring: headings, captions, citations, references") | |
| prepared, rep = prepare(doc, spec, crossref=crossref, | |
| progress=(lambda f, m: prog(0.2 + 0.4 * f, m)) if crossref else None) | |
| docx_bytes = latex_bytes = None | |
| if want_docx: | |
| prog(0.65, "Writing Word document") | |
| docx_bytes, w = render_docx(prepared, spec, profile) | |
| for x in w: | |
| rep.warn(x) | |
| if want_latex: | |
| prog(0.8, "Writing LaTeX project") | |
| lw = [] | |
| latex_bytes = render_latex(prepared, spec, rep.refs, lw) | |
| for x in lw: | |
| rep.warn(x) | |
| pdf = None | |
| if preview and docx_bytes: | |
| prog(0.9, "Rendering preview") | |
| pdf = docx_to_pdf(docx_bytes) | |
| prog(1.0, "Done") | |
| return Result(docx_bytes, latex_bytes, rep, report_markdown(rep, spec, filename), spec, pdf) | |
| def docx_to_pdf(data: bytes) -> Optional[bytes]: | |
| soffice = shutil.which("soffice") or shutil.which("libreoffice") | |
| if not soffice: | |
| return None | |
| with tempfile.TemporaryDirectory() as td: | |
| src = os.path.join(td, "out.docx") | |
| open(src, "wb").write(data) | |
| try: | |
| subprocess.run([soffice, "--headless", "--convert-to", "pdf", "--outdir", td, src], | |
| capture_output=True, timeout=180) | |
| except Exception: | |
| return None | |
| pdf = os.path.join(td, "out.pdf") | |
| return open(pdf, "rb").read() if os.path.exists(pdf) else None | |
| def report_markdown(rep: Report, spec: dict, filename: str) -> str: | |
| L = [f"# Formatting report — {filename}", "", f"**Target format:** {spec.get('name', 'Custom')}", ""] | |
| L += ["## Compliance checks", "", "| Check | Status | Detail |", "|---|---|---|"] | |
| for name, ok, detail in rep.checks: | |
| L.append(f"| {name} | {'✅ Pass' if ok else '⚠️ Fix'} | {detail} |") | |
| L += ["", "## Changes made", ""] + [f"- {c}" for c in rep.changes] | |
| if rep.warnings: | |
| L += ["", "## Needs your attention", ""] + [f"- {w}" for w in rep.warnings] | |
| S = spec | |
| L += ["", "## Format applied", "", | |
| f"- Page: {S['page']['size']}, {S['page']['columns']} column(s), margins (T/B/L/R) {S['page']['margins_cm']} cm", | |
| f"- Body: {S['font']['family']} {S['font']['size']} pt, line spacing {S['font']['line_spacing']}", | |
| f"- Headings: {S['headings']['scheme']} numbering", | |
| f"- References: {S['references']['style']}; citations: {S['citations']['mode']}", | |
| "", "_Generated by FormatMatch — CS Software Solutions (cssoftwaresolutions.in)_"] | |
| return "\n".join(L) | |