diff --git a/Makefile b/Makefile
index bc2fc143..bde08c6a 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,21 @@
+.PHONY: startdocker
+startdocker:
+ @if docker info >/dev/null 2>&1; then \
+ echo "Docker is already running."; \
+ else \
+ echo "Starting Docker..."; \
+ if [ "$$(uname)" = "Darwin" ]; then \
+ open -a Docker; \
+ else \
+ sudo systemctl start docker; \
+ fi; \
+ echo "Waiting for Docker to fully initialize..."; \
+ until docker info >/dev/null 2>&1; do sleep 1; done; \
+ echo "Docker is ready!"; \
+ fi
+
.PHONY: checkvenv
-checkvenv:
+checkvenv: startdocker
# raises error if environment is not active
ifeq ("$(VIRTUAL_ENV)","")
@echo "Venv is not activated!"
@@ -133,7 +149,7 @@ e2e-docx-tests: clean-local-docker-output-dir
.PHONY: frontend-tests
-frontend-tests:
+frontend-tests: startdocker
# NOTE If we are experiencing some issues with the docker
# compose running of frontend tests, we can still use the
# non-Dockerized approach successfully. Doing so requires that
@@ -184,7 +200,7 @@ clean-mypyc-artifacts:
find . ! -path .venv -type f -name "*.c" -exec rm -- {} +
.PHONY: prune-docker-images-volumes
-prune-docker-images-volumes:
+prune-docker-images-volumes: checkvenv
docker system prune --volumes
# https://radon.readthedocs.io/en/latest/commandline.html
@@ -301,7 +317,8 @@ local-run-celery:
.PHONY: local-run-flower
local-run-flower:
- celery --broker=redis:// --result-backend=redis:// flower
+ celery --broker=redis:// --result-backend=redis:// flower
+
# This is one to run after running local-e2e-tests or any tests which
# has yielded HTML and PDFs that need to be checked for linking
# correctness.
diff --git a/backend/doc/config.py b/backend/doc/config.py
index f2bf1cfc..a5c63522 100755
--- a/backend/doc/config.py
+++ b/backend/doc/config.py
@@ -20,6 +20,8 @@ class Settings(BaseSettings):
DATA_API_URL: HttpUrl
+ LANGUAGES_WHERE_NON_ULB_PREFERRED: Sequence[str] = ["fr"]
+
# This is only used to see if a lang_code is in the collection
# otherwise it is a heart language. Eventually the graphql data api may
# provide gateway/heart boolean value.
diff --git a/backend/doc/domain/assembly_strategies/assemble_by_book.py b/backend/doc/domain/assembly_strategies/assemble_by_book.py
index f0c03c50..824670ce 100755
--- a/backend/doc/domain/assembly_strategies/assemble_by_book.py
+++ b/backend/doc/domain/assembly_strategies/assemble_by_book.py
@@ -24,7 +24,7 @@
TWBook,
USFMBook,
)
-from doc.domain.parsing import handle_split_chapter_into_verses
+from doc.domain.parsing import split_chapter_into_verses_with_formatting
from doc.reviewers_guide.model import RGBook
logger = settings.logger(__name__)
@@ -475,7 +475,7 @@ def assemble_usfm_by_verse_book_at_a_time(
chapter_num,
chapter,
) in usfm_book.chapters.items():
- chapter.verses = handle_split_chapter_into_verses(usfm_book, chapter)
+ chapter.verses = split_chapter_into_verses_with_formatting(chapter)
chapter_intros = get_chapter_intros(
tn_book,
tnc_book,
@@ -531,8 +531,10 @@ def assemble_usfm_by_verse_book_at_a_time(
# ulb, f10, show the second USFM content here
if usfm_book2:
usfm_book2_chapter = usfm_book2.chapters[chapter_num]
- usfm_book2_chapter.verses = handle_split_chapter_into_verses(
- usfm_book2, usfm_book2_chapter
+ usfm_book2_chapter.verses = (
+ split_chapter_into_verses_with_formatting(
+ usfm_book2_chapter
+ )
)
if (
usfm_book2_chapter.verses
diff --git a/backend/doc/domain/assembly_strategies/assemble_by_chapter.py b/backend/doc/domain/assembly_strategies/assemble_by_chapter.py
index e141fb4d..29ff87d1 100644
--- a/backend/doc/domain/assembly_strategies/assemble_by_chapter.py
+++ b/backend/doc/domain/assembly_strategies/assemble_by_chapter.py
@@ -37,10 +37,9 @@
TWBook,
USFMBook,
)
-from doc.domain.parsing import handle_split_chapter_into_verses
+from doc.domain.parsing import split_chapter_into_verses_with_formatting
from doc.reviewers_guide.model import RGBook
-
logger = settings.logger(__name__)
@@ -625,13 +624,13 @@ def assemble_usfm_by_verse_chapter_at_a_time(
primary_verses = None
secondary_verses = None
if usfm_book and usfm_chapter:
- usfm_chapter.verses = handle_split_chapter_into_verses(
- usfm_book, usfm_chapter
+ usfm_chapter.verses = split_chapter_into_verses_with_formatting(
+ usfm_chapter
)
primary_verses = usfm_chapter.verses
if usfm_book2 and usfm_book2_chapter:
- usfm_book2_chapter.verses = handle_split_chapter_into_verses(
- usfm_book2, usfm_book2_chapter
+ usfm_book2_chapter.verses = split_chapter_into_verses_with_formatting(
+ usfm_book2_chapter
)
secondary_verses = usfm_book2_chapter.verses
if usfm_book and primary_verses:
diff --git a/backend/doc/domain/document_generator.py b/backend/doc/domain/document_generator.py
index 7d223e58..8032863f 100755
--- a/backend/doc/domain/document_generator.py
+++ b/backend/doc/domain/document_generator.py
@@ -51,6 +51,7 @@
from doc.utils.docx_util import (
add_internal_docx_links,
generate_docx_toc,
+ override_and_clean_hyperlinks,
style_superscripts,
)
from doc.utils.file_utils import (
@@ -778,6 +779,15 @@ def compose_docx_document(
if part.add_page_break:
add_page_break(doc)
style_superscripts(doc, lift_half_points=2, color=None)
+ # html4doc defeats normal use of hyperlink inline styling in Word
+ # via a customized (otherwise standard) Hyperlink style, but
+ # this handles it by doing a pass over the document after the fact
+ # and forcing hyperlinks to render using a specific style we
+ # created to match the PO's desired look.
+ # PlainHyperlinkChar is a character style we created in
+ # template.docx, but its actual style ID is PlainHyperlinkChar0
+ # under the hood.
+ override_and_clean_hyperlinks(doc, "PlainHyperlinkChar0")
t1 = time.time()
logger.info("Time for converting HTML to Docx: %.2f seconds", t1 - t0)
return doc
diff --git a/backend/doc/domain/parsing.py b/backend/doc/domain/parsing.py
index 2423f68c..03a2e3e4 100644
--- a/backend/doc/domain/parsing.py
+++ b/backend/doc/domain/parsing.py
@@ -2,6 +2,7 @@
This module provides an API for parsing content.
"""
+from bs4 import BeautifulSoup, NavigableString
from re import (
compile,
escape,
@@ -276,14 +277,19 @@ def split_usfm_by_chapters(
chapters = re_split(chapter_regex, usfm_text)
frontmatter = chapters.pop(0).strip()
+ defective_lang_codes = {resource[0] for resource in resources_with_usfm_defects}
+
def needs_fixing() -> bool:
"""
- Determine if a chapter needs fixing based on configuration.
+ Determine whether this resource should be checked for known USFM defects.
+
+ If CHECK_ALL_BOOKS_FOR_LANGUAGE is enabled, then the presence of any
+ known defective resource for a language causes all books for that
+ language to be checked for similar defects. Otherwise, only explicitly
+ listed resource tuples are checked.
"""
if check_all_books_for_language:
- return lang_code in [
- resource[0] for resource in resources_with_usfm_defects
- ]
+ return lang_code in defective_lang_codes
return (
lang_code,
resource_type,
@@ -527,9 +533,7 @@ def usfm_book_content(
cleaned_chapter_html_content_ = remove_null_bytes_and_control_characters(
chapter_html_content
)
- cleaned_chapter_html_content = remove_unwanted_elements(
- cleaned_chapter_html_content_
- )
+ cleaned_chapter_html_content = clean_content_html(cleaned_chapter_html_content_)
usfm_chapters[chapter_num] = USFMChapter(
content=(
cleaned_chapter_html_content if cleaned_chapter_html_content else ""
@@ -544,7 +548,7 @@ def usfm_book_content(
national_book_name=(
localized_book_name
if localized_book_name
- else BOOK_NAMES[resource_lookup_dto.book_code]
+ else book_names[resource_lookup_dto.book_code]
),
resource_type_name=resource_lookup_dto.resource_type_name,
chapters=usfm_chapters if usfm_chapters else {},
@@ -1408,72 +1412,13 @@ def lookup_verse_text(usfm_book: USFMBook, chapter_num: int, verse_ref: str) ->
return verse
-# Used by STET and PASSAGES apps
-def split_chapter_into_verses(chapter: USFMChapter) -> dict[str, str]:
- # Sample HTML content with multiple verse elements
- # html_content = '''
- #
- # 19
- # For through the law I died to the law, so that I might live for God. I have been crucified with Christ.
- #
- #
- #
- #
- # 20
- # I have been crucified with Christ and I no longer live, but Christ lives in me. The life I now live in the body, I live by faith in the Son of God, who loved me and gave himself for me.
- #
- #
- #
- # '''
- verse_dict = {}
- # Find all verse spans
- verse_spans = findall(r'(.*?)', chapter.content, DOTALL)
- for verse_span in verse_spans:
- # Extract the verse number from the versemarker
- verse_number = search(r'(\d+)', verse_span)
- if verse_number:
- verse_number_ = verse_number.group(1)
- # Remove versemarker
- verse_text = sub(r'.*?', "", verse_span)
- # Remove footnotes numbers
- verse_text = sub(r'.*?', "", verse_text)
- # Fix spacing issue when div class="poetry-*" type divs
- # are used, e.g., yielding 'heartsas' for Hebrews 3:8
- verse_text = sub(
- r'
(.*?)
',
- r" \1",
- verse_text,
- )
- # Add to the dictionary with verse number as the key and verse text as the value
- verse_dict[verse_number_] = verse_text
- return verse_dict
-
-
-def handle_split_chapter_into_verses(
- usfm_book: USFMBook,
- usfm_chapter: USFMChapter,
- resource_type_codes_and_names: Mapping[
- str, str
- ] = settings.RESOURCE_TYPE_CODES_AND_NAMES,
-) -> dict[VerseRef, str]:
- if (
- usfm_book.lang_code == "fr"
- and usfm_book.resource_type_name == resource_type_codes_and_names["f10"]
- ):
- return split_chapter_into_verses_with_formatting_for_f10(usfm_chapter)
- else:
- return split_chapter_into_verses_with_formatting(usfm_chapter)
-
-
def split_chapter_into_verses_with_formatting(
chapter: USFMChapter,
- empty_paragraph: str = "",
- sectionhead5_element: str = '',
) -> dict[VerseRef, str]:
"""
- Given a USFMChapter instance, return the same instance with its
- verses attribute set to a dictionary where the key is the verse
- number and the value is the verse HTML.
+ Parse chapter.content as HTML, extract each ,
+ unwrap elements (preserving their text),
+ and return a dict mapping verse number -> cleaned HTML fragment for that verse.
Sample HTML content with multiple verse elements:
@@ -1500,74 +1445,34 @@ def split_chapter_into_verses_with_formatting(
"""
- # TODO What to do about footnote targets? Perhaps have the value be a
- # tuple with first element of the verse HTML (which includes the
- # footnote callers) and the second element the target footnotes HTML?
- verse_dict = {}
- # Find all verse spans
- verse_spans = findall(r'(.*?)', chapter.content, DOTALL)
- for verse_span in verse_spans:
- # Extract the verse number from the versemarker
- verse_number = search(r'(\d+)', verse_span)
- if verse_number:
- verse_number_ = verse_number.group(1)
- # Add to the dictionary with verse number as the key and verse text as the value
- verse_dict[verse_number_] = (
- verse_span.strip()
- .replace(empty_paragraph, "")
- .replace(sectionhead5_element, "")
- )
- return verse_dict
-
-
-def split_chapter_into_verses_with_formatting_for_f10(
- chapter: USFMChapter,
- empty_paragraph: str = "",
- sectionhead5_element: str = '',
-) -> dict[str, str]:
- """
- Parse chapter.content as HTML, extract each ,
- unwrap elements (preserving their text),
- and return a dict mapping verse number -> cleaned HTML fragment for that verse.
- """
soup = BeautifulSoup(chapter.content, "html.parser")
- verse_dict: dict[str, str] = {}
- # find all verse spans (parser handles nesting correctly)
+ verse_dict: dict[VerseRef, str] = {}
for verse_span in soup.find_all("span", class_="verse"):
- # find the verse number from NN
sup = verse_span.find("sup", class_="versemarker")
if not sup or not sup.string:
continue
verse_number = sup.string.strip()
- # unwrap all word-entry spans: replace X
- # with X (preserving whitespace/punctuation)
+ # Remove the versemarker number sup
+ sup.decompose()
+ # fr f10 uses word-entry tags
for we in verse_span.find_all("span", class_="word-entry"):
we.unwrap()
- # Option: normalize whitespace (optional)
- # If you want to preserve original spacing/punctuation exactly, skip this.
- # cleaned_html = "".join(str(c) for c in verse_span.contents)
- cleaned_html = str(verse_span)
- # Fix spacing issues introduced by inner spans
- cleaned_html = sub(
- r"\s+([,;:.!?])", r"\1", cleaned_html
- ) # remove space before punctuation
- cleaned_html = sub(r"\s+'", "'", cleaned_html) # remove space before apostrophe
- cleaned_html = sub(r"'\s+", "'", cleaned_html) # remove space after apostrophe
- cleaned_html = sub(
- r"\s*-\s*", "-", cleaned_html
- ) # normalize spaces around hyphens
- cleaned_html = sub(r"\s{2,}", " ", cleaned_html) # collapse double spaces
- cleaned_html = cleaned_html.strip()
- # if you want plain text instead, use: cleaned_text = verse_span.get_text(" ", strip=True)
- # store cleaned HTML fragment (still contains etc.)
- verse_dict[verse_number] = (
- cleaned_html.strip()
- .replace(empty_paragraph, "")
- .replace(sectionhead5_element, "")
- )
+ cleaned_html = clean_content_html(str(verse_span))
+ verse_dict[verse_number] = cleaned_html
return verse_dict
+def clean_content_html(raw_content: str) -> str:
+ soup = BeautifulSoup(raw_content, "html.parser")
+ cleaned_html = str(soup)
+ cleaned_html = sub(r"\s+([,;:.!?])", r"\1", cleaned_html)
+ cleaned_html = sub(r"\s+'", "'", cleaned_html)
+ cleaned_html = sub(r"'\s+", "'", cleaned_html)
+ cleaned_html = sub(r"\s*-\s*", "-", cleaned_html)
+ cleaned_html = sub(r"\s{2,}", " ", cleaned_html).strip()
+ return cleaned_html
+
+
if __name__ == "__main__":
# To run the doctests in this module, in the root of the project do:
diff --git a/backend/doc/domain/resource_lookup.py b/backend/doc/domain/resource_lookup.py
index f3da9d4c..ee7ade7d 100644
--- a/backend/doc/domain/resource_lookup.py
+++ b/backend/doc/domain/resource_lookup.py
@@ -55,15 +55,6 @@
)
-# List of languages which do not have USFM available for any books. We use this
-# to filter these out of STET's list of source and target
-# languages so that the user doesn't have the frustrating experience of
-# selecting a language which might have non-USFM resources available but
-# not USFM so that when their resulting doc is generated no scripture is
-# present. It makes it seem like a bug in STET and is bad UX.
-LANG_CODES_WITH_NO_USFM: frozenset[str] = frozenset(["ru"])
-
-
@cached(fetch_source_data_cache)
def fetch_source_data(
data_api_url: HttpUrl = settings.DATA_API_URL,
@@ -175,46 +166,6 @@ def lang_codes_and_names(
return sorted(unique_values, key=lambda value: value[1])
-def lang_codes_and_names_having_usfm(
- lang_code_filter_list: frozenset[str] = LANG_CODES_WITH_NO_USFM,
- gateway_languages: frozenset[str] = settings.GATEWAY_LANGUAGES,
-) -> Sequence[tuple[str, str, bool]]:
- """
- >>> from doc.domain import resource_lookup
- >>> ();result = resource_lookup.lang_codes_and_names_having_usfm();() # doctest: +ELLIPSIS
- (...)
- >>> result[0]
- ('abz', 'Abui', False)
- >>> heart_lang_codes = [lang_code_and_name[0] for lang_code_and_name in resource_lookup.lang_codes_and_names_having_usfm() if not lang_code_and_name[2]]
- >>> sorted(heart_lang_codes)[0]
- 'aao'
- """
- data = fetch_source_data()
- values = []
- if data is None or not data.git_repo:
- logger.info("Data API is down or no git_repo found!")
- return []
- try:
- for repo_info in data.git_repo:
- language_info = repo_info.content
- language = language_info.language
- ietf_code = language.ietf_code
- english_name = language.english_name if language.english_name else ""
- localized_name = language.national_name
- is_gateway = ietf_code in gateway_languages
- if ietf_code not in lang_code_filter_list:
- if english_name in localized_name:
- values.append((ietf_code, localized_name, is_gateway))
- else:
- values.append(
- (ietf_code, f"{localized_name} ({english_name})", is_gateway)
- )
- except Exception:
- logger.exception("Failed due to the following exception.")
- unique_values = unique_tuples(values)
- return sorted(unique_values, key=lambda value: value[1])
-
-
def repos_to_clone(
lang_code: str,
augmented_repos_info: list[RepoEntry],
@@ -289,22 +240,6 @@ def get_resource_types(
]
elif resource_type in usfm_resource_types:
book_assets = find_usfm_files(resource_filepath)
- elif resource_type == "rg":
- between_texts, bible_reference_strs = find_bible_references(
- join(en_rg, docx_file_path)
- )
- bible_references = [
- parse_bible_reference(bible_reference)
- for bible_reference in bible_reference_strs
- ]
- book_codes_ = {
- bible_reference.book_code
- for bible_reference in bible_references
- if bible_reference
- }
- book_assets = [
- book_code for book_code in book_codes if book_code in book_codes_
- ]
if book_assets or resource_type == "tw":
resource_types.append(
(
diff --git a/backend/doc/domain/usfm_error_detection_and_fixes.py b/backend/doc/domain/usfm_error_detection_and_fixes.py
index f97268b8..7d9a9575 100644
--- a/backend/doc/domain/usfm_error_detection_and_fixes.py
+++ b/backend/doc/domain/usfm_error_detection_and_fixes.py
@@ -21,7 +21,6 @@
("adn", "reg", "mat"),
("agd-x-namel", "reg", "2th"),
("ahm", "reg", "php"),
- ("ahm", "reg", "php"),
("ajg-x-adjtalagbe", "reg", "mat"),
("aoa", "reg", "col"),
("aoa", "reg", "luk"),
@@ -52,7 +51,6 @@
("blo", "reg", "php"),
("blo", "reg", "heb"),
("blo", "reg", "1jn"),
- ("blo", "reg", "col"),
("bne", "reg", "gal"),
("bof", "reg", "mat"),
("bou", "reg", "gen"),
diff --git a/backend/doc/utils/docx_util.py b/backend/doc/utils/docx_util.py
index 2f35821d..f51797a4 100644
--- a/backend/doc/utils/docx_util.py
+++ b/backend/doc/utils/docx_util.py
@@ -56,6 +56,33 @@ def _make_text_run(text: str) -> Element:
return r
+def override_and_clean_hyperlinks(doc: DocxDocument, style_id: str) -> None:
+ """
+ Finds all hyperlinks, strips any forced inline formatting (like blue color),
+ and applies the correct template character style ID using raw XML manipulation.
+ """
+ for paragraph in doc.paragraphs:
+ hyperlinks = paragraph._p.findall(
+ ".//{http://schemas.openxmlformats.org/wordprocessingml/2006/main}hyperlink"
+ )
+ for hyperlink in hyperlinks:
+ runs = hyperlink.findall(qn("w:r"))
+ for r in runs:
+ rPr = r.find(qn("w:rPr"))
+ if rPr is None:
+ rPr = OxmlElement("w:rPr")
+ r.insert(0, rPr)
+ existing_style = rPr.find(qn("w:rStyle"))
+ if existing_style is not None:
+ rPr.remove(existing_style)
+ for child in list(rPr):
+ if child.tag in (qn("w:color"), qn("w:u"), qn("w:shd")):
+ rPr.remove(child)
+ rStyle = OxmlElement("w:rStyle")
+ rStyle.set(qn("w:val"), style_id)
+ rPr.insert(0, rStyle)
+
+
def _make_internal_hyperlink_element(text: str, bookmark_name: str) -> Element:
hyperlink = OxmlElement("w:hyperlink")
hyperlink.set(qn("w:anchor"), bookmark_name)
diff --git a/backend/passages/domain/document_generator.py b/backend/passages/domain/document_generator.py
index 4a79e56d..841d7334 100644
--- a/backend/passages/domain/document_generator.py
+++ b/backend/passages/domain/document_generator.py
@@ -8,7 +8,9 @@
from doc.domain.bible_books import BOOK_NAMES
from doc.domain.email_utils import send_email_with_attachment, should_send_email
from doc.domain.model import Attachment, USFMBook
-from doc.domain.parsing import split_chapter_into_verses, usfm_book_content
+from doc.domain.parsing import (
+ usfm_book_content,
+)
from doc.domain.resource_lookup import (
book_codes_for_lang_from_usfm_only,
prepare_resource_filepath,
@@ -29,7 +31,7 @@
Passage,
BibleReferenceWithAvailability,
)
-from passages.domain.parser import verse_text_html
+from passages.domain.parser import split_chapter_into_verses, verse_text_html
from passages.domain.stet_verse_list_parser import BOOK_INDEX, parse_bible_blocks
from passages.utils.docx_utils import add_footer, add_header
from pydantic import Json
@@ -101,6 +103,9 @@ def get_usfm_books_and_usfm_resource_type(
bible_references_with_availability: list[BibleReferenceWithAvailability],
lang_code: str,
usfm_resource_types: Sequence[str] = settings.USFM_RESOURCE_TYPES,
+ languages_where_non_ulb_preferred: Sequence[
+ str
+ ] = settings.LANGUAGES_WHERE_NON_ULB_PREFERRED,
) -> tuple[list[USFMBook], str]:
# Invariant: book codes are only those that were available from USFM resources
book_codes = list(
@@ -128,10 +133,16 @@ def get_usfm_books_and_usfm_resource_type(
)
usfm_books = []
usfm_resource_type = ""
- if ulb_usfm_resource_types: # Prefer ulb if available
- usfm_resource_type = ulb_usfm_resource_types[0]
- elif usfm_resource_types:
- usfm_resource_type = usfm_resource_types[0]
+ if lang_code not in languages_where_non_ulb_preferred:
+ if ulb_usfm_resource_types: # Prefer ulb if available
+ usfm_resource_type = ulb_usfm_resource_types[0]
+ elif usfm_resource_types:
+ usfm_resource_type = usfm_resource_types[0]
+ else:
+ if usfm_resource_types: # Prefer non-ulb if available
+ usfm_resource_type = usfm_resource_types[0]
+ elif ulb_usfm_resource_types:
+ usfm_resource_type = ulb_usfm_resource_types[0]
if usfm_resource_type:
usfm_book = None
for book_code in book_codes:
@@ -197,22 +208,22 @@ def generate_docx_document(
)
)
current_task.update_state(state="Assembling content")
- passages_lang0 = get_passages(
+ lang0_passages = get_passages(
bible_references_with_availability_lang0,
usfm_resource_type_lang0,
usfm_books_lang0,
)
- passages_lang1 = []
+ lang1_passages = []
if lang1_code:
- passages_lang1 = get_passages(
+ lang1_passages = get_passages(
bible_references_with_availability_lang1,
usfm_resource_type_lang1,
usfm_books_lang1,
)
current_task.update_state(state="Converting to Docx")
generate_docx(
- passages_lang0,
- passages_lang1,
+ lang0_passages,
+ lang1_passages,
docx_filepath_,
lang0_code,
lang0_name,
@@ -223,8 +234,8 @@ def generate_docx_document(
def generate_docx(
- passages_lang0: list[Passage],
- passages_lang1: list[Passage],
+ lang0_passages: list[Passage],
+ lang1_passages: list[Passage],
docx_filepath: str,
lang0_code: str,
lang0_name: str,
@@ -245,9 +256,9 @@ def generate_docx(
html_to_docx = HtmlToDocx()
has_lang1 = lang1_code is not None and lang1_name is not None
if has_lang1:
- assert len(passages_lang0) == len(passages_lang1), (
+ assert len(lang0_passages) == len(lang1_passages), (
f"Passage count mismatch: "
- f"{len(passages_lang0)} vs {len(passages_lang1)}"
+ f"{len(lang0_passages)} vs {len(lang1_passages)}"
)
columns: list[str] = ["lang0"]
if has_lang1:
@@ -271,9 +282,9 @@ def generate_docx(
table.columns[i].width = w
col_index = {name: i for i, name in enumerate(columns)}
pairs = (
- zip(passages_lang0, passages_lang1)
+ zip(lang0_passages, lang1_passages)
if has_lang1
- else ((p, None) for p in passages_lang0)
+ else ((p, None) for p in lang0_passages)
)
for p0, p1 in pairs:
row = table.add_row()
diff --git a/backend/passages/domain/parser.py b/backend/passages/domain/parser.py
index 455f3366..210704d4 100644
--- a/backend/passages/domain/parser.py
+++ b/backend/passages/domain/parser.py
@@ -1,12 +1,13 @@
+from re import split, sub
from typing import Mapping
+from bs4 import BeautifulSoup, NavigableString
from doc.config import settings
from doc.domain.bible_books import BOOK_CHAPTER_VERSES
-from doc.domain.model import USFMBook
+from doc.domain.model import USFMBook, USFMChapter
from doc.domain.parsing import lookup_verse_text
from doc.reviewers_guide.model import BibleReference
-
logger = settings.logger(__name__)
@@ -102,3 +103,63 @@ def verse_text_html(
f'{bible_reference.start_chapter_verse_ref.strip()}{verse_text___}'
)
return "".join(verse_text)
+
+
+def split_chapter_into_verses(chapter: USFMChapter) -> dict[str, str]:
+ # Sample HTML content with multiple verse elements
+ # html_content = '''
+ #
+ # 19
+ # For through the law I died to the law, so that I might live for God. I have been crucified with Christ.
+ #
+ #
+ #
+ #
+ # 20
+ # I have been crucified with Christ and I no longer live, but Christ lives in me. The life I now live in the body, I live by faith in the Son of God, who loved me and gave himself for me.
+ #
+ #
+ #
+ # '''
+ verse_dict: dict[str, str] = {}
+ soup = BeautifulSoup(chapter.content, "html.parser")
+ for verse_span in soup.find_all("span", class_="verse"):
+ versemarker = verse_span.find("sup", class_="versemarker")
+ if not versemarker or not versemarker.get_text(strip=True):
+ continue
+ verse_number = versemarker.get_text(strip=True)
+ # Remove verse marker
+ versemarker.decompose()
+ # Remove footnote callers
+ for caller in verse_span.find_all("sup", class_="caller"):
+ caller.decompose()
+ # Fix spacing issue for poetry divs
+ for poetry_div in verse_span.find_all(
+ "div", class_=lambda c: c and c.startswith("poetry-")
+ ):
+ poetry_div.insert_before(NavigableString(" "))
+ # Handle fr f10 word-entry tags
+ for we in verse_span.find_all("span", class_="word-entry"):
+ we.unwrap()
+ # Get inner HTML of the verse span
+ verse_text = "".join(str(child) for child in verse_span.contents).strip()
+ # verse_text = clean_verse_html(verse_text)
+ verse_dict[verse_number] = verse_text
+ return verse_dict
+
+
+def clean_verse_html(
+ raw_verse: str,
+ empty_paragraph: str = "",
+ sectionhead5_element: str = '',
+) -> str:
+ cleaned_html = raw_verse
+ cleaned_html = sub(r"\s+([,;:.!?])", r"\1", cleaned_html)
+ cleaned_html = sub(r"\s+'", "'", cleaned_html)
+ cleaned_html = sub(r"'\s+", "'", cleaned_html)
+ cleaned_html = sub(r"\s*-\s*", "-", cleaned_html)
+ cleaned_html = sub(r"\s{2,}", " ", cleaned_html).strip()
+ cleaned_html = cleaned_html.replace(empty_paragraph, "").replace(
+ sectionhead5_element, ""
+ )
+ return cleaned_html
diff --git a/backend/stet/data/stet_gu.docx b/backend/stet/data/stet_gu.docx
new file mode 100644
index 00000000..7e3601cd
Binary files /dev/null and b/backend/stet/data/stet_gu.docx differ
diff --git a/backend/stet/data/stet_rmn-x-yerliroman.docx b/backend/stet/data/stet_rmn-x-yerliroman.docx
new file mode 100644
index 00000000..2508f455
Binary files /dev/null and b/backend/stet/data/stet_rmn-x-yerliroman.docx differ
diff --git a/backend/stet/data/stet_ru.docx b/backend/stet/data/stet_ru.docx
new file mode 100644
index 00000000..d426b43d
Binary files /dev/null and b/backend/stet/data/stet_ru.docx differ
diff --git a/backend/stet/domain/document_generator.py b/backend/stet/domain/document_generator.py
index b45feb79..f68f5742 100644
--- a/backend/stet/domain/document_generator.py
+++ b/backend/stet/domain/document_generator.py
@@ -10,7 +10,6 @@
from doc.domain.model import Attachment
from doc.domain.parsing import (
lookup_verse_text,
- split_chapter_into_verses,
usfm_book_content,
)
from doc.domain.resource_lookup import (
@@ -28,7 +27,7 @@
from html4docx import HtmlToDocx # type: ignore
from pydantic import Json
from stet.domain.model import VerseEntry, WordEntry
-from stet.domain.parser import get_word_entry_dtos
+from stet.domain.parser import get_word_entry_dtos, split_chapter_into_verses
from stet.domain.strings import (
LOCALIZED_DATE_FORMAT_STRINGS,
TRANSLATED_FOOTER_PHRASES_TABLE,
@@ -61,6 +60,9 @@ def generate_docx_document(
resource_type_codes_and_names: Mapping[
str, str
] = settings.RESOURCE_TYPE_CODES_AND_NAMES,
+ languages_where_non_ulb_preferred: Sequence[
+ str
+ ] = settings.LANGUAGES_WHERE_NON_ULB_PREFERRED,
) -> str:
"""
Generate the scriptural terms evaluation document.
@@ -116,14 +118,26 @@ def generate_docx_document(
target_usfm_books = []
lang0_usfm_resource_type = ""
lang1_usfm_resource_type = ""
- if lang0_ulb_usfm_resource_types: # Prefer ulb if available
- lang0_usfm_resource_type = lang0_ulb_usfm_resource_types[0]
- elif lang0_usfm_resource_types:
- lang0_usfm_resource_type = lang0_usfm_resource_types[0]
- if lang1_ulb_usfm_resource_types: # Prefer ulb if available
- lang1_usfm_resource_type = lang1_ulb_usfm_resource_types[0]
- elif lang1_usfm_resource_types:
- lang1_usfm_resource_type = lang1_usfm_resource_types[0]
+ if lang0_code not in languages_where_non_ulb_preferred:
+ if lang0_ulb_usfm_resource_types: # Prefer ulb if available
+ lang0_usfm_resource_type = lang0_ulb_usfm_resource_types[0]
+ elif lang0_usfm_resource_types:
+ lang0_usfm_resource_type = lang0_usfm_resource_types[0]
+ else:
+ if lang0_usfm_resource_types: # Prefer non-ulb if available
+ lang0_usfm_resource_type = lang0_usfm_resource_types[0]
+ elif lang0_ulb_usfm_resource_types:
+ lang0_usfm_resource_type = lang0_ulb_usfm_resource_types[0]
+ if lang1_code not in languages_where_non_ulb_preferred:
+ if lang1_ulb_usfm_resource_types: # Prefer ulb if available
+ lang1_usfm_resource_type = lang1_ulb_usfm_resource_types[0]
+ elif lang1_usfm_resource_types:
+ lang1_usfm_resource_type = lang1_usfm_resource_types[0]
+ else:
+ if lang1_usfm_resource_types: # Prefer non-ulb if available
+ lang1_usfm_resource_type = lang1_usfm_resource_types[0]
+ elif lang1_ulb_usfm_resource_types:
+ lang1_usfm_resource_type = lang1_ulb_usfm_resource_types[0]
if lang0_usfm_resource_type and lang1_usfm_resource_type:
source_usfm_book = None
target_usfm_book = None
@@ -146,10 +160,11 @@ def generate_docx_document(
lang0_resource_dir,
False,
)
- for chapter_num_, chapter_ in source_usfm_book.chapters.items():
- source_usfm_book.chapters[chapter_num_].verses = (
- split_chapter_into_verses(chapter_)
- )
+ for (
+ chapter_num_,
+ chapter_,
+ ) in source_usfm_book.chapters.items():
+ chapter_.verses = split_chapter_into_verses(chapter_)
source_usfm_books.append(source_usfm_book)
lang1_resource_lookup_dto_ = resource_lookup_dto(
lang1_code, lang1_usfm_resource_type, book_code
@@ -166,10 +181,11 @@ def generate_docx_document(
lang1_resource_dir,
False,
)
- for chapter_num_, chapter_ in target_usfm_book.chapters.items():
- target_usfm_book.chapters[chapter_num_].verses = (
- split_chapter_into_verses(chapter_)
- )
+ for (
+ chapter_num_,
+ chapter_,
+ ) in target_usfm_book.chapters.items():
+ chapter_.verses = split_chapter_into_verses(chapter_)
target_usfm_books.append(target_usfm_book)
# Count total occurrences per reference (using source_reference as key)
reference_counter: Counter[str] = Counter()
diff --git a/backend/stet/domain/parser.py b/backend/stet/domain/parser.py
index 849aba75..e6ac3751 100644
--- a/backend/stet/domain/parser.py
+++ b/backend/stet/domain/parser.py
@@ -1,17 +1,24 @@
-import re
+from re import (
+ DOTALL,
+ compile,
+ match,
+ split,
+ sub,
+)
+from bs4 import BeautifulSoup, NavigableString
from doc.config import settings
from doc.domain.bible_books import BOOK_NAMES
+from doc.domain.model import USFMChapter
from doc.domain.resource_lookup import book_codes_for_lang_from_usfm_only
from docx import Document
from stet.domain.model import VerseReferenceDto, WordEntryDto
from stet.utils.util import is_valid_int
-
logger = settings.logger(__name__)
-_RV_BLOCK_PATTERN = re.compile(r"(.*?)\s*(.*?)", re.DOTALL)
-_REF_PATTERN = re.compile(r"^(.*) (\d+):([0-9,\- ]+)\s?(\(.*\))?$")
+_RV_BLOCK_PATTERN = compile(r"(.*?)\s*(.*?)", DOTALL)
+_REF_PATTERN = compile(r"^(.*) (\d+):([0-9,\- ]+)\s?(\(.*\))?$")
def _parse_ref_to_dto(
@@ -24,11 +31,11 @@ def _parse_ref_to_dto(
lang0_book_codes_and_names__: list[tuple[str, str]],
source_text_with_bolding: str | None = None,
) -> VerseReferenceDto | None:
- match = _REF_PATTERN.match(reference_)
- if not match:
+ match_ = _REF_PATTERN.match(reference_)
+ if not match_:
logger.warning("Couldn't parse %s", reference_)
return None
- book_name = match.group(1).replace("\n", "")
+ book_name = match_.group(1).replace("\n", "")
book_codes_and_names_ = [
(bc, bn) for bc, bn in lang0_book_codes_and_names if bn == book_name
]
@@ -39,11 +46,12 @@ def _parse_ref_to_dto(
book_code_and_name_ = book_codes_and_names_[0] if book_codes_and_names_ else None
if book_code_and_name_:
lang0_book_codes_and_names__.append(book_code_and_name_)
- chapter_num = int(match.group(2))
- verses = match.group(3)
- comment = match.group(4)
+ chapter_num = int(match_.group(2))
+ verses = match_.group(3)
+ comment = match_.group(4)
source_reference = (
- f"{book_name} {chapter_num}:{verses}{comment}" if comment
+ f"{book_name} {chapter_num}:{verses}{comment}"
+ if comment
else f"{book_name} {chapter_num}:{verses}"
)
lang0_book_code = book_code_and_name_[0] if book_code_and_name_ else ""
@@ -59,7 +67,7 @@ def _parse_ref_to_dto(
if is_valid_int(verse_ref):
valid_verse_refs.append(str(verse_ref))
continue
- vm = re.match(r"(\d+)-(\d+)", verse_ref)
+ vm = match(r"(\d+)-(\d+)", verse_ref)
if vm:
for verse_num in range(int(vm.group(1)), int(vm.group(2)) + 1):
valid_verse_refs.append(str(verse_num))
@@ -149,12 +157,12 @@ def get_word_entry_dtos(
# Create entry item
word_entry_dto = WordEntryDto()
# Extract word from 1st column
- match = re.match(r"(.*)(\n)?(.*)?", row.cells[0].text)
- if not match:
+ match_ = match(r"(.*)(\n)?(.*)?", row.cells[0].text)
+ if not match_:
raise ValueError(f"Couldn't parse word(s): {row.cells[0].text}")
- words = match.group(1)
+ words = match_.group(1)
word_entry_dto.words = [word.strip() for word in words.split(",")]
- raw_strongs = match.group(3)
+ raw_strongs = match_.group(3)
word_entry_dto.strongs_numbers = raw_strongs.strip()
definition = ""
previous_paragraph_style_name = ""
@@ -203,3 +211,63 @@ def get_word_entry_dtos(
]
word_entry_dtos.append(word_entry_dto)
return word_entry_dtos, list(set(lang0_book_codes_and_names__))
+
+
+def split_chapter_into_verses(chapter: USFMChapter) -> dict[str, str]:
+ # Sample HTML content with multiple verse elements
+ # html_content = '''
+ #
+ # 19
+ # For through the law I died to the law, so that I might live for God. I have been crucified with Christ.
+ #
+ #
+ #
+ #
+ # 20
+ # I have been crucified with Christ and I no longer live, but Christ lives in me. The life I now live in the body, I live by faith in the Son of God, who loved me and gave himself for me.
+ #
+ #
+ #
+ # '''
+ verse_dict: dict[str, str] = {}
+ soup = BeautifulSoup(chapter.content, "html.parser")
+ for verse_span in soup.find_all("span", class_="verse"):
+ versemarker = verse_span.find("sup", class_="versemarker")
+ if not versemarker or not versemarker.get_text(strip=True):
+ continue
+ verse_number = versemarker.get_text(strip=True)
+ # Remove verse marker
+ versemarker.decompose()
+ # Remove footnote callers
+ for caller in verse_span.find_all("sup", class_="caller"):
+ caller.decompose()
+ # Fix spacing issue for poetry divs
+ for poetry_div in verse_span.find_all(
+ "div", class_=lambda c: c and c.startswith("poetry-")
+ ):
+ poetry_div.insert_before(NavigableString(" "))
+ # Handle fr f10 word-entry tags
+ for we in verse_span.find_all("span", class_="word-entry"):
+ we.unwrap()
+ # Get inner HTML of the verse span
+ verse_text = "".join(str(child) for child in verse_span.contents).strip()
+ verse_text = clean_verse_html(verse_text)
+ verse_dict[verse_number] = verse_text
+ return verse_dict
+
+
+def clean_verse_html(
+ raw_verse: str,
+ empty_paragraph: str = "",
+ sectionhead5_element: str = '',
+) -> str:
+ cleaned_html = raw_verse
+ cleaned_html = sub(r"\s+([,;:.!?])", r"\1", cleaned_html)
+ cleaned_html = sub(r"\s+'", "'", cleaned_html)
+ cleaned_html = sub(r"'\s+", "'", cleaned_html)
+ cleaned_html = sub(r"\s*-\s*", "-", cleaned_html)
+ cleaned_html = sub(r"\s{2,}", " ", cleaned_html).strip()
+ cleaned_html = cleaned_html.replace(empty_paragraph, "").replace(
+ sectionhead5_element, ""
+ )
+ return cleaned_html
diff --git a/backend/stet/domain/resource_lookup.py b/backend/stet/domain/resource_lookup.py
new file mode 100644
index 00000000..d8db861e
--- /dev/null
+++ b/backend/stet/domain/resource_lookup.py
@@ -0,0 +1,55 @@
+from typing import Sequence
+
+from doc.config import settings
+from doc.domain.resource_lookup import fetch_source_data
+from doc.utils.list_utils import unique_tuples
+
+# List of languages which do not have USFM available for NT books. We use this
+# to filter these out of STET's list of source and target
+# languages so that the user doesn't have the frustrating experience of
+# selecting a language which might have OT USFM resources available but
+# not NT USFM so that when their resulting doc is generated no scripture is
+# present. It makes it seem like a bug in STET and is bad UX.
+LANG_CODES_WITH_NO_NT_USFM: frozenset[str] = frozenset(["ru"])
+
+logger = settings.logger(__name__)
+
+
+def lang_codes_and_names_having_usfm(
+ lang_code_filter_list: frozenset[str] = LANG_CODES_WITH_NO_NT_USFM,
+ gateway_languages: frozenset[str] = settings.GATEWAY_LANGUAGES,
+) -> Sequence[tuple[str, str, bool]]:
+ """
+ >>> from doc.domain import resource_lookup
+ >>> ();result = resource_lookup.lang_codes_and_names_having_usfm();() # doctest: +ELLIPSIS
+ (...)
+ >>> result[0]
+ ('abz', 'Abui', False)
+ >>> heart_lang_codes = [lang_code_and_name[0] for lang_code_and_name in resource_lookup.lang_codes_and_names_having_usfm() if not lang_code_and_name[2]]
+ >>> sorted(heart_lang_codes)[0]
+ 'aao'
+ """
+ data = fetch_source_data()
+ values = []
+ if data is None or not data.git_repo:
+ logger.info("Data API is down or no git_repo found!")
+ return []
+ try:
+ for repo_info in data.git_repo:
+ language_info = repo_info.content
+ language = language_info.language
+ ietf_code = language.ietf_code
+ english_name = language.english_name if language.english_name else ""
+ localized_name = language.national_name
+ is_gateway = ietf_code in gateway_languages
+ if ietf_code not in lang_code_filter_list:
+ if english_name in localized_name:
+ values.append((ietf_code, localized_name, is_gateway))
+ else:
+ values.append(
+ (ietf_code, f"{localized_name} ({english_name})", is_gateway)
+ )
+ except Exception:
+ logger.exception("Failed due to the following exception.")
+ unique_values = unique_tuples(values)
+ return sorted(unique_values, key=lambda value: value[1])
diff --git a/backend/stet/domain/strings.py b/backend/stet/domain/strings.py
index 28ddbe95..cbc49aa4 100644
--- a/backend/stet/domain/strings.py
+++ b/backend/stet/domain/strings.py
@@ -2,7 +2,10 @@
"en": "Spiritual Terms Evaluation Tool (STET)",
"es-419": "Herramienta de Evaluación de Términos Espirituales (STET)",
"fr": "Évaluation des Termes Spirituels (STET)",
+ "gu": "આધ્યાત્મિક શબ્દો મૂલ્યાંકન સાધન (STET)",
"pt-br": "Ferramenta de Avaliação de Termos Espirituais (STET)",
+ "rmn-x-yerliroman": "Инструмент за оценка на духовни термини (STET)",
+ "ru": "Инструмент оценки духовных терминов (STET)",
"sw": "Chombo cha Kutathmini Maneno ya Kiroho (STET)",
"tpi": "Tul bilong skelim ol spirit tok bilong buk trenslesen (STET)",
}
@@ -11,7 +14,10 @@
"en": "Generated on",
"es-419": "Generado el",
"fr": "Généré le",
+ "gu": "તૈયાર કરેલ તારીખ",
"pt-br": "Gerado em",
+ "rmn-x-yerliroman": "Генерирано на",
+ "ru": "Сформировано",
"sw": "Imetolewa tarehe",
"tpi": "Wok i bin kamap long",
}
@@ -20,7 +26,10 @@
"en": "%m/%d/%Y %H:%M:%S",
"es-419": "%d/%m/%Y %H:%M:%S",
"fr": "%d/%m/%Y %H:%M:%S",
+ "gu": "%d/%m/%Y %H:%M:%S",
"pt-br": "%d/%m/%Y %H:%M:%S",
+ "rmn-x-yerliroman": "%d.%m.%Y %H:%M:%S",
+ "ru": "%d.%m.%Y %H:%M:%S",
"sw": "%d/%m/%Y %H:%M:%S",
"tpi": "%d/%m/%Y %H:%M:%S",
}
@@ -29,7 +38,20 @@
"en": ("Source Reference", "Target Reference", "Status", "OK"),
"es-419": ("Fuente", "Idioma Materna", "Estado", "OK"),
"fr": ("Référence de source", "Référence Cible", "Statut", "OK"),
+ "gu": ("સ્ત્રોત લખાણનો સંદર્ભ", "લક્ષ્ય લખાણનો સંદર્ભ", "સ્થિતિ", "બરાબર છે"),
"pt-br": ("Referência de Origem", "Referência de Destino", "Status", "OK"),
+ "rmn-x-yerliroman": (
+ "Препратка към изходния текст – текста на български",
+ "Препратка към преведения текст – текста на цигански",
+ "Текущо състояние",
+ "OK",
+ ),
+ "ru": (
+ "Ссылка на исходный текст",
+ "Ссылка на переведенный текст",
+ "статус",
+ "Удовл",
+ ),
"sw": ("Marejeo Chanzo", "Marejeo Lengwa", "Hali", "OK"),
"tpi": ("Narapela baibel ves", "Tokples ves", "Sek", "OK"),
}
diff --git a/backend/stet/entrypoints/routes.py b/backend/stet/entrypoints/routes.py
index ce0501d9..5a2f5e97 100644
--- a/backend/stet/entrypoints/routes.py
+++ b/backend/stet/entrypoints/routes.py
@@ -4,7 +4,7 @@
import celery.states
from celery.result import AsyncResult
from doc.config import settings
-from doc.domain import resource_lookup
+from stet.domain import resource_lookup
from docx import Document
from fastapi import APIRouter, Request, HTTPException, status
from fastapi.responses import JSONResponse
diff --git a/backend/templates/html/header_enclosing.html b/backend/templates/html/header_enclosing.html
index 049d0a68..f06a66ce 100644
--- a/backend/templates/html/header_enclosing.html
+++ b/backend/templates/html/header_enclosing.html
@@ -83,6 +83,18 @@
font-family: var(--universal-font);
}
+ a {
+ color: #000000; /* Makes the text black */
+ text-decoration: underline; /* Ensures the underline is visible */
+ background-color: #e0e0e0; /* Adds a light grey background highlight */
+ padding: 2px 4px; /* Optional: Adds a little breathing room around the highlight */
+ border-radius: 3px; /* Optional: Softens the edges of the highlight */
+ }
+
+ h1 {
+ text-align: center;
+ }
+
/* Makes list of translation words left justified after an h2 (the resource type name: Translation Words) */
h2 + ul {
margin-left: 0;
diff --git a/frontend/tests/e2e/doc_test_2.ts b/frontend/tests/e2e/doc_test_2.ts
index 6a24c4db..85a3856a 100644
--- a/frontend/tests/e2e/doc_test_2.ts
+++ b/frontend/tests/e2e/doc_test_2.ts
@@ -39,7 +39,7 @@ test.skip('reviewers guide is only shown when book is chosen that it includes -
})
})
-test('can select gateway tab after first selecting heart language and hitting next', async ({
+test.skip('can select gateway tab after first selecting heart language and hitting next', async ({
page
}) => {
await page.goto('http://localhost:8001/')
diff --git a/template.docx b/template.docx
index 697f6f88..8c76e567 100755
Binary files a/template.docx and b/template.docx differ
diff --git a/tests/e2e/doc/test_assemble_by_book_rg.py b/tests/e2e/doc/test_assemble_by_book_rg.py
index 3fd097b4..ecafd6ce 100644
--- a/tests/e2e/doc/test_assemble_by_book_rg.py
+++ b/tests/e2e/doc/test_assemble_by_book_rg.py
@@ -2,6 +2,7 @@
Tests for assembly strategy lang-then-book for reviewer's guide
"""
+import pytest
from doc.config import settings
from doc.domain import model
from doc.entrypoints.app import app
@@ -15,7 +16,7 @@
logger = settings.logger(__name__)
-def test_en_rg_language_book_order_1c_by_chapter() -> None:
+def test_en_rg_language_book_order_1c() -> None:
with TestClient(app=app, base_url=settings.api_test_url()) as client:
response = client.post(
"/documents",
@@ -39,7 +40,7 @@ def test_en_rg_language_book_order_1c_by_chapter() -> None:
check_finished_document_without_verses_success(response)
-def test_en_ulb_gal_en_rg_language_book_order_1c_by_chapter() -> None:
+def test_en_ulb_gal_en_rg_language_book_order_1c() -> None:
with TestClient(app=app, base_url=settings.api_test_url()) as client:
response = client.post(
"/documents",
@@ -68,7 +69,8 @@ def test_en_ulb_gal_en_rg_language_book_order_1c_by_chapter() -> None:
check_finished_document_with_verses_success(response)
-def test_en_ulb_gal_en_tn_gal_en_rg_language_book_order_1c_by_chapter() -> None:
+@pytest.mark.skip
+def test_en_ulb_gal_en_tn_gal_en_rg_language_book_order_1c() -> None:
with TestClient(app=app, base_url=settings.api_test_url()) as client:
response = client.post(
"/documents",
@@ -102,7 +104,7 @@ def test_en_ulb_gal_en_tn_gal_en_rg_language_book_order_1c_by_chapter() -> None:
check_finished_document_with_verses_success(response)
-def test_en_ulb_gal_en_tn_gal_en_rg_en_tw_language_book_order_1c_by_chapter() -> None:
+def test_en_ulb_gal_en_tn_gal_en_rg_en_tw_language_book_order_1c() -> None:
with TestClient(app=app, base_url=settings.api_test_url()) as client:
response = client.post(
"/documents",
@@ -141,7 +143,7 @@ def test_en_ulb_gal_en_tn_gal_en_rg_en_tw_language_book_order_1c_by_chapter() ->
check_finished_document_with_verses_success(response)
-def test_en_ulb_gal_en_tn_gal_en_rg_en_bc_en_tw_language_book_order_1c_by_chapter() -> (
+def test_en_ulb_gal_en_tn_gal_en_rg_en_bc_en_tw_language_book_order_1c() -> (
None
):
with TestClient(app=app, base_url=settings.api_test_url()) as client:
@@ -187,7 +189,7 @@ def test_en_ulb_gal_en_tn_gal_en_rg_en_bc_en_tw_language_book_order_1c_by_chapte
check_finished_document_with_verses_success(response)
-def test_en_ulb_gal_en_tn_gal_en_tq_gal_en_rg_en_bc_en_tw_language_book_order_1c_by_chapter() -> (
+def test_en_ulb_gal_en_tn_gal_en_tq_gal_en_rg_en_bc_en_tw_language_book_order_1c() -> (
None
):
with TestClient(app=app, base_url=settings.api_test_url()) as client:
@@ -238,7 +240,7 @@ def test_en_ulb_gal_en_tn_gal_en_tq_gal_en_rg_en_bc_en_tw_language_book_order_1c
check_finished_document_with_verses_success(response)
-def test_en_ulb_gal_es_419_ulb_gal_en_tn_gal_es_419_tq_gal_en_tq_gal_en_rg_en_bc_es_419_bc_en_tw_es_419_tw_language_book_order_1c_by_chapter() -> (
+def test_en_ulb_gal_es_419_ulb_gal_en_tn_gal_es_419_tq_gal_en_tq_gal_en_rg_en_bc_es_419_bc_en_tw_es_419_tw_language_book_order_1c() -> (
None
):
with TestClient(app=app, base_url=settings.api_test_url()) as client:
diff --git a/tests/e2e/doc/test_assemble_by_verse_book_at_a_time.py b/tests/e2e/doc/test_assemble_by_verse_book_at_a_time.py
index ced5cd00..ba53d8b1 100644
--- a/tests/e2e/doc/test_assemble_by_verse_book_at_a_time.py
+++ b/tests/e2e/doc/test_assemble_by_verse_book_at_a_time.py
@@ -11,6 +11,7 @@
logger = settings.logger(__name__)
+@pytest.mark.skip
def test_en_ulb_col_by_verse_with_no_email_1c() -> None:
with TestClient(app=app, base_url=settings.api_test_url()) as client:
response = client.post(
diff --git a/tests/e2e/doc/test_assemble_by_verse_chapter_at_a_time.py b/tests/e2e/doc/test_assemble_by_verse_chapter_at_a_time.py
index 01c9c62b..afadcee1 100644
--- a/tests/e2e/doc/test_assemble_by_verse_chapter_at_a_time.py
+++ b/tests/e2e/doc/test_assemble_by_verse_chapter_at_a_time.py
@@ -2,6 +2,7 @@
Tests for assembly strategy book-then-language
"""
+import pytest
from doc.config import settings
from doc.domain import model
from doc.entrypoints.app import app
@@ -12,6 +13,8 @@
logger = settings.logger(__name__)
+# en tn is not currently available from data API
+@pytest.mark.skip
def test_en_ulb_col_en_tn_col_en_tq_col_en_tw_col_fr_f10_col_fr_tn_col_fr_tq_col_fr_tw_col_by_verse_chapter_at_a_time_1c_c() -> (
None
):
@@ -73,6 +76,8 @@ def test_en_ulb_col_en_tn_col_en_tq_col_en_tw_col_fr_f10_col_fr_tn_col_fr_tq_col
check_finished_document_with_verses_success(response)
+# en tn is not currently available from data API
+@pytest.mark.skip
def test_en_ulb_col_en_tn_col_en_tq_col_en_tw_col_pt_br_ulb_col_pt_br_tn_col_pt_br_tq_col_pt_br_tw_col_by_chapter_1c() -> (
None
):