drf-to-mkdoc 0.1.5__py3-none-any.whl → 0.1.8__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.

Potentially problematic release.


This version of drf-to-mkdoc might be problematic. Click here for more details.

Files changed (42) hide show
  1. drf_to_mkdoc/__init__.py +1 -1
  2. drf_to_mkdoc/apps.py +6 -2
  3. drf_to_mkdoc/conf/defaults.py +0 -1
  4. drf_to_mkdoc/conf/settings.py +11 -5
  5. drf_to_mkdoc/management/commands/build_docs.py +61 -19
  6. drf_to_mkdoc/management/commands/generate_docs.py +5 -5
  7. drf_to_mkdoc/management/commands/generate_model_docs.py +37 -7
  8. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/endpoints-filter.js +189 -0
  9. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/accessibility.css +21 -0
  10. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/animations.css +11 -0
  11. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/badges.css +54 -0
  12. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/base.css +84 -0
  13. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/endpoint-content.css +165 -0
  14. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/endpoints-grid.css +194 -0
  15. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/filter-section.css +209 -0
  16. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/fixes.css +44 -0
  17. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/layout.css +31 -0
  18. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/loading.css +35 -0
  19. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/responsive.css +96 -0
  20. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/sections.css +35 -0
  21. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/stats.css +34 -0
  22. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/tags.css +92 -0
  23. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/theme-toggle.css +42 -0
  24. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/variables.css +73 -0
  25. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/animations.css +25 -0
  26. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/base.css +83 -0
  27. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/model-cards.css +126 -0
  28. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/model-tables.css +57 -0
  29. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/responsive.css +51 -0
  30. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/variables.css +46 -0
  31. drf_to_mkdoc/utils/common.py +31 -29
  32. drf_to_mkdoc/utils/{endpoint_generator.py → endpoint_detail_generator.py} +214 -384
  33. drf_to_mkdoc/utils/endpoint_list_generator.py +234 -0
  34. drf_to_mkdoc/utils/extractors/query_parameter_extractors.py +15 -16
  35. drf_to_mkdoc/utils/{model_generator.py → model_detail_generator.py} +20 -51
  36. drf_to_mkdoc/utils/model_list_generator.py +67 -0
  37. {drf_to_mkdoc-0.1.5.dist-info → drf_to_mkdoc-0.1.8.dist-info}/METADATA +3 -25
  38. drf_to_mkdoc-0.1.8.dist-info/RECORD +50 -0
  39. drf_to_mkdoc-0.1.5.dist-info/RECORD +0 -25
  40. {drf_to_mkdoc-0.1.5.dist-info → drf_to_mkdoc-0.1.8.dist-info}/WHEEL +0 -0
  41. {drf_to_mkdoc-0.1.5.dist-info → drf_to_mkdoc-0.1.8.dist-info}/licenses/LICENSE +0 -0
  42. {drf_to_mkdoc-0.1.5.dist-info → drf_to_mkdoc-0.1.8.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,51 @@
1
+ /* ===== RESPONSIVE DESIGN FOR MODELS ===== */
2
+
3
+ /* Responsive design improvements */
4
+ @media screen and (max-width: 768px) {
5
+ .field-grid, .method-grid {
6
+ grid-template-columns: 1fr;
7
+ }
8
+
9
+ .endpoint-header {
10
+ flex-direction: column;
11
+ align-items: flex-start;
12
+ }
13
+
14
+ .endpoint-path {
15
+ margin-left: 0;
16
+ margin-top: 0.5rem;
17
+ }
18
+ }
19
+
20
+ /* Responsive adjustments for model cards */
21
+ @media screen and (max-width: 768px) {
22
+ .model-cards {
23
+ grid-template-columns: 1fr;
24
+ gap: 1rem;
25
+ }
26
+
27
+ .model-card {
28
+ padding: 1rem;
29
+ }
30
+
31
+ .model-header h3 {
32
+ font-size: 1.1rem;
33
+ }
34
+
35
+ .model-badge {
36
+ font-size: 0.7rem;
37
+ padding: 0.2rem 0.6rem;
38
+ }
39
+ }
40
+
41
+ @media screen and (max-width: 480px) {
42
+ .model-header {
43
+ flex-direction: column;
44
+ align-items: flex-start;
45
+ gap: 0.5rem;
46
+ }
47
+
48
+ .model-badge {
49
+ align-self: flex-start;
50
+ }
51
+ }
@@ -0,0 +1,46 @@
1
+ /* ===== CSS VARIABLES FOR MODELS ===== */
2
+ :root {
3
+ /* Colors */
4
+ --bg-primary: #ffffff;
5
+ --bg-secondary: #f8fafc;
6
+ --bg-tertiary: #f1f5f9;
7
+
8
+ --text-primary: #0f172a;
9
+ --text-secondary: #475569;
10
+ --text-muted: #64748b;
11
+
12
+ --border-color: #e2e8f0;
13
+ --border-hover: #cbd5e1;
14
+
15
+ --accent-primary: #3b82f6;
16
+ --accent-secondary: #1d4ed8;
17
+ --accent-hover: #2563eb;
18
+
19
+ /* Shadows */
20
+ --shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
21
+ --shadow-hover: 0 4px 6px rgba(0, 0, 0, 0.1);
22
+ --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
23
+
24
+ /* Transitions */
25
+ --transition-fast: 0.15s ease;
26
+ --transition-normal: 0.3s ease;
27
+ --transition-slow: 0.5s ease;
28
+ }
29
+
30
+ /* Dark mode */
31
+ [data-md-color-scheme="slate"] {
32
+ --bg-primary: #1a1a1a;
33
+ --bg-secondary: #2d2d2d;
34
+ --bg-tertiary: #404040;
35
+
36
+ --text-primary: #ffffff;
37
+ --text-secondary: #e5e5e5;
38
+ --text-muted: #cccccc;
39
+
40
+ --border-color: #404040;
41
+ --border-hover: #555555;
42
+
43
+ --accent-primary: #60a5fa;
44
+ --accent-secondary: #3b82f6;
45
+ --accent-hover: #93c5fd;
46
+ }
@@ -1,19 +1,21 @@
1
- from asyncio.log import logger
2
1
  import importlib
3
- import yaml
4
2
  import json
5
3
  import re
4
+ from asyncio.log import logger
6
5
  from functools import lru_cache
7
6
  from pathlib import Path
8
- from typing import Any, Optional
7
+ from typing import Any
9
8
 
9
+ import yaml
10
10
  from django.apps import apps
11
11
  from django.core.exceptions import AppRegistryNotReady
12
12
  from django.urls import resolve
13
13
  from django.utils.module_loading import import_string
14
14
  from drf_spectacular.generators import SchemaGenerator
15
+
15
16
  from drf_to_mkdoc.conf.settings import drf_to_mkdoc_settings
16
17
 
18
+
17
19
  class SchemaValidationError(Exception):
18
20
  """Custom exception for schema validation errors."""
19
21
 
@@ -37,9 +39,10 @@ def substitute_path_params(path: str, parameters: list[dict[str, Any]]) -> str:
37
39
  django_path = re.sub(r"<path:[^>]+>", "dummy/path", django_path)
38
40
  django_path = re.sub(r"<[^:>]+>", "dummy", django_path) # Catch remaining simple params
39
41
 
40
- return django_path
42
+ return django_path # noqa: RET504
43
+
41
44
 
42
- def load_schema() -> Optional[dict[str, Any]]:
45
+ def load_schema() -> dict[str, Any] | None:
43
46
  """Load the OpenAPI schema from doc-schema.yaml"""
44
47
  schema_file = Path(drf_to_mkdoc_settings.CONFIG_DIR) / "doc-schema.yaml"
45
48
  if not schema_file.exists():
@@ -49,7 +52,7 @@ def load_schema() -> Optional[dict[str, Any]]:
49
52
  return yaml.safe_load(f)
50
53
 
51
54
 
52
- def load_model_json_data() -> Optional[dict[str, Any]]:
55
+ def load_model_json_data() -> dict[str, Any] | None:
53
56
  """Load the JSON mapping data for model information"""
54
57
  json_file = Path(drf_to_mkdoc_settings.MODEL_DOCS_FILE)
55
58
  if not json_file.exists():
@@ -59,7 +62,7 @@ def load_model_json_data() -> Optional[dict[str, Any]]:
59
62
  return json.load(f)
60
63
 
61
64
 
62
- def load_doc_config() -> Optional[dict[str, Any]]:
65
+ def load_doc_config() -> dict[str, Any] | None:
63
66
  """Load the documentation configuration file"""
64
67
  config_file = Path(drf_to_mkdoc_settings.DOC_CONFIG_FILE)
65
68
  if not config_file.exists():
@@ -69,7 +72,7 @@ def load_doc_config() -> Optional[dict[str, Any]]:
69
72
  return json.load(f)
70
73
 
71
74
 
72
- def get_model_docstring(class_name: str) -> Optional[str]:
75
+ def get_model_docstring(class_name: str) -> str | None:
73
76
  """Extract docstring from Django model class"""
74
77
  try:
75
78
  # Check if Django is properly initialized
@@ -167,19 +170,20 @@ def get_custom_schema():
167
170
  raise QueryParamTypeError("Invalid queryparam_type")
168
171
  return data
169
172
 
173
+
170
174
  def convert_to_django_path(path: str, parameters: list[dict[str, Any]]) -> str:
171
175
  """
172
176
  Convert a path with {param} to a Django-style path with <type:param>.
173
- If DRF_TO_MKDOC_PATH_PARAM_SUBSTITUTOR is set, use that function instead.
177
+ If PATH_PARAM_SUBSTITUTOR is set, use that function instead.
174
178
  """
175
179
  function = None
176
- func_path = getattr(drf_to_mkdoc_settings, "DRF_TO_MKDOC_PATH_PARAM_SUBSTITUTOR", None)
180
+ func_path = getattr(drf_to_mkdoc_settings, "PATH_PARAM_SUBSTITUTOR", None)
177
181
 
178
182
  if func_path:
179
183
  try:
180
184
  function = import_string(func_path)
181
185
  except ImportError:
182
- logger.warning("DRF_TO_MKDOC_PATH_PARAM_SUBSTITUTOR is not a valid import path")
186
+ logger.warning("PATH_PARAM_SUBSTITUTOR is not a valid import path")
183
187
 
184
188
  # If custom function exists and returns a valid value, use it
185
189
  if callable(function):
@@ -193,25 +197,21 @@ def convert_to_django_path(path: str, parameters: list[dict[str, Any]]) -> str:
193
197
  # Default Django path conversion
194
198
  def replacement(match):
195
199
  param_name = match.group(1)
196
- param_info = next((p for p in parameters if p.get('name') == param_name), {})
197
- param_type = param_info.get('schema', {}).get('type')
198
- param_format = param_info.get('schema', {}).get('format')
199
-
200
- if param_type == 'integer':
201
- converter = 'int'
202
- elif param_type == 'string' and param_format == 'uuid':
203
- converter = 'uuid'
200
+ param_info = next((p for p in parameters if p.get("name") == param_name), {})
201
+ param_type = param_info.get("schema", {}).get("type")
202
+ param_format = param_info.get("schema", {}).get("format")
203
+
204
+ if param_type == "integer":
205
+ converter = "int"
206
+ elif param_type == "string" and param_format == "uuid":
207
+ converter = "uuid"
204
208
  else:
205
- converter = 'str'
209
+ converter = "str"
206
210
 
207
- return f'<{converter}:{param_name}>'
211
+ return f"<{converter}:{param_name}>"
208
212
 
209
- django_path = re.sub(r'{(\w+)}', replacement, path)
213
+ return re.sub(r"{(\w+)}", replacement, path)
210
214
 
211
- if not django_path.endswith('/'):
212
- django_path += '/'
213
-
214
- return django_path
215
215
 
216
216
  @lru_cache
217
217
  def get_schema():
@@ -276,8 +276,8 @@ def extract_viewset_from_operation_id(operation_id: str):
276
276
  if not path:
277
277
  raise ValueError(f"Path not found for operation ID: {operation_id}")
278
278
 
279
+ resolved_path = substitute_path_params(path, parameters)
279
280
  try:
280
- resolved_path = substitute_path_params(path, parameters)
281
281
  match = resolve(resolved_path)
282
282
  view_func = match.func
283
283
  if hasattr(view_func, "view_class"):
@@ -291,8 +291,10 @@ def extract_viewset_from_operation_id(operation_id: str):
291
291
  else:
292
292
  return view_func
293
293
 
294
- except Exception as e:
295
- raise RuntimeError(f"Failed to resolve path {django_path}: {e}") from e
294
+ except Exception:
295
+ logger.error(
296
+ f"Failed to resolve path.\nschema_path{path}\ntried_path={resolved_path}\n---"
297
+ )
296
298
 
297
299
 
298
300
  def extract_viewset_name_from_operation_id(operation_id: str):