StrictDoc Documentation
strictdoc/core/feature.py
Source file coverage
Path:
strictdoc/core/feature.py
Lines:
106
Non-empty lines:
90
Non-empty lines covered with requirements:
90 / 90 (100.0%)
Functions:
8
Functions covered by requirements:
8 / 8 (100.0%)
1
"""
2
The Feature abstraction: facade for cross-cutting export/server
3
capabilities that are not document-content conversion (see Format for
4
that).
5
 
6
@relation(SDOC-SRS-119, scope=file)
7
"""
8
 
9
from abc import ABC, abstractmethod
10
from dataclasses import dataclass
11
from typing import TYPE_CHECKING
12
 
13
if TYPE_CHECKING:
14
    from strictdoc.core.project_config import ProjectConfig
15
    from strictdoc.core.traceability_index import TraceabilityIndex
16
    from strictdoc.export.html.html_templates import HTMLTemplates
17
 
18
 
19
@dataclass
20
class FeatureContext:
21
    """
22
    Shared, per-call state that Feature.export()/render_screen()
23
    implementations pull from. Deliberately separate from Format's
24
    ExportContext: it carries only what a Feature actually needs
25
    (project_config/traceability_index/html_templates), not
26
    Format-specific fields like parallelizer or the HTML2PDF-only
27
    bundle_* fields.
28
    """
29
 
30
    project_config: "ProjectConfig"
31
    traceability_index: "TraceabilityIndex"
32
    html_templates: "HTMLTemplates"
33
 
34
 
35
class Feature(ABC):
36
    HANDLE: str
37
    """
38
    Reuses the existing ProjectFeature enum value string verbatim (e.g.
39
    "PROJECT_STATISTICS_SCREEN"), so string-based project_features=[...]
40
    entries keep resolving to this Feature unchanged.
41
    """
42
 
43
    @staticmethod
44
    @abstractmethod
45
    def supports_export() -> bool:
46
        raise NotImplementedError
47
 
48
    def export(self, context: FeatureContext) -> None:
49
        """Only called if supports_export() is True."""
50
        raise NotImplementedError(
51
            f"{self.__class__.__name__} does not support export."
52
        )
53
 
54
    @staticmethod
55
    @abstractmethod
56
    def supports_server() -> bool:
57
        raise NotImplementedError
58
 
59
    def screen_filename(self) -> str:
60
        """
61
        Only called if supports_server() is True. The
62
        document_relative_path.relative_path value (e.g.
63
        "project_statistics.html") this Feature's screen owns, used as the
64
        dispatch key inside main_router.py's generate_document(). Not a
65
        FastAPI route: every HTML screen is served through one shared,
66
        cached, lock-protected route, and this filename is only the
67
        dispatch key into that shared machinery.
68
        """
69
        raise NotImplementedError(
70
            f"{self.__class__.__name__} does not support a server screen."
71
        )
72
 
73
    def render_screen(self, context: FeatureContext) -> None:
74
        """
75
        Only called if supports_server() is True. Must write the screen's
76
        HTML file to context.project_config.export_output_html_root, the
77
        same contract every generate_document() branch already follows.
78
        """
79
        raise NotImplementedError(
80
            f"{self.__class__.__name__} does not support a server screen."
81
        )
82
 
83
    def screen_icon(self) -> str:
84
        """
85
        Jinja template path for the left-sidebar nav icon, rendered via
86
        `{% include feature.screen_icon() %}`, e.g.
87
        "features/project_statistics/ico16_stat.svg".
88
 
89
        Resolved against the Jinja template search path, which is the
90
        static HTML_TEMPLATE_DIRS list in strictdoc/core/environment.py --
91
        the same mechanism every other feature's own .jinja templates
92
        already rely on. A built-in Feature's icon lives under its own
93
        feature's existing "features/<feature>/*.jinja" namespace inside
94
        its "<feature>/templates" entry (e.g.
95
        strictdoc/features/project_statistics/templates/features/
96
        project_statistics/ico16_stat.svg, alongside that feature's own
97
        main.jinja/index.jinja) -- no separate registration needed.
98
 
99
        TODO: a second method returning raw markup directly (for Features
100
        that can't register an entry in HTML_TEMPLATE_DIRS, e.g. a custom
101
        Feature defined outside strictdoc's own package) is a likely
102
        future addition, not implemented yet.
103
        """
104
        raise NotImplementedError(
105
            f"{self.__class__.__name__} does not provide a nav icon."
106
        )