Add translation-aware passage queries
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
esv_api
|
esv_api
|
||||||
bibleref
|
bibleref
|
||||||
tests/test_offline_kjv
|
tests/test_offline_kjv
|
||||||
|
tests/test_passage_query
|
||||||
data/private/
|
data/private/
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|||||||
+22
-17
@@ -10,6 +10,7 @@ import ./api_bible
|
|||||||
import ./esv
|
import ./esv
|
||||||
import ./kjv
|
import ./kjv
|
||||||
import ./mev
|
import ./mev
|
||||||
|
import ./passage_query
|
||||||
|
|
||||||
proc formatMarkdown(raw, translation: string): string =
|
proc formatMarkdown(raw, translation: string): string =
|
||||||
var reference = ""
|
var reference = ""
|
||||||
@@ -95,7 +96,7 @@ proc fetchPassages(reference, translation: string, cfg: CombinedConfig): seq[str
|
|||||||
else:
|
else:
|
||||||
raise newException(ValueError,
|
raise newException(ValueError,
|
||||||
"unsupported translation '" & translation &
|
"unsupported translation '" & translation &
|
||||||
"'; supported translations: akjv, amp, esv, kjv, mev, nkjv, niv")
|
"'; supported translations: " & supportedTranslationsList())
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
const USAGE = """Usage:
|
const USAGE = """Usage:
|
||||||
@@ -115,6 +116,9 @@ Options:
|
|||||||
Select a specific translation. Supported values
|
Select a specific translation. Supported values
|
||||||
are 'akjv', 'amp', 'esv', 'kjv', 'mev',
|
are 'akjv', 'amp', 'esv', 'kjv', 'mev',
|
||||||
'nkjv', and 'niv'. Defaults to 'esv'.
|
'nkjv', and 'niv'. Defaults to 'esv'.
|
||||||
|
Individual references may override this with a
|
||||||
|
trailing marker, for example:
|
||||||
|
'John 3:16 (KJV); John 3:16 (ESV)'.
|
||||||
|
|
||||||
--esv-api-token <token> Provide the API token on the command line. By
|
--esv-api-token <token> Provide the API token on the command line. By
|
||||||
default this will be read either from the
|
default this will be read either from the
|
||||||
@@ -156,24 +160,25 @@ Options:
|
|||||||
cfgFileJson = parseFile(cfgFilePath)
|
cfgFileJson = parseFile(cfgFilePath)
|
||||||
|
|
||||||
let cfg = CombinedConfig(docopt: args, json: cfgFileJson)
|
let cfg = CombinedConfig(docopt: args, json: cfgFileJson)
|
||||||
let translation = cfg.getVal("translation", "esv").strip.toLowerAscii
|
let defaultTranslation = cfg.getVal("translation", "esv")
|
||||||
let reference = $args["<reference>"]
|
let reference = $args["<reference>"]
|
||||||
|
let queries = parsePassageQueries(reference, defaultTranslation)
|
||||||
|
|
||||||
let passages = fetchPassages(reference, translation, cfg)
|
var formattedPassages: seq[string] = @[]
|
||||||
|
for query in queries:
|
||||||
let formattedPassages =
|
for passage in fetchPassages(query.referenceText, query.translation, cfg):
|
||||||
case $args["--output-format"]:
|
formattedPassages.add(
|
||||||
of "plain":
|
case $args["--output-format"]:
|
||||||
passages --> map(formatPlain(it, translation))
|
of "plain":
|
||||||
of "reading":
|
formatPlain(passage, query.translation)
|
||||||
passages -->
|
of "reading":
|
||||||
map(formatPlain(it, translation, keepVerseNumbers = false))
|
formatPlain(passage, query.translation, keepVerseNumbers = false)
|
||||||
of "text":
|
of "text":
|
||||||
passages -->
|
passage.multiReplace([(re"\[(\d+)\]", "$1")])
|
||||||
map(it.multiReplace([(re"\[(\d+)\]", "$1")]))
|
of "raw":
|
||||||
of "raw": passages
|
passage
|
||||||
else:
|
else:
|
||||||
passages --> map(formatMarkdown(it, translation))
|
formatMarkdown(passage, query.translation))
|
||||||
|
|
||||||
echo formattedPassages.join("\p\p")
|
echo formattedPassages.join("\p\p")
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import std/strutils
|
||||||
|
|
||||||
|
import ./reference_parser
|
||||||
|
|
||||||
|
type PassageQuery* = object
|
||||||
|
reference*: PassageReference
|
||||||
|
translation*: string
|
||||||
|
|
||||||
|
const SupportedTranslations* = [
|
||||||
|
"akjv", "amp", "esv", "kjv", "mev", "niv", "nkjv"
|
||||||
|
]
|
||||||
|
|
||||||
|
proc supportedTranslationsList*(): string =
|
||||||
|
SupportedTranslations.join(", ")
|
||||||
|
|
||||||
|
proc normalizeTranslation*(translation: string): string =
|
||||||
|
result = translation.strip.toLowerAscii
|
||||||
|
|
||||||
|
for supported in SupportedTranslations:
|
||||||
|
if result == supported:
|
||||||
|
return
|
||||||
|
|
||||||
|
raise newException(ValueError,
|
||||||
|
"unsupported translation '" & translation &
|
||||||
|
"'; supported translations: " & supportedTranslationsList())
|
||||||
|
|
||||||
|
proc splitTrailingTranslationMarker(
|
||||||
|
input: string): tuple[referenceText: string, translation: string] =
|
||||||
|
|
||||||
|
let text = input.strip
|
||||||
|
if not text.endsWith(")"):
|
||||||
|
return (text, "")
|
||||||
|
|
||||||
|
let openIdx = text.rfind("(")
|
||||||
|
if openIdx < 0:
|
||||||
|
return (text, "")
|
||||||
|
|
||||||
|
let referenceText = text[0 ..< openIdx].strip
|
||||||
|
let translation = text[openIdx + 1 ..< text.len - 1].strip
|
||||||
|
if referenceText.len == 0 or translation.len == 0:
|
||||||
|
return (text, "")
|
||||||
|
|
||||||
|
(referenceText, translation)
|
||||||
|
|
||||||
|
proc parsePassageQuery*(input, defaultTranslation: string): PassageQuery =
|
||||||
|
let parsed = splitTrailingTranslationMarker(input)
|
||||||
|
result.reference = parseReference(parsed.referenceText)
|
||||||
|
result.translation =
|
||||||
|
if parsed.translation.len > 0:
|
||||||
|
normalizeTranslation(parsed.translation)
|
||||||
|
else:
|
||||||
|
normalizeTranslation(defaultTranslation)
|
||||||
|
|
||||||
|
proc parsePassageQueries*(input, defaultTranslation: string): seq[PassageQuery] =
|
||||||
|
for rawRef in input.split(';'):
|
||||||
|
let refText = rawRef.strip
|
||||||
|
if refText.len > 0:
|
||||||
|
result.add(parsePassageQuery(refText, defaultTranslation))
|
||||||
|
|
||||||
|
if result.len == 0:
|
||||||
|
raise newException(ValueError, "empty Bible reference")
|
||||||
|
|
||||||
|
proc referenceText*(query: PassageQuery): string =
|
||||||
|
$query.reference
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import std/unittest
|
||||||
|
|
||||||
|
import ../src/passage_query
|
||||||
|
|
||||||
|
suite "passage query parser":
|
||||||
|
test "uses the default translation when no marker is present":
|
||||||
|
let queries = parsePassageQueries("John 3:16", "kjv")
|
||||||
|
|
||||||
|
check queries.len == 1
|
||||||
|
check queries[0].referenceText == "John 3:16"
|
||||||
|
check queries[0].translation == "kjv"
|
||||||
|
|
||||||
|
test "uses a trailing translation marker":
|
||||||
|
let queries = parsePassageQueries("2 John 5 (KJV)", "esv")
|
||||||
|
|
||||||
|
check queries.len == 1
|
||||||
|
check queries[0].referenceText == "2 John 5"
|
||||||
|
check queries[0].translation == "kjv"
|
||||||
|
|
||||||
|
test "parses mixed translation queries":
|
||||||
|
let queries = parsePassageQueries("2 John 5 (KJV); 2 John 5 (ESV)", "mev")
|
||||||
|
|
||||||
|
check queries.len == 2
|
||||||
|
check queries[0].referenceText == "2 John 5"
|
||||||
|
check queries[0].translation == "kjv"
|
||||||
|
check queries[1].referenceText == "2 John 5"
|
||||||
|
check queries[1].translation == "esv"
|
||||||
|
|
||||||
|
test "uses the default translation per unmarked reference":
|
||||||
|
let queries = parsePassageQueries("John 3:16; Psalm 23 (MEV)", "nkjv")
|
||||||
|
|
||||||
|
check queries.len == 2
|
||||||
|
check queries[0].referenceText == "John 3:16"
|
||||||
|
check queries[0].translation == "nkjv"
|
||||||
|
check queries[1].referenceText == "Psalms 23"
|
||||||
|
check queries[1].translation == "mev"
|
||||||
|
|
||||||
|
test "rejects unknown translation markers":
|
||||||
|
expect ValueError:
|
||||||
|
discard parsePassageQueries("John 3:16 (XYZ)", "esv")
|
||||||
|
|
||||||
|
test "rejects unknown default translations":
|
||||||
|
expect ValueError:
|
||||||
|
discard parsePassageQueries("John 3:16", "xyz")
|
||||||
Reference in New Issue
Block a user