mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2025-04-19 08:55:15 +00:00
Add documentation that covers: - configuration - prediction Add Python scripts that support automatic documentation generation for configuration data defined with pydantic. Adapt EOS configuration to provide more methods for REST API and automatic documentation generation. Adapt REST API to allow for EOS configuration file load and save. Sort REST API on generation of openapi markdown for docs. Move logutil to core/logging to allow configuration of logging by standard config. Make Akkudoktor predictions always start extraction of prediction data at start of day. Previously extraction started at actual hour. This is to support the code that assumes prediction data to start at start of day. Signed-off-by: Bobby Noelte <b0661n0e17e@gmail.com>
242 lines
7.3 KiB
Python
Executable File
242 lines
7.3 KiB
Python
Executable File
#!.venv/bin/python
|
|
"""Utility functions for OpenAPI specification conversion tasks."""
|
|
|
|
import argparse
|
|
import json
|
|
import sys
|
|
|
|
if __package__ is None or __package__ == "":
|
|
# uses current directory visibility
|
|
import generate_openapi
|
|
else:
|
|
# uses current package visibility
|
|
from . import generate_openapi
|
|
|
|
|
|
def extract_info(openapi_json: dict) -> dict:
|
|
"""Extract basic information from OpenAPI JSON.
|
|
|
|
Args:
|
|
openapi_json (dict): The OpenAPI specification as a Python dictionary.
|
|
|
|
Returns:
|
|
dict: A dictionary containing the title, version, description, and base_url.
|
|
"""
|
|
info = openapi_json.get("info", {})
|
|
servers = openapi_json.get("servers", [{}])
|
|
|
|
return {
|
|
"title": info.get("title", "API Documentation"),
|
|
"version": info.get("version", "1.0.0"),
|
|
"description": info.get("description", "No description provided."),
|
|
"base_url": servers[0].get("url", "No base URL provided."),
|
|
}
|
|
|
|
|
|
def format_authentication(security_schemes: dict) -> str:
|
|
"""Format the authentication section for the Markdown.
|
|
|
|
Args:
|
|
security_schemes (dict): The security schemes from the OpenAPI spec.
|
|
|
|
Returns:
|
|
str: The formatted authentication section in Markdown.
|
|
"""
|
|
if not security_schemes:
|
|
return ""
|
|
|
|
markdown = "## Authentication\n\n"
|
|
for scheme, details in security_schemes.items():
|
|
auth_type = details.get("type", "unknown")
|
|
markdown += f"- **{scheme}**: {auth_type}\n\n"
|
|
return markdown
|
|
|
|
|
|
def format_parameters(parameters: list) -> str:
|
|
"""Format the parameters section for the Markdown.
|
|
|
|
Args:
|
|
parameters (list): The list of parameters from an endpoint.
|
|
|
|
Returns:
|
|
str: The formatted parameters section in Markdown.
|
|
"""
|
|
if not parameters:
|
|
return ""
|
|
|
|
markdown = "**Parameters**:\n\n"
|
|
for param in parameters:
|
|
name = param.get("name", "unknown")
|
|
location = param.get("in", "unknown")
|
|
required = param.get("required", False)
|
|
description = param.get("description", "No description provided.")
|
|
markdown += (
|
|
f"- `{name}` ({location}, {'required' if required else 'optional'}): {description}\n\n"
|
|
)
|
|
return markdown
|
|
|
|
|
|
def format_request_body(request_body: dict) -> str:
|
|
"""Format the request body section for the Markdown.
|
|
|
|
Args:
|
|
request_body (dict): The request body content from an endpoint.
|
|
|
|
Returns:
|
|
str: The formatted request body section in Markdown.
|
|
"""
|
|
if not request_body:
|
|
return ""
|
|
|
|
markdown = "**Request Body**:\n\n"
|
|
for content_type, schema in request_body.items():
|
|
markdown += f"- `{content_type}`: {json.dumps(schema.get('schema', {}), indent=2)}\n\n"
|
|
return markdown
|
|
|
|
|
|
def format_responses(responses: dict) -> str:
|
|
"""Format the responses section for the Markdown.
|
|
|
|
Args:
|
|
responses (dict): The responses from an endpoint.
|
|
|
|
Returns:
|
|
str: The formatted responses section in Markdown.
|
|
"""
|
|
if not responses:
|
|
return ""
|
|
|
|
markdown = "**Responses**:\n\n"
|
|
for status, response in responses.items():
|
|
desc = response.get("description", "No description provided.")
|
|
markdown += f"- **{status}**: {desc}\n\n"
|
|
return markdown
|
|
|
|
|
|
def format_endpoint(path: str, method: str, details: dict) -> str:
|
|
"""Format a single endpoint's details for the Markdown.
|
|
|
|
Args:
|
|
path (str): The endpoint path.
|
|
method (str): The HTTP method.
|
|
details (dict): The details of the endpoint.
|
|
|
|
Returns:
|
|
str: The formatted endpoint section in Markdown.
|
|
"""
|
|
link_summary = (
|
|
details.get("summary", "<summary missing>")
|
|
.lower()
|
|
.strip()
|
|
.replace(" ", "_")
|
|
.replace("-", "_")
|
|
)
|
|
link_path = (
|
|
path.lower().strip().replace("/", "_").replace(".", "_").replace("{", "_").replace("}", "_")
|
|
)
|
|
link_method = f"_{method.lower()})"
|
|
# [local](http://localhost:8503/docs#/default/fastapi_config_get_v1_config_get)
|
|
local_path = (
|
|
"[local](http://localhost:8503/docs#/default/" + link_summary + link_path + link_method
|
|
)
|
|
# [swagger](https://petstore3.swagger.io/?url=https://raw.githubusercontent.com/Akkudoktor-EOS/EOS/refs/heads/main/openapi.json#/default/fastapi_strompreis_strompreis_get)
|
|
swagger_path = (
|
|
"[swagger](https://petstore3.swagger.io/?url=https://raw.githubusercontent.com/Akkudoktor-EOS/EOS/refs/heads/main/openapi.json#/default/"
|
|
+ link_summary
|
|
+ link_path
|
|
+ link_method
|
|
)
|
|
|
|
markdown = f"## {method.upper()} {path}\n\n"
|
|
|
|
markdown += f"**Links**: {local_path}, {swagger_path}\n\n"
|
|
|
|
summary = details.get("summary", None)
|
|
if summary:
|
|
markdown += f"{summary}\n\n"
|
|
|
|
description = details.get("description", None)
|
|
if description:
|
|
markdown += "```\n"
|
|
markdown += f"{description}"
|
|
markdown += "\n```\n\n"
|
|
|
|
markdown += format_parameters(details.get("parameters", []))
|
|
markdown += format_request_body(details.get("requestBody", {}).get("content", {}))
|
|
markdown += format_responses(details.get("responses", {}))
|
|
markdown += "---\n\n"
|
|
|
|
return markdown
|
|
|
|
|
|
def openapi_to_markdown(openapi_json: dict) -> str:
|
|
"""Convert OpenAPI JSON specification to a Markdown representation.
|
|
|
|
Args:
|
|
openapi_json (dict): The OpenAPI specification as a Python dictionary.
|
|
|
|
Returns:
|
|
str: The Markdown representation of the OpenAPI spec.
|
|
"""
|
|
info = extract_info(openapi_json)
|
|
markdown = f"# {info['title']}\n\n"
|
|
markdown += f"**Version**: `{info['version']}`\n\n"
|
|
markdown += f"**Description**: {info['description']}\n\n"
|
|
markdown += f"**Base URL**: `{info['base_url']}`\n\n"
|
|
|
|
security_schemes = openapi_json.get("components", {}).get("securitySchemes", {})
|
|
markdown += format_authentication(security_schemes)
|
|
|
|
markdown += "**Endpoints**:\n\n"
|
|
paths = openapi_json.get("paths", {})
|
|
for path in sorted(paths):
|
|
methods = paths[path]
|
|
for method in sorted(methods):
|
|
details = methods[method]
|
|
markdown += format_endpoint(path, method, details)
|
|
|
|
# Assure the is no double \n at end of file
|
|
markdown = markdown.rstrip("\n")
|
|
markdown += "\n"
|
|
|
|
return markdown
|
|
|
|
|
|
def generate_openapi_md() -> str:
|
|
"""Generate OpenAPI specification in Markdown.
|
|
|
|
Returns:
|
|
str: The Markdown representation of the OpenAPI spec.
|
|
"""
|
|
openapi_spec = generate_openapi.generate_openapi()
|
|
openapi_md = openapi_to_markdown(openapi_spec)
|
|
return openapi_md
|
|
|
|
|
|
def main():
|
|
"""Main function to run the generation of the OpenAPI specification as Markdown."""
|
|
parser = argparse.ArgumentParser(description="Generate OpenAPI Specification as Markdown")
|
|
parser.add_argument(
|
|
"--output-file", type=str, default=None, help="File to write the OpenAPI Specification to"
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
try:
|
|
openapi_md = generate_openapi_md()
|
|
if args.output_file:
|
|
# Write to file
|
|
with open(args.output_file, "w") as f:
|
|
f.write(openapi_md)
|
|
else:
|
|
# Write to std output
|
|
print(openapi_md)
|
|
|
|
except Exception as e:
|
|
print(f"Error during OpenAPI specification generation: {e}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|