180 lines
8.1 KiB
Python
180 lines
8.1 KiB
Python
from django.core.management.base import BaseCommand
|
|
from django.apps import apps
|
|
import inspect
|
|
import importlib
|
|
import yaml
|
|
import os
|
|
from django.conf import settings
|
|
from django.template.loaders.app_directories import get_app_template_dirs
|
|
|
|
|
|
class Command(BaseCommand):
|
|
help = "Generate YAML support knowledge base from Django views, models, and templates"
|
|
|
|
def handle(self, *args, **kwargs):
|
|
output_file = "haikal_kb.yaml"
|
|
kb = {
|
|
"metadata": {
|
|
"system_name": "Haikal",
|
|
"version": "1.0",
|
|
"generated_from": "Django",
|
|
},
|
|
"features": {},
|
|
"user_workflows": {}, # New section for step-by-step instructions
|
|
"templates": {},
|
|
"glossary": {}
|
|
}
|
|
|
|
def extract_doc(item):
|
|
doc = inspect.getdoc(item)
|
|
return doc.strip() if doc else ""
|
|
|
|
def get_all_views_modules():
|
|
view_modules = []
|
|
for app in settings.INSTALLED_APPS:
|
|
try:
|
|
mod = importlib.import_module(f"{app}.views")
|
|
view_modules.append((app, mod))
|
|
except ImportError:
|
|
continue
|
|
return view_modules
|
|
|
|
def get_all_model_classes():
|
|
all_models = []
|
|
for model in apps.get_models():
|
|
all_models.append((model._meta.app_label, model.__name__, extract_doc(model)))
|
|
return all_models
|
|
|
|
def get_all_templates():
|
|
template_dirs = get_app_template_dirs('templates')
|
|
templates = []
|
|
|
|
for template_dir in template_dirs:
|
|
app_name = os.path.basename(os.path.dirname(os.path.dirname(template_dir)))
|
|
for root, dirs, files in os.walk(template_dir):
|
|
for file in files:
|
|
if file.endswith(('.html', '.htm', '.txt')):
|
|
rel_path = os.path.relpath(os.path.join(root, file), template_dir)
|
|
with open(os.path.join(root, file), 'r', encoding='utf-8', errors='ignore') as f:
|
|
try:
|
|
content = f.read()
|
|
# Extract template comment documentation if it exists
|
|
doc = ""
|
|
if '{# DOC:' in content and '#}' in content:
|
|
doc_parts = content.split('{# DOC:')
|
|
for part in doc_parts[1:]:
|
|
if '#}' in part:
|
|
doc += part.split('#}')[0].strip() + "\n"
|
|
except Exception as e:
|
|
self.stdout.write(self.style.WARNING(f"Error reading {rel_path}: {e}"))
|
|
continue
|
|
|
|
templates.append((app_name, rel_path, doc.strip()))
|
|
return templates
|
|
|
|
# Look for workflow documentation files
|
|
def get_workflow_docs():
|
|
workflows = {}
|
|
workflow_dir = os.path.join(settings.BASE_DIR, 'docs', 'workflows')
|
|
if os.path.exists(workflow_dir):
|
|
for file in os.listdir(workflow_dir):
|
|
if file.endswith('.yaml') or file.endswith('.yml'):
|
|
try:
|
|
with open(os.path.join(workflow_dir, file), 'r') as f:
|
|
workflow_data = yaml.safe_load(f)
|
|
for workflow_name, workflow_info in workflow_data.items():
|
|
workflows[workflow_name] = workflow_info
|
|
except Exception as e:
|
|
self.stdout.write(self.style.WARNING(f"Error reading workflow file {file}: {e}"))
|
|
return workflows
|
|
|
|
# Extract views
|
|
for app, mod in get_all_views_modules():
|
|
for name, obj in inspect.getmembers(mod, inspect.isfunction):
|
|
doc = extract_doc(obj)
|
|
if doc:
|
|
kb["features"][name] = {
|
|
"description": doc,
|
|
"source": f"{app}.views.{name}",
|
|
"type": "view_function"
|
|
}
|
|
|
|
# Look for @workflow decorator or WORKFLOW tag in docstring
|
|
if hasattr(obj, 'workflow_steps') or 'WORKFLOW:' in doc:
|
|
workflow_name = name.replace('_', ' ').title()
|
|
steps = []
|
|
|
|
if hasattr(obj, 'workflow_steps'):
|
|
steps = obj.workflow_steps
|
|
elif 'WORKFLOW:' in doc:
|
|
workflow_section = doc.split('WORKFLOW:')[1].strip()
|
|
steps_text = workflow_section.split('\n')
|
|
steps = [step.strip() for step in steps_text if step.strip()]
|
|
|
|
if steps:
|
|
kb["user_workflows"][workflow_name] = {
|
|
"description": f"How to {name.replace('_', ' ')}",
|
|
"steps": steps,
|
|
"source": f"{app}.views.{name}"
|
|
}
|
|
|
|
# Extract models
|
|
for app, name, doc in get_all_model_classes():
|
|
if doc:
|
|
kb["features"][name] = {
|
|
"description": doc,
|
|
"source": f"{app}.models.{name}",
|
|
"type": "model_class"
|
|
}
|
|
|
|
# Extract templates
|
|
for app, template_path, doc in get_all_templates():
|
|
template_id = f"{app}:{template_path}"
|
|
if doc: # Only include templates with documentation
|
|
kb["templates"][template_id] = {
|
|
"description": doc,
|
|
"path": template_path,
|
|
"app": app
|
|
}
|
|
|
|
# Add workflow documentation
|
|
kb["user_workflows"].update(get_workflow_docs())
|
|
|
|
# Add manual workflow examples if no workflows were found
|
|
if not kb["user_workflows"]:
|
|
kb["user_workflows"] = {
|
|
"Add New Car": {
|
|
"description": "How to add a new car to the inventory",
|
|
"steps": [
|
|
"Navigate to the Inventory section by clicking 'Inventory' in the main menu",
|
|
"Click the 'Add Car' button in the top right corner",
|
|
"Enter the VIN number or scan it using the barcode scanner",
|
|
"Select the car make from the dropdown menu",
|
|
"Select the car series from the available options",
|
|
"Select the trim level for the car",
|
|
"Fill in additional details like color, mileage, and price",
|
|
"Click 'Save' to add the car to inventory, or 'Save & Add Another' to continue adding cars"
|
|
],
|
|
"source": "manual_documentation"
|
|
},
|
|
"Create New Invoice": {
|
|
"description": "How to create a new invoice",
|
|
"steps": [
|
|
"Navigate to the Finance section by clicking 'Finance' in the main menu",
|
|
"Click the 'Invoices' tab",
|
|
"Click the 'Create New Invoice' button",
|
|
"Select a customer from the dropdown or click 'Add New Customer'",
|
|
"Select the car(s) to include in the invoice",
|
|
"Add any additional services or parts by clicking 'Add Item'",
|
|
"Set the payment terms and due date",
|
|
"Click 'Save Draft' to save without finalizing, or 'Finalize Invoice' to complete"
|
|
],
|
|
"source": "manual_documentation"
|
|
}
|
|
}
|
|
|
|
with open(output_file, "w", encoding="utf-8") as f:
|
|
yaml.dump(kb, f, allow_unicode=True, sort_keys=False)
|
|
|
|
self.stdout.write(self.style.SUCCESS(f"✅ YAML knowledge base saved to {output_file}"))
|