mirror of
https://github.com/inventree/InvenTree.git
synced 2026-11-14 12:32:44 +00:00
* 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
195 lines
6.0 KiB
Python
195 lines
6.0 KiB
Python
"""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()
|