Skip to content

Commit

Permalink
feat(complex):MVP complete
Browse files Browse the repository at this point in the history
  • Loading branch information
tavallaie committed May 27, 2024
1 parent a51281e commit ced6e6e
Show file tree
Hide file tree
Showing 18 changed files with 703 additions and 103 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"python.REPL.enableREPLSmartSend": false
}
18 changes: 15 additions & 3 deletions djangowiz/cli.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# djangowiz/app.py
# djangowiz/cli.py

import typer
import os
Expand Down Expand Up @@ -37,7 +37,13 @@ def command_function(
):
model_names = ModelExtractor.extract_model_names(model_file)
project_generator = ProjectGenerator(
app_name, project_name, model_names, template_dir, config_file, repo_dir
app_name,
project_name,
model_names,
template_dir,
config_file,
repo_dir,
model_file=model_file,
)
project_generator.generate([generator_name], option, overwrite)

Expand Down Expand Up @@ -67,7 +73,13 @@ def generate_files(
):
model_names = ModelExtractor.extract_model_names(model_file)
project_generator = ProjectGenerator(
app_name, project_name, model_names, template_dir, config_file, repo_dir
app_name,
project_name,
model_names,
template_dir,
config_file,
repo_dir,
model_file=model_file,
)
project_generator.generate(
[
Expand Down
2 changes: 2 additions & 0 deletions djangowiz/core/base_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ def __init__(
project_name: str,
model_names: List[str],
template_dir: str,
model_file,
**kwargs,
):
self.app_name = app_name
self.project_name = project_name
self.model_names = model_names
self.model_file = model_file
self.env = Environment(loader=FileSystemLoader(template_dir))
self.options = kwargs

Expand Down
3 changes: 3 additions & 0 deletions djangowiz/core/project_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ def __init__(
template_dir: str = None,
config_file: str = None,
repo_dir: str = None,
model_file: str = None,
):
self.app_name = app_name
self.project_name = project_name
self.model_names = model_names
self.model_file = model_file
self.default_template_dir = os.path.join(
os.path.dirname(__file__), "..", "repo", "templates"
)
Expand Down Expand Up @@ -99,6 +101,7 @@ def load_generator(self, name: str, option: str, config: Dict[str, Any]):
self.project_name,
self.model_names,
self.template_dir,
self.model_file,
**config,
),
"template": template_path,
Expand Down
92 changes: 55 additions & 37 deletions djangowiz/repo/generators.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# djangowiz/repo/generators.yaml

generators:
serializers:
options:
Expand All @@ -15,40 +17,56 @@ generators:
multi:
class: djangowiz.repo.generators.viewset_generator.ViewsetGenerator
template: multi/viewsets.py.j2
# urls:
# options:
# single:
# class: repo.generators.custom_generator.CustomGenerator
# template: urls.py.j2
# multi:
# class: repo.generators.custom_generator.CustomGenerator
# template: urls.py.j2
# # routes:
# options:
# single:
# class: repo.generators.custom_generator.CustomGenerator
# template: routes.py.j2
# multi:
# class: repo.generators.custom_generator.CustomGenerator
# template: routes.py.j2
# dockerfile:
# options:
# default:
# class: repo.generators.custom_generator.CustomGenerator
# template: Dockerfile.j2
# docker_compose:
# options:
# dev:
# class: repo.generators.custom_generator.CustomGenerator
# template: docker-compose.dev.yml.j2
# prod:
# class: repo.generators.custom_generator.CustomGenerator
# template: docker-compose.prod.yml.j2
# env_files:
# options:
# dev:
# class: repo.generators.custom_generator.CustomGenerator
# template: env.dev.j2
# prod:
# class: repo.generators.custom_generator.CustomGenerator
# template: env.prod.j2
admin:
options:
single:
class: djangowiz.repo.generators.admin_generator.AdminGenerator
template: single/admin.py.j2
multi:
class: djangowiz.repo.generators.admin_generator.AdminGenerator
template: multi/admin.py.j2
tests:
options:
single:
class: djangowiz.repo.generators.test_generator.TestGenerator
template: single/tests.py.j2
multi:
class: djangowiz.repo.generators.test_generator.TestGenerator
template: multi/tests.py.j2
# urls:
# options:
# single:
# class: repo.generators.custom_generator.CustomGenerator
# template: urls.py.j2
# multi:
# class: repo.generators.custom_generator.CustomGenerator
# template: urls.py.j2
# # routes:
# options:
# single:
# class: repo.generators.custom_generator.CustomGenerator
# template: routes.py.j2
# multi:
# class: repo.generators.custom_generator.CustomGenerator
# template: routes.py.j2
# dockerfile:
# options:
# default:
# class: repo.generators.custom_generator.CustomGenerator
# template: Dockerfile.j2
# docker_compose:
# options:
# dev:
# class: repo.generators.custom_generator.CustomGenerator
# template: docker-compose.dev.yml.j2
# prod:
# class: repo.generators.custom_generator.CustomGenerator
# template: docker-compose.prod.yml.j2
# env_files:
# options:
# dev:
# class: repo.generators.custom_generator.CustomGenerator
# template: env.dev.j2
# prod:
# class: repo.generators.custom_generator.CustomGenerator
# template: env.prod.j2
27 changes: 27 additions & 0 deletions djangowiz/repo/generators/admin_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# djangowiz/repo/generators/admin_generator.py

from djangowiz.core.base_generator import BaseGenerator
import os


class AdminGenerator(BaseGenerator):
def generate(self, overwrite: bool = False, template: str = None, **kwargs):
if "single" in template:
self.generate_single_file(overwrite, template)
else:
self.generate_multi_file(overwrite, template)

def generate_single_file(self, overwrite: bool, template: str):
context = {"app_name": self.app_name, "model_names": self.model_names}
content = self.render_template(template, context)
file_path = os.path.join(self.app_name, "admin.py")
self.write_file(file_path, content, overwrite)

def generate_multi_file(self, overwrite: bool, template: str):
for model_name in self.model_names:
context = {"app_name": self.app_name, "model_name": model_name}
content = self.render_template(template, context)
file_path = os.path.join(
self.app_name, "admin", f"{model_name.lower()}_admin.py"
)
self.write_file(file_path, content, overwrite)
50 changes: 50 additions & 0 deletions djangowiz/repo/generators/test_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# djangowiz/repo/generators/test_generator.py

from djangowiz.core.base_generator import BaseGenerator
import os
import re


class TestGenerator(BaseGenerator):
def generate(self, overwrite: bool = False, template: str = None, **kwargs):
if "single" in template:
self.generate_single_file(overwrite, template)
else:
self.generate_multi_file(overwrite, template)

def generate_single_file(self, overwrite: bool, template: str):
fixtures = {
model_name: self.get_fixtures(model_name) for model_name in self.model_names
}
context = {
"app_name": self.app_name,
"model_names": self.model_names,
"fixtures": fixtures,
}
content = self.render_template(template, context)
file_path = os.path.join(self.app_name, "tests", "test_api.py")
self.write_file(file_path, content, overwrite)

def generate_multi_file(self, overwrite: bool, template: str):
fixtures = self.get_fixtures()
for model_name in self.model_names:
context = {
"app_name": self.app_name,
"model_name": model_name,
"fixtures": fixtures.get(model_name, []),
}
content = self.render_template(template, context)
file_path = os.path.join(
self.app_name, "tests", f"test_{model_name.lower()}.py"
)
self.write_file(file_path, content, overwrite)

def get_fixtures(self, model_name):
fixtures_dir = os.path.join(os.path.dirname(self.model_file), "fixtures")
for root, _, files in os.walk(fixtures_dir):
for file in files:
if re.match(rf"^\d+_{model_name.lower()}_seeding\.json$", file):
return os.path.relpath(
os.path.join(root, file), start=os.path.dirname(self.model_file)
)
return f"{model_name.lower()}_fixtures.json" # Fallback fixture file name
7 changes: 7 additions & 0 deletions djangowiz/repo/templates/multi/admin.py.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from django.contrib import admin
from {{ app_name }}.models import {{ model_name }}

@admin.register({{ model_name }})
class {{ model_name }}Admin(admin.ModelAdmin):
list_display = [field.name for field in {{ model_name }}._meta.fields]
search_fields = [field.name for field in {{ model_name }}._meta.fields if field.get_internal_type() == 'CharField']
49 changes: 49 additions & 0 deletions djangowiz/repo/templates/multi/tests.py.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from django.test import TestCase
from django.urls import reverse
from rest_framework import status
from {{ app_name }}.models import {{ model_name }}

class {{ model_name }}APITests(TestCase):
fixtures = {{ fixtures | tojson | default('[]', true) }}

def setUp(self):
# Create some initial data for testing
self.instances = {{ model_name }}.objects.all()
self.first_instance = self.instances.first()

def test_list_{{ model_name | lower }}s(self):
url = reverse('{{ model_name | lower }}_list')
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), len(self.instances))

def test_create_{{ model_name | lower }}(self):
url = reverse('{{ model_name | lower }}_list')
data = {field.name: getattr(self.first_instance, field.name) for field in {{ model_name }}._meta.fields if field.name != "id"}
response = self.client.post(url, data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual({{ model_name }}.objects.count(), len(self.instances) + 1)

def test_retrieve_{{ model_name | lower }}(self):
url = reverse('{{ model_name | lower }}_detail', args=[self.first_instance.pk])
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['id'], self.first_instance.id)

def test_update_{{ model_name | lower }}(self):
url = reverse('{{ model_name | lower }}_detail', args=[self.first_instance.pk])
data = {field.name: getattr(self.first_instance, field.name) for field in {{ model_name }}._meta.fields if field.name != "id"}
response = self.client.put(url, data)
self.assertEqual(response.status_code, status.HTTP_200_OK)

def test_delete_{{ model_name | lower }}(self):
url = reverse('{{ model_name | lower }}_detail', args=[self.first_instance.pk])
response = self.client.delete(url)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.assertEqual({{ model_name }}.objects.count(), len(self.instances) - 1)

def test_invalid_create(self):
url = reverse('{{ model_name | lower }}_list')
data = {}
response = self.client.post(url, data)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
9 changes: 9 additions & 0 deletions djangowiz/repo/templates/single/admin.py.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from django.contrib import admin
from {{ app_name }}.models import {{ model_names | join(', ') }}

{% for model_name in model_names %}
@admin.register({{ model_name }})
class {{ model_name }}Admin(admin.ModelAdmin):
list_display = [field.name for field in {{ model_name }}._meta.fields]
search_fields = [field.name for field in {{ model_name }}._meta.fields if field.get_internal_type() == 'CharField']
{% endfor %}
51 changes: 51 additions & 0 deletions djangowiz/repo/templates/single/tests.py.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from django.test import TestCase
from django.urls import reverse
from rest_framework import status
from {{ app_name }}.models import {{ model_names | join(', ') }}

{% for model_name in model_names %}
class {{ model_name }}APITests(TestCase):
fixtures = {{ fixtures[model_name] | tojson | default('[]', true) }}

def setUp(self):
# Create some initial data for testing
self.instances = {{ model_name }}.objects.all()
self.first_instance = self.instances.first()

def test_list_{{ model_name | lower }}s(self):
url = reverse('{{ model_name | lower }}_list')
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), len(self.instances))

def test_create_{{ model_name | lower }}(self):
url = reverse('{{ model_name | lower }}_list')
data = {field.name: getattr(self.first_instance, field.name) for field in {{ model_name }}._meta.fields if field.name != "id"}
response = self.client.post(url, data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual({{ model_name }}.objects.count(), len(self.instances) + 1)

def test_retrieve_{{ model_name | lower }}(self):
url = reverse('{{ model_name | lower }}_detail', args=[self.first_instance.pk])
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['id'], self.first_instance.id)

def test_update_{{ model_name | lower }}(self):
url = reverse('{{ model_name | lower }}_detail', args=[self.first_instance.pk])
data = {field.name: getattr(self.first_instance, field.name) for field in {{ model_name }}._meta.fields if field.name != "id"}
response = self.client.put(url, data)
self.assertEqual(response.status_code, status.HTTP_200_OK)

def test_delete_{{ model_name | lower }}(self):
url = reverse('{{ model_name | lower }}_detail', args=[self.first_instance.pk])
response = self.client.delete(url)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.assertEqual({{ model_name }}.objects.count(), len(self.instances) - 1)

def test_invalid_create(self):
url = reverse('{{ model_name | lower }}_list')
data = {}
response = self.client.post(url, data)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
{% endfor %}
Loading

0 comments on commit ced6e6e

Please sign in to comment.