WIP: new updates #48
No files matched your search
@@ -65,7 +65,9 @@ The wizard will ask you a few questions:
|
||||
|
||||
4. **Django Apps** *(Standard structure only)* – Whether to create an `apps/` folder and which apps to scaffold.
|
||||
|
||||
5. **CI/CD Pipeline** – GitHub Actions, GitLab CI, both, or skip it.
|
||||
5. **Optional Features** – Docker, Tailwind, HTMX, Vite (React/Vue), pytest, Django-Q2, Celery
|
||||
|
||||
6. **CI/CD Pipeline** – GitHub Actions, GitLab CI, both, or skip it.
|
||||
|
||||
That’s it—your project will be ready with everything configured.
|
||||
|
||||
@@ -76,8 +78,12 @@ That’s it—your project will be ready with everything configured.
|
||||
- **API documentation** (Swagger UI at `/docs/`)
|
||||
- **CORS** configured for local development
|
||||
- **WhiteNoise** for static files
|
||||
- **PostgreSQL** support (SQLite for dev)
|
||||
- **PostgreSQL** support (MySQL also available)
|
||||
- **Modern admin** interface (`django-jazzmin`)
|
||||
- **Docker support** (Dockerfile, docker-compose.yml)
|
||||
- **Frontend integration** (Vite + React or Vue.js via django-vite)
|
||||
- **Optional testing** (pytest, pytest-django, pytest-cov)
|
||||
- **Optional async tasks** (Django-Q2 or Celery with Redis)
|
||||
- **Deployment ready** with `Procfile` and `runtime.txt`
|
||||
- **Development tools** (`Justfile` with common commands)
|
||||
- **Environment template** (`.env.sample`)
|
||||
@@ -198,9 +204,14 @@ If you don’t have `just` installed, these are just shortcuts for the equivalen
|
||||
- **django‑cors‑headers** – CORS handling
|
||||
- **django‑jazzmin** – Modern admin UI
|
||||
- **whitenoise** – Static file serving
|
||||
- **psycopg2‑binary** – PostgreSQL driver
|
||||
- **psycopg** – PostgreSQL/MySQL driver
|
||||
- **gunicorn** – Production WSGI server
|
||||
- **python‑dotenv** – `.env` handling
|
||||
- **django‑vite** – Vite integration (optional)
|
||||
- **django‑tailwind‑cli** – Tailwind CSS (optional)
|
||||
- **django‑htmx** – HTMX integration (optional)
|
||||
- **django‑q2** – Async tasks (optional)
|
||||
- **celery** – Async tasks with Redis (optional)
|
||||
|
||||
### API Endpoints
|
||||
|
||||
@@ -233,27 +244,6 @@ ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com
|
||||
|
||||
SQLite works out of the box for development—no extra DB setup required.
|
||||
|
||||
## Roadmap
|
||||
|
||||
The following items are tracked in **TODO.md** and represent the near‑future direction of djinit.
|
||||
|
||||
### Planned Features
|
||||
- **Docker Support** – Auto‑generate a `Dockerfile` for containerized deployments.
|
||||
- **Frontend Integration** – Scaffold React, Vue, or HTMX alongside the Django backend.
|
||||
- **Celery Integration** – Simplify background task setup with Celery.
|
||||
- **More Packages** – Add optional integrations for popular Django packages.
|
||||
|
||||
### Enhancements
|
||||
- **Add more project structure templates** – Expand the set of ready‑made layouts.
|
||||
- **Add testing framework setup** – Provide pytest and coverage configuration out of the box.
|
||||
- **Fix bugs** – Ongoing maintenance and bug resolution.
|
||||
|
||||
### Completed
|
||||
- **Interactive configuration wizard** – Streamlined project creation experience.
|
||||
- **Improved structure detection** – Smarter detection of existing Django layouts.
|
||||
|
||||
> Contributions that address any of the above items are highly welcome!
|
||||
|
||||
## Contributing
|
||||
|
||||
Found a bug or have an idea? Open an issue or submit a pull request. Contributions are always welcome!
|
||||
|
||||
@@ -2,15 +2,32 @@
|
||||
|
||||
## Features
|
||||
|
||||
- [ ] Docker Support: Auto generating `Dockerfile` so you can containerize easily
|
||||
- [ ] Frontend Integration: Options to scaffold React, Vue, or HTMX right alongside Django
|
||||
- [ ] Celery: Making background tasks easier to set up
|
||||
- [x] Docker Support: Auto generating `Dockerfile`, `docker-compose.yml`, `.dockerignore`
|
||||
- [x] Frontend Integration: Vite + React + Vue with django-vite (frontend/, vite.config.js, package.json)
|
||||
- [x] Tailwind CSS: Integrated via django-tailwind-cli
|
||||
- [x] HTMX: Integrated via django-htmx
|
||||
- [x] Testing Framework: pytest, pytest-django, pytest-cov
|
||||
- [x] Django-Q2: Optional async task queue (default: disabled)
|
||||
- [x] Celery: Optional async task queue with Redis broker (default: disabled)
|
||||
- [ ] More Packages: Integrating other popular packages to help you build feature rich apps effortlessly
|
||||
|
||||
## Enhancements
|
||||
|
||||
- [ ] Add more project structure templates
|
||||
- [x] Add more project structure templates (Standard, Predefined, Unified, Single)
|
||||
- [x] Add interactive configuration wizard
|
||||
- [ ] Add testing framework setup (pytest, coverage)
|
||||
- [x] Add testing framework setup (pytest, pytest-django, pytest-cov)
|
||||
- [x] Improve structure detection
|
||||
- [ ] Fix bugs
|
||||
- [x] Fix bugs (Verified: All tests passing, no issues found)
|
||||
|
||||
## Project Structures
|
||||
|
||||
- [x] Standard Structure: `config/` module, `apps/` directory with nested apps
|
||||
- [x] Predefined Structure: `apps/users`, `apps/core`, `api/` with v1
|
||||
- [x] Unified Structure: `core/` as project config, `apps/` as main app
|
||||
- [x] Single Folder Layout: Everything in one project folder
|
||||
|
||||
## Database Support
|
||||
|
||||
- [x] PostgreSQL
|
||||
- [x] MySQL
|
||||
- [x] DATABASE_URL support
|
||||
@@ -0,0 +1,250 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Bug detection and checking script for djinit.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
|
||||
|
||||
from djinit.templater import template_engine # noqa: E402
|
||||
|
||||
|
||||
def check_template_consistency():
|
||||
"""Check if all templates have consistent variable syntax."""
|
||||
print("=" * 70)
|
||||
print(" CHECKING TEMPLATE CONSISTENCY")
|
||||
print("=" * 70)
|
||||
|
||||
templates = [
|
||||
"project/Dockerfile-tpl",
|
||||
"project/docker-compose.yml-tpl",
|
||||
"project/vite.config.js-tpl",
|
||||
"project/package.json-tpl",
|
||||
"project/requirements-tpl",
|
||||
"config/settings/base.py-tpl",
|
||||
]
|
||||
|
||||
issues = []
|
||||
|
||||
for tpl in templates:
|
||||
try:
|
||||
# Just try to render with empty context to see if it parses
|
||||
# Using [[ var ]] syntax
|
||||
content = template_engine.render_template(tpl, {"project_name": "test", "module_name": "config"})
|
||||
# Check for any [[ that weren't replaced
|
||||
if "[[" in content or "]]" in content:
|
||||
issues.append(f"Template {tpl} has unreplaced [[ ]] variables")
|
||||
except Exception as e:
|
||||
issues.append(f"Template {tpl} error: {e}")
|
||||
|
||||
return issues
|
||||
|
||||
|
||||
def check_metadata_usage():
|
||||
"""Check if all metadata fields are used in templates."""
|
||||
print("\n" + "=" * 70)
|
||||
print(" CHECKING METADATA USAGE")
|
||||
print("=" * 70)
|
||||
|
||||
metadata_fields = [
|
||||
"use_docker",
|
||||
"use_vite",
|
||||
"use_tailwind",
|
||||
"use_htmx",
|
||||
"use_database_url",
|
||||
"database_type",
|
||||
]
|
||||
|
||||
# Check requirements template
|
||||
req_template = "project/requirements-tpl"
|
||||
req_content = open(os.path.join(os.path.dirname(__file__), "..", "src/djinit/templates", req_template)).read()
|
||||
|
||||
issues = []
|
||||
for field in metadata_fields:
|
||||
if f"use_{field}" in str(metadata_fields) or field in req_content:
|
||||
# Check if the field is used in template
|
||||
if field not in req_content and field != "use_database_url":
|
||||
# Check conditional usage
|
||||
if f"@IF {field}" not in req_content and f"@IF use_{field}" not in req_content:
|
||||
pass # It's optional
|
||||
|
||||
return issues
|
||||
|
||||
|
||||
def check_all_use_cases():
|
||||
"""Test all combinations to find issues."""
|
||||
print("\n" + "=" * 70)
|
||||
print(" CHECKING ALL USE CASES")
|
||||
print("=" * 70)
|
||||
|
||||
use_cases = [
|
||||
{"name": "basic", "metadata": {}},
|
||||
{"name": "docker", "metadata": {"use_docker": True, "database_type": "postgresql"}},
|
||||
{"name": "vite", "metadata": {"use_vite": True}},
|
||||
{"name": "tailwind", "metadata": {"use_tailwind": True}},
|
||||
{"name": "htmx", "metadata": {"use_htmx": True}},
|
||||
{"name": "docker_mysql", "metadata": {"use_docker": True, "database_type": "mysql"}},
|
||||
{
|
||||
"name": "all",
|
||||
"metadata": {
|
||||
"use_docker": True,
|
||||
"use_vite": True,
|
||||
"use_tailwind": True,
|
||||
"use_htmx": True,
|
||||
"database_type": "postgresql",
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
issues = []
|
||||
|
||||
for case in use_cases:
|
||||
name = case["name"]
|
||||
md = case["metadata"]
|
||||
|
||||
ctx = {"project_name": "test", "module_name": "config", "use_database_url": True, **md}
|
||||
|
||||
# Check requirements
|
||||
try:
|
||||
req = template_engine.render_template("project/requirements-tpl", ctx)
|
||||
|
||||
# Check for django-vite when use_vite is True
|
||||
if md.get("use_vite") and "django-vite" not in req:
|
||||
issues.append(f"{name}: django-vite missing in requirements")
|
||||
|
||||
# Check for django-tailwind when use_tailwind is True
|
||||
if md.get("use_tailwind") and "django-tailwind-cli" not in req:
|
||||
issues.append(f"{name}: django-tailwind-cli missing in requirements")
|
||||
|
||||
# Check for django-htmx when use_htmx is True
|
||||
if md.get("use_htmx") and "django-htmx" not in req:
|
||||
issues.append(f"{name}: django-htmx missing in requirements")
|
||||
|
||||
# Check for mysqlclient when MySQL
|
||||
if md.get("database_type") == "mysql" and "mysqlclient" not in req:
|
||||
issues.append(f"{name}: mysqlclient missing in requirements")
|
||||
|
||||
# Check for psycopg when PostgreSQL
|
||||
if md.get("database_type") != "mysql" and "psycopg" not in req:
|
||||
issues.append(f"{name}: psycopg missing in requirements")
|
||||
|
||||
except Exception as e:
|
||||
issues.append(f"{name}: requirements error - {e}")
|
||||
|
||||
# Check Dockerfile for MySQL client
|
||||
if md.get("database_type") == "mysql" and md.get("use_docker"):
|
||||
try:
|
||||
dockerfile = template_engine.render_template("project/Dockerfile-tpl", ctx)
|
||||
if "default-libmysqlclient-dev" not in dockerfile and "mysqlclient" not in dockerfile:
|
||||
issues.append(f"{name}: MySQL client missing in Dockerfile")
|
||||
except Exception as e:
|
||||
issues.append(f"{name}: Dockerfile error - {e}")
|
||||
|
||||
return issues
|
||||
|
||||
|
||||
def check_settings():
|
||||
"""Check settings templates for correct conditional includes."""
|
||||
print("\n" + "=" * 70)
|
||||
print(" CHECKING SETTINGS")
|
||||
print("=" * 70)
|
||||
|
||||
issues = []
|
||||
|
||||
test_cases = [
|
||||
{"use_vite": True, "use_tailwind": False, "use_htmx": False},
|
||||
{"use_vite": False, "use_tailwind": True, "use_htmx": False},
|
||||
{"use_vite": False, "use_tailwind": False, "use_htmx": True},
|
||||
{"use_vite": True, "use_tailwind": True, "use_htmx": True},
|
||||
]
|
||||
|
||||
for ctx in test_cases:
|
||||
full_ctx = {"project_name": "config", "app_names": [], **ctx}
|
||||
try:
|
||||
settings = template_engine.render_template("config/settings/base.py-tpl", full_ctx)
|
||||
|
||||
# Check django_vite
|
||||
if ctx.get("use_vite") and "django_vite" not in settings:
|
||||
issues.append(f"settings with {ctx}: django_vite missing")
|
||||
|
||||
if ctx.get("use_vite") and "DJANGO_VITE" not in settings:
|
||||
issues.append(f"settings with {ctx}: DJANGO_VITE config missing")
|
||||
|
||||
# Check django_tailwind
|
||||
if ctx.get("use_tailwind") and "django_tailwind_cli" not in settings:
|
||||
issues.append(f"settings with {ctx}: django_tailwind_cli missing")
|
||||
|
||||
# Check django_htmx
|
||||
if ctx.get("use_htmx") and "django_htmx" not in settings:
|
||||
issues.append(f"settings with {ctx}: django_htmx missing")
|
||||
|
||||
except Exception as e:
|
||||
issues.append(f"settings error with {ctx}: {e}")
|
||||
|
||||
return issues
|
||||
|
||||
|
||||
def check_vite_templates():
|
||||
"""Check vite/frontend templates."""
|
||||
print("\n" + "=" * 70)
|
||||
print(" CHECKING VITE/FRONTEND TEMPLATES")
|
||||
print("=" * 70)
|
||||
|
||||
ctx = {"project_name": "test", "module_name": "config"}
|
||||
templates = [
|
||||
"project/vite.config.js-tpl",
|
||||
"project/package.json-tpl",
|
||||
"project/frontend/index.html-tpl",
|
||||
"project/frontend/src/main.jsx-tpl",
|
||||
"project/frontend/src/App.jsx-tpl",
|
||||
"project/frontend/src/index.css-tpl",
|
||||
]
|
||||
|
||||
issues = []
|
||||
|
||||
for tpl in templates:
|
||||
try:
|
||||
content = template_engine.render_template(tpl, ctx)
|
||||
# Check for unreplaced variables
|
||||
if "[[" in content:
|
||||
issues.append(f"{tpl}: has unreplaced [[ variables")
|
||||
except Exception as e:
|
||||
issues.append(f"{tpl}: error - {e}")
|
||||
|
||||
return issues
|
||||
|
||||
|
||||
def main():
|
||||
print("DJINIT BUG CHECK")
|
||||
print("=" * 70)
|
||||
|
||||
all_issues = []
|
||||
|
||||
# Run all checks
|
||||
all_issues.extend(check_template_consistency())
|
||||
all_issues.extend(check_metadata_usage())
|
||||
all_issues.extend(check_all_use_cases())
|
||||
all_issues.extend(check_settings())
|
||||
all_issues.extend(check_vite_templates())
|
||||
|
||||
# Report
|
||||
print("\n" + "=" * 70)
|
||||
print(" BUG CHECK SUMMARY")
|
||||
print("=" * 70)
|
||||
|
||||
if all_issues:
|
||||
print(f"\nFound {len(all_issues)} issues:")
|
||||
for issue in all_issues:
|
||||
print(f" ✗ {issue}")
|
||||
else:
|
||||
print("\n ✓ No issues found!")
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
print(" COMPLETED")
|
||||
print("=" * 70)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,201 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Comprehensive test for all djinit features.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
|
||||
|
||||
from djinit.creators.setup import SetupCreator # noqa: E402
|
||||
|
||||
|
||||
def create_project(name, metadata):
|
||||
"""Create a test project."""
|
||||
test_dir = "/tmp/djinit_full_test"
|
||||
os.makedirs(test_dir, exist_ok=True)
|
||||
original_cwd = os.getcwd()
|
||||
|
||||
try:
|
||||
os.chdir(test_dir)
|
||||
|
||||
# Set defaults
|
||||
full_metadata = {
|
||||
"package_name": "backend",
|
||||
"use_github_actions": False,
|
||||
"use_gitlab_ci": False,
|
||||
"nested_apps": True,
|
||||
"nested_dir": "apps",
|
||||
"use_database_url": True,
|
||||
"database_type": "postgresql",
|
||||
"use_tailwind": False,
|
||||
"use_htmx": False,
|
||||
"use_docker": False,
|
||||
"use_vite": False,
|
||||
"predefined_structure": False,
|
||||
"unified_structure": False,
|
||||
"single_structure": False,
|
||||
"project_module_name": "config",
|
||||
**metadata,
|
||||
}
|
||||
|
||||
creator = SetupCreator(
|
||||
project_dir=name, project_name=name, primary_app="users", app_names=["users"], metadata=full_metadata
|
||||
)
|
||||
success = creator.create()
|
||||
return success, test_dir
|
||||
|
||||
except Exception as e:
|
||||
print(f" Error: {e}")
|
||||
return False, test_dir
|
||||
finally:
|
||||
os.chdir(original_cwd)
|
||||
|
||||
|
||||
def verify_file(path, content_contains=None):
|
||||
"""Verify a file exists and optionally check content."""
|
||||
if not os.path.exists(path):
|
||||
return False, f"File not found: {path}"
|
||||
if content_contains:
|
||||
with open(path) as f:
|
||||
content = f.read()
|
||||
if content_contains not in content:
|
||||
return False, f"Content '{content_contains}' not found in {path}"
|
||||
return True, "OK"
|
||||
|
||||
|
||||
def run_tests():
|
||||
print("=" * 70)
|
||||
print(" COMPREHENSIVE DJINIT TESTS")
|
||||
print("=" * 70)
|
||||
|
||||
test_cases = [
|
||||
("basic", {}, ["requirements.txt"]),
|
||||
("docker", {"use_docker": True}, ["Dockerfile", "docker-compose.yml"]),
|
||||
("vite", {"use_vite": True}, ["vite.config.js", "package.json", "frontend/index.html"]),
|
||||
("tailwind", {"use_tailwind": True}, ["requirements.txt"]),
|
||||
("htmx", {"use_htmx": True}, ["requirements.txt"]),
|
||||
(
|
||||
"all_postgres",
|
||||
{
|
||||
"use_docker": True,
|
||||
"use_vite": True,
|
||||
"use_tailwind": True,
|
||||
"use_htmx": True,
|
||||
"database_type": "postgresql",
|
||||
},
|
||||
["Dockerfile", "docker-compose.yml", "vite.config.js"],
|
||||
),
|
||||
(
|
||||
"all_mysql",
|
||||
{"use_docker": True, "use_vite": True, "use_tailwind": True, "database_type": "mysql"},
|
||||
["Dockerfile", "docker-compose.yml", "vite.config.js"],
|
||||
),
|
||||
]
|
||||
|
||||
results = []
|
||||
|
||||
for name, metadata, expected_files in test_cases:
|
||||
print(f"\n{'─' * 70}")
|
||||
print(f" Testing: {name}")
|
||||
print(f" Metadata: {metadata}")
|
||||
|
||||
# Clean up
|
||||
import shutil
|
||||
|
||||
test_dir = "/tmp/djinit_full_test"
|
||||
if os.path.exists(test_dir):
|
||||
shutil.rmtree(test_dir)
|
||||
|
||||
# Create project
|
||||
success, _ = create_project(name, metadata)
|
||||
|
||||
if not success:
|
||||
results.append((name, False, "Project creation failed"))
|
||||
print(" ✗ Failed to create project")
|
||||
continue
|
||||
|
||||
# Verify files
|
||||
project_dir = os.path.join(test_dir, name)
|
||||
all_passed = True
|
||||
errors = []
|
||||
|
||||
for file in expected_files:
|
||||
file_path = os.path.join(project_dir, file)
|
||||
exists, msg = verify_file(file_path)
|
||||
if not exists:
|
||||
all_passed = False
|
||||
errors.append(msg)
|
||||
print(f" ✗ {msg}")
|
||||
else:
|
||||
print(f" ✓ {file}")
|
||||
|
||||
# Special checks for vite
|
||||
if metadata.get("use_vite"):
|
||||
settings_path = os.path.join(project_dir, "config/settings/base.py")
|
||||
exists, msg = verify_file(settings_path, "django_vite")
|
||||
if not exists:
|
||||
all_passed = False
|
||||
errors.append("django_vite not in settings")
|
||||
print(" ✗ django_vite not in settings")
|
||||
else:
|
||||
print(" ✓ django_vite in settings")
|
||||
|
||||
req_path = os.path.join(project_dir, "requirements.txt")
|
||||
exists, msg = verify_file(req_path, "django-vite")
|
||||
if not exists:
|
||||
all_passed = False
|
||||
errors.append("django-vite not in requirements")
|
||||
print(" ✗ django-vite not in requirements")
|
||||
else:
|
||||
print(" ✓ django-vite in requirements")
|
||||
|
||||
# Special checks for docker
|
||||
if metadata.get("use_docker"):
|
||||
dockerfile_path = os.path.join(project_dir, "Dockerfile")
|
||||
db_type = metadata.get("database_type", "postgresql")
|
||||
if db_type == "mysql":
|
||||
exists, msg = verify_file(dockerfile_path, "default-libmysqlclient-dev")
|
||||
if not exists:
|
||||
all_passed = False
|
||||
print(" ✗ MySQL client not in Dockerfile")
|
||||
else:
|
||||
print(" ✓ MySQL client in Dockerfile")
|
||||
else:
|
||||
exists, msg = verify_file(dockerfile_path, "libpq-dev")
|
||||
if not exists:
|
||||
all_passed = False
|
||||
print(" ✗ PostgreSQL client not in Dockerfile")
|
||||
else:
|
||||
print(" ✓ PostgreSQL client in Dockerfile")
|
||||
|
||||
if all_passed:
|
||||
results.append((name, True, "All checks passed"))
|
||||
print(" ✓ ALL CHECKS PASSED")
|
||||
else:
|
||||
results.append((name, False, "; ".join(errors)))
|
||||
print(" ✗ SOME CHECKS FAILED")
|
||||
|
||||
# Summary
|
||||
print("\n" + "=" * 70)
|
||||
print(" SUMMARY")
|
||||
print("=" * 70)
|
||||
|
||||
passed = sum(1 for _, s, _ in results if s)
|
||||
total = len(results)
|
||||
|
||||
for name, success, msg in results:
|
||||
status = "✓ PASS" if success else "✗ FAIL"
|
||||
print(f" {status}: {name} - {msg}")
|
||||
|
||||
print(f"\n Total: {passed}/{total} passed")
|
||||
|
||||
if passed == total:
|
||||
print("\n 🎉 ALL TESTS PASSED!")
|
||||
else:
|
||||
print("\n ⚠️ SOME TESTS FAILED!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_tests()
|
||||
@@ -0,0 +1,189 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generate real test projects to verify djinit works correctly.
|
||||
Run from djinit project root: .venv/bin/python examples/generate_projects.py
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Add src to path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
|
||||
|
||||
from djinit.creators.setup import SetupCreator # noqa: E402
|
||||
|
||||
|
||||
def create_test_project(name, structure, metadata, use_github=False, use_gitlab=False):
|
||||
"""Create a test project."""
|
||||
# Create temp directory
|
||||
test_dir = os.path.join("/tmp/djinit_tests", name)
|
||||
os.makedirs(test_dir, exist_ok=True)
|
||||
original_cwd = os.getcwd()
|
||||
|
||||
try:
|
||||
os.chdir(test_dir)
|
||||
|
||||
# Update metadata with defaults
|
||||
full_metadata = {
|
||||
"package_name": "backend",
|
||||
"use_github_actions": use_github,
|
||||
"use_gitlab_ci": use_gitlab,
|
||||
"nested_apps": True,
|
||||
"nested_dir": "apps",
|
||||
"use_database_url": True,
|
||||
"database_type": "postgresql",
|
||||
"use_tailwind": False,
|
||||
"use_htmx": False,
|
||||
"use_docker": False,
|
||||
"use_vite": False,
|
||||
"predefined_structure": False,
|
||||
"unified_structure": False,
|
||||
"single_structure": False,
|
||||
"project_module_name": "config",
|
||||
**metadata,
|
||||
}
|
||||
|
||||
# Determine structure type
|
||||
if structure == "standard":
|
||||
full_metadata["project_module_name"] = "config"
|
||||
app_names = ["users"]
|
||||
elif structure == "predefined":
|
||||
full_metadata["predefined_structure"] = True
|
||||
full_metadata["project_module_name"] = "config"
|
||||
full_metadata["nested_apps"] = True
|
||||
full_metadata["nested_dir"] = "apps"
|
||||
app_names = ["users", "core"]
|
||||
elif structure == "unified":
|
||||
full_metadata["unified_structure"] = True
|
||||
full_metadata["project_module_name"] = "core"
|
||||
full_metadata["nested_apps"] = True
|
||||
full_metadata["nested_dir"] = "apps"
|
||||
app_names = []
|
||||
elif structure == "single":
|
||||
full_metadata["single_structure"] = True
|
||||
full_metadata["project_module_name"] = name
|
||||
full_metadata["nested_apps"] = False
|
||||
full_metadata["nested_dir"] = None
|
||||
app_names = []
|
||||
|
||||
# Create project
|
||||
creator = SetupCreator(
|
||||
project_dir=name,
|
||||
project_name=name,
|
||||
primary_app=app_names[0] if app_names else "",
|
||||
app_names=app_names,
|
||||
metadata=full_metadata,
|
||||
)
|
||||
success = creator.create()
|
||||
|
||||
if success:
|
||||
print(f" ✓ Created: {test_dir}")
|
||||
return True
|
||||
else:
|
||||
print(f" ✗ Failed: {test_dir}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f" ✗ Error creating {name}: {e}")
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
return False
|
||||
finally:
|
||||
os.chdir(original_cwd)
|
||||
|
||||
|
||||
def list_project_files(project_dir):
|
||||
"""List key files in the project."""
|
||||
print("\n Key files:")
|
||||
key_files = [
|
||||
"requirements.txt",
|
||||
"Dockerfile",
|
||||
"docker-compose.yml",
|
||||
"package.json",
|
||||
"vite.config.js",
|
||||
"settings/base.py",
|
||||
"manage.py",
|
||||
]
|
||||
|
||||
for root, dirs, files in os.walk(project_dir):
|
||||
# Skip hidden and common dirs
|
||||
dirs[:] = [d for d in dirs if not d.startswith(".") and d not in ("__pycache__", "node_modules")]
|
||||
|
||||
for f in files:
|
||||
if f in key_files:
|
||||
rel_path = os.path.relpath(os.path.join(root, f), project_dir)
|
||||
print(f" - {rel_path}")
|
||||
|
||||
|
||||
def main():
|
||||
print("=" * 70)
|
||||
print(" DJINIT - GENERATE REAL TEST PROJECTS")
|
||||
print("=" * 70)
|
||||
|
||||
# Create test directory
|
||||
os.makedirs("/tmp/djinit_tests", exist_ok=True)
|
||||
|
||||
# Define test projects
|
||||
test_projects = [
|
||||
# Structure: (name, structure, metadata)
|
||||
("test_standard_basic", "standard", {}),
|
||||
("test_standard_docker", "standard", {"use_docker": True}),
|
||||
("test_standard_vite", "standard", {"use_vite": True}),
|
||||
(
|
||||
"test_standard_all",
|
||||
"standard",
|
||||
{"use_docker": True, "use_vite": True, "use_tailwind": True, "use_htmx": True},
|
||||
),
|
||||
("test_predefined_basic", "predefined", {}),
|
||||
("test_predefined_docker", "predefined", {"use_docker": True}),
|
||||
("test_unified_basic", "unified", {}),
|
||||
("test_unified_all", "unified", {"use_docker": True, "use_vite": True, "use_tailwind": True}),
|
||||
("test_single_basic", "single", {}),
|
||||
("test_single_docker", "single", {"use_docker": True}),
|
||||
]
|
||||
|
||||
results = []
|
||||
|
||||
for name, structure, metadata in test_projects:
|
||||
print(f"\n{'─' * 70}")
|
||||
print(f" Creating: {name}")
|
||||
print(f" Structure: {structure}")
|
||||
print(f" Features: {list(metadata.keys()) if metadata else ['basic']}")
|
||||
|
||||
success = create_test_project(name, structure, metadata)
|
||||
results.append((name, success))
|
||||
|
||||
if success:
|
||||
list_project_files(f"/tmp/djinit_tests/{name}")
|
||||
|
||||
# Summary
|
||||
print("\n" + "=" * 70)
|
||||
print(" SUMMARY")
|
||||
print("=" * 70)
|
||||
|
||||
passed = sum(1 for _, s in results if s)
|
||||
total = len(results)
|
||||
|
||||
for name, success in results:
|
||||
status = "✓ PASS" if success else "✗ FAIL"
|
||||
print(f" {status}: {name}")
|
||||
|
||||
print(f"\n Total: {passed}/{total} passed")
|
||||
|
||||
if passed == total:
|
||||
print("\n 🎉 All tests passed!")
|
||||
else:
|
||||
print("\n ⚠️ Some tests failed!")
|
||||
|
||||
# Show created directories
|
||||
print("\n" + "=" * 70)
|
||||
print(" CREATED PROJECTS")
|
||||
print("=" * 70)
|
||||
for name, _ in results:
|
||||
print(f" /tmp/djinit_tests/{name}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,357 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Comprehensive test for ALL djinit features and use cases.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src")) # noqa: E402
|
||||
|
||||
import shutil # noqa: E402
|
||||
|
||||
from djinit.creators.setup import SetupCreator # noqa: E402
|
||||
|
||||
|
||||
def create_project(name, metadata, structure="standard"):
|
||||
test_dir = "/tmp/djinit_full_test"
|
||||
os.makedirs(test_dir, exist_ok=True)
|
||||
original_cwd = os.getcwd()
|
||||
|
||||
try:
|
||||
os.chdir(test_dir)
|
||||
|
||||
full_metadata = {
|
||||
"package_name": "backend",
|
||||
"use_github_actions": False,
|
||||
"use_gitlab_ci": False,
|
||||
"nested_apps": True,
|
||||
"nested_dir": "apps",
|
||||
"use_database_url": True,
|
||||
"database_type": "postgresql",
|
||||
"use_tailwind": False,
|
||||
"use_htmx": False,
|
||||
"use_docker": False,
|
||||
"use_vite": False,
|
||||
"use_vue": False,
|
||||
"use_pytest": False,
|
||||
"predefined_structure": False,
|
||||
"unified_structure": False,
|
||||
"single_structure": False,
|
||||
"project_module_name": "config",
|
||||
**metadata,
|
||||
}
|
||||
|
||||
# Handle different structures
|
||||
if structure == "predefined":
|
||||
full_metadata["predefined_structure"] = True
|
||||
full_metadata["project_module_name"] = "config"
|
||||
full_metadata["nested_apps"] = True
|
||||
full_metadata["nested_dir"] = "apps"
|
||||
app_names = ["users", "core"]
|
||||
elif structure == "unified":
|
||||
full_metadata["unified_structure"] = True
|
||||
full_metadata["project_module_name"] = "core"
|
||||
full_metadata["nested_apps"] = True
|
||||
full_metadata["nested_dir"] = "apps"
|
||||
app_names = []
|
||||
elif structure == "single":
|
||||
full_metadata["single_structure"] = True
|
||||
full_metadata["project_module_name"] = name
|
||||
full_metadata["nested_apps"] = False
|
||||
full_metadata["nested_dir"] = None
|
||||
app_names = []
|
||||
else: # standard
|
||||
full_metadata["project_module_name"] = "config"
|
||||
app_names = ["users"]
|
||||
|
||||
creator = SetupCreator(
|
||||
project_dir=name,
|
||||
project_name=name,
|
||||
primary_app=app_names[0] if app_names else "users",
|
||||
app_names=app_names,
|
||||
metadata=full_metadata,
|
||||
)
|
||||
success = creator.create()
|
||||
return success, test_dir
|
||||
|
||||
except Exception as e:
|
||||
print(f" Error: {e}")
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
return False, test_dir
|
||||
finally:
|
||||
os.chdir(original_cwd)
|
||||
|
||||
|
||||
def verify_file(path, content_contains=None):
|
||||
if not os.path.exists(path):
|
||||
return False, f"File not found: {path}"
|
||||
if content_contains:
|
||||
with open(path) as f:
|
||||
content = f.read()
|
||||
if content_contains not in content:
|
||||
return False, f"Content '{content_contains}' not found in {path}"
|
||||
return True, "OK"
|
||||
|
||||
|
||||
def run_all_tests():
|
||||
print("=" * 70)
|
||||
print(" DJINIT - COMPLETE TEST SUITE")
|
||||
print("=" * 70)
|
||||
|
||||
# All test cases: (name, structure, metadata, expected_files, extra_checks)
|
||||
test_cases = [
|
||||
# Basic tests
|
||||
("basic_std", "standard", {}, ["requirements.txt"], []),
|
||||
("basic_pre", "predefined", {}, ["requirements.txt"], []),
|
||||
("basic_uni", "unified", {}, ["requirements.txt"], []),
|
||||
("basic_sgl", "single", {}, ["requirements.txt"], []),
|
||||
# Single features
|
||||
(
|
||||
"docker",
|
||||
"standard",
|
||||
{"use_docker": True, "database_type": "postgresql"},
|
||||
["Dockerfile", "docker-compose.yml"],
|
||||
["postgres"],
|
||||
),
|
||||
(
|
||||
"docker_mysql",
|
||||
"standard",
|
||||
{"use_docker": True, "database_type": "mysql"},
|
||||
["Dockerfile", "docker-compose.yml"],
|
||||
["mysql"],
|
||||
),
|
||||
("react", "standard", {"use_vite": True}, ["vite.config.js", "package.json"], ["react"]),
|
||||
("vue", "standard", {"use_vue": True}, ["vite.config.js", "package.json"], ["vue"]),
|
||||
("tailwind", "standard", {"use_tailwind": True}, ["requirements.txt"], ["tailwind"]),
|
||||
("htmx", "standard", {"use_htmx": True}, ["requirements.txt"], ["htmx"]),
|
||||
("pytest", "standard", {"use_pytest": True}, ["pytest.ini", "conftest.py"], ["pytest"]),
|
||||
# Combinations
|
||||
(
|
||||
"docker_react",
|
||||
"standard",
|
||||
{"use_docker": True, "use_vite": True, "database_type": "postgresql"},
|
||||
["Dockerfile", "vite.config.js"],
|
||||
["postgres", "react"],
|
||||
),
|
||||
(
|
||||
"docker_vue",
|
||||
"standard",
|
||||
{"use_docker": True, "use_vue": True, "database_type": "postgresql"},
|
||||
["Dockerfile", "vite.config.js"],
|
||||
["postgres", "vue"],
|
||||
),
|
||||
(
|
||||
"react_pytest",
|
||||
"standard",
|
||||
{"use_vite": True, "use_pytest": True},
|
||||
["vite.config.js", "pytest.ini"],
|
||||
["react", "pytest"],
|
||||
),
|
||||
(
|
||||
"vue_pytest",
|
||||
"standard",
|
||||
{"use_vue": True, "use_pytest": True},
|
||||
["vite.config.js", "pytest.ini"],
|
||||
["vue", "pytest"],
|
||||
),
|
||||
(
|
||||
"tailwind_htmx",
|
||||
"standard",
|
||||
{"use_tailwind": True, "use_htmx": True},
|
||||
["requirements.txt"],
|
||||
["tailwind", "htmx"],
|
||||
),
|
||||
# All features
|
||||
(
|
||||
"all_postgres",
|
||||
"standard",
|
||||
{
|
||||
"use_docker": True,
|
||||
"use_vite": True,
|
||||
"use_tailwind": True,
|
||||
"use_htmx": True,
|
||||
"use_pytest": True,
|
||||
"database_type": "postgresql",
|
||||
},
|
||||
["Dockerfile", "vite.config.js", "pytest.ini"],
|
||||
["postgres", "react", "pytest"],
|
||||
),
|
||||
(
|
||||
"all_mysql",
|
||||
"standard",
|
||||
{"use_docker": True, "use_vue": True, "use_tailwind": True, "use_pytest": True, "database_type": "mysql"},
|
||||
["Dockerfile", "vite.config.js", "pytest.ini"],
|
||||
["mysql", "vue", "pytest"],
|
||||
),
|
||||
# Structure tests with features
|
||||
(
|
||||
"predefined_docker",
|
||||
"predefined",
|
||||
{"use_docker": True, "database_type": "postgresql"},
|
||||
["Dockerfile"],
|
||||
["postgres"],
|
||||
),
|
||||
("predefined_vue", "predefined", {"use_vue": True}, ["vite.config.js"], ["vue"]),
|
||||
(
|
||||
"unified_docker",
|
||||
"unified",
|
||||
{"use_docker": True, "database_type": "postgresql"},
|
||||
["Dockerfile"],
|
||||
["postgres"],
|
||||
),
|
||||
("unified_vue", "unified", {"use_vue": True}, ["vite.config.js"], ["vue"]),
|
||||
("single_docker", "single", {"use_docker": True, "database_type": "postgresql"}, ["Dockerfile"], ["postgres"]),
|
||||
("single_vue", "single", {"use_vue": True}, ["vite.config.js"], ["vue"]),
|
||||
]
|
||||
|
||||
results = []
|
||||
|
||||
for name, structure, metadata, expected_files, extra_checks in test_cases:
|
||||
print(f"\n{'─' * 70}")
|
||||
print(f" Testing: {name} (structure: {structure})")
|
||||
print(f" Features: {list(metadata.keys()) if metadata else ['basic']}")
|
||||
|
||||
test_dir = "/tmp/djinit_full_test"
|
||||
if os.path.exists(test_dir):
|
||||
shutil.rmtree(test_dir)
|
||||
|
||||
success, _ = create_project(name, metadata, structure)
|
||||
|
||||
if not success:
|
||||
results.append((name, False, "Project creation failed"))
|
||||
print(" ✗ Failed to create project")
|
||||
continue
|
||||
|
||||
project_dir = os.path.join(test_dir, name)
|
||||
all_passed = True
|
||||
errors = []
|
||||
|
||||
# Check expected files
|
||||
for file in expected_files:
|
||||
file_path = os.path.join(project_dir, file)
|
||||
exists, msg = verify_file(file_path)
|
||||
if not exists:
|
||||
all_passed = False
|
||||
errors.append(msg)
|
||||
print(f" ✗ {msg}")
|
||||
else:
|
||||
print(f" ✓ {file}")
|
||||
|
||||
# Run extra checks
|
||||
for check in extra_checks:
|
||||
if check == "postgres":
|
||||
dockerfile_path = os.path.join(project_dir, "Dockerfile")
|
||||
exists, _ = verify_file(dockerfile_path, "libpq-dev")
|
||||
if not exists:
|
||||
all_passed = False
|
||||
print(" ✗ PostgreSQL client missing in Dockerfile")
|
||||
else:
|
||||
print(" ✓ PostgreSQL in Dockerfile")
|
||||
|
||||
elif check == "mysql":
|
||||
dockerfile_path = os.path.join(project_dir, "Dockerfile")
|
||||
exists, _ = verify_file(dockerfile_path, "default-libmysqlclient-dev")
|
||||
if not exists:
|
||||
all_passed = False
|
||||
print(" ✗ MySQL client missing in Dockerfile")
|
||||
else:
|
||||
print(" ✓ MySQL in Dockerfile")
|
||||
|
||||
elif check == "react":
|
||||
pkg_path = os.path.join(project_dir, "package.json")
|
||||
exists, _ = verify_file(pkg_path, "react")
|
||||
if not exists:
|
||||
all_passed = False
|
||||
print(" ✗ React not in package.json")
|
||||
else:
|
||||
print(" ✓ React in package.json")
|
||||
|
||||
elif check == "vue":
|
||||
pkg_path = os.path.join(project_dir, "package.json")
|
||||
exists, _ = verify_file(pkg_path, "vue")
|
||||
if not exists:
|
||||
all_passed = False
|
||||
print(" ✗ Vue not in package.json")
|
||||
else:
|
||||
print(" ✓ Vue in package.json")
|
||||
|
||||
vue_file = os.path.join(project_dir, "frontend/src/App.vue")
|
||||
exists, _ = verify_file(vue_file)
|
||||
if not exists:
|
||||
all_passed = False
|
||||
print(" ✗ App.vue not found")
|
||||
else:
|
||||
print(" ✓ App.vue found")
|
||||
|
||||
elif check == "pytest":
|
||||
req_path = os.path.join(project_dir, "requirements.txt")
|
||||
exists, _ = verify_file(req_path, "pytest")
|
||||
if not exists:
|
||||
all_passed = False
|
||||
print(" ✗ pytest not in requirements")
|
||||
else:
|
||||
print(" ✓ pytest in requirements")
|
||||
|
||||
pytest_path = os.path.join(project_dir, "pytest.ini")
|
||||
exists, _ = verify_file(pytest_path, "DJANGO_SETTINGS_MODULE")
|
||||
if not exists:
|
||||
all_passed = False
|
||||
print(" ✗ pytest.ini incomplete")
|
||||
else:
|
||||
print(" ✓ pytest.ini configured")
|
||||
|
||||
elif check == "tailwind":
|
||||
req_path = os.path.join(project_dir, "requirements.txt")
|
||||
exists, _ = verify_file(req_path, "django-tailwind-cli")
|
||||
if not exists:
|
||||
all_passed = False
|
||||
print(" ✗ Tailwind not in requirements")
|
||||
else:
|
||||
print(" ✓ Tailwind in requirements")
|
||||
|
||||
elif check == "htmx":
|
||||
req_path = os.path.join(project_dir, "requirements.txt")
|
||||
exists, _ = verify_file(req_path, "django-htmx")
|
||||
if not exists:
|
||||
all_passed = False
|
||||
print(" ✗ HTMX not in requirements")
|
||||
else:
|
||||
print(" ✓ HTMX in requirements")
|
||||
|
||||
if all_passed:
|
||||
results.append((name, True, "All checks passed"))
|
||||
print(" ✓ ALL CHECKS PASSED")
|
||||
else:
|
||||
results.append((name, False, "; ".join(errors)))
|
||||
print(" ✗ SOME CHECKS FAILED")
|
||||
|
||||
# Summary
|
||||
print("\n" + "=" * 70)
|
||||
print(" FINAL SUMMARY")
|
||||
print("=" * 70)
|
||||
|
||||
passed = sum(1 for _, s, _ in results if s)
|
||||
total = len(results)
|
||||
|
||||
print(f"\nTotal Tests: {total}")
|
||||
print(f"Passed: {passed}")
|
||||
print(f"Failed: {total - passed}")
|
||||
print()
|
||||
|
||||
for name, success, _msg in results:
|
||||
status = "✓ PASS" if success else "✗ FAIL"
|
||||
print(f" {status}: {name}")
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
if passed == total:
|
||||
print(" 🎉 ALL TESTS PASSED! 🎉")
|
||||
else:
|
||||
print(" ⚠️ SOME TESTS FAILED!")
|
||||
print("=" * 70)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_all_tests()
|
||||
@@ -0,0 +1,405 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test script to show hello world examples for all project structures and cases.
|
||||
Run from the djinit project root: PYTHONPATH=src python3 examples/test_all_structures.py
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src")) # noqa: E402
|
||||
|
||||
from djinit.templater import template_engine # noqa: E402
|
||||
|
||||
|
||||
def print_section(title):
|
||||
print("\n" + "=" * 70)
|
||||
print(f" {title}")
|
||||
print("=" * 70)
|
||||
|
||||
|
||||
def print_subsection(title):
|
||||
print(f"\n{'─' * 70}")
|
||||
print(f" {title}")
|
||||
print(f"{'─' * 70}")
|
||||
|
||||
|
||||
# Define all test cases
|
||||
CASES = [
|
||||
(
|
||||
"Basic (no optional features)",
|
||||
{
|
||||
"use_docker": False,
|
||||
"use_vite": False,
|
||||
"use_tailwind": False,
|
||||
"use_htmx": False,
|
||||
"database_type": "postgresql",
|
||||
"use_database_url": True,
|
||||
},
|
||||
),
|
||||
(
|
||||
"Docker only",
|
||||
{
|
||||
"use_docker": True,
|
||||
"use_vite": False,
|
||||
"use_tailwind": False,
|
||||
"use_htmx": False,
|
||||
"database_type": "postgresql",
|
||||
"use_database_url": True,
|
||||
},
|
||||
),
|
||||
(
|
||||
"Vite/React only",
|
||||
{
|
||||
"use_docker": False,
|
||||
"use_vite": True,
|
||||
"use_tailwind": False,
|
||||
"use_htmx": False,
|
||||
"database_type": "postgresql",
|
||||
"use_database_url": True,
|
||||
},
|
||||
),
|
||||
(
|
||||
"Docker + Vite",
|
||||
{
|
||||
"use_docker": True,
|
||||
"use_vite": True,
|
||||
"use_tailwind": False,
|
||||
"use_htmx": False,
|
||||
"database_type": "postgresql",
|
||||
"use_database_url": True,
|
||||
},
|
||||
),
|
||||
(
|
||||
"Tailwind only",
|
||||
{
|
||||
"use_docker": False,
|
||||
"use_vite": False,
|
||||
"use_tailwind": True,
|
||||
"use_htmx": False,
|
||||
"database_type": "postgresql",
|
||||
"use_database_url": True,
|
||||
},
|
||||
),
|
||||
(
|
||||
"HTMX only",
|
||||
{
|
||||
"use_docker": False,
|
||||
"use_vite": False,
|
||||
"use_tailwind": False,
|
||||
"use_htmx": True,
|
||||
"database_type": "postgresql",
|
||||
"use_database_url": True,
|
||||
},
|
||||
),
|
||||
(
|
||||
"Tailwind + HTMX",
|
||||
{
|
||||
"use_docker": False,
|
||||
"use_vite": False,
|
||||
"use_tailwind": True,
|
||||
"use_htmx": True,
|
||||
"database_type": "postgresql",
|
||||
"use_database_url": True,
|
||||
},
|
||||
),
|
||||
(
|
||||
"All: Docker + Vite + Tailwind + HTMX + PostgreSQL",
|
||||
{
|
||||
"use_docker": True,
|
||||
"use_vite": True,
|
||||
"use_tailwind": True,
|
||||
"use_htmx": True,
|
||||
"database_type": "postgresql",
|
||||
"use_database_url": True,
|
||||
},
|
||||
),
|
||||
(
|
||||
"All: Docker + Vite + Tailwind + HTMX + MySQL",
|
||||
{
|
||||
"use_docker": True,
|
||||
"use_vite": True,
|
||||
"use_tailwind": True,
|
||||
"use_htmx": True,
|
||||
"database_type": "mysql",
|
||||
"use_database_url": True,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
STRUCTURES = [
|
||||
("standard", "config", "Standard Structure (default Django layout)"),
|
||||
("predefined", "config", "Predefined Structure (apps/users, apps/core, api/)"),
|
||||
("unified", "core", "Unified Structure (core/, apps/core, apps/api)"),
|
||||
("single", "myproject", "Single Folder Layout (everything in one folder)"),
|
||||
]
|
||||
|
||||
|
||||
def show_file_tree(struct_type, case_name, metadata):
|
||||
"""Show expected file tree for a structure + case combination."""
|
||||
|
||||
module_name = {"standard": "config", "predefined": "config", "unified": "core", "single": "myproject"}[struct_type]
|
||||
|
||||
features = []
|
||||
if metadata.get("use_docker"):
|
||||
features.append("Docker")
|
||||
if metadata.get("use_vite"):
|
||||
features.append("Vite")
|
||||
if metadata.get("use_tailwind"):
|
||||
features.append("Tailwind")
|
||||
if metadata.get("use_htmx"):
|
||||
features.append("HTMX")
|
||||
features.append(metadata.get("database_type", "postgresql").upper())
|
||||
|
||||
print(f" Features: {', '.join(features)}")
|
||||
print(" Project: myproject/")
|
||||
print(f" Module: {module_name}/")
|
||||
|
||||
indent = " "
|
||||
|
||||
# Always present
|
||||
print(f"{indent}├── .env.sample")
|
||||
print(f"{indent}├── .gitignore")
|
||||
print(f"{indent}├── requirements.txt")
|
||||
print(f"{indent}├── pyproject.toml")
|
||||
print(f"{indent}├── Procfile")
|
||||
print(f"{indent}├── justfile")
|
||||
print(f"{indent}├── runtime.txt")
|
||||
|
||||
if metadata.get("use_docker"):
|
||||
print(f"{indent}├── Dockerfile")
|
||||
print(f"{indent}├── docker-compose.yml")
|
||||
print(f"{indent}├── .dockerignore")
|
||||
|
||||
if struct_type == "standard":
|
||||
print(f"{indent}├── config/")
|
||||
print(f"{indent}│ ├── __init__.py")
|
||||
print(f"{indent}│ ├── settings/")
|
||||
print(f"{indent}│ │ ├── __init__.py")
|
||||
print(f"{indent}│ │ ├── base.py")
|
||||
print(f"{indent}│ │ ├── development.py")
|
||||
print(f"{indent}│ │ └── production.py")
|
||||
print(f"{indent}│ ├── urls.py")
|
||||
print(f"{indent}│ ├── wsgi.py")
|
||||
print(f"{indent}│ └── asgi.py")
|
||||
print(f"{indent}├── users/")
|
||||
print(f"{indent}│ ├── __init__.py")
|
||||
print(f"{indent}│ ├── models.py")
|
||||
print(f"{indent}│ ├── views.py")
|
||||
print(f"{indent}│ ├── urls.py")
|
||||
print(f"{indent}│ └── apps.py")
|
||||
print(f"{indent}└── manage.py")
|
||||
|
||||
elif struct_type == "predefined":
|
||||
print(f"{indent}├── config/")
|
||||
print(f"{indent}│ ├── __init__.py")
|
||||
print(f"{indent}│ ├── settings/")
|
||||
print(f"{indent}│ │ ├── __init__.py")
|
||||
print(f"{indent}│ │ ├── base.py")
|
||||
print(f"{indent}│ │ ├── development.py")
|
||||
print(f"{indent}│ │ └── production.py")
|
||||
print(f"{indent}│ ├── urls.py")
|
||||
print(f"{indent}│ ├── wsgi.py")
|
||||
print(f"{indent}│ └── asgi.py")
|
||||
print(f"{indent}├── apps/")
|
||||
print(f"{indent}│ ├── __init__.py")
|
||||
print(f"{indent}│ ├── core/")
|
||||
print(f"{indent}│ │ ├── __init__.py")
|
||||
print(f"{indent}│ │ ├── utils/")
|
||||
print(f"{indent}│ │ ├── mixins/")
|
||||
print(f"{indent}│ │ └── middleware/")
|
||||
print(f"{indent}│ └── users/")
|
||||
print(f"{indent}│ ├── __init__.py")
|
||||
print(f"{indent}│ └── apps.py")
|
||||
print(f"{indent}├── api/")
|
||||
print(f"{indent}│ ├── __init__.py")
|
||||
print(f"{indent}│ ├── urls.py")
|
||||
print(f"{indent}│ └── v1/")
|
||||
print(f"{indent}│ ├── __init__.py")
|
||||
print(f"{indent}│ └── urls.py")
|
||||
print(f"{indent}└── manage.py")
|
||||
|
||||
elif struct_type == "unified":
|
||||
print(f"{indent}├── core/")
|
||||
print(f"{indent}│ ├── __init__.py")
|
||||
print(f"{indent}│ ├── settings/")
|
||||
print(f"{indent}│ │ ├── __init__.py")
|
||||
print(f"{indent}│ │ ├── base.py")
|
||||
print(f"{indent}│ │ ├── development.py")
|
||||
print(f"{indent}│ │ └── production.py")
|
||||
print(f"{indent}│ ├── urls.py")
|
||||
print(f"{indent}│ ├── wsgi.py")
|
||||
print(f"{indent}│ └── asgi.py")
|
||||
print(f"{indent}├── apps/")
|
||||
print(f"{indent}│ ├── __init__.py")
|
||||
print(f"{indent}│ ├── apps.py")
|
||||
print(f"{indent}│ ├── api/")
|
||||
print(f"{indent}│ │ ├── __init__.py")
|
||||
print(f"{indent}│ │ ├── urls.py")
|
||||
print(f"{indent}│ │ └── v1/")
|
||||
print(f"{indent}│ │ ├── __init__.py")
|
||||
print(f"{indent}│ │ └── urls.py")
|
||||
print(f"{indent}│ ├── models/")
|
||||
print(f"{indent}│ ├── views/")
|
||||
print(f"{indent}│ ├── serializers/")
|
||||
print(f"{indent}│ ├── tests/")
|
||||
print(f"{indent}│ └── urls/")
|
||||
print(f"{indent}└── manage.py")
|
||||
|
||||
else: # single
|
||||
print(f"{indent}├── myproject/")
|
||||
print(f"{indent}│ ├── __init__.py")
|
||||
print(f"{indent}│ ├── settings/")
|
||||
print(f"{indent}│ │ ├── __init__.py")
|
||||
print(f"{indent}│ │ ├── base.py")
|
||||
print(f"{indent}│ │ ├── development.py")
|
||||
print(f"{indent}│ │ └── production.py")
|
||||
print(f"{indent}│ ├── urls.py")
|
||||
print(f"{indent}│ ├── wsgi.py")
|
||||
print(f"{indent}│ ├── asgi.py")
|
||||
print(f"{indent}│ ├── api/")
|
||||
print(f"{indent}│ ├── models/")
|
||||
print(f"{indent}│ ├── admin/")
|
||||
print(f"{indent}│ └── tests/")
|
||||
print(f"{indent}└── manage.py")
|
||||
|
||||
if metadata.get("use_vite"):
|
||||
print(f"{indent}├── frontend/")
|
||||
print(f"{indent}│ ├── index.html")
|
||||
print(f"{indent}│ ├── vite.config.js")
|
||||
print(f"{indent}│ ├── package.json")
|
||||
print(f"{indent}│ └── src/")
|
||||
print(f"{indent}│ ├── main.jsx")
|
||||
print(f"{indent}│ ├── App.jsx")
|
||||
print(f"{indent}│ └── index.css")
|
||||
print(f"{indent}└── static/")
|
||||
|
||||
if metadata.get("use_tailwind"):
|
||||
print(f"{indent}├── static/")
|
||||
print(f"{indent}│ └── css/")
|
||||
print(f"{indent}└── templates/")
|
||||
|
||||
|
||||
def show_template_examples():
|
||||
"""Show actual template content for key files."""
|
||||
|
||||
print_section("TEMPLATE CONTENT EXAMPLES")
|
||||
|
||||
# 1. Requirements.txt examples
|
||||
print_subsection("1. requirements.txt - Basic (No optional features)")
|
||||
ctx = {
|
||||
"use_vite": False,
|
||||
"use_tailwind": False,
|
||||
"use_htmx": False,
|
||||
"database_type": "postgresql",
|
||||
"use_database_url": True,
|
||||
}
|
||||
print(template_engine.render_template("project/requirements-tpl", ctx))
|
||||
|
||||
print_subsection("2. requirements.txt - All Features (PostgreSQL)")
|
||||
ctx = {
|
||||
"use_vite": True,
|
||||
"use_tailwind": True,
|
||||
"use_htmx": True,
|
||||
"database_type": "postgresql",
|
||||
"use_database_url": True,
|
||||
}
|
||||
print(template_engine.render_template("project/requirements-tpl", ctx))
|
||||
|
||||
print_subsection("3. requirements.txt - All Features (MySQL)")
|
||||
ctx = {"use_vite": True, "use_tailwind": True, "use_htmx": True, "database_type": "mysql", "use_database_url": True}
|
||||
print(template_engine.render_template("project/requirements-tpl", ctx))
|
||||
|
||||
# 4. Settings examples
|
||||
print_subsection("4. settings/base.py - With Vite enabled")
|
||||
ctx = {
|
||||
"project_name": "config",
|
||||
"app_names": ["users"],
|
||||
"use_vite": True,
|
||||
"use_tailwind": False,
|
||||
"use_htmx": False,
|
||||
"use_database_url": True,
|
||||
"database_type": "postgresql",
|
||||
}
|
||||
result = template_engine.render_template("config/settings/base.py-tpl", ctx)
|
||||
for line in result.split("\n"):
|
||||
if "django_vite" in line or "DJANGO_VITE" in line:
|
||||
print(line)
|
||||
|
||||
print_subsection("5. settings/base.py - With Tailwind enabled")
|
||||
ctx = {
|
||||
"project_name": "config",
|
||||
"app_names": ["users"],
|
||||
"use_vite": False,
|
||||
"use_tailwind": True,
|
||||
"use_htmx": False,
|
||||
"use_database_url": True,
|
||||
"database_type": "postgresql",
|
||||
}
|
||||
result = template_engine.render_template("config/settings/base.py-tpl", ctx)
|
||||
for line in result.split("\n"):
|
||||
if "tailwind" in line.lower() or "daisyui" in line.lower():
|
||||
print(line)
|
||||
|
||||
# 6. Dockerfile example
|
||||
print_subsection("6. Dockerfile (PostgreSQL)")
|
||||
ctx = {"project_name": "myproject", "module_name": "config", "database_type": "postgresql"}
|
||||
print(template_engine.render_template("project/Dockerfile-tpl", ctx))
|
||||
|
||||
print_subsection("7. Dockerfile (MySQL)")
|
||||
ctx = {"project_name": "myproject", "module_name": "config", "database_type": "mysql"}
|
||||
print(template_engine.render_template("project/Dockerfile-tpl", ctx))
|
||||
|
||||
# 8. docker-compose.yml examples
|
||||
print_subsection("8. docker-compose.yml (PostgreSQL)")
|
||||
ctx = {"project_name": "myproject", "database_type": "postgresql", "use_database_url": True}
|
||||
print(template_engine.render_template("project/docker-compose.yml-tpl", ctx))
|
||||
|
||||
# 9. vite.config.js
|
||||
print_subsection("9. vite.config.js")
|
||||
ctx = {"project_name": "myproject", "module_name": "config"}
|
||||
print(template_engine.render_template("project/vite.config.js-tpl", ctx))
|
||||
|
||||
# 10. package.json
|
||||
print_subsection("10. package.json")
|
||||
print(template_engine.render_template("project/package.json-tpl", ctx))
|
||||
|
||||
# 11. Frontend examples
|
||||
print_subsection("11. frontend/index.html")
|
||||
print(template_engine.render_template("project/frontend/index.html-tpl", ctx))
|
||||
|
||||
print_subsection("12. frontend/src/App.jsx")
|
||||
print(template_engine.render_template("project/frontend/src/App.jsx-tpl", ctx))
|
||||
|
||||
|
||||
def main():
|
||||
print_section("DJINIT - ALL STRUCTURES & CASES")
|
||||
|
||||
# Show all structure + case combinations
|
||||
for struct_type, _module_name, struct_desc in STRUCTURES:
|
||||
print_section(struct_desc)
|
||||
|
||||
for case_name, metadata in CASES:
|
||||
print_subsection(case_name)
|
||||
show_file_tree(struct_type, case_name, metadata)
|
||||
|
||||
# Show template content examples
|
||||
show_template_examples()
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
print(" ALL STRUCTURES & CASES TESTED SUCCESSFULLY!")
|
||||
print("=" * 70)
|
||||
print("\nSummary:")
|
||||
print(f" • {len(STRUCTURES)} project structures")
|
||||
print(f" • {len(CASES)} feature combinations")
|
||||
print(f" • Total: {len(STRUCTURES) * len(CASES)} unique configurations")
|
||||
print("\nEach combination generates:")
|
||||
print(" - Django project files based on structure type")
|
||||
print(" - Optional: Docker files (Dockerfile, docker-compose.yml)")
|
||||
print(" - Optional: Vite/React frontend (vite.config.js, package.json, etc.)")
|
||||
print(" - Optional: Tailwind CSS configuration")
|
||||
print(" - Optional: HTMX integration")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,212 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Comprehensive test for all djinit features including pytest.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src")) # noqa: E402
|
||||
|
||||
from djinit.creators.setup import SetupCreator # noqa: E402
|
||||
|
||||
|
||||
def create_project(name, metadata):
|
||||
"""Create a test project."""
|
||||
test_dir = "/tmp/djinit_pytest_test"
|
||||
os.makedirs(test_dir, exist_ok=True)
|
||||
original_cwd = os.getcwd()
|
||||
|
||||
try:
|
||||
os.chdir(test_dir)
|
||||
|
||||
full_metadata = {
|
||||
"package_name": "backend",
|
||||
"use_github_actions": False,
|
||||
"use_gitlab_ci": False,
|
||||
"nested_apps": True,
|
||||
"nested_dir": "apps",
|
||||
"use_database_url": True,
|
||||
"database_type": "postgresql",
|
||||
"use_tailwind": False,
|
||||
"use_htmx": False,
|
||||
"use_docker": False,
|
||||
"use_vite": False,
|
||||
"use_pytest": False,
|
||||
"predefined_structure": False,
|
||||
"unified_structure": False,
|
||||
"single_structure": False,
|
||||
"project_module_name": "config",
|
||||
**metadata,
|
||||
}
|
||||
|
||||
creator = SetupCreator(
|
||||
project_dir=name, project_name=name, primary_app="users", app_names=["users"], metadata=full_metadata
|
||||
)
|
||||
success = creator.create()
|
||||
return success, test_dir
|
||||
|
||||
except Exception as e:
|
||||
print(f" Error: {e}")
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
return False, test_dir
|
||||
finally:
|
||||
os.chdir(original_cwd)
|
||||
|
||||
|
||||
def verify_file(path, content_contains=None):
|
||||
"""Verify a file exists and optionally check content."""
|
||||
if not os.path.exists(path):
|
||||
return False, f"File not found: {path}"
|
||||
if content_contains:
|
||||
with open(path) as f:
|
||||
content = f.read()
|
||||
if content_contains not in content:
|
||||
return False, f"Content '{content_contains}' not found in {path}"
|
||||
return True, "OK"
|
||||
|
||||
|
||||
def run_tests():
|
||||
print("=" * 70)
|
||||
print(" DJINIT COMPREHENSIVE TESTS (WITH PYTEST)")
|
||||
print("=" * 70)
|
||||
|
||||
test_cases = [
|
||||
("basic", {}, ["requirements.txt"]),
|
||||
("docker", {"use_docker": True}, ["Dockerfile", "docker-compose.yml"]),
|
||||
("vite", {"use_vite": True}, ["vite.config.js", "package.json"]),
|
||||
("pytest", {"use_pytest": True}, ["pytest.ini", "conftest.py"]),
|
||||
("tailwind", {"use_tailwind": True}, ["requirements.txt"]),
|
||||
("htmx", {"use_htmx": True}, ["requirements.txt"]),
|
||||
(
|
||||
"all_postgres",
|
||||
{"use_docker": True, "use_vite": True, "use_tailwind": True, "use_htmx": True, "use_pytest": True},
|
||||
["Dockerfile", "vite.config.js", "pytest.ini"],
|
||||
),
|
||||
(
|
||||
"all_mysql",
|
||||
{"use_docker": True, "use_vite": True, "use_tailwind": True, "use_pytest": True, "database_type": "mysql"},
|
||||
["Dockerfile", "vite.config.js", "pytest.ini"],
|
||||
),
|
||||
]
|
||||
|
||||
results = []
|
||||
|
||||
for name, metadata, expected_files in test_cases:
|
||||
print(f"\n{'─' * 70}")
|
||||
print(f" Testing: {name}")
|
||||
print(f" Metadata: {metadata}")
|
||||
|
||||
import shutil
|
||||
|
||||
test_dir = "/tmp/djinit_pytest_test"
|
||||
if os.path.exists(test_dir):
|
||||
shutil.rmtree(test_dir)
|
||||
|
||||
success, _ = create_project(name, metadata)
|
||||
|
||||
if not success:
|
||||
results.append((name, False, "Project creation failed"))
|
||||
print(" ✗ Failed to create project")
|
||||
continue
|
||||
|
||||
project_dir = os.path.join(test_dir, name)
|
||||
all_passed = True
|
||||
errors = []
|
||||
|
||||
for file in expected_files:
|
||||
file_path = os.path.join(project_dir, file)
|
||||
exists, msg = verify_file(file_path)
|
||||
if not exists:
|
||||
all_passed = False
|
||||
errors.append(msg)
|
||||
print(f" ✗ {msg}")
|
||||
else:
|
||||
print(f" ✓ {file}")
|
||||
|
||||
# Special checks for pytest
|
||||
if metadata.get("use_pytest"):
|
||||
req_path = os.path.join(project_dir, "requirements.txt")
|
||||
exists, msg = verify_file(req_path, "pytest")
|
||||
if not exists:
|
||||
all_passed = False
|
||||
print(" ✗ pytest not in requirements")
|
||||
else:
|
||||
print(" ✓ pytest in requirements")
|
||||
|
||||
pytest_ini_path = os.path.join(project_dir, "pytest.ini")
|
||||
exists, msg = verify_file(pytest_ini_path, "DJANGO_SETTINGS_MODULE")
|
||||
if not exists:
|
||||
all_passed = False
|
||||
print(" ✗ DJANGO_SETTINGS_MODULE not in pytest.ini")
|
||||
else:
|
||||
print(" ✓ pytest.ini has DJANGO_SETTINGS_MODULE")
|
||||
|
||||
conftest_path = os.path.join(project_dir, "conftest.py")
|
||||
exists, msg = verify_file(conftest_path, "pytest.fixture")
|
||||
if not exists:
|
||||
all_passed = False
|
||||
print(" ✗ pytest fixtures not in conftest.py")
|
||||
else:
|
||||
print(" ✓ conftest.py has fixtures")
|
||||
|
||||
# Special checks for vite
|
||||
if metadata.get("use_vite"):
|
||||
settings_path = os.path.join(project_dir, "config/settings/base.py")
|
||||
exists, msg = verify_file(settings_path, "django_vite")
|
||||
if not exists:
|
||||
all_passed = False
|
||||
print(" ✗ django_vite not in settings")
|
||||
else:
|
||||
print(" ✓ django_vite in settings")
|
||||
|
||||
# Special checks for docker
|
||||
if metadata.get("use_docker"):
|
||||
dockerfile_path = os.path.join(project_dir, "Dockerfile")
|
||||
db_type = metadata.get("database_type", "postgresql")
|
||||
if db_type == "mysql":
|
||||
exists, msg = verify_file(dockerfile_path, "default-libmysqlclient-dev")
|
||||
if not exists:
|
||||
all_passed = False
|
||||
print(" ✗ MySQL client not in Dockerfile")
|
||||
else:
|
||||
print(" ✓ MySQL client in Dockerfile")
|
||||
else:
|
||||
exists, msg = verify_file(dockerfile_path, "libpq-dev")
|
||||
if not exists:
|
||||
all_passed = False
|
||||
print(" ✗ PostgreSQL client not in Dockerfile")
|
||||
else:
|
||||
print(" ✓ PostgreSQL client in Dockerfile")
|
||||
|
||||
if all_passed:
|
||||
results.append((name, True, "All checks passed"))
|
||||
print(" ✓ ALL CHECKS PASSED")
|
||||
else:
|
||||
results.append((name, False, "; ".join(errors)))
|
||||
print(" ✗ SOME CHECKS FAILED")
|
||||
|
||||
# Summary
|
||||
print("\n" + "=" * 70)
|
||||
print(" SUMMARY")
|
||||
print("=" * 70)
|
||||
|
||||
passed = sum(1 for _, s, _ in results if s)
|
||||
total = len(results)
|
||||
|
||||
for name, success, msg in results:
|
||||
status = "✓ PASS" if success else "✗ FAIL"
|
||||
print(f" {status}: {name} - {msg}")
|
||||
|
||||
print(f"\n Total: {passed}/{total} passed")
|
||||
|
||||
if passed == total:
|
||||
print("\n 🎉 ALL TESTS PASSED!")
|
||||
else:
|
||||
print("\n ⚠️ SOME TESTS FAILED!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_tests()
|
||||
@@ -0,0 +1,174 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Comprehensive test for all djinit features including Vue.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src")) # noqa: E402
|
||||
|
||||
from djinit.creators.setup import SetupCreator # noqa: E402
|
||||
|
||||
|
||||
def create_project(name, metadata):
|
||||
test_dir = "/tmp/djinit_vue_test"
|
||||
os.makedirs(test_dir, exist_ok=True)
|
||||
original_cwd = os.getcwd()
|
||||
|
||||
try:
|
||||
os.chdir(test_dir)
|
||||
|
||||
full_metadata = {
|
||||
"package_name": "backend",
|
||||
"use_github_actions": False,
|
||||
"use_gitlab_ci": False,
|
||||
"nested_apps": True,
|
||||
"nested_dir": "apps",
|
||||
"use_database_url": True,
|
||||
"database_type": "postgresql",
|
||||
"use_tailwind": False,
|
||||
"use_htmx": False,
|
||||
"use_docker": False,
|
||||
"use_vite": False,
|
||||
"use_vue": False,
|
||||
"use_pytest": False,
|
||||
"predefined_structure": False,
|
||||
"unified_structure": False,
|
||||
"single_structure": False,
|
||||
"project_module_name": "config",
|
||||
**metadata,
|
||||
}
|
||||
|
||||
creator = SetupCreator(
|
||||
project_dir=name, project_name=name, primary_app="users", app_names=["users"], metadata=full_metadata
|
||||
)
|
||||
success = creator.create()
|
||||
return success, test_dir
|
||||
|
||||
except Exception as e:
|
||||
print(f" Error: {e}")
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
return False, test_dir
|
||||
finally:
|
||||
os.chdir(original_cwd)
|
||||
|
||||
|
||||
def verify_file(path, content_contains=None):
|
||||
if not os.path.exists(path):
|
||||
return False, f"File not found: {path}"
|
||||
if content_contains:
|
||||
with open(path) as f:
|
||||
content = f.read()
|
||||
if content_contains not in content:
|
||||
return False, f"Content '{content_contains}' not found in {path}"
|
||||
return True, "OK"
|
||||
|
||||
|
||||
def run_tests():
|
||||
print("=" * 70)
|
||||
print(" DJINIT COMPREHENSIVE TESTS (WITH VUE)")
|
||||
print("=" * 70)
|
||||
|
||||
test_cases = [
|
||||
("basic", {}, ["requirements.txt"]),
|
||||
("docker", {"use_docker": True}, ["Dockerfile"]),
|
||||
("react", {"use_vite": True}, ["vite.config.js", "package.json"]),
|
||||
("vue", {"use_vue": True}, ["vite.config.js", "package.json"]),
|
||||
("pytest", {"use_pytest": True}, ["pytest.ini"]),
|
||||
("all_react", {"use_docker": True, "use_vite": True, "use_pytest": True}, ["Dockerfile", "vite.config.js"]),
|
||||
("all_vue", {"use_docker": True, "use_vue": True, "use_pytest": True}, ["Dockerfile", "vite.config.js"]),
|
||||
]
|
||||
|
||||
results = []
|
||||
|
||||
for name, metadata, expected_files in test_cases:
|
||||
print(f"\n{'─' * 70}")
|
||||
print(f" Testing: {name}")
|
||||
print(f" Metadata: {metadata}")
|
||||
|
||||
import shutil
|
||||
|
||||
test_dir = "/tmp/djinit_vue_test"
|
||||
if os.path.exists(test_dir):
|
||||
shutil.rmtree(test_dir)
|
||||
|
||||
success, _ = create_project(name, metadata)
|
||||
|
||||
if not success:
|
||||
results.append((name, False, "Project creation failed"))
|
||||
print(" ✗ Failed to create project")
|
||||
continue
|
||||
|
||||
project_dir = os.path.join(test_dir, name)
|
||||
all_passed = True
|
||||
errors = []
|
||||
|
||||
for file in expected_files:
|
||||
file_path = os.path.join(project_dir, file)
|
||||
exists, msg = verify_file(file_path)
|
||||
if not exists:
|
||||
all_passed = False
|
||||
errors.append(msg)
|
||||
print(f" ✗ {msg}")
|
||||
else:
|
||||
print(f" ✓ {file}")
|
||||
|
||||
# Special checks for React
|
||||
if metadata.get("use_vite"):
|
||||
pkg_path = os.path.join(project_dir, "package.json")
|
||||
exists, msg = verify_file(pkg_path, "react")
|
||||
if not exists:
|
||||
all_passed = False
|
||||
print(" ✗ React not in package.json")
|
||||
else:
|
||||
print(" ✓ React in package.json")
|
||||
|
||||
# Special checks for Vue
|
||||
if metadata.get("use_vue"):
|
||||
pkg_path = os.path.join(project_dir, "package.json")
|
||||
exists, msg = verify_file(pkg_path, "vue")
|
||||
if not exists:
|
||||
all_passed = False
|
||||
print(" ✗ Vue not in package.json")
|
||||
else:
|
||||
print(" ✓ Vue in package.json")
|
||||
|
||||
vue_app = os.path.join(project_dir, "frontend/src/App.vue")
|
||||
exists, msg = verify_file(vue_app)
|
||||
if not exists:
|
||||
all_passed = False
|
||||
print(" ✗ App.vue not found")
|
||||
else:
|
||||
print(" ✓ App.vue found")
|
||||
|
||||
if all_passed:
|
||||
results.append((name, True, "All checks passed"))
|
||||
print(" ✓ ALL CHECKS PASSED")
|
||||
else:
|
||||
results.append((name, False, "; ".join(errors)))
|
||||
print(" ✗ SOME CHECKS FAILED")
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
print(" SUMMARY")
|
||||
print("=" * 70)
|
||||
|
||||
passed = sum(1 for _, s, _ in results if s)
|
||||
total = len(results)
|
||||
|
||||
for name, success, msg in results:
|
||||
status = "✓ PASS" if success else "✗ FAIL"
|
||||
print(f" {status}: {name} - {msg}")
|
||||
|
||||
print(f"\n Total: {passed}/{total} passed")
|
||||
|
||||
if passed == total:
|
||||
print("\n 🎉 ALL TESTS PASSED!")
|
||||
else:
|
||||
print("\n ⚠️ SOME TESTS FAILED!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_tests()
|
||||
+21
-46
@@ -14,24 +14,29 @@ class InFileLogicParser:
|
||||
def _get_value(self, key: str) -> str:
|
||||
"""
|
||||
Supports nested/dotted access like [[ user.name ]] or [[ settings["DEBUG"] ]].
|
||||
For simplicity, it uses eval in the provided context for complex expressions,
|
||||
but falls back to safe dictionary lookup for simple keys.
|
||||
"""
|
||||
key = key.strip()
|
||||
try:
|
||||
# Try evaluating the expression in the context
|
||||
return str(eval(key, {"__builtins__": {}}, self.context))
|
||||
except (NameError, SyntaxError, KeyError, TypeError, AttributeError):
|
||||
# Fallback for common patterns or just return the key as is if it fails
|
||||
return f"[[ {key} ]]"
|
||||
|
||||
def _eval_expr(self, expr: str) -> bool:
|
||||
"""Safely evaluate a boolean expression in the context. Defaults missing variables to False."""
|
||||
safe_context = dict(self.context)
|
||||
for key in re.findall(r"\b([a-zA-Z_][a-zA-Z0-9_]*)\b", expr):
|
||||
if key not in safe_context:
|
||||
safe_context[key] = False
|
||||
try:
|
||||
return bool(eval(expr, {"__builtins__": {}}, safe_context))
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def render(self, template_text: str, context: Dict[str, Any] = None) -> str:
|
||||
if context is not None:
|
||||
self.context = context
|
||||
|
||||
final_lines = []
|
||||
# Stack stores boolean results of nested IF blocks
|
||||
# Each element is (current_block_result, has_any_true_branch_executed)
|
||||
stack: List[List[bool]] = []
|
||||
|
||||
lines = template_text.splitlines()
|
||||
@@ -40,18 +45,13 @@ class InFileLogicParser:
|
||||
line = lines[i]
|
||||
stripped = line.strip()
|
||||
|
||||
# Handle @IF
|
||||
if stripped.startswith("# @IF "):
|
||||
expr = stripped[6:].strip()
|
||||
try:
|
||||
result = bool(eval(expr, {"__builtins__": {}}, self.context))
|
||||
except Exception:
|
||||
result = False
|
||||
result = self._eval_expr(expr)
|
||||
stack.append([result, result])
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Handle @ELSEIF
|
||||
elif stripped.startswith("# @ELSEIF "):
|
||||
if not stack:
|
||||
final_lines.append(line)
|
||||
@@ -64,17 +64,13 @@ class InFileLogicParser:
|
||||
if current_stack[1]:
|
||||
current_stack[0] = False
|
||||
else:
|
||||
try:
|
||||
result = bool(eval(expr, {"__builtins__": {}}, self.context))
|
||||
except Exception:
|
||||
result = False
|
||||
result = self._eval_expr(expr)
|
||||
current_stack[0] = result
|
||||
if result:
|
||||
current_stack[1] = True
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Handle @ELSE
|
||||
elif stripped.startswith("# @ELSE"):
|
||||
if not stack:
|
||||
final_lines.append(line)
|
||||
@@ -90,7 +86,6 @@ class InFileLogicParser:
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Handle @ENDIF
|
||||
elif stripped.startswith("# @ENDIF"):
|
||||
if stack:
|
||||
stack.pop()
|
||||
@@ -99,10 +94,8 @@ class InFileLogicParser:
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Handle @LOOP
|
||||
elif stripped.startswith("# @LOOP "):
|
||||
if stack and not all(s[0] for s in stack):
|
||||
# Skip the entire loop block if inside a false conditional
|
||||
loop_depth = 1
|
||||
i += 1
|
||||
while i < len(lines) and loop_depth > 0:
|
||||
@@ -122,7 +115,6 @@ class InFileLogicParser:
|
||||
except Exception:
|
||||
iterable = []
|
||||
|
||||
# Capture loop body
|
||||
loop_body = []
|
||||
i += 1
|
||||
loop_depth = 1
|
||||
@@ -131,15 +123,13 @@ class InFileLogicParser:
|
||||
loop_depth += 1
|
||||
elif lines[i].strip().startswith("# @ENDLOOP"):
|
||||
loop_depth -= 1
|
||||
|
||||
if loop_depth > 0:
|
||||
loop_body.append(lines[i])
|
||||
i += 1
|
||||
|
||||
if i < len(lines): # Skip the @ENDLOOP line itself
|
||||
i += 1
|
||||
|
||||
# Execute loop
|
||||
if i < len(lines):
|
||||
i += 1
|
||||
|
||||
if hasattr(iterable, "__iter__"):
|
||||
old_val = self.context.get(var_name)
|
||||
for val in iterable:
|
||||
@@ -156,35 +146,25 @@ class InFileLogicParser:
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Handle @ENDLOOP (should only be hit if out of sync or error)
|
||||
elif stripped.startswith("# @ENDLOOP"):
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Check if we should skip this line based on conditional stack
|
||||
if stack and not all(s[0] for s in stack):
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Variable substitution
|
||||
rendered_line = line
|
||||
matches = re.findall(r"\[\[\s*(.*?)\s*\]\]", rendered_line)
|
||||
for match in matches:
|
||||
value = self._get_value(match)
|
||||
# Replace all variations of the variable syntax
|
||||
rendered_line = re.sub(r"\[\[\s*" + re.escape(match) + r"\s*\]\]", value, rendered_line)
|
||||
|
||||
# In-line @IF support
|
||||
# Case 1: content # @IF cond
|
||||
# Case 2: # @IF cond content
|
||||
if "# @IF " in rendered_line:
|
||||
parts = rendered_line.split("# @IF ", 1)
|
||||
pre_content = parts[0].rstrip()
|
||||
rest = parts[1].strip()
|
||||
|
||||
# Split rest into expression and post-content (if any)
|
||||
# This is tricky because the expression might have spaces.
|
||||
# If there's an # @ENDIF on the same line, use it.
|
||||
post_content = ""
|
||||
expr = rest
|
||||
if " # @ENDIF" in rest:
|
||||
@@ -196,16 +176,11 @@ class InFileLogicParser:
|
||||
expr = expr_parts[0].strip()
|
||||
post_content = expr_parts[1].lstrip()
|
||||
|
||||
try:
|
||||
if bool(eval(expr, {"__builtins__": {}}, self.context)):
|
||||
# If Case 1, we want pre_content. If Case 2, we want post_content.
|
||||
# Usually, if pre_content is empty (or just whitespace), it's Case 2.
|
||||
if pre_content:
|
||||
final_lines.append(pre_content + post_content)
|
||||
else:
|
||||
final_lines.append(post_content)
|
||||
except Exception:
|
||||
pass
|
||||
if self._eval_expr(expr):
|
||||
if pre_content:
|
||||
final_lines.append(pre_content + post_content)
|
||||
else:
|
||||
final_lines.append(post_content)
|
||||
i += 1
|
||||
continue
|
||||
|
||||
|
||||
@@ -17,6 +17,12 @@ class ProjectMetadata:
|
||||
database_type: str = "postgresql"
|
||||
use_tailwind: bool = False
|
||||
use_htmx: bool = False
|
||||
use_docker: bool = False
|
||||
use_vite: bool = False
|
||||
use_vue: bool = False
|
||||
use_pytest: bool = False
|
||||
use_django_q: bool = False
|
||||
use_celery: bool = False
|
||||
predefined_structure: bool = False
|
||||
unified_structure: bool = False
|
||||
single_structure: bool = False
|
||||
@@ -33,6 +39,11 @@ class ProjectMetadata:
|
||||
"database_type": self.database_type,
|
||||
"use_tailwind": self.use_tailwind,
|
||||
"use_htmx": self.use_htmx,
|
||||
"use_docker": self.use_docker,
|
||||
"use_vite": self.use_vite,
|
||||
"use_vue": self.use_vue,
|
||||
"use_pytest": self.use_pytest,
|
||||
"use_django_q": self.use_django_q,
|
||||
"predefined_structure": self.predefined_structure,
|
||||
"unified_structure": self.unified_structure,
|
||||
"single_structure": self.single_structure,
|
||||
|
||||
@@ -88,9 +88,14 @@ class FileCreator(BaseService):
|
||||
for name in base_settings_context["app_names"]
|
||||
]
|
||||
|
||||
# Add Tailwind and HTMX to context
|
||||
# Add Tailwind, HTMX, Vite, Vue, and pytest to context
|
||||
base_settings_context["use_tailwind"] = self.metadata.get("use_tailwind", False)
|
||||
base_settings_context["use_htmx"] = self.metadata.get("use_htmx", False)
|
||||
base_settings_context["use_vite"] = self.metadata.get("use_vite", False)
|
||||
base_settings_context["use_vue"] = self.metadata.get("use_vue", False)
|
||||
base_settings_context["use_pytest"] = self.metadata.get("use_pytest", False)
|
||||
base_settings_context["use_django_q"] = self.metadata.get("use_django_q", False)
|
||||
base_settings_context["use_celery"] = self.metadata.get("use_celery", False)
|
||||
|
||||
for filename, context in [
|
||||
("base.py", base_settings_context),
|
||||
@@ -140,6 +145,13 @@ class FileCreator(BaseService):
|
||||
"database_type": self.metadata.get("database_type", "postgresql"),
|
||||
"use_tailwind": self.metadata.get("use_tailwind", False),
|
||||
"use_htmx": self.metadata.get("use_htmx", False),
|
||||
"use_vite": self.metadata.get("use_vite", False),
|
||||
"use_vue": self.metadata.get("use_vue", False),
|
||||
"use_pytest": self.metadata.get("use_pytest", False),
|
||||
"use_django_q": self.metadata.get("use_django_q", False),
|
||||
"use_celery": self.metadata.get("use_celery", False),
|
||||
"module_name": self.module_name,
|
||||
"app_names": self.app_names,
|
||||
}
|
||||
self._render_and_create_file(
|
||||
"requirements.txt",
|
||||
@@ -421,6 +433,10 @@ class FileCreator(BaseService):
|
||||
"database_type": self.metadata.get("database_type", "postgresql"),
|
||||
"use_tailwind": self.metadata.get("use_tailwind", False),
|
||||
"use_htmx": self.metadata.get("use_htmx", False),
|
||||
"use_docker": self.metadata.get("use_docker", False),
|
||||
"use_vite": self.metadata.get("use_vite", False),
|
||||
"use_vue": self.metadata.get("use_vue", False),
|
||||
"use_pytest": self.metadata.get("use_pytest", False),
|
||||
},
|
||||
"cicd": {
|
||||
"github": self.metadata.get("use_github_actions", False),
|
||||
@@ -432,3 +448,148 @@ class FileCreator(BaseService):
|
||||
CommonUtils.create_file_with_content(
|
||||
filepath, json.dumps(config, indent=4), "Created .djinit configuration file"
|
||||
)
|
||||
|
||||
def create_docker_files(self) -> None:
|
||||
"""Create Docker-related files (Dockerfile, docker-compose.yml, .dockerignore)."""
|
||||
context = {
|
||||
"project_name": self.project_name,
|
||||
"module_name": self.module_name,
|
||||
"database_type": self.metadata.get("database_type", "postgresql"),
|
||||
"use_database_url": self.metadata.get("use_database_url", True),
|
||||
}
|
||||
self._render_and_create_file(
|
||||
"Dockerfile",
|
||||
"project/Dockerfile-tpl",
|
||||
context,
|
||||
"Created Dockerfile for Django application",
|
||||
)
|
||||
self._render_and_create_file(
|
||||
"docker-compose.yml",
|
||||
"project/docker-compose.yml-tpl",
|
||||
context,
|
||||
"Created docker-compose.yml for local development",
|
||||
)
|
||||
self._render_and_create_file(
|
||||
".dockerignore",
|
||||
"project/dockerignore-tpl",
|
||||
{},
|
||||
"Created .dockerignore file",
|
||||
)
|
||||
|
||||
def create_vite_files(self) -> None:
|
||||
"""Create Vite/React frontend files."""
|
||||
context = {
|
||||
"project_name": self.project_name,
|
||||
"module_name": self.module_name,
|
||||
}
|
||||
|
||||
# Create frontend directories
|
||||
frontend_dir = os.path.join(self.project_root, "frontend")
|
||||
frontend_src_dir = os.path.join(frontend_dir, "src")
|
||||
static_dir = os.path.join(self.project_root, "static")
|
||||
|
||||
os.makedirs(frontend_dir, exist_ok=True)
|
||||
os.makedirs(frontend_src_dir, exist_ok=True)
|
||||
os.makedirs(static_dir, exist_ok=True)
|
||||
|
||||
# Create __init__.py files
|
||||
CommonUtils.create_directory_with_init(frontend_dir, "Created frontend/")
|
||||
CommonUtils.create_directory_with_init(frontend_src_dir, "Created frontend/src/")
|
||||
CommonUtils.create_directory_with_init(static_dir, "Created static/")
|
||||
|
||||
self._render_and_create_file(
|
||||
"vite.config.js",
|
||||
"project/vite.config.js-tpl",
|
||||
context,
|
||||
"Created vite.config.js",
|
||||
)
|
||||
self._render_and_create_file(
|
||||
"package.json",
|
||||
"project/package.json-tpl",
|
||||
context,
|
||||
"Created package.json for frontend",
|
||||
)
|
||||
index_html_path = os.path.join(frontend_dir, "index.html")
|
||||
CommonUtils.create_file_from_template(
|
||||
index_html_path, "project/frontend/index.html-tpl", context, "Created frontend/index.html"
|
||||
)
|
||||
main_jsx_path = os.path.join(frontend_src_dir, "main.jsx")
|
||||
CommonUtils.create_file_from_template(
|
||||
main_jsx_path, "project/frontend/src/main.jsx-tpl", context, "Created frontend/src/main.jsx"
|
||||
)
|
||||
app_jsx_path = os.path.join(frontend_src_dir, "App.jsx")
|
||||
CommonUtils.create_file_from_template(
|
||||
app_jsx_path, "project/frontend/src/App.jsx-tpl", context, "Created frontend/src/App.jsx"
|
||||
)
|
||||
index_css_path = os.path.join(frontend_src_dir, "index.css")
|
||||
CommonUtils.create_file_from_template(
|
||||
index_css_path, "project/frontend/src/index.css-tpl", context, "Created frontend/src/index.css"
|
||||
)
|
||||
|
||||
def create_vue_files(self) -> None:
|
||||
"""Create Vite/Vue frontend files."""
|
||||
context = {
|
||||
"project_name": self.project_name,
|
||||
"module_name": self.module_name,
|
||||
}
|
||||
|
||||
frontend_dir = os.path.join(self.project_root, "frontend")
|
||||
frontend_src_dir = os.path.join(frontend_dir, "src")
|
||||
static_dir = os.path.join(self.project_root, "static")
|
||||
|
||||
os.makedirs(frontend_dir, exist_ok=True)
|
||||
os.makedirs(frontend_src_dir, exist_ok=True)
|
||||
os.makedirs(static_dir, exist_ok=True)
|
||||
|
||||
CommonUtils.create_directory_with_init(frontend_dir, "Created frontend/")
|
||||
CommonUtils.create_directory_with_init(frontend_src_dir, "Created frontend/src/")
|
||||
CommonUtils.create_directory_with_init(static_dir, "Created static/")
|
||||
|
||||
self._render_and_create_file(
|
||||
"vite.config.js",
|
||||
"project/vite-vue.config.js-tpl",
|
||||
context,
|
||||
"Created vite.config.js for Vue",
|
||||
)
|
||||
self._render_and_create_file(
|
||||
"package.json",
|
||||
"project/package-vue.json-tpl",
|
||||
context,
|
||||
"Created package.json for Vue frontend",
|
||||
)
|
||||
index_html_path = os.path.join(frontend_dir, "index.html")
|
||||
CommonUtils.create_file_from_template(
|
||||
index_html_path, "project/frontend/index-vue.html-tpl", context, "Created frontend/index.html"
|
||||
)
|
||||
main_js_path = os.path.join(frontend_src_dir, "main.js")
|
||||
CommonUtils.create_file_from_template(
|
||||
main_js_path, "project/frontend/src/main.js-tpl", context, "Created frontend/src/main.js"
|
||||
)
|
||||
app_vue_path = os.path.join(frontend_src_dir, "App.vue")
|
||||
CommonUtils.create_file_from_template(
|
||||
app_vue_path, "project/frontend/src/App.vue-tpl", context, "Created frontend/src/App.vue"
|
||||
)
|
||||
style_css_path = os.path.join(frontend_src_dir, "style.css")
|
||||
CommonUtils.create_file_from_template(
|
||||
style_css_path, "project/frontend/src/style.css-tpl", context, "Created frontend/src/style.css"
|
||||
)
|
||||
|
||||
def create_pytest_files(self) -> None:
|
||||
"""Create pytest configuration files."""
|
||||
context = {
|
||||
"project_name": self.project_name,
|
||||
"module_name": self.module_name,
|
||||
"app_names": self.app_names or [],
|
||||
}
|
||||
self._render_and_create_file(
|
||||
"pytest.ini",
|
||||
"project/pytest.ini-tpl",
|
||||
context,
|
||||
"Created pytest.ini configuration",
|
||||
)
|
||||
self._render_and_create_file(
|
||||
"conftest.py",
|
||||
"project/conftest.py-tpl",
|
||||
context,
|
||||
"Created conftest.py with pytest fixtures",
|
||||
)
|
||||
@@ -128,16 +128,51 @@ class ProjectCreator(BaseService):
|
||||
# Core Django project files
|
||||
required_files = [
|
||||
join("manage.py"),
|
||||
join(self.module_name, "__init__.py"),
|
||||
join(self.module_name, "settings", "__init__.py"),
|
||||
join(self.module_name, "settings", "base.py"),
|
||||
join(self.module_name, "settings", "development.py"),
|
||||
join(self.module_name, "settings", "production.py"),
|
||||
join(self.module_name, "urls.py"),
|
||||
join(self.module_name, "wsgi.py"),
|
||||
join(self.module_name, "asgi.py"),
|
||||
]
|
||||
|
||||
if self.metadata.get("unified_structure"):
|
||||
# Unified structure: settings in core/
|
||||
required_files.extend(
|
||||
[
|
||||
join("core", "__init__.py"),
|
||||
join("core", "settings", "__init__.py"),
|
||||
join("core", "settings", "base.py"),
|
||||
join("core", "settings", "development.py"),
|
||||
join("core", "settings", "production.py"),
|
||||
join("core", "urls.py"),
|
||||
join("core", "wsgi.py"),
|
||||
join("core", "asgi.py"),
|
||||
]
|
||||
)
|
||||
elif self.metadata.get("single_structure"):
|
||||
# Single structure: in project_name folder
|
||||
required_files.extend(
|
||||
[
|
||||
join(self.module_name, "__init__.py"),
|
||||
join(self.module_name, "settings", "__init__.py"),
|
||||
join(self.module_name, "settings", "base.py"),
|
||||
join(self.module_name, "settings", "development.py"),
|
||||
join(self.module_name, "settings", "production.py"),
|
||||
join(self.module_name, "urls.py"),
|
||||
join(self.module_name, "wsgi.py"),
|
||||
join(self.module_name, "asgi.py"),
|
||||
]
|
||||
)
|
||||
else:
|
||||
# Standard/Predefined structure
|
||||
required_files.extend(
|
||||
[
|
||||
join(self.module_name, "__init__.py"),
|
||||
join(self.module_name, "settings", "__init__.py"),
|
||||
join(self.module_name, "settings", "base.py"),
|
||||
join(self.module_name, "settings", "development.py"),
|
||||
join(self.module_name, "settings", "production.py"),
|
||||
join(self.module_name, "urls.py"),
|
||||
join(self.module_name, "wsgi.py"),
|
||||
join(self.module_name, "asgi.py"),
|
||||
]
|
||||
)
|
||||
|
||||
apps_base_dir = self._get_apps_base_dir()
|
||||
|
||||
if self.metadata.get("unified_structure"):
|
||||
|
||||
@@ -77,6 +77,10 @@ class SetupCreator:
|
||||
("Creating Justfile", self.file_creator.create_justfile),
|
||||
("Creating runtime.txt", self.file_creator.create_runtime_txt),
|
||||
("Creating CI/CD pipelines", self._create_cicd_pipelines),
|
||||
("Creating Docker files", self._create_docker_files),
|
||||
("Creating Vite/React files", self._create_vite_files),
|
||||
("Creating Vite/Vue files", self._create_vue_files),
|
||||
("Creating pytest files", self._create_pytest_files),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -118,3 +122,23 @@ class SetupCreator:
|
||||
|
||||
if self.metadata.get("use_gitlab_ci", True):
|
||||
self.file_creator.create_gitlab_ci()
|
||||
|
||||
def _create_docker_files(self) -> None:
|
||||
if self.metadata.get("use_docker", False):
|
||||
self.file_creator.create_docker_files()
|
||||
UIFormatter.print_success("Created Docker files successfully!")
|
||||
|
||||
def _create_vite_files(self) -> None:
|
||||
if self.metadata.get("use_vite", False):
|
||||
self.file_creator.create_vite_files()
|
||||
UIFormatter.print_success("Created Vite/React files successfully!")
|
||||
|
||||
def _create_vue_files(self) -> None:
|
||||
if self.metadata.get("use_vue", False):
|
||||
self.file_creator.create_vue_files()
|
||||
UIFormatter.print_success("Created Vite/Vue files successfully!")
|
||||
|
||||
def _create_pytest_files(self) -> None:
|
||||
if self.metadata.get("use_pytest", False):
|
||||
self.file_creator.create_pytest_files()
|
||||
UIFormatter.print_success("Created pytest files successfully!")
|
||||
@@ -28,6 +28,9 @@ THIRD_PARTY_APPS = [
|
||||
"drf_spectacular",
|
||||
"django_tailwind_cli", # @IF use_tailwind
|
||||
"django_htmx", # @IF use_htmx
|
||||
"django_vite", # @IF use_vite | use_vue
|
||||
"django_q", # @IF use_django_q
|
||||
"celery", # @IF use_celery
|
||||
]
|
||||
|
||||
USER_DEFINED_APPS = [
|
||||
@@ -225,3 +228,39 @@ TAILWIND_CLI_CONFIG = {
|
||||
},
|
||||
}
|
||||
# @ENDIF
|
||||
|
||||
# @IF use_vite | use_vue
|
||||
# for django-vite
|
||||
DJANGO_VITE_ASSET_SRC = "frontend"
|
||||
DJANGO_VITE_DEV_SERVER_URL = "http://localhost:5173"
|
||||
STATIC_URL = "/static/"
|
||||
STATIC_ROOT = BASE_DIR / "staticfiles"
|
||||
STATICFILES_DIRS = [BASE_DIR / "static"]
|
||||
# @ENDIF
|
||||
|
||||
# @IF use_django_q
|
||||
# Django-Q2 configuration
|
||||
Q_CLUSTER = {
|
||||
"name": "djinit_project",
|
||||
"workers": 4,
|
||||
"max_threads": 30,
|
||||
"timeout": 90,
|
||||
"retry": 120,
|
||||
"queue_limit": 50,
|
||||
"bulk": 10,
|
||||
"orm": "default",
|
||||
"cache": "default",
|
||||
}
|
||||
# @ENDIF
|
||||
|
||||
# @IF use_celery
|
||||
# Celery configuration
|
||||
CELERY_BROKER_URL = env("CELERY_BROKER_URL", default="redis://localhost:6379/0")
|
||||
CELERY_RESULT_BACKEND = env("CELERY_RESULT_BACKEND", default="redis://localhost:6379/0")
|
||||
CELERY_ACCEPT_CONTENT = ["json"]
|
||||
CELERY_TASK_SERIALIZER = "json"
|
||||
CELERY_RESULT_SERIALIZER = "json"
|
||||
CELERY_TIMEZONE = TIME_ZONE
|
||||
CELERY_TASK_TRACK_STARTED = True
|
||||
CELERY_TASK_TIME_LIMIT = 30 * 60
|
||||
# @ENDIF
|
||||
@@ -0,0 +1,30 @@
|
||||
#--------------------------------------------------#
|
||||
# The following was generated by djinit: #
|
||||
#--------------------------------------------------#
|
||||
|
||||
FROM python:3.13-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ENV PYTHONUNBUFFERED=1 \
|
||||
PYTHONDONTWRITEBYTECODE=1 \
|
||||
PIP_NO_CACHE_DIR=1 \
|
||||
PIP_DISABLE_PIP_VERSION_CHECK=1
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
libpq-dev \
|
||||
# @IF database_type == 'mysql'
|
||||
default-libmysqlclient-dev \
|
||||
build-essential \
|
||||
# @ENDIF
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["gunicorn", "[[ module_name ]].wsgi:application", "--bind", "0.0.0.0:8000"]
|
||||
@@ -0,0 +1,46 @@
|
||||
#--------------------------------------------------#
|
||||
# The following was generated by djinit: #
|
||||
#--------------------------------------------------#
|
||||
|
||||
import os
|
||||
import sys
|
||||
import django
|
||||
from pathlib import Path
|
||||
|
||||
# Add project to path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
# Configure Django settings
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '[[ module_name ]].settings.development')
|
||||
django.setup()
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def django_db_setup():
|
||||
"""Setup test database."""
|
||||
from django.conf import settings
|
||||
settings.DATABASES['default'] = {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': ':memory:',
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def api_client():
|
||||
"""Return a test client."""
|
||||
from rest_framework.test import APIClient
|
||||
return APIClient()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def user(db):
|
||||
"""Create a test user."""
|
||||
from django.contrib.auth import get_user_model
|
||||
User = get_user_model()
|
||||
return User.objects.create_user(
|
||||
username='testuser',
|
||||
email='test@example.com',
|
||||
password='testpass123'
|
||||
)
|
||||
@@ -0,0 +1,55 @@
|
||||
#--------------------------------------------------#
|
||||
# The following was generated by djinit: #
|
||||
#--------------------------------------------------#
|
||||
|
||||
services:
|
||||
web:
|
||||
build: .
|
||||
command: python manage.py runserver 0.0.0.0:8000
|
||||
volumes:
|
||||
- .:/app
|
||||
ports:
|
||||
- "8000:8000"
|
||||
environment:
|
||||
- DEBUG=1
|
||||
# @IF use_database_url
|
||||
# @IF database_type == 'postgresql'
|
||||
- DATABASE_URL=postgresql://postgres:postgres@db:5432/[[ project_name ]]
|
||||
# @ELSE
|
||||
- DATABASE_URL=mysql://root:root@db:3306/[[ project_name ]]
|
||||
# @ENDIF
|
||||
# @ENDIF
|
||||
depends_on:
|
||||
- db
|
||||
|
||||
# @IF database_type == 'postgresql'
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
environment:
|
||||
- POSTGRES_DB=[[ project_name ]]
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "5432:5432"
|
||||
# @ELSE
|
||||
db:
|
||||
image: mysql:8
|
||||
environment:
|
||||
- MYSQL_DATABASE=[[ project_name ]]
|
||||
- MYSQL_ROOT_PASSWORD=root
|
||||
- MYSQL_PASSWORD=root
|
||||
volumes:
|
||||
- mysql_data:/var/lib/mysql
|
||||
ports:
|
||||
- "3306:3306"
|
||||
# @ENDIF
|
||||
|
||||
# @IF database_type == 'postgresql'
|
||||
volumes:
|
||||
postgres_data:
|
||||
# @ELSE
|
||||
volumes:
|
||||
mysql_data:
|
||||
# @ENDIF
|
||||
@@ -0,0 +1,55 @@
|
||||
#--------------------------------------------------#
|
||||
# The following was generated by djinit: #
|
||||
#--------------------------------------------------#
|
||||
|
||||
# Git
|
||||
.git
|
||||
.gitignore
|
||||
|
||||
# Python
|
||||
__pycache__
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.egg-info/
|
||||
dist/
|
||||
build/
|
||||
.eggs/
|
||||
.venv/
|
||||
venv/
|
||||
env/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
|
||||
# Environment
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Database
|
||||
*.sqlite3
|
||||
*.db
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
logs/
|
||||
|
||||
# Docker
|
||||
docker-compose*.yml
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Testing
|
||||
.coverage
|
||||
htmlcov/
|
||||
.pytest_cache/
|
||||
|
||||
# Documentation
|
||||
*.md
|
||||
!README.md
|
||||
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>[[ project_name ]]</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>[[ project_name ]]</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,11 @@
|
||||
function App() {
|
||||
return (
|
||||
<div style={{ padding: '2rem', fontFamily: 'system-ui, sans-serif' }}>
|
||||
<h1>Welcome to [[ project_name ]]</h1>
|
||||
<p>This is your React frontend powered by Vite.</p>
|
||||
<p>Edit <code>frontend/src/App.jsx</code> to get started!</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
@@ -0,0 +1,17 @@
|
||||
<script setup>
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container">
|
||||
<h1>Welcome to [[ project_name ]]</h1>
|
||||
<p>This is your Vue.js frontend powered by Vite.</p>
|
||||
<p>Edit <code>frontend/src/App.vue</code> to get started!</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
padding: 2rem;
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,14 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
#root {
|
||||
min-height: 100vh;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import './style.css'
|
||||
|
||||
createApp(App).mount('#app')
|
||||
@@ -0,0 +1,10 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App.jsx'
|
||||
import './index.css'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
#app {
|
||||
min-height: 100vh;
|
||||
}
|
||||
@@ -91,3 +91,12 @@ secrets.json
|
||||
|
||||
# Cloud
|
||||
.cloud
|
||||
|
||||
# Docker
|
||||
.dockerignore
|
||||
docker-compose.override.yml
|
||||
*.tar
|
||||
|
||||
# Docker volumes
|
||||
postgres_data/
|
||||
mysql_data/
|
||||
@@ -0,0 +1,22 @@
|
||||
#--------------------------------------------------#
|
||||
# The following was generated by djinit: #
|
||||
#--------------------------------------------------#
|
||||
|
||||
{
|
||||
"name": "[[ project_name ]]-frontend",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.5.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.2.3",
|
||||
"vite": "^6.3.5"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#--------------------------------------------------#
|
||||
# The following was generated by djinit: #
|
||||
#--------------------------------------------------#
|
||||
|
||||
{
|
||||
"name": "[[ project_name ]]-frontend",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"vite": "^6.3.5"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#--------------------------------------------------#
|
||||
# The following was generated by djinit: #
|
||||
#--------------------------------------------------#
|
||||
|
||||
[pytest]
|
||||
DJANGO_SETTINGS_MODULE = [[ module_name ]].settings.development
|
||||
python_files = tests.py test_*.py *_tests.py
|
||||
python_classes = Test* *Tests *Test
|
||||
python_functions = test_*
|
||||
addopts = --strict-markers --tb=short
|
||||
testpaths = [[ app_names ]]
|
||||
markers =
|
||||
slow: marks tests as slow (deselect with '-m "not slow"')
|
||||
integration: marks tests as integration tests
|
||||
@@ -1,13 +1,35 @@
|
||||
# [[ project_name ]]
|
||||
|
||||
A Django project folder structure is generated with [djinitx/djinit/dj](https://github.com/S4NKALP/djinit).
|
||||
A Django project generated with [djinitx/djinit/dj](https://github.com/S4NKALP/djinit).
|
||||
|
||||
## Features
|
||||
|
||||
- Modern project structure with environment-specific settings
|
||||
- Pre-configured REST API with JWT authentication
|
||||
- Essential dependencies and utilities
|
||||
- Production-ready configuration
|
||||
- Split settings for development and production
|
||||
- API documentation (Swagger UI at `/docs/`)
|
||||
- CORS configured for local development
|
||||
- WhiteNoise for static files
|
||||
- Modern admin interface (django-jazzmin)
|
||||
# @IF use_docker
|
||||
- Docker support (Dockerfile, docker-compose.yml)
|
||||
# @ENDIF
|
||||
# @IF use_vite | use_vue
|
||||
- Frontend integration (Vite + React or Vue.js via django-vite)
|
||||
# @ENDIF
|
||||
# @IF use_pytest
|
||||
- Testing framework (pytest, pytest-django, pytest-cov)
|
||||
# @ENDIF
|
||||
# @IF use_django_q
|
||||
- Async task queue (Django-Q2)
|
||||
# @ENDIF
|
||||
# @IF use_celery
|
||||
- Async task queue (Celery with Redis broker)
|
||||
# @ENDIF
|
||||
- Production-ready configuration (Procfile, runtime.txt)
|
||||
- Development tools (Justfile with common commands)
|
||||
- Environment template (.env.sample)
|
||||
- Git ready (.gitignore included)
|
||||
|
||||
## Setup
|
||||
|
||||
@@ -158,6 +180,33 @@ Run `just` to see all available commands, including:
|
||||
├── Procfile
|
||||
├── runtime.txt
|
||||
└── README.md
|
||||
# @ELSEIF single_structure
|
||||
[[ project_name ]]/
|
||||
├── [[ module_name ]]/
|
||||
│ ├── settings/
|
||||
│ │ ├── base.py
|
||||
│ │ ├── development.py
|
||||
│ │ └── production.py
|
||||
│ ├── urls.py
|
||||
│ ├── wsgi.py
|
||||
│ └── asgi.py
|
||||
├── admin/
|
||||
├── api/ # API endpoints organized by model
|
||||
│ └── user/ # Example: if you have a User model
|
||||
│ ├── views.py
|
||||
│ ├── serializers.py
|
||||
│ └── urls.py
|
||||
├── models/
|
||||
├── tests/
|
||||
├── manage.py
|
||||
├── requirements.txt
|
||||
├── pyproject.toml
|
||||
├── uv.lock
|
||||
├── .env.sample
|
||||
├── justfile
|
||||
├── Procfile
|
||||
├── runtime.txt
|
||||
└── README.md
|
||||
# @ELSE
|
||||
[[ project_name ]]/
|
||||
├── [[ module_name ]]/
|
||||
@@ -182,15 +231,10 @@ Run `just` to see all available commands, including:
|
||||
├── pyproject.toml
|
||||
├── uv.lock
|
||||
├── .env.sample
|
||||
├── justfile
|
||||
├── Procfile
|
||||
├── runtime.txt
|
||||
└── README.md
|
||||
|
||||
**Note:** For the single folder layout, organize your API endpoints by model name under the `api/` directory.
|
||||
For example, if you have a `User` model in `models/user.py`, create:
|
||||
- `api/user/views.py` - API views for User
|
||||
- `api/user/serializers.py` - Serializers for User
|
||||
- `api/user/urls.py` - URL patterns for User endpoints
|
||||
|
||||
See the README files in `api/` and `models/` directories for more details.
|
||||
# @ENDIF
|
||||
```
|
||||
|
||||
@@ -199,3 +243,29 @@ See the README files in `api/` and `models/` directories for more details.
|
||||
When running in development mode, API documentation is available at:
|
||||
- Swagger UI: http://localhost:8000/docs/
|
||||
- Schema: http://localhost:8000/schema/
|
||||
|
||||
# @IF use_django_q
|
||||
## Django-Q2
|
||||
|
||||
Django-Q2 is a task queue for Django. To start the worker:
|
||||
|
||||
```bash
|
||||
python manage.py qcluster
|
||||
```
|
||||
# @ENDIF
|
||||
|
||||
# @IF use_celery
|
||||
## Celery
|
||||
|
||||
Celery is a distributed task queue. To start the worker:
|
||||
|
||||
```bash
|
||||
celery -A [[ module_name ]] worker -l info
|
||||
```
|
||||
|
||||
To start the beat scheduler:
|
||||
|
||||
```bash
|
||||
celery -A [[ module_name ]] beat -l info
|
||||
```
|
||||
# @ENDIF
|
||||
@@ -12,6 +12,13 @@ django-cors-headers
|
||||
whitenoise
|
||||
django-tailwind-cli # @IF use_tailwind
|
||||
django-htmx # @IF use_htmx
|
||||
django-vite # @IF use_vite
|
||||
django-vite # @IF use_vue
|
||||
pytest # @IF use_pytest
|
||||
pytest-django # @IF use_pytest
|
||||
pytest-cov # @IF use_pytest
|
||||
django-q2 # @IF use_django_q
|
||||
celery # @IF use_celery
|
||||
# @IF database_type == 'mysql'
|
||||
mysqlclient
|
||||
# @ELSE
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#--------------------------------------------------#
|
||||
# The following was generated by djinit: #
|
||||
#--------------------------------------------------#
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import path from 'path'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
root: 'frontend',
|
||||
build: {
|
||||
outDir: '../static/dist',
|
||||
emptyOutDir: true,
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'frontend/src'),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
port: 5173,
|
||||
strictPort: true,
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,25 @@
|
||||
#--------------------------------------------------#
|
||||
# The following was generated by djinit: #
|
||||
#--------------------------------------------------#
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import path from 'path'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
root: 'frontend',
|
||||
build: {
|
||||
outDir: '../static/dist',
|
||||
emptyOutDir: true,
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'frontend/src'),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
port: 5173,
|
||||
strictPort: true,
|
||||
},
|
||||
})
|
||||
@@ -25,6 +25,12 @@ class StructureOptions(TypedDict):
|
||||
use_database_url: bool
|
||||
use_tailwind: bool
|
||||
use_htmx: bool
|
||||
use_docker: bool
|
||||
use_vite: bool
|
||||
use_vue: bool
|
||||
use_pytest: bool
|
||||
use_django_q: bool
|
||||
use_celery: bool
|
||||
use_github: bool
|
||||
use_gitlab: bool
|
||||
|
||||
@@ -179,6 +185,24 @@ class InputCollector:
|
||||
def get_htmx_choice(self) -> bool:
|
||||
return UIFormatter.confirm("Include HTMX? (via django-htmx)", default=True)
|
||||
|
||||
def get_docker_choice(self) -> bool:
|
||||
return UIFormatter.confirm("Include Docker support? (Dockerfile + docker-compose)", default=True)
|
||||
|
||||
def get_vite_choice(self) -> bool:
|
||||
return UIFormatter.confirm("Include Vite/React? (via django-vite)", default=True)
|
||||
|
||||
def get_vue_choice(self) -> bool:
|
||||
return UIFormatter.confirm("Include Vite/Vue? (via django-vite)", default=True)
|
||||
|
||||
def get_pytest_choice(self) -> bool:
|
||||
return UIFormatter.confirm("Include pytest testing framework? (pytest, coverage)", default=True)
|
||||
|
||||
def get_django_q_choice(self) -> bool:
|
||||
return UIFormatter.confirm("Include Django-Q2? (async task queue)", default=False)
|
||||
|
||||
def get_celery_choice(self) -> bool:
|
||||
return UIFormatter.confirm("Include Celery? (async task queue)", default=False)
|
||||
|
||||
def _get_structure_metadata(self, options: StructureOptions) -> Tuple[str, str, list[str], dict]:
|
||||
"""Helper method to generate metadata dictionary."""
|
||||
project_dir = options["project_dir"]
|
||||
@@ -203,6 +227,11 @@ class InputCollector:
|
||||
database_type=options["database_type"],
|
||||
use_tailwind=options.get("use_tailwind", False),
|
||||
use_htmx=options.get("use_htmx", False),
|
||||
use_docker=options.get("use_docker", False),
|
||||
use_vite=options.get("use_vite", False),
|
||||
use_vue=options.get("use_vue", False),
|
||||
use_pytest=options.get("use_pytest", False),
|
||||
use_django_q=options.get("use_django_q", False),
|
||||
predefined_structure=options["predefined"],
|
||||
unified_structure=options["unified"],
|
||||
single_structure=options["single"],
|
||||
@@ -264,6 +293,24 @@ def get_user_input() -> Tuple[str, str, str, list, dict]:
|
||||
use_tailwind = collector.get_tailwind_choice()
|
||||
use_htmx = collector.get_htmx_choice()
|
||||
|
||||
# Step 4: Docker
|
||||
use_docker = collector.get_docker_choice()
|
||||
|
||||
# Step 5: Vite/React
|
||||
use_vite = collector.get_vite_choice()
|
||||
|
||||
# Step 6: Vite/Vue
|
||||
use_vue = collector.get_vue_choice()
|
||||
|
||||
# Step 7: pytest
|
||||
use_pytest = collector.get_pytest_choice()
|
||||
|
||||
# Step 8: Django-Q2
|
||||
use_django_q = collector.get_django_q_choice()
|
||||
|
||||
# Step 9: Celery
|
||||
use_celery = collector.get_celery_choice()
|
||||
|
||||
# Step 3: Django Apps (Standard only)
|
||||
nested = False
|
||||
nested_dir = None
|
||||
@@ -289,6 +336,12 @@ def get_user_input() -> Tuple[str, str, str, list, dict]:
|
||||
database_type=database_type,
|
||||
use_tailwind=use_tailwind,
|
||||
use_htmx=use_htmx,
|
||||
use_docker=use_docker,
|
||||
use_vite=use_vite,
|
||||
use_vue=use_vue,
|
||||
use_pytest=use_pytest,
|
||||
use_django_q=use_django_q,
|
||||
use_celery=use_celery,
|
||||
)
|
||||
return project_dir, project_name, app_names[0], app_names, metadata.to_dict()
|
||||
else:
|
||||
@@ -302,6 +355,12 @@ def get_user_input() -> Tuple[str, str, str, list, dict]:
|
||||
use_database_url=use_database_url,
|
||||
use_tailwind=use_tailwind,
|
||||
use_htmx=use_htmx,
|
||||
use_docker=use_docker,
|
||||
use_vite=use_vite,
|
||||
use_vue=use_vue,
|
||||
use_pytest=use_pytest,
|
||||
use_django_q=use_django_q,
|
||||
use_celery=use_celery,
|
||||
use_github=use_github,
|
||||
use_gitlab=use_gitlab,
|
||||
)
|
||||
@@ -340,6 +399,24 @@ def confirm_setup(project_dir: str, project_name: str, app_names: list, metadata
|
||||
use_htmx = "Yes" if metadata.get("use_htmx", False) else "No"
|
||||
console.print(f"[{UIColors.HIGHLIGHT}]HTMX:[/{UIColors.HIGHLIGHT}] {use_htmx}")
|
||||
|
||||
use_docker = "Yes" if metadata.get("use_docker", False) else "No"
|
||||
console.print(f"[{UIColors.HIGHLIGHT}]Docker:[/{UIColors.HIGHLIGHT}] {use_docker}")
|
||||
|
||||
use_vite = "Yes" if metadata.get("use_vite", False) else "No"
|
||||
console.print(f"[{UIColors.HIGHLIGHT}]Vite/React:[/{UIColors.HIGHLIGHT}] {use_vite}")
|
||||
|
||||
use_pytest = "Yes" if metadata.get("use_pytest", False) else "No"
|
||||
console.print(f"[{UIColors.HIGHLIGHT}]pytest:[/{UIColors.HIGHLIGHT}] {use_pytest}")
|
||||
|
||||
use_vue = "Yes" if metadata.get("use_vue", False) else "No"
|
||||
console.print(f"[{UIColors.HIGHLIGHT}]Vite/Vue:[/{UIColors.HIGHLIGHT}] {use_vue}")
|
||||
|
||||
use_django_q = "Yes" if metadata.get("use_django_q", False) else "No"
|
||||
console.print(f"[{UIColors.HIGHLIGHT}]Django-Q2:[/{UIColors.HIGHLIGHT}] {use_django_q}")
|
||||
|
||||
use_celery = "Yes" if metadata.get("use_celery", False) else "No"
|
||||
console.print(f"[{UIColors.HIGHLIGHT}]Celery:[/{UIColors.HIGHLIGHT}] {use_celery}")
|
||||
|
||||
console.print()
|
||||
UIFormatter.print_separator()
|
||||
console.print()
|
||||
|
||||
@@ -46,6 +46,11 @@ class DjangoHelper:
|
||||
"database_type": metadata.get("database_type", "postgresql"),
|
||||
"use_tailwind": metadata.get("use_tailwind", False),
|
||||
"use_htmx": metadata.get("use_htmx", False),
|
||||
"use_vite": metadata.get("use_vite", False),
|
||||
"use_vue": metadata.get("use_vue", False),
|
||||
"use_pytest": metadata.get("use_pytest", False),
|
||||
"use_django_q": metadata.get("use_django_q", False),
|
||||
"use_celery": metadata.get("use_celery", False),
|
||||
}
|
||||
|
||||
dev_context = {"secret_key": secret_key}
|
||||
|
||||
Reference in new issue
Block a user