diff --git a/code_annotations/contrib/config/__init__.py b/code_annotations/contrib/config/__init__.py index 1a8c0e9..3e83f7a 100644 --- a/code_annotations/contrib/config/__init__.py +++ b/code_annotations/contrib/config/__init__.py @@ -13,3 +13,7 @@ "code_annotations", os.path.join("contrib", "config", "setting_annotations.yaml"), ) +OPENEDX_EVENTS_ANNOTATIONS_CONFIG_PATH = pkg_resources.resource_filename( + "code_annotations", + os.path.join("contrib", "config", "openedx_events_annotations.yaml"), +) diff --git a/code_annotations/contrib/config/openedx_events_annotations.yaml b/code_annotations/contrib/config/openedx_events_annotations.yaml new file mode 100644 index 0000000..2c25ce0 --- /dev/null +++ b/code_annotations/contrib/config/openedx_events_annotations.yaml @@ -0,0 +1,18 @@ +# This code-annotations configuration file supports openedx-events + +source_path: ./ +report_path: reports +safelist_path: .annotation_safe_list.yml +coverage_target: 100.0 +annotations: + feature_toggle: + # See annotation format documentation: https://edx-toggles.readthedocs.io/en/latest/how_to/documenting_new_feature_toggles.html + - ".. event_type:": + - ".. event_name:": + - ".. event_description:": + - ".. event_data:": + - ".. event_key_field:": +extensions: + python: + - py +rst_template: doc.rst.j2 diff --git a/code_annotations/contrib/sphinx/extensions/openedx_events.py b/code_annotations/contrib/sphinx/extensions/openedx_events.py new file mode 100644 index 0000000..9a9f05d --- /dev/null +++ b/code_annotations/contrib/sphinx/extensions/openedx_events.py @@ -0,0 +1,134 @@ +""" +Sphinx extension for viewing openedx events annotations. +""" +import os + +from docutils import nodes +from sphinx.util.docutils import SphinxDirective + +from code_annotations.contrib.config import OPENEDX_EVENTS_ANNOTATIONS_CONFIG_PATH + +from .base import find_annotations, quote_value + + +def find_events(source_path): + """ + Find the feature toggles as defined in the configuration file. + + Return: + toggles (dict): feature toggles indexed by name. + """ + return find_annotations( + source_path, OPENEDX_EVENTS_ANNOTATIONS_CONFIG_PATH, ".. event_type:" + ) + + +class OpenedxEvents(SphinxDirective): + """ + Sphinx directive to list the events in a single documentation page. + + Use this directive as follows:: + + .. openedxevents:: + + This directive supports the following configuration parameters: + + - ``openedxevents_source_path``: absolute path to the repository file tree. E.g: + + openedxevents_source_path = os.path.join(os.path.dirname(__file__), "..", "..") + + - ``openedxevents_repo_url``: Github repository where the code is hosted. E.g: + + openedxevents_repo_url = "https://github.com/openedx/myrepo" + + - ``openedxevents_repo_version``: current version of the git repository. E.g: + + import git + try: + repo = git.Repo(search_parent_directories=True) + openedxevents_repo_version = repo.head.object.hexsha + except git.InvalidGitRepositoryError: + openedxevents_repo_version = "main" + """ + + required_arguments = 0 + optional_arguments = 0 + option_spec = {} + + def run(self): + """ + Public interface of the Directive class. + + Return: + nodes (list): nodes to be appended to the resulting document. + """ + return list(self.iter_nodes()) + + def iter_nodes(self): + """ + Iterate on the docutils nodes generated by this directive. + """ + events = find_events(self.env.config.openedxevents_source_path) + + current_domain = "" + domain_header = None + + for event_type in sorted(events): + domain = event_type.split(".")[2] + if domain != current_domain: + if domain_header: + yield domain_header + + current_domain = domain + domain_header = nodes.section("", ids=[f"openedxevent-domain-{domain}"]) + domain_header += nodes.title(text=f"Architectural domain: {domain}") + + event = events[event_type] + event_name = event[".. event_name:"] + event_name_literal = nodes.literal(text=quote_value(event_name)) + event_data = event[".. event_data:"] + event_key_field = event.get(".. event_key_field:", None) + event_key_literal = nodes.literal(text=quote_value(event_key_field)) + event_description = event[".. event_description:"] + + event_section = nodes.section("", ids=[f"openedxevent-{event_type}"]) + event_section += nodes.title(text=event_type, ids=[f"title-{event_type}"]) + event_section += nodes.paragraph("", "Signal name:", event_name_literal) + if event_key_field: + event_section += nodes.paragraph( + "", + "Event key field:", + event_key_literal + ) + event_section += nodes.paragraph(text=f"Description:" + f" {event_description}") + event_section += nodes.paragraph(text=f"Event data: {event_data}") + event_section += nodes.paragraph( + text=f"Defined at: {event['filename']} (line" + f" {event['line_number']})" + ) + + domain_header += event_section + + if domain_header: + yield domain_header + + +def setup(app): + """ + Declare the Sphinx extension. + """ + app.add_config_value( + "openedxevents_source_path", + os.path.abspath(".."), + "env", + ) + app.add_config_value("openedxevents_repo_url", "", "env") + app.add_config_value("openedxevents_repo_version", "main", "env") + app.add_directive("openedxevents", OpenedxEvents) + + return { + "version": "0.1", + "parallel_read_safe": True, + "parallel_write_safe": True, + }