Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate documentation with Windows #196

Merged
merged 14 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ Contributors to this version: Trevor James Smith (:user:`Zeitsperre`), Gabriel R
New features and enhancements
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* `xhydro` now supports `RavenPy` v0.15.0 (`RavenHydroFramework` v3.8.1). (:pull:`161`).
* Regional frequency analysis functions have been added under `xhydro.frequency_analysis` module. (:pull:`186`).
* Ressampling function for uncertainitieshave been added under `xhydro.frequency_analysis` module. (:pull:`186`).

* Regional frequency analysis functions as well as Resampling function for uncertainties have been added to the ``xhydro.frequency_analysis`` module. (:pull:`186`).

Internal changes
^^^^^^^^^^^^^^^^
Expand All @@ -20,12 +18,13 @@ Internal changes
* The `conda` environment now relies on the newly created `xdatasets` package. (:pull:`164`).
* The cookiecutter has been updated to the latest commit. Changes include workflow fixes, stricter coding standards, and many small adjustments to the documentation. (:pull:`164`).
* A previously uncaught YAML formatting issue has been addressed. Stricter style conventions are now enforced. (:pull:`174`).
* Chunking was adjusted in a few functions to work with the new requirements of `apply_ufunc`. (:pull:`180`).
* Chunking was adjusted in a few functions to work with the new requirements of ``apply_ufunc``. (:pull:`180`).
* Updated the cookiecutter template to the latest commit. (:pull:`177`):
* Actions have been updated and synchronized.
* Warnings in Pull Requests from forks are now less buggy.
* A new pre-commit hook and linting step for validating numpy docstrings has been added (`numpydoc`).
* All `pip`-based dependencies used to run in CI are now managed by a ``CI/requirements_ci.txt`` that uses hashes of packages for security.
* Added two new Batch (`.bat`) files to help facilitate the translation of and the generation of the `xhydro` documentation in Windows environments. (:pull:`196`).

Breaking changes
^^^^^^^^^^^^^^^^
Expand Down
168 changes: 86 additions & 82 deletions CI/translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ def translate_missing_po_entries( # noqa: C901
The target language of the .po files. Defaults to "fr".
clean_old_entries : bool
Whether to clean old entries in the .po files. Defaults to True.
These are entries that do not exist anymore in the source code, but are still present in the .po files.
overwrite_fuzzy : bool
Whether to overwrite fuzzy entries in the .po files. Defaults to True.
Fuzzy entries are entries that have been marked as needing review, so they are usually wrong.
\*\*kwargs : dict
Additional keyword arguments to pass to the translator.
"""
Expand All @@ -49,91 +51,93 @@ def translate_missing_po_entries( # noqa: C901
)

# Get all .po files
files = Path(dir_path).rglob("*.po")
files = [
f
for f in Path(dir_path).rglob("*.po")
if not any(d in str(f) for d in ["changelog", "apidoc"])
]

number_of_calls = 0
for file_path in files:
if not any(
dont_translate in file_path for dont_translate in ["changelog", "apidoc"]
):
with Path(file_path).open("r+", encoding="utf-8") as file:
content = file.read()

# Find all fuzzy entries
fuzzy_entries = fuzzy_pattern.findall(str(content))
if len(fuzzy_entries) > 0 and overwrite_fuzzy:
msg = f"Found {len(fuzzy_entries)} fuzzy entries in {file_path}"
logger.info(msg)
for i in fuzzy_entries:
entry = i[1].split("\nmsgstr ")
# Remove the fuzzy entry
content = content.replace(entry[1], '""\n\n')
# Since we can't guarantee the exact way the fuzzy entry was written
# we remove the fuzzy tag in 2 steps
content = content.replace(", fuzzy", "")
content = content.replace("#\nmsgid", "msgid")

# Find all msgid and msgstr pairs
msgids = []
msgstrs = []
for i in msg_pattern.findall(str(content)):
ids, strs = i[0].split("\nmsgstr ")
ids = ids if ids != '""' else ""
strs = strs.replace('\\"', "'").replace('"', "").replace("\n", "")
msgids.extend([ids])
msgstrs.extend([strs])

# Track if the file was modified
modified = False

for msgid, msgstr in zip(msgids, msgstrs):
# Check if translation is missing
if msgid and not msgstr:
# Translate the missing string
translated_text = translator.translate(
msgid.replace('\\"', "'").replace('"', "").replace("\n", "")
)

# Split the translated text into lines of max 60 characters
if len(translated_text) > 70: # 70 to include the spaces
words = translated_text.split()
length = 0
words[0] = '"\n"' + words[0]
for i in range(len(words)):
length += len(words[i])
if length > 60:
words[i] = '"\n"' + words[i]
length = 0
translated_text = " ".join(words)

# Replace the empty msgstr with the translated text
content = content.replace(
f'msgid {msgid}\nmsgstr ""',
f'msgid {msgid}\nmsgstr "{translated_text}"',
1,
)
modified = True

# Sleep to avoid rate limiting
number_of_calls += 1
if number_of_calls % 100 == 0:
time.sleep(60)
else:
time.sleep(1)

if clean_old_entries:
is_old = str(content).split("#~")
if len(is_old) > 1:
content = is_old[0]
modified = True

# If modifications were made, write them back to the file
if modified:
msg = f"Updating translations in {file_path}"
logger.info(msg)
file.seek(0)
file.write(content)
file.truncate()
with Path(file_path).open("r+", encoding="utf-8") as file:
content = file.read()

# Find all fuzzy entries
fuzzy_entries = fuzzy_pattern.findall(str(content))
if len(fuzzy_entries) > 0 and overwrite_fuzzy:
msg = f"Found {len(fuzzy_entries)} fuzzy entries in {file_path}"
logger.info(msg)
for i in fuzzy_entries:
entry = i[1].split("\nmsgstr ")
# Remove the fuzzy entry
content = content.replace(entry[1], '""\n\n')
# Since we can't guarantee the exact way the fuzzy entry was written
# we remove the fuzzy tag in 2 steps
content = content.replace(", fuzzy", "")
content = content.replace("#\nmsgid", "msgid")

# Find all msgid and msgstr pairs
msgids = []
msgstrs = []
content_valid = content.split("#~")[0] # Skip old entries
for i in msg_pattern.findall(str(content_valid)):
ids, strs = i[0].split("\nmsgstr ")
ids = ids if ids != '""' else ""
strs = strs.replace('\\"', "'").replace('"', "").replace("\n", "")
msgids.extend([ids])
msgstrs.extend([strs])

# Track if the file was modified
modified = False

for msgid, msgstr in zip(msgids, msgstrs):
# Check if translation is missing
if msgid and not msgstr:
# Translate the missing string
translated_text = translator.translate(
msgid.replace('\\"', "'").replace('"', "").replace("\n", "")
)

# Split the translated text into lines of max 60 characters
if len(translated_text) > 70: # 70 to include the spaces
words = translated_text.split()
length = 0
words[0] = '"\n"' + words[0]
for i in range(len(words)):
length += len(words[i])
if length > 60:
words[i] = '"\n"' + words[i]
length = 0
translated_text = " ".join(words)

# Replace the empty msgstr with the translated text
content = content.replace(
f'msgid {msgid}\nmsgstr ""',
f'msgid {msgid}\nmsgstr "{translated_text}"',
1,
)
modified = True

# Sleep to avoid rate limiting
number_of_calls += 1
if number_of_calls % 100 == 0:
time.sleep(60)
else:
time.sleep(1)

if clean_old_entries:
is_old = str(content).split("#~")
if len(is_old) > 1:
content = is_old[0]
modified = True

# If modifications were made, write them back to the file
if modified:
msg = f"Updating translations in {file_path}"
logger.info(msg)
file.seek(0)
file.write(content)
file.truncate()


# FIXME: Add argparse to make it a command-line tool
Expand Down
2 changes: 2 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@
"special-members": False,
}

autodoc_mock_imports = ["esmpy", "xesmf"]

# For styling class attributes
napoleon_use_ivar = True

Expand Down
1 change: 1 addition & 0 deletions environment-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ dependencies:
- cartopy
- geopandas
- haversine
- importlib-metadata <8.0 # Required for xesmf >=8.4.0,<8.6 to work on Windows
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a danger to pin this for everyone?

Also, I don't know exactly why this is required for it to work on GitHub, but my local setup works with importlib-metadata 8.4.0, esmpy 8.4.2, xesmf 0.8.7 without any issue...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I grabbed this fix from here: CLIMADA-project/climada_petals#132.

I don't think it is dangerous, but from my understanding, the fact that the metadata is corrupted in esmpy/esmf, we need to have an older version here, otherwise this raises ValueErrors on import.

- leafmap
- numpy <2.0.0
- pandas >=2.2.0
Expand Down
1 change: 1 addition & 0 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ dependencies:
- cartopy
- geopandas
- haversine
- importlib-metadata <8.0 # Required for xesmf >=8.4.0,<8.6 to work on Windows
- leafmap
- numpy <2.0.0
- pandas >=2.2.0
Expand Down
32 changes: 32 additions & 0 deletions make-docs.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@echo off
rem Batch script to clean docs artifacts and generate Sphinx HTML documentation

rem Remove docs/notebooks/_data/
if exist docs\notebooks\_data\ (
rd /s /q docs\notebooks\_data\
)

rem Remove docs/notebooks/.ipynb_checkpoints/
if exist docs\notebooks\.ipynb_checkpoints\ (
rd /s /q docs\notebooks\.ipynb_checkpoints\
)

rem Remove specific rst files from docs/apidoc
del /q docs\apidoc\xhydro*.rst
del /q docs\apidoc\modules.rst

rem Generate API documentation
sphinx-apidoc -o docs\apidoc --private --module-first src\xhydro

rem Generate Sphinx HTML documentation in English
call docs\make.bat html "-D language=en"

rem Generate Sphinx HTML documentation in French
call docs\make.bat html "-D language=fr"

if %ERRORLEVEL% neq 0 (
echo Error occurred during documentation generation.
exit /b %ERRORLEVEL%
)

echo Documentation generation completed successfully.
18 changes: 18 additions & 0 deletions make-translations.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
@echo off
rem Batch script to handle translation work

rem Remove French locale .mo files
del /q docs\locales\fr\LC_MESSAGES\*.mo

rem Generate gettext files
sphinx-build -b gettext docs docs\_build\gettext

rem Run sphinx-intl update
sphinx-intl update -p docs\_build\gettext -d docs\locales -l fr

if %ERRORLEVEL% neq 0 (
echo Error occurred during translation update.
exit /b %ERRORLEVEL%
)

echo Translation update completed successfully.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ dependencies = [
"cartopy",
"geopandas",
"haversine",
"importlib-metadata <8.0", # Required for xesmf >=8.4.0,<8.6 to work on Windows
"leafmap",
"numpy <2.0.0",
"pandas >=2.2.0",
Expand Down