blob: 28cdee78c47cfbbb734dc9b785e5d6f6fdf3f48c [file] [log] [blame]
from io import TextIOWrapper
import os
import json
from functools import reduce
from typing import Dict, List, Tuple
class Document:
def __init__(self, path: str) -> None:
self.path = path
if len(self.path.split("/")) == 1:
self.relative_path = None
elif len(self.path.split("/")) == 2:
self.relative_path = self.path.split("/")[-1]
else:
self.relative_path = reduce(lambda x,y : f"{x}/{y}", self.path.split("/")[1:-1])
self.name = self.path.split("/")[-1]
@property
def rankFromFile(self) -> int:
return 1
def dumps(self) -> Dict:
return {}
def writeIndexRst(self) -> None:
pass
class Md(Document):
def __init__(self, path: str, rank: int) -> None:
super().__init__(path)
self.rank = rank
@property
def rankFromFile(self) -> int:
with open(self.path, mode="r") as f:
lines = f.readlines()
lines = [line for line in lines if line.startswith("sidebar_position: ")]
if not lines:
raise Exception(f"{self.path} do not have sidebar_position")
# if "sidebar_position: 1", return 1
return int(lines[0].split(":")[-1].strip())
def dumps(self) -> Dict:
return {
"name": self.name,
"path": self.path,
"rank": self.rank
}
def writeIndexRst(self) -> None:
pass
class Folder(Document):
def __init__(self, path: str) -> None:
super().__init__(path)
self.props = None
files_name: List[str] = os.listdir(self.path)
files_full_name = [os.path.join(self.path, file_name) for file_name in files_name]
folders_name = [name for name in files_full_name if os.path.isdir(name)]
md_files_name = [name for name in files_full_name if name.endswith(".md") and os.path.isfile(name)]
self.folders: List[Folder] = []
self.md_files: List[Md] = []
self.sequence: List[Document] = [None] * (len(folders_name) + len(md_files_name))
for folder_name in folders_name:
# append folder
folder = Folder(folder_name)
self.folders.append(folder)
self.sequence[folder.rankFromFile - 1] = folder
for md_file_name in md_files_name:
idx = Folder.getFirstPosEmpty(self.sequence)
file = Md(md_file_name, rank=idx+1)
self.md_files.append(file)
if idx < len(self.sequence):
self.sequence[idx] = file
@staticmethod
def getFirstPosEmpty(l: List):
for idx, elem in enumerate(l):
if not elem:
return idx
return len(l)
@property
def rankFromFile(self) -> int:
if not self.props:
self.loadCategory()
if "position" not in self.props:
raise Exception(f"{self.path}/_category_.json do not contain position")
return int(self.props["position"])
@property
def labelFromFile(self) -> str:
if not self.props:
self.loadCategory()
if "label" not in self.props:
raise Exception(f"{self.path}/_category_.json do not contain label")
return self.props["label"]
def loadCategory(self):
try:
with open(f"{self.path}/_category_.json", mode="r") as f:
res = f.read()
self.props = json.loads(res)
except FileNotFoundError as e:
# only top don't include _category_.json
self.props = {"label": "Top", "position": 1}
def dumps(self) -> Dict:
return {
"name": self.name,
"path": self.path,
"rank": self.rankFromFile,
"label": self.labelFromFile,
"contents": [doc.dumps() for doc in self.sequence]
}
def writeIndexRst(self) -> None:
docs_name = []
for doc in self.sequence:
if type(doc) == Folder:
docs_name.append(f"./{doc.name}/index")
print(doc.dumps())
doc.writeIndexRst()
elif type(doc) == Md:
docs_name.append(f"./{doc.name}")
docs_name = [f" {doc_name}\n" for doc_name in docs_name]
print(docs_name, self.labelFromFile)
index_pst_path = f"source/{self.relative_path}/index.rst" if self.relative_path else "source/index.rst"
with open(index_pst_path, mode="w") as f:
f.writelines([
"============================\n",
self.labelFromFile + "\n",
"============================\n",
".. toctree::\n",
" :maxdepth: 1\n",
" :titlesonly:\n"
"\n"
] + docs_name)
doc = Folder("docs")
print(json.dumps(doc.dumps(), indent=2))
doc.writeIndexRst()