st-johns-bulletins/make_scripture.py

121 lines
4.5 KiB
Python
Executable file

#!/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]} <book> <start verse> <end verse (inclusive)>
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']
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)
last_seen_verse = 0 # to start; TODO: does this include extra e.g. headers sometimes?
for element in content:
if element['type'] == 'verse':
last_seen_verse = element['number']
if chapter_number == start_chapter and last_seen_verse < start_verse:
continue
if chapter_number == end_chapter and last_seen_verse >= end_verse:
break
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 verse_number == 1:
# First verse: show chapter number
latex_lines.append(rf"\ch{{{chapter_number}}}{verse_body}")
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))