drf-to-mkdoc 0.1.9__py3-none-any.whl → 0.2.1__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.
- drf_to_mkdoc/conf/defaults.py +5 -0
- drf_to_mkdoc/conf/settings.py +123 -9
- drf_to_mkdoc/management/commands/build_docs.py +8 -7
- drf_to_mkdoc/management/commands/build_endpoint_docs.py +69 -0
- drf_to_mkdoc/management/commands/build_model_docs.py +50 -0
- drf_to_mkdoc/management/commands/{generate_model_docs.py → extract_model_data.py} +18 -24
- drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out-sidebar.js +879 -0
- drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/try-out-sidebar.css +728 -0
- drf_to_mkdoc/utils/ai_tools/__init__.py +0 -0
- drf_to_mkdoc/utils/ai_tools/enums.py +13 -0
- drf_to_mkdoc/utils/ai_tools/exceptions.py +19 -0
- drf_to_mkdoc/utils/ai_tools/providers/__init__.py +0 -0
- drf_to_mkdoc/utils/ai_tools/providers/base_provider.py +123 -0
- drf_to_mkdoc/utils/ai_tools/providers/gemini_provider.py +80 -0
- drf_to_mkdoc/utils/ai_tools/types.py +81 -0
- drf_to_mkdoc/utils/commons/__init__.py +0 -0
- drf_to_mkdoc/utils/commons/code_extractor.py +22 -0
- drf_to_mkdoc/utils/commons/file_utils.py +35 -0
- drf_to_mkdoc/utils/commons/model_utils.py +83 -0
- drf_to_mkdoc/utils/commons/operation_utils.py +83 -0
- drf_to_mkdoc/utils/commons/path_utils.py +78 -0
- drf_to_mkdoc/utils/commons/schema_utils.py +230 -0
- drf_to_mkdoc/utils/endpoint_detail_generator.py +16 -35
- drf_to_mkdoc/utils/endpoint_list_generator.py +1 -1
- drf_to_mkdoc/utils/extractors/query_parameter_extractors.py +33 -30
- drf_to_mkdoc/utils/model_detail_generator.py +44 -40
- drf_to_mkdoc/utils/model_list_generator.py +25 -15
- drf_to_mkdoc/utils/schema.py +259 -0
- {drf_to_mkdoc-0.1.9.dist-info → drf_to_mkdoc-0.2.1.dist-info}/METADATA +16 -5
- {drf_to_mkdoc-0.1.9.dist-info → drf_to_mkdoc-0.2.1.dist-info}/RECORD +33 -16
- drf_to_mkdoc/management/commands/generate_docs.py +0 -138
- drf_to_mkdoc/utils/common.py +0 -353
- {drf_to_mkdoc-0.1.9.dist-info → drf_to_mkdoc-0.2.1.dist-info}/WHEEL +0 -0
- {drf_to_mkdoc-0.1.9.dist-info → drf_to_mkdoc-0.2.1.dist-info}/licenses/LICENSE +0 -0
- {drf_to_mkdoc-0.1.9.dist-info → drf_to_mkdoc-0.2.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import logging
|
|
3
|
+
from importlib import import_module
|
|
4
|
+
from types import SimpleNamespace
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from drf_spectacular.openapi import AutoSchema as SpectacularAutoSchema
|
|
8
|
+
from drf_spectacular.plumbing import ComponentRegistry
|
|
9
|
+
from rest_framework.serializers import BaseSerializer, ListSerializer
|
|
10
|
+
from rest_framework.viewsets import ViewSetMixin
|
|
11
|
+
|
|
12
|
+
from drf_to_mkdoc.conf.settings import drf_to_mkdoc_settings
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ViewMetadataExtractor:
|
|
18
|
+
"""Extracts metadata from DRF views."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, view, callback, method):
|
|
21
|
+
self.view = view
|
|
22
|
+
self.callback = callback
|
|
23
|
+
self.method = method
|
|
24
|
+
self.view_instance = None
|
|
25
|
+
self.action = None
|
|
26
|
+
self.error_message = None
|
|
27
|
+
|
|
28
|
+
def _create_view_instance(self):
|
|
29
|
+
"""Create view instance for introspection."""
|
|
30
|
+
try:
|
|
31
|
+
self.view_instance = self.view()
|
|
32
|
+
except (TypeError, AttributeError, ImportError) as e:
|
|
33
|
+
self.error_message = str(e)
|
|
34
|
+
return False
|
|
35
|
+
else:
|
|
36
|
+
return True
|
|
37
|
+
|
|
38
|
+
def _extract_permissions(self):
|
|
39
|
+
"""Extract permission classes from view."""
|
|
40
|
+
permission_classes = []
|
|
41
|
+
if hasattr(self.view, "permission_classes"):
|
|
42
|
+
for perm_class in self.view.permission_classes:
|
|
43
|
+
permission_classes.append(f"{perm_class.__module__}.{perm_class.__name__}")
|
|
44
|
+
return permission_classes
|
|
45
|
+
|
|
46
|
+
def _extract_action(self):
|
|
47
|
+
"""Extract action name from ViewSet."""
|
|
48
|
+
if isinstance(self.view_instance, ViewSetMixin):
|
|
49
|
+
self.action = self.callback.actions.get(self.method.lower())
|
|
50
|
+
if self.action:
|
|
51
|
+
self.view_instance.action = self.action
|
|
52
|
+
self.view_instance.request = SimpleNamespace(method=self.method.upper())
|
|
53
|
+
|
|
54
|
+
def _extract_serializer_from_view_instance(self):
|
|
55
|
+
"""Try to get serializer class from view instance."""
|
|
56
|
+
if not hasattr(self.view_instance, "get_serializer_class"):
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
serializer_cls = self.view_instance.get_serializer_class()
|
|
61
|
+
except (AttributeError, TypeError, ImportError) as e:
|
|
62
|
+
logger.debug(f"Failed to get serializer from view instance: {e}")
|
|
63
|
+
return None
|
|
64
|
+
else:
|
|
65
|
+
return f"{serializer_cls.__module__}.{serializer_cls.__name__}"
|
|
66
|
+
|
|
67
|
+
def _extract_serializer_from_action(self):
|
|
68
|
+
"""Try to get serializer class from action method."""
|
|
69
|
+
if not self.action:
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
action_method = getattr(self.view, self.action, None)
|
|
73
|
+
if not (action_method and callable(action_method)):
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
if hasattr(action_method, "serializer_class"):
|
|
77
|
+
serializer_cls = action_method.serializer_class
|
|
78
|
+
return f"{serializer_cls.__module__}.{serializer_cls.__name__}"
|
|
79
|
+
if hasattr(action_method, "kwargs") and "serializer_class" in action_method.kwargs:
|
|
80
|
+
serializer_cls = action_method.kwargs["serializer_class"]
|
|
81
|
+
return f"{serializer_cls.__module__}.{serializer_cls.__name__}"
|
|
82
|
+
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
def _extract_serializer_from_class(self):
|
|
86
|
+
"""Try to get serializer class from view class."""
|
|
87
|
+
if hasattr(self.view, "serializer_class") and self.view.serializer_class:
|
|
88
|
+
serializer_cls = self.view.serializer_class
|
|
89
|
+
return f"{serializer_cls.__module__}.{serializer_cls.__name__}"
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
def _extract_action_source(self):
|
|
93
|
+
"""Get action source info if no serializer found."""
|
|
94
|
+
if not self.action:
|
|
95
|
+
return {}
|
|
96
|
+
|
|
97
|
+
action_method = getattr(self.view, self.action, None)
|
|
98
|
+
if not (action_method and callable(action_method)):
|
|
99
|
+
return {}
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
"importable_path": f"{self.view.__module__}.{self.view.__name__}.{self.action}",
|
|
103
|
+
"module": self.view.__module__,
|
|
104
|
+
"class_name": self.view.__name__,
|
|
105
|
+
"method_name": self.action,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
def _extract_serializer_parents(self, serializer_cls):
|
|
109
|
+
"""Extract parent classes of serializer up to specified depth."""
|
|
110
|
+
if not serializer_cls:
|
|
111
|
+
return []
|
|
112
|
+
|
|
113
|
+
parents = []
|
|
114
|
+
for base in inspect.getmro(serializer_cls)[
|
|
115
|
+
1 : drf_to_mkdoc_settings.SERIALIZERS_INHERITANCE_DEPTH + 1
|
|
116
|
+
]:
|
|
117
|
+
if base is object or base.__module__ == "builtins":
|
|
118
|
+
continue
|
|
119
|
+
parents.append(f"{base.__module__}.{base.__name__}")
|
|
120
|
+
|
|
121
|
+
return parents
|
|
122
|
+
|
|
123
|
+
def _extract_serializer_attrs(self, serializer_cls):
|
|
124
|
+
"""Extract nested serializer attributes up to specified depth."""
|
|
125
|
+
if not serializer_cls:
|
|
126
|
+
return {}
|
|
127
|
+
|
|
128
|
+
attrs = {}
|
|
129
|
+
try:
|
|
130
|
+
serializer_instance = serializer_cls()
|
|
131
|
+
for field_name, field in getattr(serializer_instance, "fields", {}).items():
|
|
132
|
+
if isinstance(field, ListSerializer) and isinstance(
|
|
133
|
+
field.child, BaseSerializer
|
|
134
|
+
):
|
|
135
|
+
# Handle ListSerializer and similar fields
|
|
136
|
+
child_class = field.child.__class__
|
|
137
|
+
attrs[field_name] = {
|
|
138
|
+
"type": "list",
|
|
139
|
+
"child_serializer": f"{child_class.__module__}.{child_class.__name__}",
|
|
140
|
+
}
|
|
141
|
+
elif isinstance(field, BaseSerializer):
|
|
142
|
+
# Handle nested serializers
|
|
143
|
+
nested_class = field.__class__
|
|
144
|
+
attrs[field_name] = {
|
|
145
|
+
"type": "nested",
|
|
146
|
+
"serializer": f"{nested_class.__module__}.{nested_class.__name__}",
|
|
147
|
+
}
|
|
148
|
+
except (TypeError, AttributeError) as e:
|
|
149
|
+
logger.debug(f"Failed to extract serializer attributes: {e}")
|
|
150
|
+
|
|
151
|
+
return attrs
|
|
152
|
+
|
|
153
|
+
def extract(self):
|
|
154
|
+
"""Extract all metadata from view."""
|
|
155
|
+
if not self._create_view_instance():
|
|
156
|
+
return {
|
|
157
|
+
"view_class": f"{self.view.__module__}.{self.view.__name__}",
|
|
158
|
+
"action": None,
|
|
159
|
+
"serializer_class": None,
|
|
160
|
+
"permission_classes": [],
|
|
161
|
+
"error_message": str(self.error_message),
|
|
162
|
+
"action_source": {},
|
|
163
|
+
"serializer_parents": [],
|
|
164
|
+
"serializer_attrs": {},
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
permission_classes = self._extract_permissions()
|
|
168
|
+
self._extract_action()
|
|
169
|
+
|
|
170
|
+
serializer_class = None
|
|
171
|
+
serializer_class_str = (
|
|
172
|
+
self._extract_serializer_from_view_instance()
|
|
173
|
+
or self._extract_serializer_from_action()
|
|
174
|
+
or self._extract_serializer_from_class()
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
if serializer_class_str:
|
|
178
|
+
module_name, class_name = serializer_class_str.rsplit(".", 1)
|
|
179
|
+
try:
|
|
180
|
+
module = import_module(module_name)
|
|
181
|
+
serializer_class = getattr(module, class_name)
|
|
182
|
+
except (ImportError, AttributeError) as e:
|
|
183
|
+
logger.debug(f"Failed to import serializer class: {e}")
|
|
184
|
+
|
|
185
|
+
action_source = {} if serializer_class_str else self._extract_action_source()
|
|
186
|
+
serializer_parents = (
|
|
187
|
+
self._extract_serializer_parents(serializer_class) if serializer_class else []
|
|
188
|
+
)
|
|
189
|
+
serializer_attrs = (
|
|
190
|
+
self._extract_serializer_attrs(serializer_class) if serializer_class else {}
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
"view_class": f"{self.view.__module__}.{self.view.__name__}",
|
|
195
|
+
"action": self.action,
|
|
196
|
+
"serializer_class": serializer_class_str,
|
|
197
|
+
"permission_classes": permission_classes,
|
|
198
|
+
"error_message": str(self.error_message) if self.error_message else None,
|
|
199
|
+
"action_source": action_source,
|
|
200
|
+
"serializer_parents": serializer_parents,
|
|
201
|
+
"serializer_attrs": serializer_attrs,
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
class AutoSchema(SpectacularAutoSchema):
|
|
206
|
+
"""
|
|
207
|
+
Custom AutoSchema that extends drf_spectacular's AutoSchema to add view metadata
|
|
208
|
+
directly to the operation during schema generation instead of using a postprocessing hook.
|
|
209
|
+
"""
|
|
210
|
+
|
|
211
|
+
def get_operation(
|
|
212
|
+
self,
|
|
213
|
+
path: str,
|
|
214
|
+
path_regex: str,
|
|
215
|
+
path_prefix: str,
|
|
216
|
+
method: str,
|
|
217
|
+
registry: ComponentRegistry,
|
|
218
|
+
) -> dict[str, Any] | None:
|
|
219
|
+
# Call the parent's get_operation to get the base operation
|
|
220
|
+
operation = super().get_operation(path, path_regex, path_prefix, method, registry)
|
|
221
|
+
|
|
222
|
+
if operation:
|
|
223
|
+
try:
|
|
224
|
+
# Extract metadata from the view
|
|
225
|
+
view = self.view.__class__
|
|
226
|
+
callback = self._get_callback_obj(method)
|
|
227
|
+
metadata = ViewMetadataExtractor(view, callback, method).extract()
|
|
228
|
+
|
|
229
|
+
# Add metadata to the operation
|
|
230
|
+
operation.setdefault("x-metadata", {})
|
|
231
|
+
operation["x-metadata"].update(metadata)
|
|
232
|
+
except Exception:
|
|
233
|
+
# Log the error but don't break schema generation
|
|
234
|
+
logger.exception("Error adding metadata to operation")
|
|
235
|
+
|
|
236
|
+
return operation
|
|
237
|
+
|
|
238
|
+
def _get_callback_obj(self, method: str):
|
|
239
|
+
"""
|
|
240
|
+
Helper method to get the callback object with actions.
|
|
241
|
+
This is needed to extract the action name from the callback.
|
|
242
|
+
"""
|
|
243
|
+
# Access the view's action_map or action to determine the mapping
|
|
244
|
+
actions = {}
|
|
245
|
+
|
|
246
|
+
# For ViewSets, the action_map contains the method->action mapping
|
|
247
|
+
if hasattr(self.view, "action_map") and self.view.action_map is not None:
|
|
248
|
+
actions = {m.lower(): a for m, a in self.view.action_map.items()}
|
|
249
|
+
# For APIViews with an explicit action
|
|
250
|
+
elif hasattr(self.view, "action"):
|
|
251
|
+
actions = {method.lower(): self.view.action}
|
|
252
|
+
|
|
253
|
+
# Create a callback-like object with the necessary attributes
|
|
254
|
+
class CallbackObj:
|
|
255
|
+
def __init__(self, view_cls, actions_dict):
|
|
256
|
+
self.cls = view_cls
|
|
257
|
+
self.actions = actions_dict
|
|
258
|
+
|
|
259
|
+
return CallbackObj(self.view.__class__, actions)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: drf-to-mkdoc
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Generate Markdown API docs from Django/DRF OpenAPI schema for MkDocs
|
|
5
5
|
Author-email: Hossein Shayesteh <shayestehhs1@gmail.com>
|
|
6
6
|
Maintainer-email: Hossein Shayesteh <shayestehhs1@gmail.com>
|
|
@@ -76,13 +76,14 @@ INSTALLED_APPS = [
|
|
|
76
76
|
|
|
77
77
|
# Required for OpenAPI schema generation
|
|
78
78
|
REST_FRAMEWORK = {
|
|
79
|
-
'DEFAULT_SCHEMA_CLASS': '
|
|
79
|
+
'DEFAULT_SCHEMA_CLASS': 'drf_to_mkdoc.utils.schema.AutoSchema', # Use our custom AutoSchema
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
SPECTACULAR_SETTINGS = {
|
|
83
83
|
'TITLE': 'Your API',
|
|
84
84
|
'DESCRIPTION': 'Your API description',
|
|
85
85
|
'VERSION': '1.0.0',
|
|
86
|
+
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
DRF_TO_MKDOC = {
|
|
@@ -110,10 +111,19 @@ DRF_TO_MKDOC = {
|
|
|
110
111
|
python manage.py build_docs --settings=docs_settings
|
|
111
112
|
```
|
|
112
113
|
|
|
114
|
+
## Available Commands
|
|
115
|
+
|
|
116
|
+
- `build_docs`: Build the complete documentation site with MkDocs
|
|
117
|
+
- `build_endpoint_docs`: Build endpoint documentation from OpenAPI schema
|
|
118
|
+
- `build_model_docs`: Build model documentation from model JSON data
|
|
119
|
+
- `extract_model_data`: Extract model data from Django model introspection and save as JSON
|
|
120
|
+
- `update_doc_schema`: Update the final schema by copying the documented schema
|
|
121
|
+
|
|
113
122
|
## What you get
|
|
114
123
|
|
|
115
124
|
See a detailed overview of generated files in `docs/structure.md` and a feature breakdown in `docs/features.md`.
|
|
116
125
|
|
|
126
|
+
|
|
117
127
|
## How it works
|
|
118
128
|
|
|
119
129
|
Under the hood, drf-to-mkdoc introspects your models and reads your DRF OpenAPI schema to generate clean, organized Markdown. Then MkDocs turns it into a polished static site. Always current, no manual updates.
|
|
@@ -154,8 +164,9 @@ drf-to-mkdoc/
|
|
|
154
164
|
│ ├── management/
|
|
155
165
|
│ │ └── commands/
|
|
156
166
|
│ │ ├── build_docs.py # Build MkDocs site
|
|
157
|
-
│ │ ├──
|
|
158
|
-
│ │ ├──
|
|
167
|
+
│ │ ├── build_endpoint_docs.py # Build endpoint documentation
|
|
168
|
+
│ │ ├── build_model_docs.py # Build model documentation
|
|
169
|
+
│ │ ├── extract_model_data.py # Extract model data from Django
|
|
159
170
|
│ │ └── update_doc_schema.py # Schema updates
|
|
160
171
|
│ └── utils/
|
|
161
172
|
│ ├── common.py # Shared utilities
|
|
@@ -222,4 +233,4 @@ your-project/
|
|
|
222
233
|
## Contributing
|
|
223
234
|
|
|
224
235
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed contribution guidelines.
|
|
225
|
-
This will ensure that only the source configuration and scripts are versioned, while the generated documentation is excluded.
|
|
236
|
+
This will ensure that only the source configuration and scripts are versioned, while the generated documentation is excluded.
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
drf_to_mkdoc/__init__.py,sha256=IbTW5uKQvJRG9ncHRuk_AGKHPn4ruxs5LqDpUFgUhws,180
|
|
2
2
|
drf_to_mkdoc/apps.py,sha256=-NrC_dRr6GmLmNQhkNh819B7V1SS4DYDv5JOR0TtuJM,560
|
|
3
3
|
drf_to_mkdoc/conf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
drf_to_mkdoc/conf/defaults.py,sha256=
|
|
5
|
-
drf_to_mkdoc/conf/settings.py,sha256=
|
|
4
|
+
drf_to_mkdoc/conf/defaults.py,sha256=iG5ssgaJgDoI92cyCDCHmuY1uvMCeU8T0D3gxrDPWYM,986
|
|
5
|
+
drf_to_mkdoc/conf/settings.py,sha256=BruquzEGvGzJ3YhXnWHn4gxxZA6Ed1Twtry8rokgdUI,5557
|
|
6
6
|
drf_to_mkdoc/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
7
|
drf_to_mkdoc/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
drf_to_mkdoc/management/commands/build_docs.py,sha256=
|
|
8
|
+
drf_to_mkdoc/management/commands/build_docs.py,sha256=k2N8i7sNWPJGVzUSdkDu47FleCSjIxClRq1dWBHwhjQ,4057
|
|
9
|
+
drf_to_mkdoc/management/commands/build_endpoint_docs.py,sha256=UcKoHFzmsEhs90kHMRvRe2XWx6xigrGAnwA5iEs839s,2450
|
|
10
|
+
drf_to_mkdoc/management/commands/build_model_docs.py,sha256=8d7UjwwIsEsReIH4b93nTqFnrZO8kPHXdQaSTYudUGw,1926
|
|
11
|
+
drf_to_mkdoc/management/commands/extract_model_data.py,sha256=XoMO4C22ZPKQ99bh1WskEUT1JkA3GpDN5wb3_D5cN0I,13583
|
|
9
12
|
drf_to_mkdoc/management/commands/generate_doc_json.py,sha256=mWdYgMbSeLP4iQZeUm2DxwYQmdGy8w05XTEfbT_nOJo,19833
|
|
10
|
-
drf_to_mkdoc/management/commands/generate_docs.py,sha256=LUvKeJQ_cDL48rEfbWyjDsYEwzkQEpxpEjqUm11zQJg,5011
|
|
11
|
-
drf_to_mkdoc/management/commands/generate_model_docs.py,sha256=A_Q6o10kfy-GN_ZDD9YS6jv3RTyxBy28DEsi5qKZZoU,13421
|
|
12
13
|
drf_to_mkdoc/management/commands/update_doc_schema.py,sha256=TtHVQxnVpB_VELRkVcdsDXDU5zXdguFleB1mXCDMAbg,2009
|
|
13
14
|
drf_to_mkdoc/static/drf-to-mkdoc/javascripts/endpoints-filter.js,sha256=KtfWroqsXg-wwmk36hpoH--M9WIW85EFNGeswMjFu4g,6138
|
|
15
|
+
drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out-sidebar.js,sha256=zvEIm5rxTdMdmzElmmAqniIrI4ms2vsqGOMI0VFb_RQ,38953
|
|
14
16
|
drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/accessibility.css,sha256=DwCGPoaxaUvyifryrKqmkFDH06XBNf65kYsflMTbi0M,494
|
|
15
17
|
drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/animations.css,sha256=61m9SLAbatVUNuFeTUTxktoMu9SskYcwFjTsHYbsCRo,393
|
|
16
18
|
drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/badges.css,sha256=kUlUcf72uRw6x6Gn7cUq_aTuSGBbhumZ4Us-eBDM7DM,1251
|
|
@@ -26,6 +28,7 @@ drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/sections.css,sha256=xdrO6
|
|
|
26
28
|
drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/stats.css,sha256=0cDD8s63r6zQid_O1schNvfIwys1Y526xO6-B6s4Lxc,667
|
|
27
29
|
drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/tags.css,sha256=dOw13qsvVjx9cibzgzXlOutXVosNp6LzFfEFKvumG8w,1785
|
|
28
30
|
drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/theme-toggle.css,sha256=j1P5xDQDfos8jeVYz5s1jjEeujMlZtLi39OC6VeuMcA,1034
|
|
31
|
+
drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/try-out-sidebar.css,sha256=tkfQRpX0HTe09dqdjA4GxG5lOCLpw3Mp7VuxkFQtCvI,15829
|
|
29
32
|
drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/variables.css,sha256=Sg2vcQOHdpmEFDn43OeZcMIKxtr5EOEI_wISkCmtcSU,1895
|
|
30
33
|
drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/animations.css,sha256=IrqN9vJKgaHAWk2PBRKKmFHgH-lQlw5YZvEOGDqYk_g,656
|
|
31
34
|
drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/base.css,sha256=qdXDVScWoEvFbSRfjDlnxvQZBy2JFX9yXPngMWNSZ7o,1849
|
|
@@ -34,17 +37,31 @@ drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/model-tables.css,sha256=8CSy
|
|
|
34
37
|
drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/responsive.css,sha256=ygqyUtpiWchTBBIQil1C6mN0AC5xinLoP7whSKfBmwg,944
|
|
35
38
|
drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/variables.css,sha256=2HvyjnJrygSzdzpE-FYpo6FGhrYhmZ7NwDFAkADXQNg,1094
|
|
36
39
|
drf_to_mkdoc/utils/__init__.py,sha256=6dFTb07S6yIf-INMy0Mlgf5purNir687ZU9WZtITh4k,68
|
|
37
|
-
drf_to_mkdoc/utils/
|
|
38
|
-
drf_to_mkdoc/utils/
|
|
39
|
-
drf_to_mkdoc/utils/
|
|
40
|
-
drf_to_mkdoc/utils/
|
|
41
|
-
drf_to_mkdoc/utils/
|
|
40
|
+
drf_to_mkdoc/utils/endpoint_detail_generator.py,sha256=1HvnOzqczZNqO8pbXedz6cSQ_pOFPDkmeD9yVxNOz7A,26989
|
|
41
|
+
drf_to_mkdoc/utils/endpoint_list_generator.py,sha256=Mtk93QNkSz_J1m2sQ5_43CmvZW9yEqDaueEx4OhW8XQ,9722
|
|
42
|
+
drf_to_mkdoc/utils/model_detail_generator.py,sha256=oS07F_sDfqnfF1vSs79skUR6A7Wios2SqrQ_PqqyFUM,8179
|
|
43
|
+
drf_to_mkdoc/utils/model_list_generator.py,sha256=xdZbZDz4Ujj7OI5k60xc6ZeFHQjQV8mALqiD65Odpqs,2578
|
|
44
|
+
drf_to_mkdoc/utils/schema.py,sha256=14Gs4OhKDlTlk6DRFjszWNA08U3WlExUrItFHwYIdB8,10101
|
|
45
|
+
drf_to_mkdoc/utils/ai_tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
46
|
+
drf_to_mkdoc/utils/ai_tools/enums.py,sha256=K39bJjHgXwNst2NL6z-5bztW3ZU5iCxg2b0KEodD6eM,238
|
|
47
|
+
drf_to_mkdoc/utils/ai_tools/exceptions.py,sha256=yFauOtuSRGRnQt41u2qCMvWEbN0eblgKwXuH-GX3Wbk,634
|
|
48
|
+
drf_to_mkdoc/utils/ai_tools/types.py,sha256=KZhE92sXbifEo0hx-DA4smKnIUgCmCQXvhUvYjX_3LY,2032
|
|
49
|
+
drf_to_mkdoc/utils/ai_tools/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
50
|
+
drf_to_mkdoc/utils/ai_tools/providers/base_provider.py,sha256=mSp5xeWkym4Fg2m0iIXaXGotSVSWWdfN6RfeI_8e2VY,4132
|
|
51
|
+
drf_to_mkdoc/utils/ai_tools/providers/gemini_provider.py,sha256=BJGwhy7rEsgV03catSmAq1Kx6kXAzCh_-5WoT3SCS94,2969
|
|
52
|
+
drf_to_mkdoc/utils/commons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
53
|
+
drf_to_mkdoc/utils/commons/code_extractor.py,sha256=a4SyfTCyh9KGB8-p9pi760E_l8ZoWA6yGhaBi6Zn6XA,665
|
|
54
|
+
drf_to_mkdoc/utils/commons/file_utils.py,sha256=pdjrNZ_oR664tIwKinhlHL3Oeo-pKwaI4vG_PQT-9R8,1182
|
|
55
|
+
drf_to_mkdoc/utils/commons/model_utils.py,sha256=IC2X-SchY448N_T6HA0iOkjuEkmRN92fnDHiaD95aR0,3033
|
|
56
|
+
drf_to_mkdoc/utils/commons/operation_utils.py,sha256=0nQqJYuXcmUeIgEg5mvRPGC9uwMKJFTpswV0L8UX35w,2691
|
|
57
|
+
drf_to_mkdoc/utils/commons/path_utils.py,sha256=Pi9g1xXDPsRzmn4kTeNSVtXG9v6n1h2ZphUgOCYAduw,2992
|
|
58
|
+
drf_to_mkdoc/utils/commons/schema_utils.py,sha256=0sw3zp0onsF3NVv4ImpHbXfypTA2yIIFMIId_D9NZfg,7837
|
|
42
59
|
drf_to_mkdoc/utils/extractors/__init__.py,sha256=BvC8gKOPVI9gU1Piw0jRhKQ2ER5s1moAxgq7ZYkJWNI,86
|
|
43
|
-
drf_to_mkdoc/utils/extractors/query_parameter_extractors.py,sha256=
|
|
60
|
+
drf_to_mkdoc/utils/extractors/query_parameter_extractors.py,sha256=5QY5_PGQ5XpXRL9ZLr0570ywPcygQ8ZxnlfnSCHhnF0,8540
|
|
44
61
|
drf_to_mkdoc/utils/md_generators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
45
62
|
drf_to_mkdoc/utils/md_generators/query_parameters_generators.py,sha256=N-XqZ_FUODSR5V4xM9oEA3aaIiNGNmNwpvrWbQTx6RI,2566
|
|
46
|
-
drf_to_mkdoc-0.1.
|
|
47
|
-
drf_to_mkdoc-0.1.
|
|
48
|
-
drf_to_mkdoc-0.1.
|
|
49
|
-
drf_to_mkdoc-0.1.
|
|
50
|
-
drf_to_mkdoc-0.1.
|
|
63
|
+
drf_to_mkdoc-0.2.1.dist-info/licenses/LICENSE,sha256=3n9_ckIREsH8ogCxWW6dFsw_WfGcluG2mHcgl9i_UU0,1068
|
|
64
|
+
drf_to_mkdoc-0.2.1.dist-info/METADATA,sha256=ceVXnbJZXWmzEXhEKKhhwb64I9Nwqlds-4exCLYcvEE,7563
|
|
65
|
+
drf_to_mkdoc-0.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
66
|
+
drf_to_mkdoc-0.2.1.dist-info/top_level.txt,sha256=ZzJecR6j_tvLZiubUBEgawHflozC4DQy9ooNf1yDJ3Q,13
|
|
67
|
+
drf_to_mkdoc-0.2.1.dist-info/RECORD,,
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
|
|
5
|
-
from django.core.management.base import BaseCommand
|
|
6
|
-
|
|
7
|
-
from drf_to_mkdoc.conf.settings import drf_to_mkdoc_settings
|
|
8
|
-
from drf_to_mkdoc.utils.common import get_schema, load_model_json_data
|
|
9
|
-
from drf_to_mkdoc.utils.endpoint_detail_generator import (
|
|
10
|
-
generate_endpoint_files,
|
|
11
|
-
parse_endpoints_from_schema,
|
|
12
|
-
)
|
|
13
|
-
from drf_to_mkdoc.utils.endpoint_list_generator import create_endpoints_index
|
|
14
|
-
from drf_to_mkdoc.utils.model_detail_generator import generate_model_docs
|
|
15
|
-
from drf_to_mkdoc.utils.model_list_generator import create_models_index
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class Command(BaseCommand):
|
|
19
|
-
help = "Generate complete API documentation (models + endpoints + navigation)"
|
|
20
|
-
|
|
21
|
-
def add_arguments(self, parser):
|
|
22
|
-
parser.add_argument(
|
|
23
|
-
"--endpoints-only",
|
|
24
|
-
action="store_true",
|
|
25
|
-
help="Generate only endpoint documentation",
|
|
26
|
-
)
|
|
27
|
-
parser.add_argument(
|
|
28
|
-
"--models-only",
|
|
29
|
-
action="store_true",
|
|
30
|
-
help="Generate only model documentation",
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
def handle(self, *args, **options):
|
|
34
|
-
self.stdout.write(self.style.SUCCESS("🚀 Starting documentation generation..."))
|
|
35
|
-
|
|
36
|
-
docs_dir = Path(drf_to_mkdoc_settings.DOCS_DIR)
|
|
37
|
-
docs_dir.mkdir(parents=True, exist_ok=True)
|
|
38
|
-
|
|
39
|
-
if options["models_only"]:
|
|
40
|
-
self._generate_models_only()
|
|
41
|
-
elif options["endpoints_only"]:
|
|
42
|
-
self._generate_endpoints_only()
|
|
43
|
-
else:
|
|
44
|
-
self._generate_all()
|
|
45
|
-
|
|
46
|
-
self.stdout.write(self.style.SUCCESS("✅ Documentation generation complete!"))
|
|
47
|
-
|
|
48
|
-
def _generate_models_only(self):
|
|
49
|
-
"""Generate only model documentation"""
|
|
50
|
-
self.stdout.write("📋 Generating model documentation...")
|
|
51
|
-
|
|
52
|
-
# Load model data
|
|
53
|
-
json_data = load_model_json_data()
|
|
54
|
-
models_data = json_data.get("models", {}) if json_data else {}
|
|
55
|
-
|
|
56
|
-
if not models_data:
|
|
57
|
-
self.stdout.write(self.style.WARNING("⚠️ No model data found"))
|
|
58
|
-
return
|
|
59
|
-
|
|
60
|
-
docs_dir = Path(drf_to_mkdoc_settings.DOCS_DIR)
|
|
61
|
-
|
|
62
|
-
# Generate model documentation
|
|
63
|
-
generate_model_docs(models_data, docs_dir)
|
|
64
|
-
create_models_index(models_data, docs_dir)
|
|
65
|
-
|
|
66
|
-
self.stdout.write(self.style.SUCCESS("✅ Model documentation generated"))
|
|
67
|
-
|
|
68
|
-
def _generate_endpoints_only(self):
|
|
69
|
-
"""Generate only endpoint documentation"""
|
|
70
|
-
self.stdout.write("🔗 Generating endpoint documentation...")
|
|
71
|
-
|
|
72
|
-
# Load schema
|
|
73
|
-
schema = get_schema()
|
|
74
|
-
if not schema:
|
|
75
|
-
self.stdout.write(self.style.ERROR("❌ Failed to load OpenAPI schema"))
|
|
76
|
-
return
|
|
77
|
-
|
|
78
|
-
paths = schema.get("paths", {})
|
|
79
|
-
components = schema.get("components", {})
|
|
80
|
-
|
|
81
|
-
self.stdout.write(f"📊 Loaded {len(paths)} API paths")
|
|
82
|
-
|
|
83
|
-
docs_dir = Path(drf_to_mkdoc_settings.DOCS_DIR)
|
|
84
|
-
|
|
85
|
-
# Parse and generate endpoints
|
|
86
|
-
endpoints_by_app = parse_endpoints_from_schema(paths)
|
|
87
|
-
total_endpoints = generate_endpoint_files(endpoints_by_app, components)
|
|
88
|
-
create_endpoints_index(endpoints_by_app, docs_dir)
|
|
89
|
-
|
|
90
|
-
self.stdout.write(
|
|
91
|
-
self.style.SUCCESS(
|
|
92
|
-
f"✅ Generated {total_endpoints} endpoint files with Django view introspection"
|
|
93
|
-
)
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
def _generate_all(self):
|
|
97
|
-
"""Generate complete documentation"""
|
|
98
|
-
self.stdout.write("📚 Generating complete documentation...")
|
|
99
|
-
|
|
100
|
-
docs_dir = Path(drf_to_mkdoc_settings.DOCS_DIR)
|
|
101
|
-
|
|
102
|
-
# Load data
|
|
103
|
-
json_data = load_model_json_data()
|
|
104
|
-
models_data = json_data.get("models", {}) if json_data else {}
|
|
105
|
-
schema = get_schema()
|
|
106
|
-
|
|
107
|
-
if not schema:
|
|
108
|
-
self.stdout.write(self.style.ERROR("❌ Failed to load OpenAPI schema"))
|
|
109
|
-
return
|
|
110
|
-
|
|
111
|
-
paths = schema.get("paths", {})
|
|
112
|
-
components = schema.get("components", {})
|
|
113
|
-
|
|
114
|
-
self.stdout.write(f"📊 Loaded {len(paths)} API paths")
|
|
115
|
-
|
|
116
|
-
# Generate model documentation
|
|
117
|
-
if models_data:
|
|
118
|
-
self.stdout.write("📋 Generating model documentation...")
|
|
119
|
-
try:
|
|
120
|
-
generate_model_docs(models_data)
|
|
121
|
-
create_models_index(models_data, docs_dir)
|
|
122
|
-
except Exception as e:
|
|
123
|
-
self.stdout.write(self.style.WARNING(f"⚠️ Failed to generate model docs: {e}"))
|
|
124
|
-
self.stdout.write(self.style.WARNING("Continuing with endpoint generation..."))
|
|
125
|
-
else:
|
|
126
|
-
self.stdout.write(self.style.WARNING("⚠️ No model data found"))
|
|
127
|
-
|
|
128
|
-
# Generate endpoint documentation
|
|
129
|
-
self.stdout.write("🔗 Generating endpoint documentation...")
|
|
130
|
-
endpoints_by_app = parse_endpoints_from_schema(paths)
|
|
131
|
-
total_endpoints = generate_endpoint_files(endpoints_by_app, components)
|
|
132
|
-
create_endpoints_index(endpoints_by_app, docs_dir)
|
|
133
|
-
|
|
134
|
-
self.stdout.write(
|
|
135
|
-
self.style.SUCCESS(
|
|
136
|
-
f"✅ Generated {total_endpoints} endpoint files with Django view introspection"
|
|
137
|
-
)
|
|
138
|
-
)
|