Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/templates/sidebar.qmd.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ website:
- text: "{{ module_name }}"
file: validmind/validmind/{{ module_name }}.qmd
contents:
{% for item in qmd_files | get_child_files(module_name) %}
{% for item in qmd_files | get_child_files(module_name, full_data) %}
{% if item.contents is defined %}
{% set cleaned_item_path = item.file | replace('<span class="prefix"></span> ', '') | replace('<span class="suffix"></span>', '') %}
- text: "{{ item.text | replace('<span class=\"', '<span class=\'') | replace('\"></span>', '\'></span>') }}"
Expand Down
50 changes: 48 additions & 2 deletions scripts/generate_quarto_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,30 @@
# Add at module level
_alias_cache = {} # Cache for resolved aliases


def _has_test_decorators(module_data: Dict[str, Any]) -> bool:
"""Check if module has any member with @tags or @tasks decorator."""
for member in module_data.get('members', {}).values():
for dec in member.get('decorators', []):
func_name = dec.get('value', {}).get('function', {}).get('name', '')
if func_name in ('tags', 'tasks'):
return True
return False


def _should_filter_module(name: str, module_data: Optional[Dict[str, Any]] = None) -> bool:
"""Check if a module should be filtered from sidebar.

Filter out if BOTH conditions are true:
- Name starts with lowercase, AND
- Has no @tags/@tasks decorated functions
"""
is_lowercase = name and name[0].islower()
has_decorators = module_data and _has_test_decorators(module_data)

return is_lowercase and not has_decorators


def resolve_alias(member: Dict[str, Any], data: Dict[str, Any]) -> Dict[str, Any]:
"""Resolve an alias to its target member."""
if member.get('kind') == 'alias' and member.get('target_path'):
Expand Down Expand Up @@ -384,6 +408,7 @@ def process_module(module: Dict[str, Any], path: List[str], env: Environment, fu
)
# Removed the underscores from the filename as Quarto treats files with underscores differently
version_path = os.path.join('docs/validmind', 'version.qmd')
ensure_dir('docs/validmind')
with open(version_path, 'w') as f:
f.write(version_output)
written_qmd_files['version.qmd'] = version_path
Expand Down Expand Up @@ -519,7 +544,8 @@ def get_inherited_members(base: Dict[str, Any], full_data: Dict[str, Any]) -> Li

return members

def get_child_files(files_dict: Dict[str, str], module_name: str) -> List[Dict[str, Any]]:
def get_child_files(files_dict: Dict[str, str], module_name: str,
full_data: Dict[str, Any] = None) -> List[Dict[str, Any]]:
"""Get all child QMD files for a given module."""
prefix = f'docs/validmind/{module_name}/'
directory_structure = {}
Expand Down Expand Up @@ -551,8 +577,28 @@ def get_child_files(files_dict: Dict[str, str], module_name: str) -> List[Dict[s
if 'contents' not in directory_structure[dir_name]:
directory_structure[dir_name]['contents'] = []

file_stem = Path(parts[-1]).stem

# Lookup module data for decorator check
module_data = None
if full_data:
try:
# Navigate: validmind.{path_parts} to find module
# e.g., tests.model_validation.statsmodels.statsutils
path_parts = rel_path.replace('.qmd', '').split('/')
current = full_data.get('validmind', {})
for part in path_parts[1:]: # Skip 'validmind'
current = current.get('members', {}).get(part, {})
module_data = current
except (KeyError, AttributeError):
pass

# Skip internal helper modules (lowercase AND no decorators)
if _should_filter_module(file_stem, module_data):
continue

directory_structure[dir_name]['contents'].append({
'text': Path(parts[-1]).stem,
'text': file_stem,
'file': f'validmind/{rel_path}' # Add validmind/ prefix
})

Expand Down