cli-wizard 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,134 @@
1
+ # AUTO-GENERATED FILE - DO NOT EDIT
2
+ # Generated from OpenAPI specification by cli-wizard
3
+
4
+ """HTTP client for API requests."""
5
+
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ import requests
10
+
11
+ from .constants import DEFAULT_BASE_URL, DEFAULT_TIMEOUT, DEFAULT_CA_FILE
12
+ from .logging import log_debug
13
+
14
+
15
+ class ApiClient:
16
+ """HTTP client for making API requests."""
17
+
18
+ def __init__(
19
+ self,
20
+ base_url: str | None = None,
21
+ access_token: str | None = None,
22
+ timeout: int = DEFAULT_TIMEOUT,
23
+ ca_file: str | None = DEFAULT_CA_FILE,
24
+ verify_ssl: bool = True,
25
+ debug: bool = False,
26
+ ) -> None:
27
+ """Initialize the API client."""
28
+ self.base_url = (base_url or DEFAULT_BASE_URL).rstrip("/")
29
+ self.access_token = access_token
30
+ self.timeout = timeout
31
+ self.ca_file = ca_file
32
+ self.verify_ssl = verify_ssl
33
+ self.debug = debug
34
+ self.session = requests.Session()
35
+ self._setup_headers()
36
+ self._setup_ssl()
37
+
38
+ def _setup_headers(self) -> None:
39
+ """Set up default headers."""
40
+ self.session.headers["Content-Type"] = "application/json"
41
+ self.session.headers["Accept"] = "application/json"
42
+ if self.access_token:
43
+ self.session.headers["Authorization"] = f"Bearer {self.access_token}"
44
+
45
+ def _setup_ssl(self) -> None:
46
+ """Set up SSL verification with custom CA file if provided."""
47
+ if not self.verify_ssl:
48
+ self.session.verify = False
49
+ elif self.ca_file:
50
+ ca_path = Path(self.ca_file) if isinstance(self.ca_file, str) else self.ca_file
51
+ if ca_path and ca_path.exists():
52
+ self.session.verify = str(ca_path)
53
+
54
+ def _url(self, path: str) -> str:
55
+ """Build full URL from path."""
56
+ return f"{self.base_url}{path}"
57
+
58
+ def _log_request(self, method: str, url: str, params: dict | None = None, json_data: dict | None = None) -> None:
59
+ """Log request details when debug is enabled."""
60
+ if not self.debug:
61
+ return
62
+ log_debug(f"Request: {method} {url}")
63
+ if params:
64
+ log_debug(f"Params: {params}")
65
+ if json_data:
66
+ log_debug(f"Body: {json_data}")
67
+ log_debug(f"Headers: {dict(self.session.headers)}")
68
+
69
+ def _log_response(self, response: requests.Response) -> None:
70
+ """Log response details when debug is enabled."""
71
+ if not self.debug:
72
+ return
73
+ log_debug(f"Response: {response.status_code} {response.reason}")
74
+ log_debug(f"Response Headers: {dict(response.headers)}")
75
+ if response.text:
76
+ log_debug(f"Response Body: {response.text[:1000]}")
77
+
78
+ def get(
79
+ self, path: str, params: dict[str, Any] | None = None
80
+ ) -> requests.Response:
81
+ """Make a GET request."""
82
+ url = self._url(path)
83
+ self._log_request("GET", url, params=params)
84
+ response = self.session.get(url, params=params, timeout=self.timeout)
85
+ self._log_response(response)
86
+ return response
87
+
88
+ def post(
89
+ self,
90
+ path: str,
91
+ json_data: dict[str, Any] | None = None,
92
+ ) -> requests.Response:
93
+ """Make a POST request."""
94
+ url = self._url(path)
95
+ self._log_request("POST", url, json_data=json_data)
96
+ response = self.session.post(url, json=json_data, timeout=self.timeout)
97
+ self._log_response(response)
98
+ return response
99
+
100
+ def put(
101
+ self,
102
+ path: str,
103
+ json_data: dict[str, Any] | None = None,
104
+ ) -> requests.Response:
105
+ """Make a PUT request."""
106
+ url = self._url(path)
107
+ self._log_request("PUT", url, json_data=json_data)
108
+ response = self.session.put(url, json=json_data, timeout=self.timeout)
109
+ self._log_response(response)
110
+ return response
111
+
112
+ def patch(
113
+ self,
114
+ path: str,
115
+ json_data: dict[str, Any] | None = None,
116
+ ) -> requests.Response:
117
+ """Make a PATCH request."""
118
+ url = self._url(path)
119
+ self._log_request("PATCH", url, json_data=json_data)
120
+ response = self.session.patch(url, json=json_data, timeout=self.timeout)
121
+ self._log_response(response)
122
+ return response
123
+
124
+ def delete(
125
+ self,
126
+ path: str,
127
+ json_data: dict[str, Any] | None = None,
128
+ ) -> requests.Response:
129
+ """Make a DELETE request."""
130
+ url = self._url(path)
131
+ self._log_request("DELETE", url, json_data=json_data)
132
+ response = self.session.delete(url, json=json_data, timeout=self.timeout)
133
+ self._log_response(response)
134
+ return response
@@ -0,0 +1,161 @@
1
+ # AUTO-GENERATED FILE - DO NOT EDIT
2
+ # Generated from OpenAPI specification by cli-wizard
3
+
4
+ """{{ group.name }} commands."""
5
+
6
+ import json
7
+ import os
8
+ from pathlib import Path
9
+
10
+ import click
11
+ from typing import Any
12
+
13
+ from {{ package_name }}.constants import DEFAULT_BASE_URL, DEFAULT_CA_FILE
14
+ from {{ package_name }}.client import ApiClient
15
+ from {{ package_name }}.logging import log_info, log_error, log_debug, set_debug
16
+ from {{ package_name }}.profile import load_profile, get_profile_value
17
+
18
+
19
+ def _get_client(
20
+ base_url: str | None,
21
+ no_verify_ssl: bool,
22
+ ca_file: Path | None,
23
+ debug: bool = False,
24
+ ) -> ApiClient:
25
+ """Create an API client with the given options."""
26
+ effective_base_url = base_url or os.environ.get("API_BASE_URL") or DEFAULT_BASE_URL
27
+ effective_ca_file = None if no_verify_ssl else (ca_file or DEFAULT_CA_FILE)
28
+ access_token = get_profile_value("accessToken")
29
+ return ApiClient(
30
+ base_url=effective_base_url,
31
+ access_token=access_token,
32
+ ca_file=effective_ca_file,
33
+ verify_ssl=not no_verify_ssl,
34
+ debug=debug,
35
+ )
36
+
37
+
38
+ @click.group(name="{{ group.cli_name }}")
39
+ def {{ group.module_name }}() -> None:
40
+ """{{ group.description }}"""
41
+ {% for op in group.operations %}
42
+
43
+
44
+ @{{ group.module_name }}.command(name="{{ op.command_name }}")
45
+ {% for param in op.parameters if param.location == "path" %}
46
+ @click.argument("{{ param.python_name }}"{% if param.click_type != "str" %}, type={{ param.click_type }}{% endif %})
47
+ {% endfor %}
48
+ {% for param in op.parameters if param.location == "query" %}
49
+ {% if param.enum %}
50
+ @click.option("--{{ param.cli_name }}", type=click.Choice([{% for e in param.enum %}"{{ e }}"{% if not loop.last %}, {% endif %}{% endfor %}]){% if param.required %}, required=True{% endif %}{% if param.default is not none %}, default="{{ param.default }}"{% endif %}, help="{{ param.description }}")
51
+ {% else %}
52
+ @click.option("--{{ param.cli_name }}"{% if param.click_type != "str" %}, type={{ param.click_type }}{% endif %}{% if param.required %}, required=True{% endif %}{% if param.default is not none %}, default={{ param.default }}{% endif %}, help="{{ param.description }}")
53
+ {% endif %}
54
+ {% endfor %}
55
+ {% for prop in op.body_properties %}
56
+ @click.option("--{{ prop.cli_name }}"{% if prop.click_type == "bool" %}, is_flag=True{% elif prop.click_type != "str" %}, type={{ prop.click_type }}{% endif %}{% if prop.required %}, required=True{% endif %}, help="{{ prop.description }}")
57
+ {% endfor %}
58
+ @click.option("--profile", "-p", default="default", help="Profile name to use")
59
+ @click.option("--debug", "-d", is_flag=True, help="Enable debug output")
60
+ @click.option("--base-url", "-u", envvar="API_BASE_URL", help="API base URL")
61
+ @click.option("--no-verify-ssl", is_flag=True, default=False, help="Disable SSL certificate verification")
62
+ @click.option("--ca-file", type=click.Path(exists=True, dir_okay=False, path_type=Path), help="CA certificate file for SSL verification")
63
+ def {{ op.function_name }}(
64
+ {% for param in op.parameters if param.location == "path" %}
65
+ {{ param.python_name }}: {{ param.click_type }},
66
+ {% endfor %}
67
+ {% for param in op.parameters if param.location == "query" %}
68
+ {{ param.python_name }}: {{ param.click_type }}{% if not param.required %} | None{% endif %},
69
+ {% endfor %}
70
+ {% for prop in op.body_properties %}
71
+ {{ prop.python_name }}: {{ prop.click_type }}{% if not prop.required %} | None{% endif %},
72
+ {% endfor %}
73
+ profile: str,
74
+ debug: bool,
75
+ base_url: str | None,
76
+ no_verify_ssl: bool,
77
+ ca_file: Path | None,
78
+ ) -> None:
79
+ """{{ op.summary or op.operation_id }}"""
80
+ # Enable debug logging if --debug flag is set
81
+ set_debug(debug)
82
+
83
+ # Load profile
84
+ load_profile(profile)
85
+ # Log command execution start
86
+ cmd_params = {
87
+ {% for param in op.parameters if param.location == "path" %}
88
+ "{{ param.name }}": {{ param.python_name }},
89
+ {% endfor %}
90
+ {% for param in op.parameters if param.location == "query" %}
91
+ "{{ param.name }}": {{ param.python_name }},
92
+ {% endfor %}
93
+ {% for prop in op.body_properties %}
94
+ "{{ prop.name }}": {{ prop.python_name }},
95
+ {% endfor %}
96
+ }
97
+ log_debug(f"Executing command '{{ group.cli_name }} {{ op.command_name }}' with params: {cmd_params}")
98
+
99
+ client = _get_client(base_url, no_verify_ssl, ca_file, debug)
100
+ {% if op.parameters | selectattr("location", "equalto", "query") | list %}
101
+
102
+ params: dict[str, Any] = {}
103
+ {% for param in op.parameters if param.location == "query" %}
104
+ if {{ param.python_name }} is not None:
105
+ params["{{ param.name }}"] = {{ param.python_name }}
106
+ {% endfor %}
107
+ {% endif %}
108
+ {% if op.body_properties %}
109
+
110
+ body: dict[str, Any] = {}
111
+ {% for prop in op.body_properties %}
112
+ if {{ prop.python_name }} is not None:
113
+ body["{{ prop.name }}"] = {{ prop.python_name }}
114
+ {% endfor %}
115
+ {% endif %}
116
+
117
+ try:
118
+ {% if op.method == "GET" %}
119
+ {% if op.parameters | selectattr("location", "equalto", "query") | list %}
120
+ response = client.get("{{ op | url_path }}", params=params)
121
+ {% else %}
122
+ response = client.get("{{ op | url_path }}")
123
+ {% endif %}
124
+ {% elif op.method == "POST" %}
125
+ {% if op.body_properties %}
126
+ response = client.post("{{ op | url_path }}", json_data=body)
127
+ {% else %}
128
+ response = client.post("{{ op | url_path }}")
129
+ {% endif %}
130
+ {% elif op.method == "PUT" %}
131
+ {% if op.body_properties %}
132
+ response = client.put("{{ op | url_path }}", json_data=body)
133
+ {% else %}
134
+ response = client.put("{{ op | url_path }}")
135
+ {% endif %}
136
+ {% elif op.method == "PATCH" %}
137
+ {% if op.body_properties %}
138
+ response = client.patch("{{ op | url_path }}", json_data=body)
139
+ {% else %}
140
+ response = client.patch("{{ op | url_path }}")
141
+ {% endif %}
142
+ {% elif op.method == "DELETE" %}
143
+ {% if op.body_properties %}
144
+ response = client.delete("{{ op | url_path }}", json_data=body)
145
+ {% else %}
146
+ response = client.delete("{{ op | url_path }}")
147
+ {% endif %}
148
+ {% endif %}
149
+ response.raise_for_status()
150
+ if response.text:
151
+ output = json.dumps(response.json(), indent=2)
152
+ log_debug(f"Command '{{ group.cli_name }} {{ op.command_name }}' completed with output: {output[:500]}")
153
+ click.echo(output)
154
+ else:
155
+ log_debug(f"Command '{{ group.cli_name }} {{ op.command_name }}' completed successfully")
156
+ click.echo("Success")
157
+ except Exception as e:
158
+ log_error(f"Command '{{ group.cli_name }} {{ op.command_name }}' failed: {e}")
159
+ click.secho(f"Error: {e}", fg="red", err=True)
160
+ raise SystemExit(1)
161
+ {% endfor %}
@@ -0,0 +1,206 @@
1
+ # AUTO-GENERATED FILE - DO NOT EDIT
2
+ # Generated from OpenAPI specification by cli-wizard
3
+
4
+ """Config commands for managing CLI profiles."""
5
+
6
+ import json
7
+ from pathlib import Path
8
+
9
+ import click
10
+ import yaml
11
+
12
+ from {{ package_name }}.constants import PROFILE_FILE
13
+ from {{ package_name }}.logging import log_info, log_error, log_warning
14
+
15
+
16
+ @click.group(name="config")
17
+ def config() -> None:
18
+ """Configure the CLI"""
19
+
20
+
21
+ @config.command(name="init")
22
+ def config_init() -> None:
23
+ """Initialize the profile file with default profile."""
24
+ if PROFILE_FILE.exists():
25
+ result = {"status": "exists", "path": str(PROFILE_FILE)}
26
+ click.echo(json.dumps(result, indent=2))
27
+ return
28
+
29
+ try:
30
+ PROFILE_FILE.parent.mkdir(parents=True, exist_ok=True)
31
+ default_content = {"default": {}}
32
+ with open(PROFILE_FILE, "w") as f:
33
+ yaml.safe_dump(default_content, f, default_flow_style=False)
34
+ log_info(f"Created profile file: {PROFILE_FILE}")
35
+ result = {"status": "created", "path": str(PROFILE_FILE)}
36
+ click.echo(json.dumps(result, indent=2))
37
+ except (IOError, OSError) as e:
38
+ log_error(f"Failed to create profile file: {e}")
39
+ result = {"status": "error", "message": str(e)}
40
+ click.echo(json.dumps(result, indent=2), err=True)
41
+ raise SystemExit(1)
42
+
43
+
44
+ @config.command(name="list-profiles")
45
+ def config_list_profiles() -> None:
46
+ """List all available profiles."""
47
+ if not PROFILE_FILE.exists():
48
+ result = {"profiles": []}
49
+ click.echo(json.dumps(result, indent=2))
50
+ return
51
+
52
+ try:
53
+ with open(PROFILE_FILE) as f:
54
+ profiles = yaml.safe_load(f) or {}
55
+ except (yaml.YAMLError, IOError) as e:
56
+ log_error(f"Failed to load profile file: {e}")
57
+ result = {"status": "error", "message": str(e)}
58
+ click.echo(json.dumps(result, indent=2), err=True)
59
+ raise SystemExit(1)
60
+
61
+ result = {"profiles": list(profiles.keys())}
62
+ click.echo(json.dumps(result, indent=2))
63
+
64
+
65
+ @config.command(name="show")
66
+ @click.option("--profile", "-p", default="default", help="Profile name")
67
+ def config_show(profile: str) -> None:
68
+ """Show all parameters and values for a profile."""
69
+ if not PROFILE_FILE.exists():
70
+ click.echo(json.dumps({}, indent=2))
71
+ return
72
+
73
+ try:
74
+ with open(PROFILE_FILE) as f:
75
+ profiles = yaml.safe_load(f) or {}
76
+ except (yaml.YAMLError, IOError) as e:
77
+ log_error(f"Failed to load profile file: {e}")
78
+ result = {"status": "error", "message": str(e)}
79
+ click.echo(json.dumps(result, indent=2), err=True)
80
+ raise SystemExit(1)
81
+
82
+ if profile not in profiles:
83
+ click.echo(json.dumps({}, indent=2))
84
+ return
85
+
86
+ profile_data = profiles[profile] or {}
87
+ click.echo(json.dumps(profile_data, indent=2))
88
+
89
+
90
+ @config.command(name="get")
91
+ @click.argument("param")
92
+ @click.option("--profile", "-p", default="default", help="Profile name")
93
+ def config_get(param: str, profile: str) -> None:
94
+ """Get a configuration value from a profile."""
95
+ if not PROFILE_FILE.exists():
96
+ result = {"key": param, "value": None}
97
+ click.echo(json.dumps(result, indent=2))
98
+ return
99
+
100
+ try:
101
+ with open(PROFILE_FILE) as f:
102
+ profiles = yaml.safe_load(f) or {}
103
+ except (yaml.YAMLError, IOError) as e:
104
+ log_error(f"Failed to load profile file: {e}")
105
+ result = {"status": "error", "message": str(e)}
106
+ click.echo(json.dumps(result, indent=2), err=True)
107
+ raise SystemExit(1)
108
+
109
+ if profile not in profiles:
110
+ result = {"key": param, "value": None}
111
+ click.echo(json.dumps(result, indent=2))
112
+ return
113
+
114
+ profile_data = profiles[profile] or {}
115
+ value = profile_data.get(param)
116
+ result = {"key": param, "value": value}
117
+ click.echo(json.dumps(result, indent=2))
118
+
119
+
120
+ @config.command(name="set")
121
+ @click.argument("param")
122
+ @click.argument("value")
123
+ @click.option("--profile", "-p", default="default", help="Profile name")
124
+ def config_set(param: str, value: str, profile: str) -> None:
125
+ """Set a configuration value in a profile."""
126
+ if not PROFILE_FILE.exists():
127
+ PROFILE_FILE.parent.mkdir(parents=True, exist_ok=True)
128
+ profiles = {}
129
+ else:
130
+ try:
131
+ with open(PROFILE_FILE) as f:
132
+ profiles = yaml.safe_load(f) or {}
133
+ except (yaml.YAMLError, IOError) as e:
134
+ log_error(f"Failed to load profile file: {e}")
135
+ result = {"status": "error", "message": str(e)}
136
+ click.echo(json.dumps(result, indent=2), err=True)
137
+ raise SystemExit(1)
138
+
139
+ if profile not in profiles:
140
+ profiles[profile] = {}
141
+
142
+ # Get old value
143
+ old_value = profiles[profile].get(param)
144
+
145
+ # Try to parse value as JSON for complex types
146
+ try:
147
+ parsed_value = json.loads(value)
148
+ except json.JSONDecodeError:
149
+ parsed_value = value
150
+
151
+ profiles[profile][param] = parsed_value
152
+
153
+ try:
154
+ with open(PROFILE_FILE, "w") as f:
155
+ yaml.safe_dump(profiles, f, default_flow_style=False)
156
+ log_info(f"Set '{param}' = '{parsed_value}' in profile '{profile}'")
157
+ result = {"key": param, "value": parsed_value, "oldValue": old_value}
158
+ click.echo(json.dumps(result, indent=2))
159
+ except (IOError, OSError) as e:
160
+ log_error(f"Failed to save profile file: {e}")
161
+ result = {"status": "error", "message": str(e)}
162
+ click.echo(json.dumps(result, indent=2), err=True)
163
+ raise SystemExit(1)
164
+
165
+
166
+ @config.command(name="unset")
167
+ @click.argument("param")
168
+ @click.option("--profile", "-p", default="default", help="Profile name")
169
+ def config_unset(param: str, profile: str) -> None:
170
+ """Remove a configuration value from a profile."""
171
+ if not PROFILE_FILE.exists():
172
+ result = {"key": param, "oldValue": None}
173
+ click.echo(json.dumps(result, indent=2))
174
+ return
175
+
176
+ try:
177
+ with open(PROFILE_FILE) as f:
178
+ profiles = yaml.safe_load(f) or {}
179
+ except (yaml.YAMLError, IOError) as e:
180
+ log_error(f"Failed to load profile file: {e}")
181
+ result = {"status": "error", "message": str(e)}
182
+ click.echo(json.dumps(result, indent=2), err=True)
183
+ raise SystemExit(1)
184
+
185
+ if profile not in profiles:
186
+ result = {"key": param, "oldValue": None}
187
+ click.echo(json.dumps(result, indent=2))
188
+ return
189
+
190
+ profile_data = profiles[profile] or {}
191
+ old_value = profile_data.get(param)
192
+
193
+ if param in profile_data:
194
+ del profiles[profile][param]
195
+ try:
196
+ with open(PROFILE_FILE, "w") as f:
197
+ yaml.safe_dump(profiles, f, default_flow_style=False)
198
+ log_info(f"Removed '{param}' from profile '{profile}'")
199
+ except (IOError, OSError) as e:
200
+ log_error(f"Failed to save profile file: {e}")
201
+ result = {"status": "error", "message": str(e)}
202
+ click.echo(json.dumps(result, indent=2), err=True)
203
+ raise SystemExit(1)
204
+
205
+ result = {"key": param, "oldValue": old_value}
206
+ click.echo(json.dumps(result, indent=2))
@@ -0,0 +1,14 @@
1
+ # AUTO-GENERATED FILE - DO NOT EDIT
2
+ # Generated from OpenAPI specification by cli-wizard
3
+
4
+ """Generated CLI commands."""
5
+
6
+ {% for tag, group in groups.items() %}
7
+ from .{{ group.module_name }} import {{ group.module_name }}
8
+ {% endfor %}
9
+
10
+ __all__ = [
11
+ {% for tag, group in groups.items() %}
12
+ "{{ group.module_name }}",
13
+ {% endfor %}
14
+ ]
@@ -0,0 +1,67 @@
1
+ # AUTO-GENERATED FILE - DO NOT EDIT
2
+ # Generated from OpenAPI specification by cli-wizard
3
+
4
+ """Constants for the generated CLI."""
5
+
6
+ import os
7
+ from pathlib import Path
8
+
9
+
10
+ def _read_version() -> str:
11
+ """Read version from VERSION file."""
12
+ version_file = Path(__file__).parent.parent.parent / "VERSION"
13
+ if version_file.exists():
14
+ return version_file.read_text().strip()
15
+ return "0.0.0"
16
+
17
+
18
+ __version__ = _read_version()
19
+
20
+ # Main directory for CLI data (config, cache, etc.)
21
+ MAIN_DIR = Path(os.path.expandvars("{{ main_dir }}"))
22
+
23
+ # Profile configuration
24
+ PROFILE_FILE = Path(os.path.expandvars("{{ config.ProfileFile | default('#[MainDir]/profiles.yaml') }}"))
25
+
26
+ DEFAULT_BASE_URL = "{{ config.DefaultBaseUrl | default('http://localhost:8080') }}"
27
+ DEFAULT_TIMEOUT = {{ config.Timeout | default(30) }}
28
+ {% if ca_file_name %}
29
+ DEFAULT_CA_FILE = Path(__file__).parent / "resources" / "{{ ca_file_name }}"
30
+ {% else %}
31
+ DEFAULT_CA_FILE = None
32
+ {% endif %}
33
+
34
+ # Splash screen configuration
35
+ {% if splash_file_name %}
36
+ SPLASH_ENABLED = True
37
+ SPLASH_FILE = Path(__file__).parent / "resources" / "{{ splash_file_name }}"
38
+ SPLASH_COLOR = "{{ config.SplashColor | default('#FFFFFF') }}"
39
+ {% else %}
40
+ SPLASH_ENABLED = False
41
+ SPLASH_FILE = None
42
+ SPLASH_COLOR = None
43
+ {% endif %}
44
+
45
+ # Log configuration
46
+ LOG_LEVEL = "{{ config.LogLevel | default('INFO') }}"
47
+ LOG_FORMAT = "{{ config.LogFormat | default('%(asctime)s [%(levelname)s] %(message)s') }}"
48
+ LOG_TIMESTAMP_FORMAT = "{{ config.LogTimestampFormat | default('%Y-%m-%dT%H:%M:%S') }}"
49
+ LOG_TIMEZONE = "{{ config.LogTimezone | default('UTC') }}"
50
+ {% if config.LogFile %}
51
+ LOG_FILE = Path(os.path.expandvars("{{ config.LogFile }}"))
52
+ {% else %}
53
+ LOG_FILE = None
54
+ {% endif %}
55
+ LOG_ROTATION_TYPE = "{{ config.LogRotationType | default('days') }}"
56
+ LOG_ROTATION_SIZE_MB = {{ config.LogRotationSize | default(10) }}
57
+ LOG_ROTATION_DAYS = {{ config.LogRotationDays | default(30) }}
58
+ LOG_ROTATION_BACKUP_COUNT = {{ config.LogRotationBackupCount | default(5) }}
59
+
60
+ # Log color configuration
61
+ LOG_COLOR_STYLE = "{{ config.LogColorStyle | default('level') }}"
62
+ LOG_COLORS = {
63
+ "DEBUG": "{{ config.LogColorDebug | default('#808080') }}",
64
+ "INFO": "{{ config.LogColorInfo | default('#00FF00') }}",
65
+ "WARNING": "{{ config.LogColorWarning | default('#FFFF00') }}",
66
+ "ERROR": "{{ config.LogColorError | default('#FF0000') }}",
67
+ }