|
| 1 | +""" |
| 2 | +Sphinx extension for viewing openedx events annotations. |
| 3 | +""" |
| 4 | +import os |
| 5 | + |
| 6 | +from docutils import nodes |
| 7 | +from sphinx.util.docutils import SphinxDirective |
| 8 | + |
| 9 | +from code_annotations.contrib.config import OPENEDX_EVENTS_ANNOTATIONS_CONFIG_PATH |
| 10 | + |
| 11 | +from .base import find_annotations |
| 12 | + |
| 13 | + |
| 14 | +def find_events(source_path): |
| 15 | + """ |
| 16 | + Find the events as defined in the configuration file. |
| 17 | +
|
| 18 | + Return: |
| 19 | + events (dict): found events indexed by event type. |
| 20 | + """ |
| 21 | + return find_annotations( |
| 22 | + source_path, OPENEDX_EVENTS_ANNOTATIONS_CONFIG_PATH, ".. event_type:" |
| 23 | + ) |
| 24 | + |
| 25 | + |
| 26 | +class OpenedxEvents(SphinxDirective): |
| 27 | + """ |
| 28 | + Sphinx directive to list the events in a single documentation page. |
| 29 | +
|
| 30 | + Use this directive as follows:: |
| 31 | +
|
| 32 | + .. openedxevents:: |
| 33 | +
|
| 34 | + This directive supports the following configuration parameters: |
| 35 | +
|
| 36 | + - ``openedxevents_source_path``: absolute path to the repository file tree. E.g: |
| 37 | +
|
| 38 | + openedxevents_source_path = os.path.join(os.path.dirname(__file__), "..", "..") |
| 39 | +
|
| 40 | + - ``openedxevents_repo_url``: Github repository where the code is hosted. E.g: |
| 41 | +
|
| 42 | + openedxevents_repo_url = "https://github.com/openedx/myrepo" |
| 43 | +
|
| 44 | + - ``openedxevents_repo_version``: current version of the git repository. E.g: |
| 45 | +
|
| 46 | + import git |
| 47 | + try: |
| 48 | + repo = git.Repo(search_parent_directories=True) |
| 49 | + openedxevents_repo_version = repo.head.object.hexsha |
| 50 | + except git.InvalidGitRepositoryError: |
| 51 | + openedxevents_repo_version = "main" |
| 52 | + """ |
| 53 | + |
| 54 | + required_arguments = 0 |
| 55 | + optional_arguments = 0 |
| 56 | + option_spec = {} |
| 57 | + |
| 58 | + def run(self): |
| 59 | + """ |
| 60 | + Public interface of the Directive class. |
| 61 | +
|
| 62 | + Return: |
| 63 | + nodes (list): nodes to be appended to the resulting document. |
| 64 | + """ |
| 65 | + return list(self.iter_nodes()) |
| 66 | + |
| 67 | + def iter_nodes(self): |
| 68 | + """ |
| 69 | + Iterate on the docutils nodes generated by this directive. |
| 70 | + """ |
| 71 | + events = find_events(self.env.config.openedxevents_source_path) |
| 72 | + |
| 73 | + current_domain = "" |
| 74 | + domain_header = None |
| 75 | + current_subject = "" |
| 76 | + subject_header = None |
| 77 | + |
| 78 | + for event_type in sorted(events): |
| 79 | + domain = event_type.split(".")[2] |
| 80 | + subject = event_type.split(".")[3] |
| 81 | + if domain != current_domain: |
| 82 | + if domain_header: |
| 83 | + yield domain_header |
| 84 | + |
| 85 | + current_domain = domain |
| 86 | + domain_header = nodes.section("", ids=[f"openedxevent-domain-{domain}"]) |
| 87 | + domain_header += nodes.title(text=f"Architectural subdomain: {domain}") |
| 88 | + if subject != current_subject: |
| 89 | + current_subject = subject |
| 90 | + subject_header = nodes.section("", ids=[f"openedxevent-subject" |
| 91 | + f"-{subject}"]) |
| 92 | + subject_header += nodes.title(text=f"Subject: {subject}") |
| 93 | + domain_header += subject_header |
| 94 | + |
| 95 | + event = events[event_type] |
| 96 | + event_name = event[".. event_name:"] |
| 97 | + event_name_literal = nodes.literal(text=event_name) |
| 98 | + event_data = event[".. event_data:"] |
| 99 | + event_data_literal = nodes.literal(text=event_data) |
| 100 | + event_key_field = event.get(".. event_key_field:", "") |
| 101 | + event_key_literal = nodes.literal(text=event_key_field) |
| 102 | + event_description = event[".. event_description:"] |
| 103 | + |
| 104 | + event_section = nodes.section("", ids=[f"openedxevent-{event_type}"]) |
| 105 | + event_section += nodes.title(text=event_type, ids=[f"title-{event_type}"]) |
| 106 | + event_section += nodes.paragraph(text=f"Description: " |
| 107 | + f"{event_description}") |
| 108 | + event_section += nodes.paragraph("", "Signal name: ", event_name_literal) |
| 109 | + if event_key_field: |
| 110 | + event_section += nodes.paragraph( |
| 111 | + "", |
| 112 | + "Event key field: ", |
| 113 | + event_key_literal |
| 114 | + ) |
| 115 | + event_section += nodes.paragraph("", "Event data: ", event_data_literal) |
| 116 | + event_section += nodes.paragraph( |
| 117 | + text=f"Defined at: {event['filename']} (line" |
| 118 | + f" {event['line_number']})" |
| 119 | + ) |
| 120 | + |
| 121 | + subject_header += event_section |
| 122 | + |
| 123 | + if domain_header: |
| 124 | + yield domain_header |
| 125 | + |
| 126 | + |
| 127 | +def setup(app): |
| 128 | + """ |
| 129 | + Declare the Sphinx extension. |
| 130 | + """ |
| 131 | + app.add_config_value( |
| 132 | + "openedxevents_source_path", |
| 133 | + os.path.abspath(".."), |
| 134 | + "env", |
| 135 | + ) |
| 136 | + app.add_config_value("openedxevents_repo_url", "", "env") |
| 137 | + app.add_config_value("openedxevents_repo_version", "main", "env") |
| 138 | + app.add_directive("openedxevents", OpenedxEvents) |
| 139 | + |
| 140 | + return { |
| 141 | + "version": "0.1", |
| 142 | + "parallel_read_safe": True, |
| 143 | + "parallel_write_safe": True, |
| 144 | + } |
0 commit comments