From 395b6dcacbb6a4ee8dd8877089d51512e97f32c7 Mon Sep 17 00:00:00 2001
From: tengxianglin <eeltx@icloud.com>
Date: Tue, 2 Jul 2024 10:44:41 +0800
Subject: [PATCH] Update multiple versions for API Documentation

---
 .github/workflows/web_update.yml |   8 +-
 api.sh                           |   8 +-
 docs/update_quairkit_rst.py      | 125 ++++++++++++++++++++-----------
 version.sh                       |  75 +++++++++++++++++++
 4 files changed, 162 insertions(+), 54 deletions(-)
 create mode 100644 version.sh

diff --git a/.github/workflows/web_update.yml b/.github/workflows/web_update.yml
index 330a000..fc952e4 100644
--- a/.github/workflows/web_update.yml
+++ b/.github/workflows/web_update.yml
@@ -25,7 +25,7 @@ jobs:
         uses: actions/configure-pages@v5
 
       - name: Cache pip  # Step to cache pip packages.
-        uses: actions/cache@v3
+        uses: actions/cache@v4
         with:
           path: ~/.cache/pip
           key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
@@ -34,12 +34,10 @@ jobs:
 
       - name: Install dependencies  # Step to install dependencies.
         run: |
-          pip install quairkit sphinx_immaterial
+          pip install quairkit sphinx_immaterial nbsphinx
 
       - name: Generate Sphinx Documentation for API Documentation  # Step to generate Sphinx documentation.
-        run: |
-          python docs/update_quairkit_rst.py
-          sphinx-build -b html docs/sphinx_src docs/api
+        run: bash ./version.sh
 
       - name: Upload artifact  # Step to upload the built site as an artifact.
         uses: actions/upload-pages-artifact@v3
diff --git a/api.sh b/api.sh
index 6ccdacf..34ba931 100644
--- a/api.sh
+++ b/api.sh
@@ -1,15 +1,15 @@
 #!/bin/bash
 
 # Step 0: 删除 html 和 sphinx_src 文件夹
-rm -rf docs/api/html
-rm -rf docs/api/sphinx_src
+rm -rf docs/api
+rm -rf docs/sphinx_src
 
 # Step 1: 安装 Sphinx 和 sphinx_material(如果尚未安装)
-pip show quairkit > /dev/null 2>&1 || pip install quairkit
-pip show sphinx_immaterial > /dev/null 2>&1 || pip install sphinx_immaterial
+pip show sphinx_material || pip install sphinx_material
 
 # Step 2: 生成所有必要的 rst 和 conf.py 文件
 python docs/update_quairkit_rst.py
+cp -r tutorials docs/sphinx_src/
 
 # Step 3: 使用 Sphinx 构建 HTML 文档
 sphinx-build -b html docs/sphinx_src docs/api
diff --git a/docs/update_quairkit_rst.py b/docs/update_quairkit_rst.py
index 9789860..3687a7b 100644
--- a/docs/update_quairkit_rst.py
+++ b/docs/update_quairkit_rst.py
@@ -9,6 +9,9 @@
 
 _platform_name = "QuAIRKit"
 
+# set the module names you want to include in API documentation in _module_names
+_module_names = ["quairkit", "tutorials"]
+
 # set the file name you want to ignore in API documentation in _ignore_file_names
 _ignore_file_names = ["quairkit.core.utils", "quairkit.core.intrinsic"]
 
@@ -22,7 +25,7 @@ def is_correct_directory():
     return os.path.exists(required_file)
 
 def _list_quairkit_files(
-    path: str = os.path.join(".", "quairkit"),
+    path: str = os.path.join("."),
     base_path: str = "",
     file_name_attr_list: List[Tuple[str, str]] = None,
 ) -> List[Tuple[str, str]]:
@@ -47,15 +50,26 @@ def _list_quairkit_files(
         relative_path = os.path.join(base_path, child).replace(os.path.sep, ".")
 
         if os.path.isdir(child_path):
-            sub_list = _list_quairkit_files(child_path, relative_path, [])
-            if sub_list:  # 仅当子目录非空时才添加
-                file_name_attr_list.append((f"quairkit.{relative_path}", "folder"))
+            if sub_list := _list_quairkit_files(child_path, relative_path, []):
+                file_name_attr_list.append((f"{relative_path}", "folder"))
                 file_name_attr_list.extend(sub_list)
         elif child.endswith(".py"):
             file_name_attr_list.append(
-                (f"quairkit.{relative_path.replace('.py', '')}", "python")
+                (f"{relative_path.rstrip('.py')}", "python")
             )
-    
+        # elif child.endswith(".ipynb"):
+        #     file_name_attr_list.append(
+        #         (f"{relative_path.rstrip('.ipynb')}", "notebook")
+        #     )
+
+    file_name_attr_list = [
+        sub_array
+        for sub_array in file_name_attr_list
+        if any(
+            sub_array[0].startswith(module_name) for module_name in _module_names
+        )
+    ]
+
     file_name_attr_list = [
         sub_array
         for sub_array in file_name_attr_list
@@ -63,6 +77,7 @@ def _list_quairkit_files(
             sub_array[0].startswith(ignore_item) for ignore_item in _ignore_file_names
         )
     ]
+
     file_name_attr_list.sort()
 
     return file_name_attr_list
@@ -106,6 +121,23 @@ def _update_function_rst(
 
         with open(file_path, "w") as file:
             file.write(rst_content)
+    
+    rst_content = \
+"""\
+tutorials
+=========
+
+.. toctree::
+    :maxdepth: 4
+    :glob:
+
+    tutorials/*
+"""
+
+    file_path = os.path.join(source_directory, "tutorials.rst")
+
+    with open(file_path, "w") as file:
+        file.write(rst_content)
 
     return
 
@@ -113,10 +145,9 @@ def _update_function_rst(
 def _update_index_rst(
     file_list: List[Tuple[str, str]], source_directory: str = _sphinx_source_dir
 ) -> None:
-    """_summary_
-
+    """
     Args:
-        source_directory (str, optional): _description_. Defaults to source_dir.
+        source_directory (str, optional): Defaults to source_dir. The directory where .rst files will be created.
     """
     rst_content = ""
     if len(sys.argv) == 1:
@@ -126,7 +157,7 @@ def _update_index_rst(
 Welcome to {_platform_name}'s documentation!
 ====================================
 
-|quairkit| `Go to {_platform_name} Home <https://quair.github.io/quairkit/>`_
+|quairkit| `Go to QuAIR-Platform Home <https://www.quairkit.com/>`_
 
 """
 
@@ -134,24 +165,13 @@ def _update_index_rst(
 .. toctree::
     :maxdepth: 1
 """
-    rst_content += _get_modules_rst(file_list)
+    rst_content += "".join(f"\n    {item}" for item in _module_names)
     file_path = os.path.join(source_directory, "index.rst")
 
     with open(file_path, "w") as file:
         file.write(rst_content)
 
 
-def _get_modules_rst(
-    file_list: List[Tuple[str, str]], source_directory: str = _sphinx_source_dir
-):
-    file_list_copy = file_list.copy()
-    rst_content = ""
-    for item in file_list_copy:
-        if item[0].count(".") == 1:
-            rst_content += f"\n    {item[0]}"
-    return rst_content
-
-
 def _update_conf_py(source_directory: str = _sphinx_source_dir):
     """_summary_
 
@@ -173,7 +193,7 @@ def _update_conf_py(source_directory: str = _sphinx_source_dir):
 import os
 import sys
 
-sys.path.insert(0, os.path.join('..', '..', ''))
+sys.path.insert(0, os.path.join('..', '..'))
 # -- Project information -----------------------------------------------------
 
 project = "{_platform_name}"
@@ -197,6 +217,7 @@ def _update_conf_py(source_directory: str = _sphinx_source_dir):
     "sphinx.ext.mathjax",
     "sphinx.ext.todo",
     "sphinx_immaterial",
+    "nbsphinx",
 """
     if len(sys.argv) == 2 and sys.argv[1] == "wiki":
         rst_content += """\
@@ -228,22 +249,12 @@ def _update_conf_py(source_directory: str = _sphinx_source_dir):
 html_title = "QuAIRKit"
 html_short_title = "QuAIRKit"
 build_dir = "api"
-# html_theme_options = {
-#     'navigation_depth': 1,
-# }
 html_theme_options = {
-    'base_url': 'https://quair.github.io/quairkit/',
-    'repo_url': 'https://github.com/QuAIR/QuAIRKit',
-    'repo_name': 'QuAIRKit',
-    # 'google_analytics_account': 'UA-XXXXX',
-    'html_minify': True,
-    'css_minify': True,
-    'nav_title': 'QuAIRKit API Documentation',
-    # 'logo_icon': '&#xe869',
-    # 'globaltoc_depth': 2,
-    'color_primary': "green",
-    'color_accent': 'indigo',
-    "palette": { "primary": "green" }
+    "repo_url": 'https://github.com/QuAIR/QuAIRKit',
+    "repo_name": 'QuAIRKit',
+    "palette": { "primary": "green" },
+    "version_dropdown": True,
+    "version_json": "versions.json",
 }
 html_favicon = '../favicon.svg'
 # Add any paths that contain custom static files (such as style sheets) here,
@@ -262,6 +273,8 @@ def _update_conf_py(source_directory: str = _sphinx_source_dir):
 autodoc_docstring_signature = False
 autodoc_typehints_description_target = "documented"
 autodoc_typehints_format = "short"
+
+
 """
 
     file_path = os.path.join(source_directory, "conf.py")
@@ -269,16 +282,38 @@ def _update_conf_py(source_directory: str = _sphinx_source_dir):
         file.write(rst_content)
 
 
+def _create_redirect_html():
+    html_content = """
+<!DOCTYPE HTML>
+<html lang="en">
+    <head>
+        <meta charset="utf-8">
+        <meta http-equiv="refresh" content="0; url=latest/index.html" />
+        <link rel="canonical" href="latest/index.html" />
+    </head>
+    <body>
+        <p>If this page does not refresh automatically, then please direct your browser to
+            <a href="latest/index.html">our latest docs</a>.
+        </p>
+    </body>
+</html>
+"""
+    file_dir = os.path.join(".", "docs", "api")
+    os.makedirs(file_dir, exist_ok=True)
+    with open(os.path.join(file_dir, "index.html"), 'w', encoding='utf-8') as file:
+        file.write(html_content)
+
+
 if __name__ == "__main__":
     _current_script_path = os.path.abspath(__file__)
     _platform_dir_path = os.path.dirname(os.path.dirname(_current_script_path))
 
     _current_working_dir = os.getcwd()
-    if _current_working_dir == _platform_dir_path:
-        result = _list_quairkit_files()
-        os.makedirs(_sphinx_source_dir, exist_ok=True)
-        _update_index_rst(result)
-        _update_function_rst(result)
-        _update_conf_py()
-    else:
+    if _current_working_dir != _platform_dir_path:
         raise SystemExit(f"The current working directory is not {_platform_dir_path}.")
+    result = _list_quairkit_files()
+    os.makedirs(_sphinx_source_dir, exist_ok=True)
+    _update_index_rst(result)
+    _update_function_rst(result)
+    _update_conf_py()
+    _create_redirect_html()
diff --git a/version.sh b/version.sh
new file mode 100644
index 0000000..02e9442
--- /dev/null
+++ b/version.sh
@@ -0,0 +1,75 @@
+#!/bin/bash
+
+# Update system repositories and install Pandoc
+sudo apt-get update
+sudo apt-get install -y pandoc
+
+# Check and install the Sphinx Material theme and nbsphinx if not already installed
+pip show sphinx_immaterial || pip install sphinx_immaterial
+pip show nbsphinx || pip install nbsphinx
+
+# Clean up the existing API documentation directories
+rm -rf docs/api
+rm -rf docs/sphinx_src
+rm -rf docs/source
+
+# Retrieve all Git tags and store them in an array
+tags=($(git tag))
+
+# Retrieve the name of the current branch
+current_branch=$(git rev-parse --abbrev-ref HEAD)
+
+# Initialize the version_info list for Python configuration
+version_info_content="html_theme_options['version_info'] = ["
+
+# Populate the version_info list with tag data
+for i in "${!tags[@]}"; do
+    tag="${tags[$i]}"
+    if [ $i -eq 0 ]; then
+        version_info_content+="{'version': '$tag', 'title': '$tag', 'aliases': ['$tag']}"
+    else
+        version_info_content+=", {'version': '$tag', 'title': '$tag', 'aliases': ['$tag']}"
+    fi
+done
+
+# Close the version_info list
+version_info_content+="]"
+
+# Fetch all tags from the repository
+git fetch --all --tags
+
+# Loop through all tags and generate Sphinx documentation for each with specific conditions
+for tag in "${tags[@]}"; do
+    case $tag in
+        v0.0.1)
+            git checkout $tag
+            python docs/update_avocado_rst.py
+            sphinx-build docs/source docs/api/$tag
+            rm -rf docs/source
+            ;;
+        v0.0.2-alpha)
+            git checkout $tag
+            python docs/avocado/update_avocado_rst.py
+            sphinx-build docs/avocado/sphinx_src docs/api/$tag
+            rm -rf docs/avocado/sphinx_src
+            ;;
+        *)
+            git checkout $tag
+            python docs/update_quairkit_rst.py
+            sphinx-build docs/sphinx_src docs/api/$tag
+            rm -rf docs/sphinx_src
+            ;;
+    esac
+done
+
+# Revert to the current branch
+git checkout $current_branch
+
+# Build the final Sphinx documentation for the current branch
+python docs/update_quairkit_rst.py
+cp -r tutorials docs/sphinx_src/tutorials
+echo "$version_info_content" >> docs/sphinx_src/conf.py
+sphinx-build docs/sphinx_src docs/api/latest
+
+# Notify the user where to find the built documentation
+echo "The documentation is built. You can open it by navigating to 'docs/api/index.html' in your browser."