Source code for scitex_template

#!/usr/bin/env python3
# File: /home/ywatanabe/proj/scitex_repo/src/scitex/template/__init__.py

"""
Template management for SciTeX projects.
"""

from __future__ import annotations

try:
    from importlib.metadata import version as _v, PackageNotFoundError
    try:
        __version__ = _v("scitex-template")
    except PackageNotFoundError:
        __version__ = "0.0.0+local"
    del _v, PackageNotFoundError
except ImportError:  # pragma: no cover — only on ancient Pythons
    __version__ = "0.0.0+local"

from pathlib import Path

# scitex_git is an optional dep (install via `scitex-template[legacy]`,
# alongside `pip install scitex`) — re-export when available, otherwise
# expose stubs that raise a clear ImportError if called. Keeps the
# standalone install import-clean.
from scitex_dev import try_import_optional

_scitex_git = try_import_optional("scitex_git", pkg="scitex-git")
if _scitex_git is not None:  # pragma: no cover
    create_child_git = _scitex_git.create_child_git
    find_parent_git = _scitex_git.find_parent_git
    init_git_repo = _scitex_git.init_git_repo
    remove_child_git = _scitex_git.remove_child_git
else:

    def _missing_scitex_git(name):
        def _stub(*_a, **_k):
            raise ImportError(
                f"{name} requires the scitex umbrella. "
                "Install with: pip install scitex-template[legacy]"
            )

        _stub.__name__ = name
        return _stub

    create_child_git = _missing_scitex_git("create_child_git")
    find_parent_git = _missing_scitex_git("find_parent_git")
    init_git_repo = _missing_scitex_git("init_git_repo")
    remove_child_git = _missing_scitex_git("remove_child_git")

from ._code._code_templates import (
    CODE_TEMPLATES,
    get_all_templates,
    get_code_template,
    list_code_templates,
)
from ._project._clone_template import clone_template
from ._project._customize import customize_minimal_template, customize_template
from ._project._directory_structure import (
    PROJECT_STRUCTURE,
    build_directory_tree,
)
from ._project._generate_configs import (
    create_env_template,
    create_paths_config,
    create_project_config,
    create_requirements_file,
)
from ._project._generate_readme import create_minimal_readme, create_project_readme
from ._project._scholar_writer_integration import (
    ensure_integration,
    setup_scholar_writer_integration,
)
from ._project.clone_module import clone_module
from ._project.clone_pip_project import TEMPLATE_REPO_URL as PIP_PROJECT_URL
from ._project.clone_pip_project import clone_pip_project
from ._project.clone_research import TEMPLATE_REPO_URL as RESEARCH_URL
from ._project.clone_research import clone_research
from ._project.clone_research_minimal import (
    MINIMAL_INCLUDE_DIRS,
    clone_research_minimal,
)
from ._project.clone_scitex_minimal import clone_scitex_minimal
from ._project.clone_singularity import TEMPLATE_REPO_URL as SINGULARITY_URL
from ._project.clone_singularity import clone_singularity
from ._project.clone_writer_directory import TEMPLATE_REPO_URL as PAPER_DIRECTORY_URL
from ._project.clone_writer_directory import clone_writer_directory


def get_template_tree(template_id):
    """Create template in a tempdir and return its ``tree`` output.

    Actually runs the template's directory-creation logic in a
    temporary directory on the fly, then captures the real ``tree``
    output. This guarantees the displayed tree always matches
    the current template code.

    Parameters
    ----------
    template_id : str
        Template identifier (e.g. 'minimal', 'research').

    Returns
    -------
    str
        ``tree`` output string, or empty string if unknown.
    """
    import shutil
    import subprocess
    import tempfile

    try:
        from scitex_scholar.ensure_workspace import SCHOLAR_SUBDIRS  # type: ignore[import-not-found]
    except ImportError:
        # scitex.scholar is optional (scitex-template[legacy]) — fall back
        # to a hardcoded list matching the scholar workspace convention.
        SCHOLAR_SUBDIRS = ["bib_files", "library", "prompts"]

    tmpdir = tempfile.mkdtemp(prefix="scitex_tree_")
    try:
        project_dir = Path(tmpdir) / "project"
        project_dir.mkdir()

        if template_id == "minimal":
            # Reproduce scitex_minimal directory scaffold without git clone
            writer_dir = project_dir / "scitex" / "writer"
            for name in MINIMAL_INCLUDE_DIRS:
                (writer_dir / name).mkdir(parents=True, exist_ok=True)
            scholar_dir = project_dir / "scitex" / "scholar"
            for name in SCHOLAR_SUBDIRS:
                (scholar_dir / name).mkdir(parents=True, exist_ok=True)

        elif template_id == "research":
            build_directory_tree(str(project_dir), PROJECT_STRUCTURE)

        elif template_id == "app":
            # Reproduce pip-project-template structure
            src_dir = project_dir / "src" / "package_name"
            src_dir.mkdir(parents=True)
            (src_dir / "__init__.py").touch()
            tests_dir = project_dir / "tests"
            tests_dir.mkdir()
            (tests_dir / "__init__.py").touch()
            (tests_dir / "test_main.py").touch()
            (project_dir / "pyproject.toml").touch()
            (project_dir / "README.md").touch()
            (project_dir / "LICENSE").touch()

        else:
            return ""

        result = subprocess.run(
            ["tree", "--noreport", "--dirsfirst", str(project_dir)],
            capture_output=True,
            text=True,
            timeout=5,
        )
        if result.returncode == 0:
            # Replace the temp path prefix with "."
            tree_output = result.stdout.strip()
            first_newline = tree_output.find("\n")
            if first_newline != -1:
                return "." + tree_output[first_newline:]
            return "."
        return ""

    except Exception:
        return ""
    finally:
        shutil.rmtree(tmpdir, ignore_errors=True)


[docs] def get_available_templates_info(): """ Get information about all available SciTeX project templates. Returns ------- list[dict] List of template information dictionaries, each containing: - id: Template identifier (used in code) - name: Human-readable template name - description: Template description - github_url: GitHub repository URL - use_case: When to use this template - tree: Tree-command-style directory listing Example ------- >>> from scitex.template import get_available_templates_info >>> templates = get_available_templates_info() >>> for template in templates: ... print(f"{template['name']}: {template['description']}") """ return [ { "id": "minimal", "name": "SciTeX Minimal", "description": "Minimal project with writer + scholar workspaces", "github_url": RESEARCH_URL, "use_case": "Manuscript writing with integrated bibliography management", "tree": get_template_tree("minimal"), }, { "id": "research", "name": "SciTeX Full", "description": "Full scientific workflow with data analysis, experiments, and paper writing", "github_url": RESEARCH_URL, "use_case": "End-to-end scientific research projects", "tree": get_template_tree("research"), }, { "id": "app", "name": "SciTeX App", "description": "Pip-installable Python package for building reusable SciTeX apps", "github_url": PIP_PROJECT_URL, "use_case": "Creating reusable tools and apps for the SciTeX ecosystem", "tree": get_template_tree("app"), }, ]
__all__ = [ "__version__", "clone_template", "clone_module", "clone_research", "clone_research_minimal", "clone_scitex_minimal", "MINIMAL_INCLUDE_DIRS", "clone_pip_project", "clone_singularity", "clone_writer_directory", "get_available_templates_info", "get_code_template", "list_code_templates", "get_all_templates", "CODE_TEMPLATES", "init_git_repo", "find_parent_git", "create_child_git", "remove_child_git", # Template customization (moved from Django) "customize_template", "customize_minimal_template", "create_project_config", "create_paths_config", "create_env_template", "create_requirements_file", "create_minimal_readme", "create_project_readme", "build_directory_tree", "PROJECT_STRUCTURE", # Scholar-Writer integration "setup_scholar_writer_integration", "ensure_integration", ] # EOF