From 3208ac7251cbaee521ca3b8c2501acd19a55aff5 Mon Sep 17 00:00:00 2001 From: Chandler Swift Date: Wed, 23 Jul 2025 00:24:24 -0500 Subject: [PATCH] Add initial attempt at make_scripture command Heavily changed from this output: https://chatgpt.com/share/68807254-9e40-800c-b6ac-5695710dd5d8 Currently does not respect verses; only chapters, despite help text saying otherwise. --- make_scripture.py | 116 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100755 make_scripture.py diff --git a/make_scripture.py b/make_scripture.py new file mode 100755 index 0000000..fa36fc3 --- /dev/null +++ b/make_scripture.py @@ -0,0 +1,116 @@ +#!/usr/bin/env nix-shell +#! nix-shell -i python3 +#! nix-shell -p python3 python3Packages.requests + +import requests +import sys + +url_base = "https://bible.chandlerswift.com/api/" +# url_base = "https://bible.helloao.org/api/" + + +translation="BSB" # Berean Standard Bible + +def help(): + print(f""" + usage: {sys.argv[0]} + + Generates a LaTeX rendering of the given bible verses + + For example, `{sys.argv[0]} Genesis 1:1 2:3` + + Note that the start and end can span chapters of a book, but not books. + """) + +if len(sys.argv) != 4: + help() + raise SystemExit + +book = sys.argv[1] +start_chapter, start_verse = map(int, sys.argv[2].split(':')) +end_chapter, end_verse = map(int, sys.argv[3].split(':')) + +books = requests.get(f"{url_base}{translation}/books.json").json() + +book_id = [b for b in books['books'] if b['name'] == book or b['commonName'] == book][0]['id'] + +import json + +def escape_latex(text): + return (text.replace('\\', r'\textbackslash{}') + .replace('&', r'\&') + .replace('%', r'\%') + .replace('$', r'\$') + .replace('#', r'\#') + .replace('_', r'\_') + .replace('{', r'\{') + .replace('}', r'\}') + .replace('~', r'\textasciitilde{}') + .replace('^', r'\textasciicircum{}')) + +def render_latex(json_data): + content = json_data['chapter']['content'] + footnotes = {note['noteId']: note for note in json_data['chapter'].get('footnotes', [])} + latex_lines = [] + + chapter_number = json_data['chapter']['number'] + first_verse_written = False + + def render_verse_content(parts): + result = [] + for part in parts: + if isinstance(part, str): + result.append(escape_latex(part)) + elif isinstance(part, dict): + if 'noteId' in part: + note = footnotes.get(part['noteId']) + if note: + result.append(rf"\footnote{{{escape_latex(note['text'])}}}") + elif 'lineBreak' in part and part['lineBreak']: + # Only add \\ if already inside a paragraph + result.append(r"\newline ") + elif 'heading' in part: + result.append(rf"\textit{{{escape_latex(part['heading'])}}}") + elif 'text' in part: + result.append(escape_latex(part['text'])) + return ' '.join(result) + + for element in content: + etype = element['type'] + if etype == 'heading': + heading_text = ' '.join(element['content']) + latex_lines.append(rf"\heading{{{escape_latex(heading_text)}}}") + latex_lines.append("") # Blank line to start new paragraph + elif etype == 'line_break': + # Blank line for paragraph break + latex_lines.append("") + elif etype == 'verse': + verse_number = element['number'] + verse_body = render_verse_content(element['content']) + + if not first_verse_written: + # First verse: show chapter number + latex_lines.append(rf"\ch{{{chapter_number}}}{verse_body}") + first_verse_written = True + else: + latex_lines.append(rf"\vs{{{verse_number}}}{verse_body}") + elif etype == 'hebrew_subtitle': + subtitle_parts = [] + for part in element['content']: + if isinstance(part, str): + subtitle_parts.append(escape_latex(part)) + elif isinstance(part, dict): + if 'noteId' in part and part['noteId'] in footnotes: + subtitle_parts.append(rf"\footnote{{{escape_latex(footnotes[part['noteId']]['text'])}}}") + elif 'text' in part: + subtitle_parts.append(escape_latex(part['text'])) + subtitle = ' '.join(subtitle_parts) + latex_lines.append(rf"\textit{{{subtitle}}}") + latex_lines.append("") + + return '\n'.join(latex_lines) + +for chapter in range(start_chapter, end_chapter + 1): + chapter_data = requests.get(f"{url_base}{translation}/{book_id}/{chapter}.json").json() + + print(render_latex(chapter_data))