mirror of
https://github.com/inventree/InvenTree.git
synced 2026-11-14 12:32:44 +00:00
[docs] Zensical docs (#13253)
* add zensical workflow * rebuild schema dynamically * load in new module * Use zensical for docs invoke tasks * modern * improved carousel layout * Increased content width * fix for app icons * Ensure schema dir exists * remove unused requirement * update readthedocs process * fix polar.sh link * adjust link checker pattern * Revert some of the original style * remove legacy mkdocs requirements * fix for ready.py * reduce db warnings during schema generation * Adjust padding * fix index issues * fix page title * fix pages with broken or missing headers * split data import API schema * cleanup old carousel * remove djlint link
This commit is contained in:
1 parent
80ad822688
commit
19dfb81bfb
52 files changed
+1272
-4885
No files matched your search
@@ -0,0 +1,194 @@
|
||||
"""Shared data for the documentation build process.
|
||||
|
||||
This module loads the generated InvenTree definition files (settings, status codes,
|
||||
report context, etc) which are used by the custom macros defined in 'main.py'.
|
||||
|
||||
It is deliberately separate from 'main.py', as some static site generators
|
||||
(e.g. zensical) re-execute the macros module for *every* page. As a regular
|
||||
module, this file is imported (and cached) only once per build process:
|
||||
|
||||
- Generated data files are loaded lazily, and reloaded only if they change on disk
|
||||
- Observed settings / status codes are accumulated across all pages of the build
|
||||
- The 'on_config' hook values are computed once (for generators without hook support)
|
||||
"""
|
||||
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
import threading
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional
|
||||
|
||||
import yaml
|
||||
|
||||
HERE = Path(__file__).parent
|
||||
GENERATED_DIR = HERE.joinpath('generated')
|
||||
|
||||
# Files where we *store* information on what has been documented during the build.
|
||||
# These are checked after the build completes (see 'on_post_build' in docs/hooks.py)
|
||||
OBSERVED_SETTINGS_FILE = GENERATED_DIR.joinpath('observed_settings.json')
|
||||
OBSERVED_STATUS_CODES_FILE = GENERATED_DIR.joinpath('observed_status_codes.json')
|
||||
|
||||
_lock = threading.RLock()
|
||||
|
||||
# Cache of loaded data files: {filename: (mtime, data)}
|
||||
_file_cache: dict[str, tuple[float, Any]] = {}
|
||||
|
||||
# Things which have been rendered somewhere in the documentation
|
||||
_observed_settings: dict[str, dict[str, bool]] = {'global': {}, 'user': {}}
|
||||
_observed_status_codes: dict[str, bool] = {}
|
||||
|
||||
# Cached result of the 'on_config' hook
|
||||
_hook_config: Optional[dict] = None
|
||||
|
||||
|
||||
def print_build_environment():
|
||||
"""Print some useful debugging information - useful for diagnosing CI build issues.
|
||||
|
||||
Ref: https://docs.readthedocs.io/en/stable/reference/environment-variables.html
|
||||
"""
|
||||
print('loading ./docs/docs_data.py...')
|
||||
|
||||
for key in [
|
||||
'GITHUB_ACTIONS',
|
||||
'GITHUB_REF',
|
||||
'READTHEDOCS',
|
||||
'READTHEDOCS_GIT_IDENTIFIER',
|
||||
'READTHEDOCS_GIT_CLONE_URL',
|
||||
'READTHEDOCS_GIT_COMMIT_HASH',
|
||||
'READTHEDOCS_PROJECT',
|
||||
'READTHEDOCS_VERSION',
|
||||
'READTHEDOCS_VERSION_NAME',
|
||||
'READTHEDOCS_VERSION_TYPE',
|
||||
]:
|
||||
val = os.environ.get(key, None) or '-- MISSING --'
|
||||
print(f' - {key}: {val}')
|
||||
|
||||
|
||||
def _write_json(path: Path, data: Any):
|
||||
"""Write data to a JSON file."""
|
||||
with open(path, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, indent=4)
|
||||
|
||||
|
||||
def reset_observations():
|
||||
"""Reset the record of observed settings and status codes.
|
||||
|
||||
Called once when this module is first imported (i.e. once per build process).
|
||||
"""
|
||||
with _lock:
|
||||
_observed_settings.clear()
|
||||
_observed_settings.update({'global': {}, 'user': {}})
|
||||
_observed_status_codes.clear()
|
||||
|
||||
_write_json(OBSERVED_SETTINGS_FILE, _observed_settings)
|
||||
_write_json(OBSERVED_STATUS_CODES_FILE, _observed_status_codes)
|
||||
|
||||
|
||||
def observe_setting(key: str, group: str):
|
||||
"""Record that a particular setting has been documented.
|
||||
|
||||
Arguments:
|
||||
key: The name of the setting
|
||||
group: The group of the setting (e.g. 'global', 'user', 'config')
|
||||
"""
|
||||
with _lock:
|
||||
group_data = _observed_settings.setdefault(group, {})
|
||||
|
||||
if key not in group_data:
|
||||
group_data[key] = True
|
||||
_write_json(OBSERVED_SETTINGS_FILE, _observed_settings)
|
||||
|
||||
|
||||
def observe_status_code(class_name: str):
|
||||
"""Record that a particular status code class has been documented."""
|
||||
with _lock:
|
||||
if class_name not in _observed_status_codes:
|
||||
_observed_status_codes[class_name] = True
|
||||
_write_json(OBSERVED_STATUS_CODES_FILE, _observed_status_codes)
|
||||
|
||||
|
||||
def load_file(filename: str) -> Any:
|
||||
"""Load a generated data file (JSON or YAML), caching the result.
|
||||
|
||||
The file is reloaded only if it has been modified since it was last loaded.
|
||||
|
||||
Arguments:
|
||||
filename: Name of the file, relative to the 'generated' directory
|
||||
"""
|
||||
path = GENERATED_DIR.joinpath(filename)
|
||||
mtime = path.stat().st_mtime
|
||||
|
||||
with _lock:
|
||||
cached = _file_cache.get(filename)
|
||||
|
||||
if cached and cached[0] == mtime:
|
||||
return cached[1]
|
||||
|
||||
with open(path, encoding='utf-8') as f:
|
||||
if path.suffix in ('.yml', '.yaml'):
|
||||
data = yaml.load(f, yaml.BaseLoader)
|
||||
else:
|
||||
data = json.load(f)
|
||||
|
||||
_file_cache[filename] = (mtime, data)
|
||||
return data
|
||||
|
||||
|
||||
def settings(group: str) -> dict:
|
||||
"""Return the settings definitions for a given group ('global', 'user', 'config')."""
|
||||
return load_file('inventree_settings.json')[group]
|
||||
|
||||
|
||||
def tags() -> Any:
|
||||
"""Return the custom template tags definitions."""
|
||||
return load_file('inventree_tags.yml')
|
||||
|
||||
|
||||
def filters() -> Any:
|
||||
"""Return the custom template filters definitions."""
|
||||
return load_file('inventree_filters.yml')
|
||||
|
||||
|
||||
def status_codes() -> dict:
|
||||
"""Return the status code class definitions."""
|
||||
return load_file('inventree_status_codes.json')
|
||||
|
||||
|
||||
def report_context() -> dict:
|
||||
"""Return the report context definitions."""
|
||||
return load_file('inventree_report_context.json')
|
||||
|
||||
|
||||
def roles() -> Any:
|
||||
"""Return the user permission role definitions."""
|
||||
return load_file('inventree_roles.json')
|
||||
|
||||
|
||||
def audited_models() -> Any:
|
||||
"""Return the audited model definitions."""
|
||||
return load_file('inventree_audited_models.json')
|
||||
|
||||
|
||||
def hook_config(site_url: Optional[str] = None) -> dict:
|
||||
"""Return the config values provided by the 'on_config' hook in docs/hooks.py.
|
||||
|
||||
Zensical does not run mkdocs hooks, so
|
||||
the hook is run once here, and the result cached for subsequent pages.
|
||||
"""
|
||||
global _hook_config
|
||||
|
||||
with _lock:
|
||||
if _hook_config is None:
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
'inventree_docs_hooks', HERE.joinpath('docs', 'hooks.py')
|
||||
)
|
||||
hooks = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(hooks)
|
||||
_hook_config = hooks.on_config({'site_url': site_url})
|
||||
|
||||
return _hook_config
|
||||
|
||||
|
||||
print_build_environment()
|
||||
reset_observations()
|
||||
Reference in new issue
Block a user