p = 'backend/app/services/citations.py'
s = open(p).read()

s = s.replace('''#: Written once, used wherever the corpus names the same book differently.
#: Vancouver order — authors, title, edition, publisher, year — because that is
#: what a reference list in medicine looks like and it reads short.
#: authors, title, edition, year.''', '''#: authors, title, edition, year.''')

old = s[s.index('def cite(entry: dict) -> str | None:'):]
new = '''def source_line(entry: dict) -> str | None:
    """The book, without the topic or the page. None if it is not a source."""
    raw = (entry or {}).get("title") or ""
    key = _key(raw)
    if key in JUNK:
        return None
    for prefix, book in ALIASES:
        if prefix in key:
            return book_line(book)
    topic = UPTODATE.match(raw.strip())
    if topic:
        return f"{topic.group(1).strip()}. UpToDate, Wolters Kluwer"
    # Unmapped: a real source with an unhelpful name. Cleaned, and given its
    # author where the corpus recorded one, rather than thrown away.
    title = _tidy_title(raw)
    if not title:
        return None
    author = (entry.get("author") or "").strip().strip(";").strip()
    if author and author.lower() not in ("vitalsource download", "camscanner", "meiersa"):
        people = ", ".join(part.strip() for part in re.split(r";", author) if part.strip())
        return f"{people}. {title}"
    return title


def cite(entry: dict, topic: str | None = None) -> str | None:
    """One reference line, in the house style.

        Periventricular leukomalacia — Kliegman R, ed. Nelson Textbook of
        Pediatrics, 22nd ed., 2024, p. 1069.

    The topic in front is what that page was read for. Without it a list of
    fifteen lines is fifteen books and no way to tell which one answers the
    question you have.
    """
    book = source_line(entry)
    if not book:
        return None
    page = entry.get("page")
    if page is None:
        pages = entry.get("pages") or []
        page = pages[0] if len(pages) == 1 else None
    where = f", p. {page}" if isinstance(page, int) else ""
    head = " ".join((topic or "").split())
    return f"{head} — {book}{where}." if head else f"{book}{where}."


#: A chunk usually opens by naming what it is about, in bold or as a heading.
#: That is the topic; nothing else in the corpus records one.
LEAD_BOLD = re.compile(r"^\\s*\\*\\*(.{3,80}?)\\*\\*")
LEAD_HEAD = re.compile(r"^\\s*#{1,6}\\s+(.{3,80})$", re.M)


def topic_of(text: str, fallback: str = "") -> str:
    """What a passage announces itself as, for the head of its reference line.

    A trailing "(PVL) is a disorder of..." is not part of the name, so the
    bold run is cut at the first sentence-like break. When a chunk opens
    mid-paragraph — which many do — there is nothing to find and the article's
    own subject stands in, which is what a reference list normally says anyway.
    """
    for pattern in (LEAD_BOLD, LEAD_HEAD):
        found = pattern.search(text or "")
        if not found:
            continue
        name = " ".join(found.group(1).split()).strip(" .:;,-—")
        name = re.split(r"\\s+(?:is|are|was|were|refers|describes|means)\\b", name)[0].strip()
        if len(name) >= 3:
            return name[:90]
    return " ".join((fallback or "").split())[:90]


def tidy(refs: list[dict] | None, limit: int = 16, topic: str | None = None) -> list[dict]:
    """A reference list a reader can use, out of what the corpus recorded.

    One entry per book and page. Junk goes; the same book named two ways is
    still the same book, so two spellings at one page are one line.
    """
    out: dict[str, dict] = {}
    for entry in refs or []:
        if not isinstance(entry, dict):
            continue
        # An entry carrying several pages is several entries: the old shape
        # collapsed them, and a page list is not somewhere a reader can look.
        pages = entry.get("pages")
        spread = ([{**entry, "page": page} for page in pages]
                  if isinstance(pages, list) and pages else [entry])
        for one in spread:
            head = one.get("topic") or topic
            text = cite(one, head)
            if not text:
                continue
            out.setdefault(text, {
                "text": text,
                "topic": " ".join((head or "").split()) or None,
                "title": one.get("title"),
                "author": one.get("author"),
                "page": one.get("page") if isinstance(one.get("page"), int) else None,
            })
    # By the book, then by the page within it — the order somebody reads a
    # reference list in, and it puts the several pages of one book together.
    def order(row: dict) -> tuple:
        book = source_line({"title": row.get("title"), "author": row.get("author")}) or ""
        return (book, row.get("page") if row.get("page") is not None else -1)

    return sorted(out.values(), key=order)[:limit]
'''
s = s.replace(old, new)
open(p, 'w').write(s)
print('written')
