diff --git a/docs/Makefile b/docs/Makefile index b63b368a..8ac15275 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -19,10 +19,10 @@ help: %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -docs: - # rm -rf source/apidoc docs/_build - sphinx-apidoc -fMeT -o source/apidoc ../src/bsk_rl - for f in source/apidoc/*.rst; do\ - ./apidoc_format.sh $$f ;\ - done - $(MAKE) html \ No newline at end of file +clean: + rm -rf "source/Examples" "source/API Reference" + + +view: + # works on macOS + open build/html/index.html \ No newline at end of file diff --git a/docs/apidoc_format.sh b/docs/apidoc_format.sh deleted file mode 100755 index 8f2d987c..00000000 --- a/docs/apidoc_format.sh +++ /dev/null @@ -1,21 +0,0 @@ -for file in "$@"; do - # Check if file exists - if [ -f "$file" ]; then - # Get the first line of the file - body=$(tail -n +3 "$file") - first_line=$(head -n 1 "$file") - package_loc=$(echo "$first_line" | sed -E 's/( module| package)$//') - page_title="${package_loc##*.}" - echo "TITLE" "$page_title" - - # Update the file with the modified first line - echo $page_title > $file - echo $(printf '=%.0s' $(seq 1 ${#page_title})) >> $file - echo "\`$package_loc\`" >> $file - echo "$body" >> $file - - echo "Processed $file" - else - echo "File '$file' does not exist." - fi -done diff --git a/docs/source/conf.py b/docs/source/conf.py index 5efa50c5..051d9e29 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -3,12 +3,16 @@ # For the full list of built-in configuration values, see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html + import datetime # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information import os +import re import sys +from glob import glob +from pathlib import Path # sys.path.insert(0, os.path.abspath(os.path.join("..", "..", "src"))) now = datetime.datetime.now() @@ -33,6 +37,8 @@ templates_path = ["_templates"] exclude_patterns = [] +source_suffix = ".rst" +master_doc = "index" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -51,7 +57,7 @@ "navigation_depth": -1, } html_static_path = ["_static"] -# html_css_files = ["custom.css"] +html_css_files = ["custom.css"] html_logo = "./_images/static/Basilisk-Logo.png" add_module_names = False @@ -65,3 +71,167 @@ def skip(app, what, name, obj, would_skip, options): def setup(app): app.connect("autodoc-skip-member", skip) + + +class FileCrawler: + def __init__(self, base_source_dir, base_doc_dir): + self.base_source_dir = base_source_dir + self.base_doc_dir = base_doc_dir + + def grab_files(self, dir_path): + dirs_in_dir = [x for x in dir_path.iterdir() if x.is_dir()] + files_in_dir = dir_path.glob("*.py") + + # Remove any directories that shouldn't be added directly to the website + dir_filters = [ + r".*__pycache__.*", + r".*\.ruff_cache.*", + r".*\.egg-info", + r".*\/simplemaps_worldcities", + ] + dirs_in_dir = list( + filter( + lambda dir: not any( + re.match(filter, str(dir)) for filter in dir_filters + ), + dirs_in_dir, + ) + ) + + file_filters = [ + r".*__init__\.py", + r"(.*\/|)_[a-zA-Z0-9_]*\.py", + ] + files_in_dir = list( + filter( + lambda file: not any( + re.match(filter, str(file)) for filter in file_filters + ), + files_in_dir, + ) + ) + + return sorted(list(files_in_dir)), sorted(list(dirs_in_dir)) + + def populate_doc_index(self, index_path, file_paths, dir_paths): + name = index_path.stem + lines = "" + + try: + path_to_folder = ( + dir_paths[0].parent if len(dir_paths) > 0 else file_paths[0].parent + ) + except IndexError: + path_to_folder = None + + # if a _default.rst file exists in a folder, then use it to generate the index.rst file + try: + docFileName = path_to_folder / "_default.rst" + with open(docFileName, "r") as docFile: + docContents = docFile.read() + lines += docContents + "\n\n" + except: # FileNotFoundError: # Auto-generate the index.rst file + # add page tag + lines += ".. _Folder_" + name.replace(" ", "_") + ":\n\n" + + # Title the page + lines += name + "\n" + "=" * len(name) + "\n\n" + + # pull in folder _doc.rst file if it exists + try: + path_to_folder = ( + dir_paths[0].parent if len(dir_paths) > 0 else file_paths[0].parent + ) + docFileName = path_to_folder / "_doc.rst" + if os.path.isfile(docFileName): + with open(docFileName, "r") as docFile: + docContents = docFile.read() + lines += docContents + "\n\n" + except: # FileNotFoundError: + pass + + # Add a linking point to all local files + lines += ( + """\n\n.. toctree::\n :maxdepth: 1\n :caption: """ + "Files:\n\n" + ) + added_names = [] + for file_path in sorted(file_paths): + file_name = os.path.basename(os.path.normpath(file_path)) + file_name = file_name[: file_name.rfind(".")] + + if file_name not in added_names: + lines += " " + file_name + "\n" + added_names.append(file_name) + lines += "\n" + + # Add a linking point to all local directories + lines += ( + """.. toctree::\n :maxdepth: 1\n :caption: """ + "Directories:\n\n" + ) + for dir_path in sorted(dir_paths): + dirName = os.path.basename(os.path.normpath(dir_path)) + lines += " " + dirName + "/index\n" + + with open(os.path.join(index_path, "index.rst"), "w") as f: + f.write(lines) + + def generate_autodoc(self, doc_path, source_file): + short_name = source_file.name.replace(".py", "") + qual_name = ( + self.base_source_dir.name + + "." + + ( + str(source_file.relative_to(self.base_source_dir)) + .replace("/", ".") + .replace(".py", "") + ) + ) + + # Add the module path to sys.path so sphinx can produce docs + sys.path.append(source_file.resolve().parent) + sys.path.append(source_file.resolve()) + + # Generate the autodoc file + lines = ".. _" + qual_name + ":\n\n" + lines += short_name + "\n" + "=" * len(short_name) + "\n\n" + lines += f"``{qual_name}``\n\n" + lines += """.. toctree::\n :maxdepth: 1\n :caption: """ + "Files" + ":\n\n" + lines += ( + """.. automodule:: """ + + qual_name + + """\n :members:\n :show-inheritance:\n\n""" + ) + + # Write to file + with open(doc_path / f"{short_name}.rst", "w") as f: + f.write(lines) + + def run(self, source_dir=None): + if source_dir is None: + source_dir = self.base_source_dir + + file_paths, dir_paths = self.grab_files(source_dir) + index_path = source_dir.relative_to(self.base_source_dir) + + # Populate the index.rst file of the local directory + os.makedirs(self.base_doc_dir / index_path, exist_ok=True) + self.populate_doc_index(self.base_doc_dir / index_path, file_paths, dir_paths) + + # Generate the correct auto-doc function for python modules + for file in file_paths: + self.generate_autodoc( + self.base_doc_dir / index_path, + file, + ) + + # Recursively go through all directories in source, documenting what is available. + for dir_path in sorted(dir_paths): + self.run( + source_dir=dir_path, + ) + + return + + +FileCrawler(Path("../../src/bsk_rl/"), Path("./API Reference/")).run() +FileCrawler(Path("../../examples"), Path("./Examples/")).run() diff --git a/docs/source/index.rst b/docs/source/index.rst index 9ff62891..266edbee 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,36 +1,23 @@ -.. bsk_rl documentation master file, created by - sphinx-quickstart on Wed Nov 15 09:35:59 2023. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - Welcome to bsk_rl's documentation! ================================== -**bsk_rl** is a Python library used to develop reinforcement learning environments for autonomouse satellites. +`bsk-rl` is a Python package consisting of various `Gymnasium `_ environments, agents, training scripts, and examples for spacecraft planning and scheduling problems, with an emphasis on reinforcement learning. -Check out the :doc:`usage` section for further information. +Installation instruction can be found at :doc:`install`. -.. note:: - This project is under active development. +New environments should be based on :ref:`Folder_general_satellite_tasking`. .. toctree:: :maxdepth: 2 - :caption: Contents: :titlesonly: - apidoc/bsk_rl - - -Indices and tables -================== + install + API Reference/index + Examples/index -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` -Contents --------- - -.. toctree:: +.. Indices +.. ======= - usage +.. * :ref:`genindex` +.. * :ref:`modindex` diff --git a/docs/source/install.rst b/docs/source/install.rst new file mode 100644 index 00000000..12b0c94c --- /dev/null +++ b/docs/source/install.rst @@ -0,0 +1,30 @@ +Installation +============ + +.. note:: + `bsk-rl` requires `Basilisk `_, a spacecraft simulation framework package, to be installed. Instructions for installing and compiling Basilisk may be found `here `_. + + +To install bsk_rl, clone the repo + +.. code-block:: console + + $ git clone git@github.com:AVSLab/bsk_rl.git + +and run the following command in your virtual environment: + +.. code-block:: console + + (.venv) $ python -m pip install -e . && finish_install + +while in the base directory. This will install `pip` dependencies and download data dependencies. + +.. note:: + See `#51 `_ for issues with `chebpy` installation on Silicon Macs. + +Test the installation by running + +.. code-block:: console + pytest tests/examples + +in the base directory. \ No newline at end of file diff --git a/docs/source/usage.rst b/docs/source/usage.rst deleted file mode 100644 index 0adbf59f..00000000 --- a/docs/source/usage.rst +++ /dev/null @@ -1,12 +0,0 @@ -Installation -============ - -.. note:: - To use Lumache, bsk_rl, Basilisk must be installed first. You can find instructions on how to do so - `at this link `_ - -To install bsk_rl, clone the repo and run the following command in your virtual environment: - -.. code-block:: console - - (.venv) $ python -m pip install -e . && finish_install