drf-to-mkdoc 0.1.9__tar.gz → 0.2.1__tar.gz
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-0.1.9 → drf_to_mkdoc-0.2.1}/PKG-INFO +16 -5
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/README.md +15 -4
- drf_to_mkdoc-0.2.1/docs/customizing_endpoints.md +173 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/docs/serving_mkdocs_with_django.md +31 -16
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/conf/defaults.py +5 -0
- drf_to_mkdoc-0.2.1/drf_to_mkdoc/conf/settings.py +164 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/management/commands/build_docs.py +8 -7
- drf_to_mkdoc-0.2.1/drf_to_mkdoc/management/commands/build_endpoint_docs.py +69 -0
- drf_to_mkdoc-0.2.1/drf_to_mkdoc/management/commands/build_model_docs.py +50 -0
- drf_to_mkdoc-0.1.9/drf_to_mkdoc/management/commands/generate_model_docs.py → drf_to_mkdoc-0.2.1/drf_to_mkdoc/management/commands/extract_model_data.py +18 -24
- drf_to_mkdoc-0.2.1/drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out-sidebar.js +879 -0
- drf_to_mkdoc-0.2.1/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/try-out-sidebar.css +728 -0
- drf_to_mkdoc-0.2.1/drf_to_mkdoc/utils/ai_tools/enums.py +13 -0
- drf_to_mkdoc-0.2.1/drf_to_mkdoc/utils/ai_tools/exceptions.py +19 -0
- drf_to_mkdoc-0.2.1/drf_to_mkdoc/utils/ai_tools/providers/__init__.py +0 -0
- drf_to_mkdoc-0.2.1/drf_to_mkdoc/utils/ai_tools/providers/base_provider.py +123 -0
- drf_to_mkdoc-0.2.1/drf_to_mkdoc/utils/ai_tools/providers/gemini_provider.py +80 -0
- drf_to_mkdoc-0.2.1/drf_to_mkdoc/utils/ai_tools/types.py +81 -0
- drf_to_mkdoc-0.2.1/drf_to_mkdoc/utils/commons/__init__.py +0 -0
- drf_to_mkdoc-0.2.1/drf_to_mkdoc/utils/commons/code_extractor.py +22 -0
- drf_to_mkdoc-0.2.1/drf_to_mkdoc/utils/commons/file_utils.py +35 -0
- drf_to_mkdoc-0.2.1/drf_to_mkdoc/utils/commons/model_utils.py +83 -0
- drf_to_mkdoc-0.2.1/drf_to_mkdoc/utils/commons/operation_utils.py +83 -0
- drf_to_mkdoc-0.2.1/drf_to_mkdoc/utils/commons/path_utils.py +78 -0
- drf_to_mkdoc-0.2.1/drf_to_mkdoc/utils/commons/schema_utils.py +230 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/utils/endpoint_detail_generator.py +16 -35
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/utils/endpoint_list_generator.py +1 -1
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/utils/extractors/query_parameter_extractors.py +33 -30
- drf_to_mkdoc-0.2.1/drf_to_mkdoc/utils/md_generators/__init__.py +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/utils/model_detail_generator.py +44 -40
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/utils/model_list_generator.py +25 -15
- drf_to_mkdoc-0.2.1/drf_to_mkdoc/utils/schema.py +259 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc.egg-info/PKG-INFO +16 -5
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc.egg-info/SOURCES.txt +20 -3
- drf_to_mkdoc-0.1.9/docs/customizing_endpoints.md +0 -106
- drf_to_mkdoc-0.1.9/drf_to_mkdoc/conf/settings.py +0 -50
- drf_to_mkdoc-0.1.9/drf_to_mkdoc/management/commands/generate_docs.py +0 -138
- drf_to_mkdoc-0.1.9/drf_to_mkdoc/utils/common.py +0 -353
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/.github/workflows/publish.yaml +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/.pre-commit-config.yaml +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/CONTRIBUTING.md +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/LICENSE +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/MANIFEST.in +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/conf/__init__.py +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/conf/defaults.py +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/conf/settings.py +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/docs/mkdocs.yml +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/__init__.py +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/apps.py +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/conf/__init__.py +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/management/__init__.py +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/management/commands/__init__.py +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/management/commands/generate_doc_json.py +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/management/commands/update_doc_schema.py +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/static/drf-to-mkdoc/javascripts/endpoints-filter.js +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/accessibility.css +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/animations.css +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/badges.css +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/base.css +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/endpoint-content.css +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/endpoints-grid.css +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/filter-section.css +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/fixes.css +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/layout.css +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/loading.css +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/responsive.css +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/sections.css +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/stats.css +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/tags.css +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/theme-toggle.css +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/variables.css +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/animations.css +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/base.css +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/model-cards.css +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/model-tables.css +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/responsive.css +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/models/variables.css +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/utils/__init__.py +0 -0
- {drf_to_mkdoc-0.1.9/drf_to_mkdoc/utils/md_generators → drf_to_mkdoc-0.2.1/drf_to_mkdoc/utils/ai_tools}/__init__.py +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/utils/extractors/__init__.py +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc/utils/md_generators/query_parameters_generators.py +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc.egg-info/dependency_links.txt +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc.egg-info/requires.txt +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/drf_to_mkdoc.egg-info/top_level.txt +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/pyproject.toml +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/setup.cfg +0 -0
- {drf_to_mkdoc-0.1.9 → drf_to_mkdoc-0.2.1}/setup.py +0 -0
|
@@ -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.
|
|
@@ -27,13 +27,14 @@ INSTALLED_APPS = [
|
|
|
27
27
|
|
|
28
28
|
# Required for OpenAPI schema generation
|
|
29
29
|
REST_FRAMEWORK = {
|
|
30
|
-
'DEFAULT_SCHEMA_CLASS': '
|
|
30
|
+
'DEFAULT_SCHEMA_CLASS': 'drf_to_mkdoc.utils.schema.AutoSchema', # Use our custom AutoSchema
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
SPECTACULAR_SETTINGS = {
|
|
34
34
|
'TITLE': 'Your API',
|
|
35
35
|
'DESCRIPTION': 'Your API description',
|
|
36
36
|
'VERSION': '1.0.0',
|
|
37
|
+
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
DRF_TO_MKDOC = {
|
|
@@ -61,10 +62,19 @@ DRF_TO_MKDOC = {
|
|
|
61
62
|
python manage.py build_docs --settings=docs_settings
|
|
62
63
|
```
|
|
63
64
|
|
|
65
|
+
## Available Commands
|
|
66
|
+
|
|
67
|
+
- `build_docs`: Build the complete documentation site with MkDocs
|
|
68
|
+
- `build_endpoint_docs`: Build endpoint documentation from OpenAPI schema
|
|
69
|
+
- `build_model_docs`: Build model documentation from model JSON data
|
|
70
|
+
- `extract_model_data`: Extract model data from Django model introspection and save as JSON
|
|
71
|
+
- `update_doc_schema`: Update the final schema by copying the documented schema
|
|
72
|
+
|
|
64
73
|
## What you get
|
|
65
74
|
|
|
66
75
|
See a detailed overview of generated files in `docs/structure.md` and a feature breakdown in `docs/features.md`.
|
|
67
76
|
|
|
77
|
+
|
|
68
78
|
## How it works
|
|
69
79
|
|
|
70
80
|
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.
|
|
@@ -105,8 +115,9 @@ drf-to-mkdoc/
|
|
|
105
115
|
│ ├── management/
|
|
106
116
|
│ │ └── commands/
|
|
107
117
|
│ │ ├── build_docs.py # Build MkDocs site
|
|
108
|
-
│ │ ├──
|
|
109
|
-
│ │ ├──
|
|
118
|
+
│ │ ├── build_endpoint_docs.py # Build endpoint documentation
|
|
119
|
+
│ │ ├── build_model_docs.py # Build model documentation
|
|
120
|
+
│ │ ├── extract_model_data.py # Extract model data from Django
|
|
110
121
|
│ │ └── update_doc_schema.py # Schema updates
|
|
111
122
|
│ └── utils/
|
|
112
123
|
│ ├── common.py # Shared utilities
|
|
@@ -173,4 +184,4 @@ your-project/
|
|
|
173
184
|
## Contributing
|
|
174
185
|
|
|
175
186
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed contribution guidelines.
|
|
176
|
-
This will ensure that only the source configuration and scripts are versioned, while the generated documentation is excluded.
|
|
187
|
+
This will ensure that only the source configuration and scripts are versioned, while the generated documentation is excluded.
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
|
|
2
|
+
# Customizing API Endpoint Documentation
|
|
3
|
+
|
|
4
|
+
`drf-to-mkdoc` automatically generates API documentation from your Django REST Framework (DRF) project using the OpenAPI schema from **DRF Spectacular**. You can refine and extend that documentation using a **custom JSON file**.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. Where to put your custom schema
|
|
9
|
+
|
|
10
|
+
By default, the generator looks for:
|
|
11
|
+
docs/configs/custom\_schema.json
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
You can change this path by setting `CUSTOM_SCHEMA_FILE` in your `DRF_TO_MKDOC` settings.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 2. JSON File Format
|
|
21
|
+
|
|
22
|
+
The file should be a JSON object where **keys are `operationId`s** from your OpenAPI schema. Each key can override or extend the operation’s documentation.
|
|
23
|
+
|
|
24
|
+
Supported fields for each operation:
|
|
25
|
+
|
|
26
|
+
- `description` → Text description of the endpoint
|
|
27
|
+
- `parameters` → Array of OpenAPI parameter objects
|
|
28
|
+
- `requestBody` → OpenAPI RequestBody object
|
|
29
|
+
- `responses` → OpenAPI Responses object
|
|
30
|
+
- `append_fields` → A list of keys that should **append to existing lists instead of replacing them**.
|
|
31
|
+
- Currently, this is only useful for fields that are arrays in the schema (e.g., `parameters`).
|
|
32
|
+
- If the target field is not a list (like `description`, `responses`, or `requestBody`), `append_fields` is ignored and the value is replaced as usual.
|
|
33
|
+
- Example: If you want to **keep auto-generated query parameters** and add your own, include `"parameters"` in `append_fields`.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
### Example `custom_schema.json` using all supported keys
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"clinic_panel_appointments_available_appointment_times_list": {
|
|
42
|
+
"description": "Shows all available appointment times for a clinic.",
|
|
43
|
+
"parameters": [
|
|
44
|
+
{
|
|
45
|
+
"name": "date",
|
|
46
|
+
"in": "query",
|
|
47
|
+
"description": "Filter appointments by date",
|
|
48
|
+
"required": false,
|
|
49
|
+
"schema": { "type": "string", "format": "date" },
|
|
50
|
+
"queryparam_type": "filter_fields"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "search",
|
|
54
|
+
"in": "query",
|
|
55
|
+
"description": "Search appointments by doctor or patient name",
|
|
56
|
+
"required": false,
|
|
57
|
+
"schema": { "type": "string" },
|
|
58
|
+
"queryparam_type": "search_fields"
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
"requestBody": {
|
|
62
|
+
"description": "Request body for creating an appointment",
|
|
63
|
+
"required": true,
|
|
64
|
+
"content": {
|
|
65
|
+
"application/json": {
|
|
66
|
+
"schema": {
|
|
67
|
+
"type": "object",
|
|
68
|
+
"properties": {
|
|
69
|
+
"doctor_id": { "type": "integer" },
|
|
70
|
+
"patient_id": { "type": "integer" },
|
|
71
|
+
"date": { "type": "string", "format": "date" },
|
|
72
|
+
"time_slot": { "type": "string" }
|
|
73
|
+
},
|
|
74
|
+
"required": ["doctor_id", "patient_id", "date", "time_slot"]
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"responses": {
|
|
80
|
+
"200": {
|
|
81
|
+
"description": "List of available time slots",
|
|
82
|
+
"content": {
|
|
83
|
+
"application/json": {
|
|
84
|
+
"schema": {
|
|
85
|
+
"type": "array",
|
|
86
|
+
"items": {
|
|
87
|
+
"type": "object",
|
|
88
|
+
"properties": {
|
|
89
|
+
"date": { "type": "string", "description": "Date in DATE_FORMAT" },
|
|
90
|
+
"time_slots": {
|
|
91
|
+
"type": "array",
|
|
92
|
+
"items": {
|
|
93
|
+
"type": "object",
|
|
94
|
+
"properties": {
|
|
95
|
+
"start_datetime": { "type": "string", "description": "Start datetime" },
|
|
96
|
+
"end_datetime": { "type": "string", "description": "End datetime" }
|
|
97
|
+
},
|
|
98
|
+
"required": ["start_datetime", "end_datetime"]
|
|
99
|
+
},
|
|
100
|
+
"description": "Available time slots for the date"
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
"required": ["date", "time_slots"]
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"404": {
|
|
110
|
+
"description": "No appointments found for the given filters",
|
|
111
|
+
"content": {
|
|
112
|
+
"application/json": {
|
|
113
|
+
"schema": {
|
|
114
|
+
"type": "object",
|
|
115
|
+
"properties": {
|
|
116
|
+
"detail": { "type": "string", "example": "Appointments not found" }
|
|
117
|
+
},
|
|
118
|
+
"required": ["detail"]
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
"append_fields": ["parameters"]
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
````
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 3. Adding Query Parameters
|
|
132
|
+
|
|
133
|
+
If you add a **query parameter** (`"in": "query"`), include a `queryparam_type` so it’s categorized properly in the generated docs.
|
|
134
|
+
|
|
135
|
+
Supported `queryparam_type` values:
|
|
136
|
+
|
|
137
|
+
* `search_fields` → Used for search filters
|
|
138
|
+
* `filter_fields` → Standard filters
|
|
139
|
+
* `ordering_fields` → Sort fields
|
|
140
|
+
* `filter_backends` → Backend-specific filters
|
|
141
|
+
* `pagination_fields` → Pagination-related fields
|
|
142
|
+
|
|
143
|
+
> ⚠️ If `queryparam_type` is missing or invalid, the generator will raise an error.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 4. How the custom schema is applied
|
|
148
|
+
|
|
149
|
+
1. `drf-to-mkdoc` loads your OpenAPI schema.
|
|
150
|
+
2. It reads your `custom_schema.json`.
|
|
151
|
+
3. For each `operationId` in your JSON:
|
|
152
|
+
|
|
153
|
+
* Finds the corresponding endpoint in the schema
|
|
154
|
+
* Replaces fields like `description`, `responses`, `parameters`, etc.
|
|
155
|
+
* Appends items for fields listed in `append_fields` instead of replacing
|
|
156
|
+
4. Generates your markdown documentation using the merged schema
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## 5. Finding `operationId`s
|
|
161
|
+
|
|
162
|
+
`operationId`s are generated by DRF Spectacular. You can find them by:
|
|
163
|
+
|
|
164
|
+
* Checking your API endpoint pages in the browser (each includes the `operationId`)
|
|
165
|
+
* Inspecting the OpenAPI JSON/YAML schema (via `/schema/` endpoint or export)
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## 6. Tips for smooth usage
|
|
170
|
+
|
|
171
|
+
* Keep `custom_schema.json` in version control so your team benefits.
|
|
172
|
+
* Start small: add descriptions first, then parameters, then responses.
|
|
173
|
+
* Use `append_fields` if you want to **add extra info** without overwriting auto-generated items.
|
|
@@ -65,30 +65,45 @@ class DocumentationView(View):
|
|
|
65
65
|
raise Http404("Invalid path")
|
|
66
66
|
|
|
67
67
|
file_path = (site_dir / path).resolve()
|
|
68
|
-
site_root = site_dir.resolve()
|
|
69
68
|
|
|
70
|
-
# Ensure file is
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
raise Http404("Documentation file not found")
|
|
69
|
+
# Ensure the file exists and is within the site directory
|
|
70
|
+
try:
|
|
71
|
+
file_path = file_path.resolve()
|
|
72
|
+
site_dir = site_dir.resolve()
|
|
75
73
|
|
|
74
|
+
# Security check: ensure file is within site directory
|
|
75
|
+
if not str(file_path).startswith(str(site_dir)):
|
|
76
|
+
return HttpResponseRedirect("/docs/404/index.html?error=access_denied")
|
|
77
|
+
|
|
78
|
+
if not file_path.exists() or not file_path.is_file():
|
|
79
|
+
return HttpResponseRedirect("/docs/404/index.html")
|
|
80
|
+
|
|
81
|
+
except (OSError, ValueError) as e:
|
|
82
|
+
return HttpResponseRedirect("/docs/404/index.html?error=invalid_file_path")
|
|
83
|
+
|
|
84
|
+
# Determine content type
|
|
76
85
|
content_type, _ = mimetypes.guess_type(str(file_path))
|
|
77
86
|
if content_type is None:
|
|
78
87
|
content_type = "application/octet-stream"
|
|
79
88
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
89
|
+
# Read and serve the file
|
|
90
|
+
try:
|
|
91
|
+
with Path(file_path).open("rb") as f:
|
|
92
|
+
response = HttpResponse(f.read(), content_type=content_type)
|
|
93
|
+
|
|
94
|
+
# Set appropriate headers
|
|
95
|
+
if content_type.startswith("text/html"):
|
|
96
|
+
response["Cache-Control"] = "no-cache, no-store, must-revalidate"
|
|
97
|
+
response["Pragma"] = "no-cache"
|
|
98
|
+
response["Expires"] = "0"
|
|
99
|
+
else:
|
|
100
|
+
response["Cache-Control"] = "public, max-age=3600"
|
|
101
|
+
|
|
102
|
+
except OSError as e:
|
|
103
|
+
return HttpResponseRedirect("/docs/404/index.html?error=could_not_read_file")
|
|
88
104
|
else:
|
|
89
|
-
response
|
|
105
|
+
return response
|
|
90
106
|
|
|
91
|
-
return response
|
|
92
107
|
```
|
|
93
108
|
|
|
94
109
|
## Permission options
|
|
@@ -7,6 +7,11 @@ DEFAULTS = {
|
|
|
7
7
|
"CUSTOM_SCHEMA_FILE": "docs/configs/custom_schema.json", # Path to custom schema file
|
|
8
8
|
"PATH_PARAM_SUBSTITUTE_FUNCTION": None,
|
|
9
9
|
"PATH_PARAM_SUBSTITUTE_MAPPING": {},
|
|
10
|
+
# AI documentation settings
|
|
11
|
+
"ENABLE_AI_DOCS": False,
|
|
12
|
+
"AI_CONFIG_DIR_NAME": "ai_code", # Directory name for AI-generated code files
|
|
13
|
+
"AI_OPERATION_MAP_FILE": "docs/configs/operation_map.json", # Path to operation map file
|
|
14
|
+
"SERIALIZERS_INHERITANCE_DEPTH": 1, # Maximum depth for class inheritance analysis
|
|
10
15
|
# Django apps - required, no default
|
|
11
16
|
"DJANGO_APPS": None, # List of Django app names to process
|
|
12
17
|
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Any, ClassVar
|
|
3
|
+
|
|
4
|
+
from django.conf import settings
|
|
5
|
+
|
|
6
|
+
from drf_to_mkdoc.conf.defaults import DEFAULTS
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DRFToMkDocSettings:
|
|
10
|
+
required_settings: ClassVar[list[str]] = ["DJANGO_APPS"]
|
|
11
|
+
project_settings: ClassVar[dict[str, Any]] = {"PROJECT_NAME": "drf-to-mkdoc"}
|
|
12
|
+
|
|
13
|
+
settings_types: ClassVar[dict[str, type]] = {
|
|
14
|
+
"ENABLE_AI_DOCS": bool,
|
|
15
|
+
"AI_CONFIG_DIR_NAME": str,
|
|
16
|
+
"SERIALIZERS_INHERITANCE_DEPTH": int,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
settings_ranges: ClassVar[dict[str, tuple[int, int]]] = {
|
|
20
|
+
"SERIALIZERS_INHERITANCE_DEPTH": (1, 3),
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
path_settings = {
|
|
24
|
+
"CONFIG_DIR",
|
|
25
|
+
"MODEL_DOCS_FILE",
|
|
26
|
+
"DOC_CONFIG_FILE",
|
|
27
|
+
"CUSTOM_SCHEMA_FILE",
|
|
28
|
+
"AI_OPERATION_MAP_FILE",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
def __init__(self, user_settings_key="DRF_TO_MKDOC", defaults=None):
|
|
32
|
+
self.user_settings_key = user_settings_key
|
|
33
|
+
self._user_settings = getattr(settings, user_settings_key, {})
|
|
34
|
+
self.defaults = defaults or {}
|
|
35
|
+
|
|
36
|
+
def _validate_type(self, key: str, value: Any) -> None:
|
|
37
|
+
"""Validate the type of setting value."""
|
|
38
|
+
if key in self.settings_types:
|
|
39
|
+
expected_type = self.settings_types[key]
|
|
40
|
+
if not isinstance(value, expected_type):
|
|
41
|
+
raise TypeError(
|
|
42
|
+
f"DRF_TO_MKDOC setting '{key}' must be of type {expected_type.__name__}, "
|
|
43
|
+
f"got {type(value).__name__} instead."
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
def _validate_range(self, key: str, value: Any) -> None:
|
|
47
|
+
"""Validate the range of a setting value."""
|
|
48
|
+
if key in self.settings_ranges:
|
|
49
|
+
min_val, max_val = self.settings_ranges[key]
|
|
50
|
+
if not min_val <= value <= max_val:
|
|
51
|
+
raise ValueError(
|
|
52
|
+
f"DRF_TO_MKDOC setting '{key}' must be between {min_val} and {max_val}, "
|
|
53
|
+
f"got {value} instead."
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
def _validate_required(self, key: str, value: Any) -> None:
|
|
57
|
+
"""Validate if a required setting is configured."""
|
|
58
|
+
if value is None and key in self.required_settings:
|
|
59
|
+
raise ValueError(
|
|
60
|
+
f"DRF_TO_MKDOC setting '{key}' is required but not configured. "
|
|
61
|
+
f"Please add it to your Django settings under {self.user_settings_key}."
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def _validate_dir(self, key: str, value: str) -> None:
|
|
65
|
+
if key not in self.path_settings or not isinstance(value, str):
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
if not value.strip():
|
|
69
|
+
raise ValueError(
|
|
70
|
+
f"DRF_TO_MKDOC path setting '{key}' cannot be empty or contain only whitespace."
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
dangerous_components = {"..", "~", "/", "\\"}
|
|
74
|
+
path_parts = Path(value).parts
|
|
75
|
+
for part in path_parts:
|
|
76
|
+
if part in dangerous_components or part.startswith("."):
|
|
77
|
+
raise ValueError(
|
|
78
|
+
f"DRF_TO_MKDOC path setting '{key}' contains unsafe path component '{part}'. "
|
|
79
|
+
f"Directory names should be simple names without separators or relative path components."
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
if Path(value).is_absolute():
|
|
83
|
+
raise ValueError(
|
|
84
|
+
f"DRF_TO_MKDOC path setting '{key}' cannot be an absolute path. "
|
|
85
|
+
f"Use relative directory names only."
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
reserved_names = {
|
|
89
|
+
"CON",
|
|
90
|
+
"PRN",
|
|
91
|
+
"AUX",
|
|
92
|
+
"NUL",
|
|
93
|
+
"COM1",
|
|
94
|
+
"COM2",
|
|
95
|
+
"COM3",
|
|
96
|
+
"COM4",
|
|
97
|
+
"COM5",
|
|
98
|
+
"COM6",
|
|
99
|
+
"COM7",
|
|
100
|
+
"COM8",
|
|
101
|
+
"COM9",
|
|
102
|
+
"LPT1",
|
|
103
|
+
"LPT2",
|
|
104
|
+
"LPT3",
|
|
105
|
+
"LPT4",
|
|
106
|
+
"LPT5",
|
|
107
|
+
"LPT6",
|
|
108
|
+
"LPT7",
|
|
109
|
+
"LPT8",
|
|
110
|
+
"LPT9",
|
|
111
|
+
}
|
|
112
|
+
if value.upper() in reserved_names:
|
|
113
|
+
raise ValueError(
|
|
114
|
+
f"DRF_TO_MKDOC path setting '{key}' uses a reserved system name '{value}'. "
|
|
115
|
+
f"Please choose a different name."
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
invalid_chars = '<>:"|?*'
|
|
119
|
+
if any(char in value for char in invalid_chars):
|
|
120
|
+
raise ValueError(
|
|
121
|
+
f"DRF_TO_MKDOC path setting '{key}' contains invalid characters. "
|
|
122
|
+
f"Avoid using: {invalid_chars}"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
def get(self, key):
|
|
126
|
+
if key in self.project_settings:
|
|
127
|
+
return self.project_settings[key]
|
|
128
|
+
|
|
129
|
+
# User-provided settings take precedence
|
|
130
|
+
if key in self._user_settings:
|
|
131
|
+
value = self._user_settings[key]
|
|
132
|
+
else:
|
|
133
|
+
value = self.defaults.get(key, None)
|
|
134
|
+
|
|
135
|
+
# Run all validations
|
|
136
|
+
self._validate_required(key, value)
|
|
137
|
+
self._validate_type(key, value)
|
|
138
|
+
self._validate_range(key, value)
|
|
139
|
+
self._validate_dir(key, value)
|
|
140
|
+
|
|
141
|
+
if value is None:
|
|
142
|
+
raise AttributeError(f"Invalid DRF_TO_MKDOC setting: '{key}'")
|
|
143
|
+
return value
|
|
144
|
+
|
|
145
|
+
def __getattr__(self, key):
|
|
146
|
+
return self.get(key)
|
|
147
|
+
|
|
148
|
+
def validate_required_settings(self):
|
|
149
|
+
missing_settings = []
|
|
150
|
+
|
|
151
|
+
for setting in self.required_settings:
|
|
152
|
+
try:
|
|
153
|
+
self.get(setting)
|
|
154
|
+
except (ValueError, AttributeError):
|
|
155
|
+
missing_settings.append(setting)
|
|
156
|
+
|
|
157
|
+
if missing_settings:
|
|
158
|
+
raise ValueError(
|
|
159
|
+
f"Missing required settings: {', '.join(missing_settings)}. "
|
|
160
|
+
f"Please configure these in your Django settings under {self.user_settings_key}."
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
drf_to_mkdoc_settings = DRFToMkDocSettings(defaults=DEFAULTS)
|
|
@@ -34,15 +34,16 @@ class Command(BaseCommand):
|
|
|
34
34
|
)
|
|
35
35
|
|
|
36
36
|
try:
|
|
37
|
-
#
|
|
38
|
-
self.stdout.write("
|
|
39
|
-
call_command("
|
|
40
|
-
self.stdout.write(self.style.SUCCESS("Model
|
|
37
|
+
# Extract model data from Django models
|
|
38
|
+
self.stdout.write("Extracting model data...")
|
|
39
|
+
call_command("extract_model_data", "--pretty")
|
|
40
|
+
self.stdout.write(self.style.SUCCESS("Model data extracted."))
|
|
41
41
|
|
|
42
42
|
# Generate the documentation content
|
|
43
|
-
self.stdout.write("
|
|
44
|
-
call_command("
|
|
45
|
-
|
|
43
|
+
self.stdout.write("Building documentation content...")
|
|
44
|
+
call_command("build_model_docs")
|
|
45
|
+
call_command("build_endpoint_docs")
|
|
46
|
+
self.stdout.write(self.style.SUCCESS("Documentation content built."))
|
|
46
47
|
|
|
47
48
|
# Build the MkDocs site
|
|
48
49
|
self.stdout.write("Building MkDocs site...")
|
|
@@ -0,0 +1,69 @@
|
|
|
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.commons.schema_utils import get_schema
|
|
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
|
+
|
|
13
|
+
|
|
14
|
+
class Command(BaseCommand):
|
|
15
|
+
help = "Build endpoint documentation from OpenAPI schema"
|
|
16
|
+
|
|
17
|
+
def handle(self, *args, **options):
|
|
18
|
+
self.stdout.write(
|
|
19
|
+
self.style.SUCCESS("🚀 Starting endpoint documentation generation...")
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
docs_dir = self._setup_docs_directory()
|
|
23
|
+
schema_data = self._load_schema_data()
|
|
24
|
+
|
|
25
|
+
if schema_data:
|
|
26
|
+
self._generate_endpoints_documentation(schema_data, docs_dir)
|
|
27
|
+
self.stdout.write(
|
|
28
|
+
self.style.SUCCESS("✅ Endpoint documentation generation complete!")
|
|
29
|
+
)
|
|
30
|
+
else:
|
|
31
|
+
self.stdout.write(self.style.ERROR("❌ Failed to generate endpoint documentation."))
|
|
32
|
+
|
|
33
|
+
def _setup_docs_directory(self):
|
|
34
|
+
docs_dir = Path(drf_to_mkdoc_settings.DOCS_DIR)
|
|
35
|
+
docs_dir.mkdir(parents=True, exist_ok=True)
|
|
36
|
+
return docs_dir
|
|
37
|
+
|
|
38
|
+
def _load_schema_data(self):
|
|
39
|
+
try:
|
|
40
|
+
schema = get_schema()
|
|
41
|
+
except Exception as e:
|
|
42
|
+
self.stdout.write(self.style.ERROR(f"❌ Failed to load OpenAPI schema: {e}"))
|
|
43
|
+
return {}
|
|
44
|
+
if not schema:
|
|
45
|
+
self.stdout.write(self.style.ERROR("❌ Failed to load OpenAPI schema"))
|
|
46
|
+
return {}
|
|
47
|
+
|
|
48
|
+
paths = schema.get("paths", {})
|
|
49
|
+
components = schema.get("components", {})
|
|
50
|
+
|
|
51
|
+
self.stdout.write(f"📊 Loaded {len(paths)} API paths")
|
|
52
|
+
|
|
53
|
+
return {"paths": paths, "components": components}
|
|
54
|
+
|
|
55
|
+
def _generate_endpoints_documentation(self, schema_data, docs_dir):
|
|
56
|
+
self.stdout.write("🔗 Generating endpoint documentation...")
|
|
57
|
+
|
|
58
|
+
paths = schema_data["paths"]
|
|
59
|
+
components = schema_data["components"]
|
|
60
|
+
|
|
61
|
+
endpoints_by_app = parse_endpoints_from_schema(paths)
|
|
62
|
+
total_endpoints = generate_endpoint_files(endpoints_by_app, components)
|
|
63
|
+
create_endpoints_index(endpoints_by_app, docs_dir)
|
|
64
|
+
|
|
65
|
+
self.stdout.write(
|
|
66
|
+
self.style.SUCCESS(
|
|
67
|
+
f"✅ Generated {total_endpoints} endpoint files with Django view introspection"
|
|
68
|
+
)
|
|
69
|
+
)
|
|
@@ -0,0 +1,50 @@
|
|
|
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.commons.file_utils import load_json_data
|
|
7
|
+
from drf_to_mkdoc.utils.model_detail_generator import generate_model_docs
|
|
8
|
+
from drf_to_mkdoc.utils.model_list_generator import create_models_index
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Command(BaseCommand):
|
|
12
|
+
help = "Build model documentation from model JSON data"
|
|
13
|
+
|
|
14
|
+
def handle(self, *args, **options):
|
|
15
|
+
self.stdout.write(self.style.SUCCESS("🚀 Starting model documentation generation..."))
|
|
16
|
+
|
|
17
|
+
docs_dir = self._setup_docs_directory()
|
|
18
|
+
models_data = self._load_models_data()
|
|
19
|
+
|
|
20
|
+
if models_data:
|
|
21
|
+
self._generate_models_documentation(models_data, docs_dir)
|
|
22
|
+
self.stdout.write(self.style.SUCCESS("✅ Model documentation generation complete!"))
|
|
23
|
+
else:
|
|
24
|
+
self.stdout.write(self.style.ERROR("❌ Failed to generate model documentation."))
|
|
25
|
+
|
|
26
|
+
def _setup_docs_directory(self):
|
|
27
|
+
docs_dir = Path(drf_to_mkdoc_settings.DOCS_DIR)
|
|
28
|
+
docs_dir.mkdir(parents=True, exist_ok=True)
|
|
29
|
+
return docs_dir
|
|
30
|
+
|
|
31
|
+
def _load_models_data(self):
|
|
32
|
+
models_data = load_json_data(
|
|
33
|
+
drf_to_mkdoc_settings.MODEL_DOCS_FILE, raise_not_found=False
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
if not models_data:
|
|
37
|
+
self.stdout.write(self.style.WARNING("⚠️ No model data found"))
|
|
38
|
+
|
|
39
|
+
return models_data
|
|
40
|
+
|
|
41
|
+
def _generate_models_documentation(self, models_data, docs_dir):
|
|
42
|
+
self.stdout.write("📋 Generating model documentation...")
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
generate_model_docs(models_data)
|
|
46
|
+
create_models_index(models_data, docs_dir)
|
|
47
|
+
self.stdout.write(self.style.SUCCESS("✅ Model documentation generated"))
|
|
48
|
+
except Exception as e:
|
|
49
|
+
self.stdout.write(self.style.WARNING(f"⚠️ Failed to generate model docs: {e}"))
|
|
50
|
+
raise
|