drf-to-mkdoc 0.2.0__py3-none-any.whl → 0.2.2__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 (55) hide show
  1. drf_to_mkdoc/conf/defaults.py +5 -0
  2. drf_to_mkdoc/conf/settings.py +121 -9
  3. drf_to_mkdoc/management/commands/build_docs.py +8 -7
  4. drf_to_mkdoc/management/commands/build_endpoint_docs.py +69 -0
  5. drf_to_mkdoc/management/commands/build_model_docs.py +50 -0
  6. drf_to_mkdoc/management/commands/{generate_model_docs.py → extract_model_data.py} +14 -19
  7. drf_to_mkdoc/templates/endpoints/detail/base.html +33 -0
  8. drf_to_mkdoc/templates/endpoints/detail/path_parameters.html +8 -0
  9. drf_to_mkdoc/templates/endpoints/detail/query_parameters.html +43 -0
  10. drf_to_mkdoc/templates/endpoints/detail/request_body.html +10 -0
  11. drf_to_mkdoc/templates/endpoints/detail/responses.html +18 -0
  12. drf_to_mkdoc/templates/endpoints/list/base.html +23 -0
  13. drf_to_mkdoc/templates/endpoints/list/endpoint_card.html +18 -0
  14. drf_to_mkdoc/templates/endpoints/list/filter_section.html +16 -0
  15. drf_to_mkdoc/templates/endpoints/list/filters/app.html +8 -0
  16. drf_to_mkdoc/templates/endpoints/list/filters/method.html +12 -0
  17. drf_to_mkdoc/templates/endpoints/list/filters/path.html +5 -0
  18. drf_to_mkdoc/templates/endpoints/list/filters/search.html +9 -0
  19. drf_to_mkdoc/templates/model_detail/base.html +34 -0
  20. drf_to_mkdoc/templates/model_detail/choices.html +12 -0
  21. drf_to_mkdoc/templates/model_detail/fields.html +11 -0
  22. drf_to_mkdoc/templates/model_detail/meta.html +6 -0
  23. drf_to_mkdoc/templates/model_detail/methods.html +9 -0
  24. drf_to_mkdoc/templates/model_detail/relationships.html +8 -0
  25. drf_to_mkdoc/templates/models_index.html +24 -0
  26. drf_to_mkdoc/templatetags/custom_filters.py +116 -0
  27. drf_to_mkdoc/utils/ai_tools/enums.py +13 -0
  28. drf_to_mkdoc/utils/ai_tools/exceptions.py +19 -0
  29. drf_to_mkdoc/utils/ai_tools/providers/__init__.py +0 -0
  30. drf_to_mkdoc/utils/ai_tools/providers/base_provider.py +123 -0
  31. drf_to_mkdoc/utils/ai_tools/providers/gemini_provider.py +80 -0
  32. drf_to_mkdoc/utils/ai_tools/types.py +81 -0
  33. drf_to_mkdoc/utils/commons/__init__.py +0 -0
  34. drf_to_mkdoc/utils/commons/code_extractor.py +22 -0
  35. drf_to_mkdoc/utils/commons/file_utils.py +35 -0
  36. drf_to_mkdoc/utils/commons/model_utils.py +83 -0
  37. drf_to_mkdoc/utils/commons/operation_utils.py +83 -0
  38. drf_to_mkdoc/utils/commons/path_utils.py +78 -0
  39. drf_to_mkdoc/utils/commons/schema_utils.py +230 -0
  40. drf_to_mkdoc/utils/endpoint_detail_generator.py +86 -202
  41. drf_to_mkdoc/utils/endpoint_list_generator.py +59 -194
  42. drf_to_mkdoc/utils/extractors/query_parameter_extractors.py +33 -30
  43. drf_to_mkdoc/utils/model_detail_generator.py +37 -211
  44. drf_to_mkdoc/utils/model_list_generator.py +38 -46
  45. drf_to_mkdoc/utils/schema.py +259 -0
  46. {drf_to_mkdoc-0.2.0.dist-info → drf_to_mkdoc-0.2.2.dist-info}/METADATA +16 -5
  47. drf_to_mkdoc-0.2.2.dist-info/RECORD +85 -0
  48. drf_to_mkdoc/management/commands/generate_docs.py +0 -113
  49. drf_to_mkdoc/utils/common.py +0 -353
  50. drf_to_mkdoc/utils/md_generators/query_parameters_generators.py +0 -72
  51. drf_to_mkdoc-0.2.0.dist-info/RECORD +0 -52
  52. /drf_to_mkdoc/utils/{md_generators → ai_tools}/__init__.py +0 -0
  53. {drf_to_mkdoc-0.2.0.dist-info → drf_to_mkdoc-0.2.2.dist-info}/WHEEL +0 -0
  54. {drf_to_mkdoc-0.2.0.dist-info → drf_to_mkdoc-0.2.2.dist-info}/licenses/LICENSE +0 -0
  55. {drf_to_mkdoc-0.2.0.dist-info → drf_to_mkdoc-0.2.2.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 Exception 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.2.0
3
+ Version: 0.2.2
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': 'drf_spectacular.openapi.AutoSchema',
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
- │ │ ├── generate_docs.py # Main documentation generator
158
- │ │ ├── generate_model_docs.py # Model documentation
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.
@@ -0,0 +1,85 @@
1
+ drf_to_mkdoc/__init__.py,sha256=IbTW5uKQvJRG9ncHRuk_AGKHPn4ruxs5LqDpUFgUhws,180
2
+ drf_to_mkdoc/apps.py,sha256=-NrC_dRr6GmLmNQhkNh819B7V1SS4DYDv5JOR0TtuJM,560
3
+ drf_to_mkdoc/conf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ drf_to_mkdoc/conf/defaults.py,sha256=iG5ssgaJgDoI92cyCDCHmuY1uvMCeU8T0D3gxrDPWYM,986
5
+ drf_to_mkdoc/conf/settings.py,sha256=3f268CZzyf9KYab6EvsjIVXP6KBik1k_B4JyJpXDwrU,5456
6
+ drf_to_mkdoc/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ drf_to_mkdoc/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
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
12
+ drf_to_mkdoc/management/commands/generate_doc_json.py,sha256=mWdYgMbSeLP4iQZeUm2DxwYQmdGy8w05XTEfbT_nOJo,19833
13
+ drf_to_mkdoc/management/commands/update_doc_schema.py,sha256=TtHVQxnVpB_VELRkVcdsDXDU5zXdguFleB1mXCDMAbg,2009
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
16
+ drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/accessibility.css,sha256=DwCGPoaxaUvyifryrKqmkFDH06XBNf65kYsflMTbi0M,494
17
+ drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/animations.css,sha256=61m9SLAbatVUNuFeTUTxktoMu9SskYcwFjTsHYbsCRo,393
18
+ drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/badges.css,sha256=kUlUcf72uRw6x6Gn7cUq_aTuSGBbhumZ4Us-eBDM7DM,1251
19
+ drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/base.css,sha256=K9lEjO-TEaUsFCaiuBwqDGrrjIJP8oBDh7igBGRTD3g,1839
20
+ drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/endpoint-content.css,sha256=1EKHfDeOSpNJe6l3mHzsBlLqLVCJn_1TKtEVfV7Dp88,3452
21
+ drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/endpoints-grid.css,sha256=Ct4H_ma3tC_1ogw9IeSNmUDZFJwzQWjCM6GomNXV8ig,4115
22
+ drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/filter-section.css,sha256=MDsGVwgFMhP28pegPLJFn3_GJqVSWZAoy6quz5_9Gz0,4424
23
+ drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/fixes.css,sha256=MIPiwWXkSMt5I_q6jN6X5CvWTBmq1CERKZffhvf3sKM,720
24
+ drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/layout.css,sha256=dyoTUJ5Y8njxdWE6t2G3nRS-bT0BT04UMat8K6yW2y4,637
25
+ drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/loading.css,sha256=C_wZjMw5Cs2inpg892S0_E7_m5bT8BwZfVBRQfvIcKw,712
26
+ drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/responsive.css,sha256=mzR4tB3XrCg6Ih-X5GEzXzZoSR0R0AEKArRIZ7LqiGU,1672
27
+ drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/sections.css,sha256=xdrO6vUpthFFN1ESummoGuG5MPtE2d2lPsBOWuv-T6o,705
28
+ drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/stats.css,sha256=0cDD8s63r6zQid_O1schNvfIwys1Y526xO6-B6s4Lxc,667
29
+ drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/tags.css,sha256=dOw13qsvVjx9cibzgzXlOutXVosNp6LzFfEFKvumG8w,1785
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
32
+ drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/variables.css,sha256=Sg2vcQOHdpmEFDn43OeZcMIKxtr5EOEI_wISkCmtcSU,1895
33
+ drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/animations.css,sha256=IrqN9vJKgaHAWk2PBRKKmFHgH-lQlw5YZvEOGDqYk_g,656
34
+ drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/base.css,sha256=qdXDVScWoEvFbSRfjDlnxvQZBy2JFX9yXPngMWNSZ7o,1849
35
+ drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/model-cards.css,sha256=IppCOptBoFeQCz2sc-exPrnxvsdDxcwYGM-foQrZoVU,2779
36
+ drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/model-tables.css,sha256=8CSy8YdFOJ3lGZ3sTVz2jd8cIxIryubQFrwcygAw06w,1352
37
+ drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/responsive.css,sha256=ygqyUtpiWchTBBIQil1C6mN0AC5xinLoP7whSKfBmwg,944
38
+ drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/variables.css,sha256=2HvyjnJrygSzdzpE-FYpo6FGhrYhmZ7NwDFAkADXQNg,1094
39
+ drf_to_mkdoc/templates/models_index.html,sha256=A_Lu3wFMG2Nf8Bv4zttz3e0gZpxBcvDjSvX_rfOYdeI,837
40
+ drf_to_mkdoc/templates/endpoints/detail/base.html,sha256=7aAjSjt6mgOMuvDlC2MQKg70i5fcK9Pi_SzTNoRUNm8,822
41
+ drf_to_mkdoc/templates/endpoints/detail/path_parameters.html,sha256=M7OPUg88e3bw01rZpZUVahZzlq3JFaxZDsnDx2Xizx8,310
42
+ drf_to_mkdoc/templates/endpoints/detail/query_parameters.html,sha256=Dx_mM2qjwFcfMWTYoEoNyPS588V_wanrHS7qh-uUGUg,1075
43
+ drf_to_mkdoc/templates/endpoints/detail/request_body.html,sha256=-9wacJIQOS-lC2zE31rPaeGs_SkOVm1tAJvebLW6zME,149
44
+ drf_to_mkdoc/templates/endpoints/detail/responses.html,sha256=jDTkiCUAe8vbRT6fF2Igtr-26z7maeUYNYv4q-n9-9w,293
45
+ drf_to_mkdoc/templates/endpoints/list/base.html,sha256=l4A4VmDvDkMKim15feh0DoJkCf2V83EoI4wISUjf4Kc,602
46
+ drf_to_mkdoc/templates/endpoints/list/endpoint_card.html,sha256=PesI9Dlt2kAU1AKcMNGUzIA3lzGcncX-Na0HYiwbR28,1114
47
+ drf_to_mkdoc/templates/endpoints/list/filter_section.html,sha256=oSc7UQazpQptYE_rZz_PHnOQPpQDOff0r4a0sYbtQnk,559
48
+ drf_to_mkdoc/templates/endpoints/list/filters/app.html,sha256=vjY0GGg1UGKKb2JAIaw-kCWkoWiA7rQzvifa7pObPXU,225
49
+ drf_to_mkdoc/templates/endpoints/list/filters/method.html,sha256=B9WHuuScIF45QRGv1wZVocCKMPUvyuEDg_gMaE02NXc,410
50
+ drf_to_mkdoc/templates/endpoints/list/filters/path.html,sha256=ZKq50BJu9AYdjiY2TXxhM8c8ivz6M8J_aZIC_q2gVvE,180
51
+ drf_to_mkdoc/templates/endpoints/list/filters/search.html,sha256=NbndD69M-iwFf-BXMo7rsHh1lrZnf6hiZGkPtGF1hU4,280
52
+ drf_to_mkdoc/templates/model_detail/base.html,sha256=uJaGnFwfv68uJgWJA85Svs7MXPQ-dXi3-SJgR9GfiNM,731
53
+ drf_to_mkdoc/templates/model_detail/choices.html,sha256=-DAbUpr5jXL_e8r--KkdRXGZuF94_TuVmN17WQDltk0,304
54
+ drf_to_mkdoc/templates/model_detail/fields.html,sha256=dOYhfwFJThAuG65r8i6_A9budCTNsZ1pQRCGgAKdFAc,1163
55
+ drf_to_mkdoc/templates/model_detail/meta.html,sha256=idbnQhV1dT_zLQDD3jZ21vXqLjCxLXIk4rdCiWHm04s,109
56
+ drf_to_mkdoc/templates/model_detail/methods.html,sha256=QZzp8sGKxuyM6_6GXDNnpKUJw0n_cF5CljklduyALTo,143
57
+ drf_to_mkdoc/templates/model_detail/relationships.html,sha256=GK7mip_-_4qxxM7MGmV3HXqiV6X9QkeBtz7oy4xZs4Q,796
58
+ drf_to_mkdoc/templatetags/custom_filters.py,sha256=2QoestQNjNvA6WJpgnXQ0avJQPzKs3d9Mea0QQLwcD0,2974
59
+ drf_to_mkdoc/utils/__init__.py,sha256=6dFTb07S6yIf-INMy0Mlgf5purNir687ZU9WZtITh4k,68
60
+ drf_to_mkdoc/utils/endpoint_detail_generator.py,sha256=aVLYRUHW6dW8sVwSdM-iyyiJ3dXPjXimbUx1i37vVHs,24160
61
+ drf_to_mkdoc/utils/endpoint_list_generator.py,sha256=yVX7qVUTuKF3TPFgm6FI-vVsVZQ7XHMXmZZalgSN8pk,3579
62
+ drf_to_mkdoc/utils/model_detail_generator.py,sha256=_ac2PYSzSwRUgUn-J-nmg7mfQGn8SU_4vkMpMRIxgEU,2619
63
+ drf_to_mkdoc/utils/model_list_generator.py,sha256=Ki-CwIYKmUbPm3_jUPLPosPfppyVLrZMkqbuPPO3Ycw,1988
64
+ drf_to_mkdoc/utils/schema.py,sha256=sDhVITdIFp1TJYnqCd4wF69i-lwXGlTsNJ2-ngXkccc,10070
65
+ drf_to_mkdoc/utils/ai_tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
66
+ drf_to_mkdoc/utils/ai_tools/enums.py,sha256=K39bJjHgXwNst2NL6z-5bztW3ZU5iCxg2b0KEodD6eM,238
67
+ drf_to_mkdoc/utils/ai_tools/exceptions.py,sha256=yFauOtuSRGRnQt41u2qCMvWEbN0eblgKwXuH-GX3Wbk,634
68
+ drf_to_mkdoc/utils/ai_tools/types.py,sha256=KZhE92sXbifEo0hx-DA4smKnIUgCmCQXvhUvYjX_3LY,2032
69
+ drf_to_mkdoc/utils/ai_tools/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
+ drf_to_mkdoc/utils/ai_tools/providers/base_provider.py,sha256=mSp5xeWkym4Fg2m0iIXaXGotSVSWWdfN6RfeI_8e2VY,4132
71
+ drf_to_mkdoc/utils/ai_tools/providers/gemini_provider.py,sha256=BJGwhy7rEsgV03catSmAq1Kx6kXAzCh_-5WoT3SCS94,2969
72
+ drf_to_mkdoc/utils/commons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
+ drf_to_mkdoc/utils/commons/code_extractor.py,sha256=a4SyfTCyh9KGB8-p9pi760E_l8ZoWA6yGhaBi6Zn6XA,665
74
+ drf_to_mkdoc/utils/commons/file_utils.py,sha256=pdjrNZ_oR664tIwKinhlHL3Oeo-pKwaI4vG_PQT-9R8,1182
75
+ drf_to_mkdoc/utils/commons/model_utils.py,sha256=IC2X-SchY448N_T6HA0iOkjuEkmRN92fnDHiaD95aR0,3033
76
+ drf_to_mkdoc/utils/commons/operation_utils.py,sha256=0nQqJYuXcmUeIgEg5mvRPGC9uwMKJFTpswV0L8UX35w,2691
77
+ drf_to_mkdoc/utils/commons/path_utils.py,sha256=Pi9g1xXDPsRzmn4kTeNSVtXG9v6n1h2ZphUgOCYAduw,2992
78
+ drf_to_mkdoc/utils/commons/schema_utils.py,sha256=0sw3zp0onsF3NVv4ImpHbXfypTA2yIIFMIId_D9NZfg,7837
79
+ drf_to_mkdoc/utils/extractors/__init__.py,sha256=BvC8gKOPVI9gU1Piw0jRhKQ2ER5s1moAxgq7ZYkJWNI,86
80
+ drf_to_mkdoc/utils/extractors/query_parameter_extractors.py,sha256=5QY5_PGQ5XpXRL9ZLr0570ywPcygQ8ZxnlfnSCHhnF0,8540
81
+ drf_to_mkdoc-0.2.2.dist-info/licenses/LICENSE,sha256=3n9_ckIREsH8ogCxWW6dFsw_WfGcluG2mHcgl9i_UU0,1068
82
+ drf_to_mkdoc-0.2.2.dist-info/METADATA,sha256=GQZJJrWXIfIDC_oSm_Y4DqvfwWBamJs1AIK_LzbflX0,7563
83
+ drf_to_mkdoc-0.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
84
+ drf_to_mkdoc-0.2.2.dist-info/top_level.txt,sha256=ZzJecR6j_tvLZiubUBEgawHflozC4DQy9ooNf1yDJ3Q,13
85
+ drf_to_mkdoc-0.2.2.dist-info/RECORD,,
@@ -1,113 +0,0 @@
1
- from pathlib import Path
2
-
3
- from django.core.management.base import BaseCommand
4
-
5
- from drf_to_mkdoc.conf.settings import drf_to_mkdoc_settings
6
- from drf_to_mkdoc.utils.common import get_schema, load_model_json_data
7
- from drf_to_mkdoc.utils.endpoint_detail_generator import (
8
- generate_endpoint_files,
9
- parse_endpoints_from_schema,
10
- )
11
- from drf_to_mkdoc.utils.endpoint_list_generator import create_endpoints_index
12
- from drf_to_mkdoc.utils.model_detail_generator import generate_model_docs
13
- from drf_to_mkdoc.utils.model_list_generator import create_models_index
14
-
15
-
16
- class Command(BaseCommand):
17
- help = "Generate complete API documentation (models + endpoints + navigation)"
18
-
19
- def add_arguments(self, parser):
20
- parser.add_argument(
21
- "--endpoints-only",
22
- action="store_true",
23
- help="Generate only endpoint documentation",
24
- )
25
- parser.add_argument(
26
- "--models-only",
27
- action="store_true",
28
- help="Generate only model documentation",
29
- )
30
-
31
- def handle(self, *args, **options):
32
- self.stdout.write(self.style.SUCCESS("🚀 Starting documentation generation..."))
33
-
34
- generate_models = not options["endpoints_only"]
35
- generate_endpoints = not options["models_only"]
36
- if not generate_models and not generate_endpoints:
37
- self.stdout.write(
38
- self.style.ERROR(
39
- "❌ No outputs selected: --models-only and --endpoints-only cannot be used together"
40
- )
41
- )
42
- return
43
- docs_dir = self._setup_docs_directory()
44
- models_data = self._load_models_data() if generate_models else {}
45
- schema_data = self._load_schema_data() if generate_endpoints else {}
46
-
47
- if generate_models and models_data:
48
- self._generate_models_documentation(models_data, docs_dir)
49
-
50
- if generate_endpoints and schema_data:
51
- self._generate_endpoints_documentation(schema_data, docs_dir)
52
-
53
- self.stdout.write(self.style.SUCCESS("✅ Documentation generation complete!"))
54
-
55
- def _setup_docs_directory(self):
56
- docs_dir = Path(drf_to_mkdoc_settings.DOCS_DIR)
57
- docs_dir.mkdir(parents=True, exist_ok=True)
58
- return docs_dir
59
-
60
- def _load_models_data(self):
61
- json_data = load_model_json_data()
62
- models_data = json_data.get("models", {}) if json_data else {}
63
-
64
- if not models_data:
65
- self.stdout.write(self.style.WARNING("⚠️ No model data found"))
66
-
67
- return models_data
68
-
69
- def _load_schema_data(self):
70
- try:
71
- schema = get_schema()
72
- except Exception as e:
73
- self.stdout.write(self.style.ERROR(f"❌ Failed to load OpenAPI schema: {e}"))
74
- return {}
75
- if not schema:
76
- self.stdout.write(self.style.ERROR("❌ Failed to load OpenAPI schema"))
77
- return {}
78
-
79
- paths = schema.get("paths", {})
80
- components = schema.get("components", {})
81
-
82
- self.stdout.write(f"📊 Loaded {len(paths)} API paths")
83
-
84
- return {"paths": paths, "components": components}
85
-
86
- def _generate_models_documentation(self, models_data, docs_dir):
87
- self.stdout.write("📋 Generating model documentation...")
88
-
89
- try:
90
- generate_model_docs(models_data)
91
- create_models_index(models_data, docs_dir)
92
- self.stdout.write(self.style.SUCCESS("✅ Model documentation generated"))
93
- except Exception as e:
94
- self.stdout.write(self.style.WARNING(f"⚠️ Failed to generate model docs: {e}"))
95
- if hasattr(self, "_generating_all"):
96
- self.stdout.write(self.style.WARNING("Continuing with endpoint generation..."))
97
- raise
98
-
99
- def _generate_endpoints_documentation(self, schema_data, docs_dir):
100
- self.stdout.write("🔗 Generating endpoint documentation...")
101
-
102
- paths = schema_data["paths"]
103
- components = schema_data["components"]
104
-
105
- endpoints_by_app = parse_endpoints_from_schema(paths)
106
- total_endpoints = generate_endpoint_files(endpoints_by_app, components)
107
- create_endpoints_index(endpoints_by_app, docs_dir)
108
-
109
- self.stdout.write(
110
- self.style.SUCCESS(
111
- f"✅ Generated {total_endpoints} endpoint files with Django view introspection"
112
- )
113
- )