Skip to content

Commit 4a2fac7

Browse files
authored
doc: new layout for getting started section (#464)
1 parent 832ef85 commit 4a2fac7

File tree

21 files changed

+886
-440
lines changed

21 files changed

+886
-440
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ distributions/
168168
# Examples are kept at the root of the repository but copied at documentation
169169
# build time inside the documentation source directory
170170
doc/source/examples/
171+
doc/source/_static/docker/
171172

172173
# Ignore trash generated by Jupyter Notebook
173174
.Trash-1000/

doc/source/conf.py

Lines changed: 127 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
"""Sphinx documentation configuration file."""
22

33
from datetime import datetime
4+
import fnmatch
5+
import hashlib
46
import os
57
import pathlib
68
import shutil
79
import subprocess
10+
import zipfile
811

912
import sphinx
1013
from sphinx.util import logging
@@ -130,20 +133,6 @@
130133
rst_epilog += links_file.read()
131134

132135

133-
# Read available Docker images for Windows and Linux
134-
DOCKER_DIR = pathlib.Path(__file__).parent.parent.parent.absolute() / "docker"
135-
WINDOWS_IMAGES, UBUNTU_IMAGES = [DOCKER_DIR / path for path in ["windows", "linux/ubuntu"]]
136-
137-
138-
def get_images_directories_from_path(path):
139-
"""Get all the Docker images present in the retrieved Path."""
140-
images = [
141-
folder.name for folder in path.glob("**/*") if folder.name != path.name and (folder / "Dockerfile").exists()
142-
] or ["No images available."]
143-
images.sort()
144-
return images
145-
146-
147136
# -- Declare the Jinja context -----------------------------------------------
148137
BUILD_API = True if os.environ.get("BUILD_API", "true") == "true" else False
149138
if not BUILD_API:
@@ -203,21 +192,106 @@ def get_images_directories_from_path(path):
203192

204193

205194
# -- Jinja context configuration ---------------------------------------------
195+
196+
197+
def zip_directory(directory_path: pathlib.Path, zip_filename: pathlib.Path, ignore_patterns=None):
198+
"""Compress a directory using ZIP.
199+
200+
Parameters
201+
----------
202+
directory_path : ~pathlib.Path
203+
Directory to compress.
204+
zip_filename : ~pathlib.Path
205+
Output file path.
206+
ignore_patterns : list
207+
List of Unix-like pattern to ignore.
208+
209+
"""
210+
if ignore_patterns is None:
211+
ignore_patterns = []
212+
213+
if not zip_filename.suffix == ".zip":
214+
zip_filename = zip_filename.with_suffix(".zip")
215+
216+
with zipfile.ZipFile(zip_filename, "w", zipfile.ZIP_DEFLATED) as zipf:
217+
for file_path in directory_path.rglob("*"):
218+
if file_path.is_file():
219+
if any(fnmatch.fnmatch(file_path.relative_to(directory_path), pattern) for pattern in ignore_patterns):
220+
continue
221+
222+
relative_path = file_path.relative_to(directory_path)
223+
zipf.write(file_path, relative_path)
224+
225+
226+
def get_sha256_from_file(filepath: pathlib.Path):
227+
"""Compute the SHA-256 for a file.
228+
229+
Parameters
230+
----------
231+
filepath : ~pathlib.Path
232+
Desired file.
233+
234+
Returns
235+
-------
236+
str
237+
String representing the SHA-256 hash.
238+
239+
"""
240+
sha256_hash = hashlib.sha256()
241+
with open(filepath, "rb") as file:
242+
while chunk := file.read(8192):
243+
sha256_hash.update(chunk)
244+
return sha256_hash.hexdigest()
245+
246+
247+
def get_file_size_in_mb(file_path):
248+
"""
249+
Compute the size of a file in megabytes.
250+
251+
Parameters
252+
----------
253+
file_path : str or Path
254+
The path to the file whose size is to be computed.
255+
256+
Returns
257+
-------
258+
float
259+
The size of the file in megabytes.
260+
261+
Raises
262+
------
263+
FileNotFoundError
264+
If the file does not exist.
265+
OSError
266+
If an OS-related error occurs while accessing the file.
267+
268+
"""
269+
path = pathlib.Path(file_path)
270+
271+
if not path.is_file():
272+
raise FileNotFoundError(f"The file at {file_path} does not exist.")
273+
274+
file_size_bytes = path.stat().st_size
275+
return file_size_bytes / (1024 * 1024)
276+
277+
278+
ARTIFACTS_PATH = pathlib.Path().parent / "_static" / "artifacts"
279+
ARTIFACTS_WHEEL = ARTIFACTS_PATH / f"{project.replace('-', '_')}-{version}-py3-none-any.whl"
280+
ARTIFACTS_SDIST = ARTIFACTS_PATH / f"{project.replace('-', '_')}-{version}.tar.gz"
281+
206282
jinja_contexts = {
207-
"docker_images": {
208-
"windows_images": get_images_directories_from_path(WINDOWS_IMAGES),
209-
"linux_images": get_images_directories_from_path(UBUNTU_IMAGES),
210-
},
211-
"install_guide": {
212-
"version": f"v{version}" if not version.endswith("dev0") else "main",
213-
},
283+
"install_guide": {"stk_version": "12.9.0"},
214284
"main_toctree": {
215285
"build_api": BUILD_API,
216286
"build_examples": BUILD_EXAMPLES,
217287
},
218288
"artifacts": {
219-
"wheels": f"{project.replace('-', '_')}-{version}-py3-none-any.whl",
220-
"source": f"{project.replace('-', '_')}-{version}.tar.gz",
289+
"wheels": ARTIFACTS_WHEEL.name,
290+
"wheels_size": f"{get_file_size_in_mb(ARTIFACTS_WHEEL):.2f} MB",
291+
"wheels_hash": get_sha256_from_file(ARTIFACTS_WHEEL),
292+
"source": ARTIFACTS_SDIST.name,
293+
"source_size": f"{get_file_size_in_mb(ARTIFACTS_SDIST):.2f} MB",
294+
"source_hash": get_sha256_from_file(ARTIFACTS_SDIST),
221295
"platforms": ["Windows", "Linux"],
222296
},
223297
}
@@ -241,6 +315,7 @@ def get_images_directories_from_path(path):
241315
# Requires sign-in
242316
f"https://github.com/{user_repo}/*",
243317
"https://support.agi.com/3d-models",
318+
"https://support.agi.com/downloads",
244319
]
245320

246321
# -- MyST Sphinx configuration -----------------------------------------------
@@ -249,7 +324,7 @@ def get_images_directories_from_path(path):
249324
# -- Sphinx application setup ------------------------------------------------
250325

251326

252-
def copy_examples_files_to_source_dir(app: sphinx.application.Sphinx):
327+
def copy_docker_files_to_static_dir(app: sphinx.application.Sphinx):
253328
"""
254329
Copy the examples directory to the source directory of the documentation.
255330
@@ -259,27 +334,36 @@ def copy_examples_files_to_source_dir(app: sphinx.application.Sphinx):
259334
Sphinx application instance containing the all the doc build configuration.
260335
261336
"""
262-
SOURCE_EXAMPLES = pathlib.Path(app.srcdir) / "examples"
263-
if not SOURCE_EXAMPLES.exists():
264-
SOURCE_EXAMPLES.mkdir(parents=True, exist_ok=True)
337+
SOURCE_DIR = pathlib.Path(app.srcdir)
338+
DOCKER_DIR = SOURCE_DIR.parent.parent / "docker"
339+
STATIC_DOCKER_DIR = SOURCE_DIR / "_static" / "docker"
340+
if not STATIC_DOCKER_DIR.exists():
341+
STATIC_DOCKER_DIR.mkdir()
265342

266-
EXAMPLES_DIRECTORY = SOURCE_EXAMPLES.parent.parent.parent / "examples"
343+
COMPRESSED_DOCKER_WINDOWS_IMAGES = STATIC_DOCKER_DIR / "windows.zip"
344+
COMPRESSED_DOCKER_LINUX_IMAGES = STATIC_DOCKER_DIR / "linux.zip"
267345

268-
all_examples = list(EXAMPLES_DIRECTORY.glob("*.py"))
269-
examples = [file for file in all_examples if f"{file.name}" not in exclude_examples]
346+
logger = logging.getLogger(__name__)
270347

271-
print(f"BUILDER: {app.builder.name}")
348+
logger.info(f"\nCompressing Docker images...")
349+
zip_directory(DOCKER_DIR / "windows", COMPRESSED_DOCKER_WINDOWS_IMAGES, ignore_patterns=["*.tgz"])
350+
zip_directory(DOCKER_DIR / "linux", COMPRESSED_DOCKER_LINUX_IMAGES, ignore_patterns=["*.tgz"])
272351

273-
for file in status_iterator(
274-
examples,
275-
f"Copying example to doc/source/examples/",
276-
"green",
277-
len(examples),
278-
verbosity=1,
279-
stringify_func=(lambda file: file.name),
280-
):
281-
destination_file = SOURCE_EXAMPLES / file.name
282-
destination_file.write_text(file.read_text(encoding="utf-8"), encoding="utf-8")
352+
# Add the new files and their information to the Jinja context. This
353+
# operation can not be performed outside of this function since the compressed files do not yet exist.
354+
355+
DOCKER_RECIPES = SOURCE_DIR / "_static" / "docker"
356+
DOCKER_RECIPES_WINDOWS = DOCKER_RECIPES / "windows.zip"
357+
DOCKER_RECIPES_LINUX = DOCKER_RECIPES / "linux.zip"
358+
359+
jinja_contexts["docker_images"] = {
360+
"docker_recipes_windows": DOCKER_RECIPES_WINDOWS.name,
361+
"docker_recipes_windows_size": f"{get_file_size_in_mb(DOCKER_RECIPES_WINDOWS):.2f} MB",
362+
"docker_recipes_windows_hash": f"{get_sha256_from_file(DOCKER_RECIPES_WINDOWS)}",
363+
"docker_recipes_linux": DOCKER_RECIPES_LINUX.name,
364+
"docker_recipes_linux_size": f"{get_file_size_in_mb(DOCKER_RECIPES_LINUX):.2f} MB",
365+
"docker_recipes_linux_hash": f"{get_sha256_from_file(DOCKER_RECIPES_LINUX)}",
366+
}
283367

284368

285369
def copy_examples_to_output_dir(app: sphinx.application.Sphinx, exception: Exception):
@@ -342,6 +426,7 @@ def render_examples_as_pdf(app: sphinx.application.Sphinx, exception: Exception)
342426
343427
Quarto needs to be installed in the system to render the PDF files. See
344428
https://quarto.org/docs/get-started/.
429+
Artifact
345430
346431
Parameters
347432
----------
@@ -401,6 +486,7 @@ def setup(app: sphinx.application.Sphinx):
401486
# However, the examples are desired to be kept in the root directory. Once the
402487
# build has completed, no matter its success, the examples are removed from
403488
# the source directory.
489+
app.connect("builder-inited", copy_docker_files_to_static_dir)
404490
if BUILD_EXAMPLES:
405491
app.connect("builder-inited", copy_examples_files_to_source_dir)
406492
app.connect("build-finished", remove_examples_from_source_dir)

doc/source/getting-started.rst

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,36 @@
11
Getting started
22
###############
33

4-
To run PySTK, you must have a licensed copy of `STK`_.
4+
This page helps you quickly get started with PySTK. It lists all the
5+
prerequisites and guides you step by step to install the library on your
6+
platform.
57

8+
.. grid:: 3
69

7-
.. grid:: 2
10+
.. grid-item-card:: :fa:`info-circle` About PySTK
11+
:link: getting-started/about
12+
:link-type: doc
13+
14+
About the next generation Python API for STK
815

9-
.. grid-item-card:: Building STK images :fab:`docker`
10-
:link: getting-started/building-stk-images
16+
.. grid-item-card:: :fa:`tasks` Prerequisites
17+
:link: getting-started/prerequisites
1118
:link-type: doc
1219

13-
Step-by-step guidelines on how to build your own Docker image for STK.
20+
What you need prior installing PySTK
1421

15-
.. grid-item-card:: Installing PySTK :fab:`python`
16-
:link: getting-started/installing-pystk
22+
.. grid-item-card:: :fa:`download` Install
23+
:link: getting-started/install
1724
:link-type: doc
1825

19-
Learn how to download and install PySTK in your development environment
20-
from various official sources including PyPI and official Ansys
21-
repositories.
26+
Guidelines on how to install PySTK in your system
2227

2328

2429
.. toctree::
2530
:maxdepth: 3
2631
:hidden:
2732

28-
getting-started/building-stk-images
29-
getting-started/installing-pystk
33+
About<getting-started/about>
34+
Prerequisites<getting-started/prerequisites>
35+
Install<getting-started/install>
3036

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
About
2+
#####
3+
4+
PySTK is the next generation Python API for Ansys Systems Toolkit (STK). Its
5+
main advantages and features include:
6+
7+
- **Remote API using gRPC**: In addition to traditional OLE communication
8+
with STK, the STK Python API has optional gRPC communication for
9+
out-of-process or remote interaction with STK.
10+
11+
- **Better namings:** API objects no longer have prefixes in their
12+
class names and methods. This improves readability, making your code easy to
13+
read and to maintain.
14+
15+
- **PyAnsys compliance:** the API integrates with the rest of the `PyAnsys`_,
16+
allowing users to connect STK with other Ansys products.

0 commit comments

Comments
 (0)