-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
416 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
from djangowiz.core.model_extractor import ModelExtractor | ||
from djangowiz.core.base_generator import BaseGenerator | ||
from djangowiz.core.io_handler import IOHandler | ||
from djangowiz.core.project_generator import ProjectGenerator | ||
from djangowiz.core.project_io_handler import ProjectIOHandler | ||
|
||
from .custom_generator import CustomGenerator | ||
from .serializer_generator import SerializerGenerator | ||
from .viewset_generator import ViewsetGenerator |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import os | ||
from jinja2 import Environment, FileSystemLoader | ||
from typing import List | ||
|
||
|
||
class BaseGenerator: | ||
def __init__( | ||
self, | ||
app_name: str, | ||
project_name: str, | ||
model_names: List[str], | ||
template_dir: str, | ||
**kwargs, | ||
): | ||
self.app_name = app_name | ||
self.project_name = project_name | ||
self.model_names = model_names | ||
self.env = Environment(loader=FileSystemLoader(template_dir)) | ||
self.options = kwargs | ||
|
||
def generate(self, overwrite: bool = False, template: str = None, **kwargs): | ||
raise NotImplementedError("Subclasses must implement this method") | ||
|
||
def write_file(self, file_path: str, content: str, overwrite: bool = False): | ||
if not overwrite and os.path.exists(file_path): | ||
print(f"Skipping existing file: {file_path}") | ||
return | ||
os.makedirs(os.path.dirname(file_path), exist_ok=True) | ||
with open(file_path, "w") as file: | ||
file.write(content) | ||
print(f"Generated file: {file_path}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import os | ||
import shutil | ||
import yaml | ||
from typing import Dict, Any | ||
|
||
|
||
class IOHandler: | ||
@staticmethod | ||
def load_yaml(file_path: str) -> Dict[str, Any]: | ||
with open(file_path, "r") as file: | ||
return yaml.safe_load(file) | ||
|
||
@staticmethod | ||
def save_yaml(data: Dict[str, Any], file_path: str): | ||
with open(file_path, "w") as file: | ||
yaml.safe_dump(data, file) | ||
|
||
@staticmethod | ||
def copy_file(src: str, dst: str): | ||
os.makedirs(os.path.dirname(dst), exist_ok=True) | ||
shutil.copy(src, dst) | ||
|
||
@staticmethod | ||
def copy_tree(src: str, dst: str, dirs_exist_ok: bool = True): | ||
shutil.copytree(src, dst, dirs_exist_ok=dirs_exist_ok) | ||
|
||
@staticmethod | ||
def remove_file(file_path: str): | ||
if os.path.exists(file_path): | ||
os.remove(file_path) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import ast | ||
from typing import List | ||
|
||
|
||
class ModelExtractor: | ||
@staticmethod | ||
def extract_model_names(file_path: str) -> List[str]: | ||
model_names = [] | ||
with open(file_path, "r") as file: | ||
tree = ast.parse(file.read(), filename=file_path) | ||
for node in ast.walk(tree): | ||
if isinstance(node, ast.ClassDef): | ||
bases = [ | ||
base.id if isinstance(base, ast.Name) else base.attr | ||
for base in node.bases | ||
] | ||
if "Model" in bases or any(base for base in bases): | ||
model_names.append(node.name) | ||
return model_names |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,220 @@ | ||
import os | ||
import importlib | ||
from typing import List, Dict, Any | ||
from djangowiz.core.io_handler import IOHandler | ||
from djangowiz.core.project_io_handler import ProjectIOHandler | ||
|
||
|
||
class ProjectGenerator: | ||
def __init__( | ||
self, | ||
app_name: str, | ||
project_name: str, | ||
model_names: List[str], | ||
template_dir: str = None, | ||
config_file: str = None, | ||
repo_dir: str = None, | ||
): | ||
self.app_name = app_name | ||
self.project_name = project_name | ||
self.model_names = model_names | ||
self.default_template_dir = os.path.join( | ||
os.path.dirname(__file__), "..", "repo", "templates" | ||
) | ||
self.template_dir = template_dir or ( | ||
os.path.join(repo_dir, "templates") | ||
if repo_dir | ||
else self.default_template_dir | ||
) | ||
self.generator_dir = ( | ||
os.path.join(repo_dir, "generators") | ||
if repo_dir | ||
else os.path.join(os.path.dirname(__file__), "..", "repo", "generators") | ||
) | ||
self.config_file = config_file or ( | ||
os.path.join(repo_dir, "generators.yaml") | ||
if repo_dir | ||
else os.path.join( | ||
os.path.dirname(__file__), "..", "repo", "generators.yaml" | ||
) | ||
) | ||
self.default_config_file = os.path.join( | ||
os.path.dirname(__file__), "..", "repo", "generators.yaml" | ||
) | ||
self.generators: Dict[str, Dict[str, Any]] = {} | ||
|
||
self.io_handler = ProjectIOHandler( | ||
self.config_file, | ||
self.default_config_file, | ||
self.template_dir, | ||
self.default_template_dir, | ||
self.generator_dir, | ||
) | ||
|
||
self.load_generators(self.config_file) | ||
|
||
def load_generators(self, config_file: str): | ||
user_config = IOHandler.load_yaml(config_file) | ||
default_config = IOHandler.load_yaml(self.default_config_file) | ||
|
||
for name, generator_config in default_config.get("generators", {}).items(): | ||
self.generators[name] = generator_config | ||
|
||
for name, generator_config in user_config.get("generators", {}).items(): | ||
if name in self.generators: | ||
self.generators[name]["options"].update( | ||
generator_config.get("options", {}) | ||
) | ||
else: | ||
self.generators[name] = generator_config | ||
|
||
for name, generator_config in self.generators.items(): | ||
for option, config in generator_config.get("options", {}).items(): | ||
self.load_generator(name, option, config) | ||
|
||
def load_generator(self, name: str, option: str, config: Dict[str, Any]): | ||
class_path = config["class"] | ||
module_path, class_name = class_path.rsplit(".", 1) | ||
if self.generator_dir not in module_path: | ||
module_path = os.path.join(self.generator_dir, module_path) | ||
module = importlib.import_module(module_path) | ||
generator_class = getattr(module, class_name) | ||
template_path = config.get("template", "") | ||
|
||
if not os.path.exists(os.path.join(self.template_dir, template_path)): | ||
template_path = os.path.join(self.default_template_dir, template_path) | ||
|
||
self.generators[f"{name}_{option}"] = { | ||
"class": generator_class( | ||
self.app_name, | ||
self.project_name, | ||
self.model_names, | ||
self.template_dir, | ||
**config, | ||
), | ||
"template": template_path, | ||
} | ||
|
||
def save_generators(self): | ||
combined_config = {"generators": {}} | ||
for name, generator in self.generators.items(): | ||
base_name, option = name.rsplit("_", 1) | ||
if base_name not in combined_config["generators"]: | ||
combined_config["generators"][base_name] = {"options": {}} | ||
combined_config["generators"][base_name]["options"][option] = { | ||
"class": generator["class"].__class__.__module__ | ||
+ "." | ||
+ generator["class"].__class__.__name__, | ||
"template": generator["template"], | ||
**{ | ||
k: v | ||
for k, v in generator["class"].options.items() | ||
if k not in ["class", "template"] | ||
}, | ||
} | ||
|
||
IOHandler.save_yaml(combined_config, self.config_file) | ||
|
||
def add_generator( | ||
self, name: str, class_path: str, template_path: str, option: str, **kwargs | ||
): | ||
generator_key = f"{name}_{option}" | ||
if generator_key in self.generators: | ||
print( | ||
f"Generator '{generator_key}' already exists. Use update_generator to update it." | ||
) | ||
return | ||
|
||
if not os.path.exists(template_path): | ||
template_path = os.path.join(self.default_template_dir, template_path) | ||
|
||
module_path, class_name = class_path.rsplit(".", 1) | ||
module = importlib.import_module(module_path) | ||
generator_class = getattr(module, class_name) | ||
generator_instance = generator_class( | ||
self.app_name, | ||
self.project_name, | ||
self.model_names, | ||
self.template_dir, | ||
**kwargs, | ||
) | ||
|
||
self.generators[generator_key] = { | ||
"class": generator_instance, | ||
"template": template_path, | ||
} | ||
|
||
self.save_generators() | ||
IOHandler.copy_file( | ||
module_path.replace(".", "/") + ".py", | ||
os.path.join(self.generator_dir, module_path.replace(".", "/") + ".py"), | ||
) | ||
self.load_generators(self.config_file) # Reload configuration | ||
print(f"Generator '{generator_key}' has been added.") | ||
|
||
def delete_generator(self, name: str, option: str): | ||
generator_key = f"{name}_{option}" | ||
if generator_key in self.generators: | ||
IOHandler.remove_file( | ||
self.generators[generator_key]["class"].__module__.replace(".", "/") | ||
+ ".py" | ||
) | ||
del self.generators[generator_key] | ||
self.save_generators() | ||
self.load_generators(self.config_file) # Reload configuration | ||
print(f"Generator '{generator_key}' has been deleted.") | ||
else: | ||
print(f"Generator '{generator_key}' does not exist.") | ||
|
||
def update_generator( | ||
self, name: str, class_path: str, template_path: str, option: str, **kwargs | ||
): | ||
generator_key = f"{name}_{option}" | ||
if not os.path.exists(template_path): | ||
template_path = os.path.join(self.default_template_dir, template_path) | ||
|
||
module_path, class_name = class_path.rsplit(".", 1) | ||
module = importlib.import_module(module_path) | ||
generator_class = getattr(module, class_name) | ||
generator_instance = generator_class( | ||
self.app_name, | ||
self.project_name, | ||
self.model_names, | ||
self.template_dir, | ||
**kwargs, | ||
) | ||
|
||
self.generators[generator_key] = { | ||
"class": generator_instance, | ||
"template": template_path, | ||
} | ||
|
||
self.save_generators() | ||
IOHandler.copy_file( | ||
module_path.replace(".", "/") + ".py", | ||
os.path.join(self.generator_dir, module_path.replace(".", "/") + ".py"), | ||
) | ||
self.load_generators(self.config_file) # Reload configuration | ||
print(f"Generator '{generator_key}' has been updated.") | ||
|
||
def show_generators(self): | ||
for name, generator in self.generators.items(): | ||
print( | ||
f"Generator: {name} ({generator['class'].__class__.__module__}.{generator['class'].__class__.__name__}), Template: {generator['template']}, Config: {generator['class'].options}" | ||
) | ||
|
||
def generate( | ||
self, components: List[str], option: str, overwrite: bool = False, **kwargs | ||
): | ||
for component in components: | ||
generator_key = f"{component}_{option}" | ||
if generator_key in self.generators: | ||
generator = self.generators[generator_key]["class"] | ||
template = self.generators[generator_key]["template"] | ||
generator.generate(overwrite=overwrite, template=template, **kwargs) | ||
|
||
def export_config(self, export_path: str): | ||
self.io_handler.export_config(export_path) | ||
|
||
def import_config(self, import_path: str): | ||
self.io_handler.import_config(import_path) |
Oops, something went wrong.