diff --git a/.husky/commit-msg b/.husky/commit-msg index 2218daa..68a2f4a 100755 --- a/.husky/commit-msg +++ b/.husky/commit-msg @@ -1,3 +1,3 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -npx --no-install commitlint --edit "$1" +#npx --no-install commitlint --edit "$1" diff --git a/Dockerfile b/Dockerfile index 56f6089..ea9af08 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Base image -FROM python:3.13.0a1-alpine +FROM python:3.13.0a4-alpine LABEL MAINTAINER="Leo Neto" @@ -17,4 +17,4 @@ COPY . . # Install dependencies RUN apk add gcc musl-dev linux-headers && pip install -e . -ENTRYPOINT ["django-clite"] +CMD ["django-clite"] diff --git a/README.md b/README.md index a7b5567..8ec2479 100644 --- a/README.md +++ b/README.md @@ -87,9 +87,9 @@ prepending or appending any number of underscores (`_`). Any code contained with The flag `--templates-dir` can be used to configure an additional path wherein the CLI can look for resource templates. Alternatively, you can use the environment variable `DJANGO_CLITE_TEMPLATES_DIR` for the same purpose. -Take a look at the [template files directory](src/cli/template_files) for a reference of what files can be overriden. The +Take a look at the [template files directory](django_clite/cli/template_files) for a reference of what files can be overriden. The paths of the templates you wish to override need to match the provided template. For example, if you wish to override the -model template, which is defined under [`src/cli/template_files/models/model.tpl`](src/cli/template_files/models/model.tpl), +model template, which is defined under [`src/cli/template_files/models/model.tpl`](django_clite/cli/template_files/models/model.tpl), you should define your own model template under your desired directory, i.e `/path/to/templates/models/model.tpl`. ## Development diff --git a/src/cli/__init__.py b/django_clite/__init__.py similarity index 72% rename from src/cli/__init__.py rename to django_clite/__init__.py index f642d03..cf4c49b 100644 --- a/src/cli/__init__.py +++ b/django_clite/__init__.py @@ -3,14 +3,12 @@ django-clite: a cli tool that handles creating and managing Django projects """ import os +from .constants import PLUGINS_ENV_VAR -__version__ = "0.19.0" __license__ = "BSD 3-Clause" __author__ = "Leo Neto" __copyright__ = "Copyright 2019-2023 Leo Neto" COMMANDS_FOLDER = os.path.join(os.path.dirname(__file__), "commands") -PLUGINS_FOLDER = os.environ.get("DJANGO_CLITE_PLUGINS", None) - -VERSION = __version__ +PLUGINS_FOLDER = os.environ.get(PLUGINS_ENV_VAR, None) diff --git a/django_clite/cli/__init__.py b/django_clite/cli/__init__.py new file mode 100644 index 0000000..e29a28c --- /dev/null +++ b/django_clite/cli/__init__.py @@ -0,0 +1 @@ +# django_clite:cli diff --git a/src/cli/app.py b/django_clite/cli/cli.py similarity index 89% rename from src/cli/app.py rename to django_clite/cli/cli.py index 106c5df..e9a3f7b 100755 --- a/src/cli/app.py +++ b/django_clite/cli/cli.py @@ -2,12 +2,11 @@ import logging from pathlib import Path -from cli.extensions.combined import AliasedAndDiscoverableGroup +from django_clite.extensions.combined import AliasedAndDiscoverableGroup from geny.core.filesystem.finder import core_project_files, project_and_app_names from geny.core.templates.template import TemplateParser -from cli import VERSION -from cli.constants import ( +from django_clite.constants import ( DJANGO_FILES_KEY, ENABLE_DRY_RUN_KEY, ENABLE_DEBUG_KEY, @@ -15,6 +14,7 @@ ENABLE_VERBOSITY_KEY, PROJECT_NAME_KEY, APPLICATION_NAME_KEY, + TEMPLATES_DIRECTORY_ENV_VAR, ) @@ -23,18 +23,18 @@ ) @click.option("--debug", is_flag=True, help="Enable debug logs.") @click.option("--dry", is_flag=True, help="Do not modify the file system.") -@click.option("-f", "--force", is_flag=True, help="Override any conflicting files.") +@click.option("-f", "--force", is_flag=True, envvar=ENABLE_FORCE_KEY, help="Override any conflicting files.") @click.option("--verbose", is_flag=True, help="Enable verbosity.") @click.option("--project", help="Project name.") @click.option("--app", help="Application name.") @click.option( "--templates-dir", "-t", - envvar="DJANGO_CLITE_TEMPLATES_DIR", + envvar=TEMPLATES_DIRECTORY_ENV_VAR, help="Template directory.", type=click.Path(), ) -@click.version_option(version=VERSION) +@click.version_option(package_name="django-clite") @click.pass_context def cli(ctx, debug, dry, force, verbose, project, app, templates_dir): """ @@ -86,7 +86,7 @@ def cli(ctx, debug, dry, force, verbose, project, app, templates_dir): django_files = core_project_files() project_name, app_name = project_and_app_names(django_files) - templates = [Path(__file__).resolve().parent / "template_files"] + templates = [Path(__file__).resolve().parent.parent / "template_files"] if templates_dir is not None: templates.append(Path(templates_dir)) diff --git a/src/cli/utils.py b/django_clite/cli/utils.py similarity index 100% rename from src/cli/utils.py rename to django_clite/cli/utils.py diff --git a/src/cli/commands/__init__.py b/django_clite/commands/__init__.py similarity index 100% rename from src/cli/commands/__init__.py rename to django_clite/commands/__init__.py diff --git a/django_clite/commands/callbacks.py b/django_clite/commands/callbacks.py new file mode 100644 index 0000000..ad4c6ae --- /dev/null +++ b/django_clite/commands/callbacks.py @@ -0,0 +1,10 @@ +from django_clite.cli.utils import sanitized_string +from django_clite.core.field_parser.factory import AttributeFactory + + +def sanitized_string_callback(_ctx, _param, value): + return sanitized_string(value) + + +def fields_callback(ctx, _param, values): + return AttributeFactory().parsed_fields(values) diff --git a/src/cli/commands/command_defaults.py b/django_clite/commands/command_defaults.py similarity index 100% rename from src/cli/commands/command_defaults.py rename to django_clite/commands/command_defaults.py diff --git a/src/cli/commands/destroy/__init__.py b/django_clite/commands/destroy/__init__.py similarity index 100% rename from src/cli/commands/destroy/__init__.py rename to django_clite/commands/destroy/__init__.py diff --git a/src/cli/commands/destroy/admin.py b/django_clite/commands/destroy/admin.py similarity index 78% rename from src/cli/commands/destroy/admin.py rename to django_clite/commands/destroy/admin.py index e77e428..fbc7288 100644 --- a/src/cli/commands/destroy/admin.py +++ b/django_clite/commands/destroy/admin.py @@ -1,9 +1,9 @@ import click from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback -from cli.decorators.scope import scoped, Scope -from cli.commands import command_defaults +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands import command_defaults +from django_clite.commands.callbacks import sanitized_string_callback @scoped(to=Scope.APP) diff --git a/src/cli/commands/destroy/dockerfile.py b/django_clite/commands/destroy/dockerfile.py similarity index 100% rename from src/cli/commands/destroy/dockerfile.py rename to django_clite/commands/destroy/dockerfile.py diff --git a/src/cli/commands/destroy/fixtures.py b/django_clite/commands/destroy/fixtures.py similarity index 72% rename from src/cli/commands/destroy/fixtures.py rename to django_clite/commands/destroy/fixtures.py index bbf4266..cd6449f 100644 --- a/src/cli/commands/destroy/fixtures.py +++ b/django_clite/commands/destroy/fixtures.py @@ -1,8 +1,8 @@ import click from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback -from cli.decorators.scope import scoped, Scope +from django_clite.commands.callbacks import sanitized_string_callback +from django_clite.decorators.scope import scoped, Scope @scoped(to=Scope.APP) diff --git a/src/cli/commands/destroy/forms.py b/django_clite/commands/destroy/forms.py similarity index 70% rename from src/cli/commands/destroy/forms.py rename to django_clite/commands/destroy/forms.py index bcbb976..ec5fc41 100644 --- a/src/cli/commands/destroy/forms.py +++ b/django_clite/commands/destroy/forms.py @@ -1,8 +1,8 @@ import click from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback -from cli.decorators.scope import scoped, Scope +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands.callbacks import sanitized_string_callback @scoped(to=Scope.APP) diff --git a/django_clite/commands/destroy/main.py b/django_clite/commands/destroy/main.py new file mode 100644 index 0000000..4f018cf --- /dev/null +++ b/django_clite/commands/destroy/main.py @@ -0,0 +1,50 @@ +# cli:commands:destroy +import click + +from django_clite.commands.destroy.admin import admin, admin_inline as admin_inline +from django_clite.commands.destroy.fixtures import fixture +from django_clite.commands.destroy.forms import form +from django_clite.commands.destroy.management import management +from django_clite.commands.destroy.managers import manager +from django_clite.commands.destroy.models import model, scaffold +from django_clite.commands.destroy.serializers import serializer +from django_clite.commands.destroy.signals import signal +from django_clite.commands.destroy.tags import tag +from django_clite.commands.destroy.template import template +from django_clite.commands.destroy.tests import test +from django_clite.commands.destroy.validators import validator +from django_clite.commands.destroy.views import view +from django_clite.commands.destroy.viewsets import viewset + + +@click.group() +@click.pass_context +def destroy(ctx): + """ + Destroy application resources. + """ + + ctx.ensure_object(dict) + + +[ + destroy.add_command(cmd) + for cmd in [ + admin, + admin_inline, + fixture, + form, + management, + manager, + model, + scaffold, + serializer, + signal, + tag, + template, + test, + validator, + view, + viewset, + ] +] diff --git a/src/cli/commands/destroy/management.py b/django_clite/commands/destroy/management.py similarity index 73% rename from src/cli/commands/destroy/management.py rename to django_clite/commands/destroy/management.py index 1789c12..89854bf 100644 --- a/src/cli/commands/destroy/management.py +++ b/django_clite/commands/destroy/management.py @@ -1,8 +1,8 @@ import click from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback -from cli.decorators.scope import scoped, Scope +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands.callbacks import sanitized_string_callback @scoped(to=Scope.APP) diff --git a/src/cli/commands/destroy/managers.py b/django_clite/commands/destroy/managers.py similarity index 72% rename from src/cli/commands/destroy/managers.py rename to django_clite/commands/destroy/managers.py index 074d850..9b8a52b 100644 --- a/src/cli/commands/destroy/managers.py +++ b/django_clite/commands/destroy/managers.py @@ -1,8 +1,8 @@ import click from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback -from cli.decorators.scope import scoped, Scope +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands.callbacks import sanitized_string_callback @scoped(to=Scope.APP) diff --git a/src/cli/commands/destroy/models.py b/django_clite/commands/destroy/models.py similarity index 92% rename from src/cli/commands/destroy/models.py rename to django_clite/commands/destroy/models.py index ea85c09..9ed0839 100644 --- a/src/cli/commands/destroy/models.py +++ b/django_clite/commands/destroy/models.py @@ -1,10 +1,10 @@ import click from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback -from cli.decorators.scope import scoped, Scope -from cli.core.logger import logger -from cli.commands import command_defaults +from django_clite.core.logger import logger +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands import command_defaults +from django_clite.commands.callbacks import sanitized_string_callback @scoped(to=Scope.APP) diff --git a/src/cli/commands/destroy/serializers.py b/django_clite/commands/destroy/serializers.py similarity index 71% rename from src/cli/commands/destroy/serializers.py rename to django_clite/commands/destroy/serializers.py index 7bc5158..b0eae91 100644 --- a/src/cli/commands/destroy/serializers.py +++ b/django_clite/commands/destroy/serializers.py @@ -1,9 +1,9 @@ import click from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback -from cli.decorators.scope import scoped, Scope -from cli.commands import command_defaults +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands import command_defaults +from django_clite.commands.callbacks import sanitized_string_callback @scoped(to=Scope.APP) diff --git a/src/cli/commands/destroy/signals.py b/django_clite/commands/destroy/signals.py similarity index 70% rename from src/cli/commands/destroy/signals.py rename to django_clite/commands/destroy/signals.py index cf79c38..47ab4ed 100644 --- a/src/cli/commands/destroy/signals.py +++ b/django_clite/commands/destroy/signals.py @@ -1,9 +1,9 @@ import click from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback -from cli.decorators.scope import scoped, Scope -from cli.commands import command_defaults +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands import command_defaults +from django_clite.commands.callbacks import sanitized_string_callback @scoped(to=Scope.APP) diff --git a/src/cli/commands/destroy/tags.py b/django_clite/commands/destroy/tags.py similarity index 70% rename from src/cli/commands/destroy/tags.py rename to django_clite/commands/destroy/tags.py index 4c76501..b4a59f8 100644 --- a/src/cli/commands/destroy/tags.py +++ b/django_clite/commands/destroy/tags.py @@ -1,9 +1,9 @@ import click from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback -from cli.decorators.scope import scoped, Scope -from cli.commands import command_defaults +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands import command_defaults +from django_clite.commands.callbacks import sanitized_string_callback @scoped(to=Scope.APP) diff --git a/src/cli/commands/destroy/template.py b/django_clite/commands/destroy/template.py similarity index 85% rename from src/cli/commands/destroy/template.py rename to django_clite/commands/destroy/template.py index 2c05f8e..a04487e 100644 --- a/src/cli/commands/destroy/template.py +++ b/django_clite/commands/destroy/template.py @@ -1,8 +1,8 @@ import click from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback -from cli.decorators.scope import scoped, Scope +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands.callbacks import sanitized_string_callback SUPPORTED_CLASSES = [ diff --git a/src/cli/commands/destroy/tests.py b/django_clite/commands/destroy/tests.py similarity index 78% rename from src/cli/commands/destroy/tests.py rename to django_clite/commands/destroy/tests.py index 0339772..cffccf4 100644 --- a/src/cli/commands/destroy/tests.py +++ b/django_clite/commands/destroy/tests.py @@ -2,10 +2,10 @@ import inflection from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string, sanitized_string_callback -from cli.decorators.scope import scoped, Scope -from cli.core.logger import logger -from cli.commands import command_defaults +from django_clite.core.logger import logger +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands import command_defaults +from django_clite.commands.callbacks import sanitized_string, sanitized_string_callback SUPPORTED_SCOPES = [ diff --git a/src/cli/commands/destroy/validators.py b/django_clite/commands/destroy/validators.py similarity index 71% rename from src/cli/commands/destroy/validators.py rename to django_clite/commands/destroy/validators.py index aa769f7..3e0022e 100644 --- a/src/cli/commands/destroy/validators.py +++ b/django_clite/commands/destroy/validators.py @@ -1,9 +1,9 @@ import click from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback -from cli.decorators.scope import scoped, Scope -from cli.commands import command_defaults +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands import command_defaults +from django_clite.commands.callbacks import sanitized_string_callback @scoped(to=Scope.APP) diff --git a/src/cli/commands/destroy/views.py b/django_clite/commands/destroy/views.py similarity index 85% rename from src/cli/commands/destroy/views.py rename to django_clite/commands/destroy/views.py index f1dc1e6..50b48eb 100644 --- a/src/cli/commands/destroy/views.py +++ b/django_clite/commands/destroy/views.py @@ -1,9 +1,9 @@ import click from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback -from cli.decorators.scope import scoped, Scope -from cli.commands import command_defaults +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands import command_defaults +from django_clite.commands.callbacks import sanitized_string_callback from .template import template diff --git a/src/cli/commands/destroy/viewsets.py b/django_clite/commands/destroy/viewsets.py similarity index 78% rename from src/cli/commands/destroy/viewsets.py rename to django_clite/commands/destroy/viewsets.py index 61df3c0..6ea1bfe 100644 --- a/src/cli/commands/destroy/viewsets.py +++ b/django_clite/commands/destroy/viewsets.py @@ -1,9 +1,9 @@ import click from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback -from cli.decorators.scope import scoped, Scope -from cli.commands import command_defaults +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands import command_defaults +from django_clite.commands.callbacks import sanitized_string_callback @scoped(to=Scope.APP) diff --git a/src/cli/commands/generate/__init__.py b/django_clite/commands/generate/__init__.py similarity index 100% rename from src/cli/commands/generate/__init__.py rename to django_clite/commands/generate/__init__.py diff --git a/src/cli/commands/generate/admin.py b/django_clite/commands/generate/admin.py similarity index 85% rename from src/cli/commands/generate/admin.py rename to django_clite/commands/generate/admin.py index c5aba8e..960f505 100644 --- a/src/cli/commands/generate/admin.py +++ b/django_clite/commands/generate/admin.py @@ -2,9 +2,9 @@ import inflection from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback, fields_callback -from cli.decorators.scope import scoped, Scope -from cli.commands import command_defaults +from django_clite.commands import command_defaults +from django_clite.commands.callbacks import sanitized_string_callback, fields_callback +from django_clite.decorators.scope import scoped, Scope @scoped(to=Scope.APP) @@ -26,8 +26,7 @@ def admin(ctx, name, fields, permissions, skip_import): Generate an admin model. """ - admin_fields, _ = fields - admin_fields = [x for x in admin_fields if x.supported_in_admin] + admin_fields = [attr_name for attr_name, v in fields.items() if v.supports_admin] file = File( name=f"admin/{name}.py", diff --git a/src/cli/commands/generate/dockerfile.py b/django_clite/commands/generate/dockerfile.py similarity index 93% rename from src/cli/commands/generate/dockerfile.py rename to django_clite/commands/generate/dockerfile.py index b90ccdf..2a9b7f5 100644 --- a/src/cli/commands/generate/dockerfile.py +++ b/django_clite/commands/generate/dockerfile.py @@ -1,7 +1,7 @@ import click from geny.core.filesystem.files import File -from cli.decorators.scope import scoped, Scope +from django_clite.decorators.scope import scoped, Scope @scoped(to=Scope.PROJECT) diff --git a/src/cli/commands/generate/fixtures.py b/django_clite/commands/generate/fixtures.py similarity index 77% rename from src/cli/commands/generate/fixtures.py rename to django_clite/commands/generate/fixtures.py index d237611..2a9a8a3 100644 --- a/src/cli/commands/generate/fixtures.py +++ b/django_clite/commands/generate/fixtures.py @@ -2,8 +2,8 @@ import inflection from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback, fields_callback -from cli.decorators.scope import scoped, Scope +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands.callbacks import sanitized_string_callback, fields_callback @scoped(to=Scope.APP) @@ -17,14 +17,12 @@ def fixture(ctx, model, total, fields): Generate model fixtures. """ - fixture_fields, _ = fields - file = File( name=f"fixtures/{model}.json", template="fixture.tpl", context={ "total": total, - "fields": fixture_fields, + "fields": fields, "classname": inflection.camelize(model), }, ) diff --git a/src/cli/commands/generate/forms.py b/django_clite/commands/generate/forms.py similarity index 80% rename from src/cli/commands/generate/forms.py rename to django_clite/commands/generate/forms.py index e232a3d..57ac320 100644 --- a/src/cli/commands/generate/forms.py +++ b/django_clite/commands/generate/forms.py @@ -2,9 +2,9 @@ import inflection from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback -from cli.decorators.scope import scoped, Scope -from cli.commands import command_defaults +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands import command_defaults +from django_clite.commands.callbacks import sanitized_string_callback @scoped(to=Scope.APP) diff --git a/django_clite/commands/generate/main.py b/django_clite/commands/generate/main.py new file mode 100644 index 0000000..9baa383 --- /dev/null +++ b/django_clite/commands/generate/main.py @@ -0,0 +1,54 @@ +# cli:commands:generate +import click + +from django_clite.commands.generate.admin import admin, admin_inline as admin_inline +from django_clite.commands.generate.dockerfile import dockerfile +from django_clite.commands.generate.fixtures import fixture +from django_clite.commands.generate.forms import form +from django_clite.commands.generate.management import management +from django_clite.commands.generate.managers import manager +from django_clite.commands.generate.models import model, scaffold +from django_clite.commands.generate.serializers import serializer +from django_clite.commands.generate.signals import signal +from django_clite.commands.generate.tags import tag +from django_clite.commands.generate.template import template +from django_clite.commands.generate.tests import test +from django_clite.commands.generate.validators import validator +from django_clite.commands.generate.views import view +from django_clite.commands.generate.viewsets import viewset + + +@click.group() +@click.option("--project", type=click.Path(), help="Project name.") +@click.option("--app", type=click.Path(), help="Application name.") +@click.pass_context +def generate(ctx, project, app): + """ + Create application resources. + """ + + ctx.ensure_object(dict) + + +[ + generate.add_command(cmd) + for cmd in [ + admin, + admin_inline, + dockerfile, + fixture, + form, + management, + manager, + model, + scaffold, + serializer, + signal, + tag, + template, + test, + validator, + view, + viewset, + ] +] diff --git a/src/cli/commands/generate/management.py b/django_clite/commands/generate/management.py similarity index 78% rename from src/cli/commands/generate/management.py rename to django_clite/commands/generate/management.py index 711e053..7b34b9d 100644 --- a/src/cli/commands/generate/management.py +++ b/django_clite/commands/generate/management.py @@ -1,8 +1,8 @@ import click from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback -from cli.decorators.scope import scoped, Scope +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands.callbacks import sanitized_string_callback @scoped(to=Scope.APP) diff --git a/src/cli/commands/generate/managers.py b/django_clite/commands/generate/managers.py similarity index 79% rename from src/cli/commands/generate/managers.py rename to django_clite/commands/generate/managers.py index e7d9c5f..47f7e93 100644 --- a/src/cli/commands/generate/managers.py +++ b/django_clite/commands/generate/managers.py @@ -1,9 +1,9 @@ import click from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback -from cli.decorators.scope import scoped, Scope -from cli.commands import command_defaults +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands import command_defaults +from django_clite.commands.callbacks import sanitized_string_callback @scoped(to=Scope.APP) diff --git a/src/cli/commands/generate/models.py b/django_clite/commands/generate/models.py similarity index 91% rename from src/cli/commands/generate/models.py rename to django_clite/commands/generate/models.py index 19b8b93..b8b57fd 100644 --- a/src/cli/commands/generate/models.py +++ b/django_clite/commands/generate/models.py @@ -3,10 +3,10 @@ from geny.core.filesystem.files import File from geny.core.templates.template import TemplateParser -from cli.commands.callbacks import sanitized_string_callback, fields_callback -from cli.decorators.scope import scoped, Scope -from cli.core.logger import logger -from cli.commands import command_defaults +from django_clite.core.logger import logger +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands import command_defaults +from django_clite.commands.callbacks import sanitized_string_callback, fields_callback @scoped(to=Scope.APP) @@ -62,8 +62,6 @@ def model( logger.error("Flags --api and --full cannot be used simultaneously.") raise click.Abort() - model_fields, model_imports = fields - file = File( name=f"models/{name}.py", template="models/model.tpl", @@ -71,8 +69,8 @@ def model( "api": api, "abstract": abstract, "classname": inflection.camelize(name), - "fields": model_fields, - "imports": model_imports, + "fields": fields, + "imports": dict((k, v) for k, v in fields.items() if v.is_relationship), "name": name, "table_name": f"{TemplateParser().app}.{inflection.pluralize(name)}", }, diff --git a/src/cli/commands/generate/serializers.py b/django_clite/commands/generate/serializers.py similarity index 84% rename from src/cli/commands/generate/serializers.py rename to django_clite/commands/generate/serializers.py index b942b61..e2d53c1 100644 --- a/src/cli/commands/generate/serializers.py +++ b/django_clite/commands/generate/serializers.py @@ -2,9 +2,9 @@ import inflection from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback -from cli.decorators.scope import scoped, Scope -from cli.commands import command_defaults +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands import command_defaults +from django_clite.commands.callbacks import sanitized_string_callback @scoped(to=Scope.APP) diff --git a/src/cli/commands/generate/signals.py b/django_clite/commands/generate/signals.py similarity index 79% rename from src/cli/commands/generate/signals.py rename to django_clite/commands/generate/signals.py index 30193af..670f9e3 100644 --- a/src/cli/commands/generate/signals.py +++ b/django_clite/commands/generate/signals.py @@ -1,9 +1,9 @@ import click from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback -from cli.decorators.scope import scoped, Scope -from cli.commands import command_defaults +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands import command_defaults +from django_clite.commands.callbacks import sanitized_string_callback @scoped(to=Scope.APP) diff --git a/src/cli/commands/generate/tags.py b/django_clite/commands/generate/tags.py similarity index 79% rename from src/cli/commands/generate/tags.py rename to django_clite/commands/generate/tags.py index 9607afe..52ce4fa 100644 --- a/src/cli/commands/generate/tags.py +++ b/django_clite/commands/generate/tags.py @@ -1,9 +1,9 @@ import click from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback -from cli.decorators.scope import scoped, Scope -from cli.commands import command_defaults +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands import command_defaults +from django_clite.commands.callbacks import sanitized_string_callback @scoped(to=Scope.APP) diff --git a/src/cli/commands/generate/template.py b/django_clite/commands/generate/template.py similarity index 88% rename from src/cli/commands/generate/template.py rename to django_clite/commands/generate/template.py index e0e556e..7474c98 100644 --- a/src/cli/commands/generate/template.py +++ b/django_clite/commands/generate/template.py @@ -2,8 +2,8 @@ import inflection from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback -from cli.decorators.scope import scoped, Scope +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands.callbacks import sanitized_string_callback SUPPORTED_CLASSES = [ diff --git a/src/cli/commands/generate/tests.py b/django_clite/commands/generate/tests.py similarity index 86% rename from src/cli/commands/generate/tests.py rename to django_clite/commands/generate/tests.py index 8f3df72..c1efa22 100644 --- a/src/cli/commands/generate/tests.py +++ b/django_clite/commands/generate/tests.py @@ -2,10 +2,10 @@ import inflection from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string, sanitized_string_callback -from cli.decorators.scope import scoped, Scope -from cli.core.logger import logger -from cli.commands import command_defaults +from django_clite.core.logger import logger +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands import command_defaults +from django_clite.commands.callbacks import sanitized_string, sanitized_string_callback SUPPORTED_SCOPES = [ diff --git a/src/cli/commands/generate/validators.py b/django_clite/commands/generate/validators.py similarity index 80% rename from src/cli/commands/generate/validators.py rename to django_clite/commands/generate/validators.py index 6f0f076..1b33ab8 100644 --- a/src/cli/commands/generate/validators.py +++ b/django_clite/commands/generate/validators.py @@ -1,9 +1,9 @@ import click from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback -from cli.decorators.scope import scoped, Scope -from cli.commands import command_defaults +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands import command_defaults +from django_clite.commands.callbacks import sanitized_string_callback @scoped(to=Scope.APP) diff --git a/src/cli/commands/generate/views.py b/django_clite/commands/generate/views.py similarity index 89% rename from src/cli/commands/generate/views.py rename to django_clite/commands/generate/views.py index 4e1dd84..26c438f 100644 --- a/src/cli/commands/generate/views.py +++ b/django_clite/commands/generate/views.py @@ -2,9 +2,9 @@ import inflection from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback -from cli.decorators.scope import scoped, Scope -from cli.commands import command_defaults +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands import command_defaults +from django_clite.commands.callbacks import sanitized_string_callback from .template import template diff --git a/src/cli/commands/generate/viewsets.py b/django_clite/commands/generate/viewsets.py similarity index 83% rename from src/cli/commands/generate/viewsets.py rename to django_clite/commands/generate/viewsets.py index ded1a3f..765a06c 100644 --- a/src/cli/commands/generate/viewsets.py +++ b/django_clite/commands/generate/viewsets.py @@ -2,9 +2,9 @@ import inflection from geny.core.filesystem.files import File -from cli.commands.callbacks import sanitized_string_callback -from cli.decorators.scope import scoped, Scope -from cli.commands import command_defaults +from django_clite.decorators.scope import scoped, Scope +from django_clite.commands import command_defaults +from django_clite.commands.callbacks import sanitized_string_callback @scoped(to=Scope.APP) @@ -37,7 +37,7 @@ def viewset(ctx, name, read_only, full, skip_import): ) file.create( - import_statementw=command_defaults.viewset(name), + import_statement=command_defaults.viewset(name), add_import_statement=not skip_import, **ctx.obj, ) diff --git a/src/cli/commands/new/__init__.py b/django_clite/commands/new/__init__.py similarity index 100% rename from src/cli/commands/new/__init__.py rename to django_clite/commands/new/__init__.py diff --git a/src/cli/commands/new/app.py b/django_clite/commands/new/app.py similarity index 96% rename from src/cli/commands/new/app.py rename to django_clite/commands/new/app.py index 8cd8918..7554459 100644 --- a/src/cli/commands/new/app.py +++ b/django_clite/commands/new/app.py @@ -1,6 +1,6 @@ import click -from cli.constants import ENABLE_DRY_RUN_KEY +from django_clite.constants import ENABLE_DRY_RUN_KEY from .defaults.app import application_callback diff --git a/src/cli/commands/new/defaults/__init__.py b/django_clite/commands/new/defaults/__init__.py similarity index 100% rename from src/cli/commands/new/defaults/__init__.py rename to django_clite/commands/new/defaults/__init__.py diff --git a/src/cli/commands/new/defaults/app.py b/django_clite/commands/new/defaults/app.py similarity index 100% rename from src/cli/commands/new/defaults/app.py rename to django_clite/commands/new/defaults/app.py diff --git a/src/cli/commands/new/defaults/project.py b/django_clite/commands/new/defaults/project.py similarity index 100% rename from src/cli/commands/new/defaults/project.py rename to django_clite/commands/new/defaults/project.py diff --git a/src/cli/commands/new/main.py b/django_clite/commands/new/main.py similarity index 65% rename from src/cli/commands/new/main.py rename to django_clite/commands/new/main.py index 174a608..90cdaa7 100644 --- a/src/cli/commands/new/main.py +++ b/django_clite/commands/new/main.py @@ -1,7 +1,7 @@ import click -from cli.commands.new.app import apps -from cli.commands.new.project import project +from django_clite.commands.new.app import apps +from django_clite.commands.new.project import project @click.group() diff --git a/src/cli/commands/new/project.py b/django_clite/commands/new/project.py similarity index 95% rename from src/cli/commands/new/project.py rename to django_clite/commands/new/project.py index b5a08ac..ef73b85 100644 --- a/src/cli/commands/new/project.py +++ b/django_clite/commands/new/project.py @@ -4,8 +4,8 @@ from django.core.management.commands import startproject from geny.core.filesystem.filesystem import working_directory -from cli.commands.callbacks import sanitized_string_callback -from cli.constants import ENABLE_DRY_RUN_KEY +from django_clite.commands.callbacks import sanitized_string_callback +from django_clite.constants import ENABLE_DRY_RUN_KEY from .defaults.project import new_project, project_transformations from .defaults.app import application_callback diff --git a/src/cli/constants.py b/django_clite/constants.py similarity index 63% rename from src/cli/constants.py rename to django_clite/constants.py index 97d7a58..731ffea 100644 --- a/src/cli/constants.py +++ b/django_clite/constants.py @@ -6,10 +6,14 @@ DJANGO_FILES_KEY = "django:files" ENABLE_DEBUG_KEY = "enable:debug" ENABLE_DRY_RUN_KEY = "enable:dry" -ENABLE_FORCE_KEY = "enable:force" +ENABLE_FORCE_KEY = "GENY_ENABLE_FORCE" ENABLE_VERBOSITY_KEY = "enable:verbosity" TEMPLATES_KEY = "templates" +TEMPLATES_DIRECTORY_ENV_VAR = "DJANGO_CLITE_TEMPLATES_DIR" +PLUGINS_ENV_VAR = "DJANGO_CLITE_PLUGINS" + +DEV_MODE_ENV_VAR = "DJANGO_CLITE_DEV_MODE" # Project information diff --git a/src/cli/core/__init__.py b/django_clite/core/__init__.py similarity index 100% rename from src/cli/core/__init__.py rename to django_clite/core/__init__.py diff --git a/src/cli/core/field_parser/__init__.py b/django_clite/core/field_parser/__init__.py similarity index 100% rename from src/cli/core/field_parser/__init__.py rename to django_clite/core/field_parser/__init__.py diff --git a/django_clite/core/field_parser/factory.py b/django_clite/core/field_parser/factory.py new file mode 100644 index 0000000..52b3c15 --- /dev/null +++ b/django_clite/core/field_parser/factory.py @@ -0,0 +1,192 @@ +# cli:core:field_parser +import inflection +# from typing import NamedTuple +from dataclasses import dataclass +from geny.core.decorators.singleton import singleton + + +json_compatible_fields = { + "BigIntegerField": "pyint", + "BooleanField": "pybool", + "CharField": "text", + "DateField": "future_date", + "DateTimeField": "iso8601", + "DecimalField": "pydecimal", + "EmailField": "safe_email", + "GenericIPAddressField": "ipv4", + "FileField": "file_path", + "FilePathField": "file_path", + "FloatField": "pyfloat", + "ImageField": "image_url", + "IntegerField": "pyint", + "SlugField": "slug", + "TextField": "text", + "TimeField": "time", + "URLField": "url", + "UUIDField": "uuid4", +} + + +@dataclass +class FieldOptions: + kind: str + options: dict = None + supports_admin: bool = True + is_fk_relationship: bool = False + is_many_relationship: bool = False + + @property + def is_relationship(self) -> bool: + return self.is_fk_relationship or self.is_many_relationship + + @property + def is_media_field(self) -> bool: + return self.kind in ["FileField", "FilePathField", "ImageField"] + + @classmethod + def klass_name(cls, attr_name): + return inflection.camelize(inflection.singularize(attr_name)).strip() + + @classmethod + def module_name(cls, attr_name): + return inflection.singularize(attr_name).strip() + + @classmethod + def upload_path(cls, attr_name, model_name) -> str: + if not cls.is_media_field: + return "" + + return f"uploads/{inflection.pluralize(model_name.lower())}/{inflection.pluralize(attr_name)}/" + + @property + def example_value(self): + x = json_compatible_fields.get(self.kind, "") + return x + + def field_options(self, attr_name: str, model_name: str): + if self.options is None: + self.options = {} + + if self.is_relationship: + self.options.update( + {"related_name": f"'{inflection.singularize(model_name.lower())}'"} + ) + + options = [self.klass_name(attr_name)] if self.is_relationship else [] + options.append(f"_('{attr_name}')") + + if self.options is None: + return "" + + for k, v in self.options.items(): + options.append(f"{k}={v}") + + if self.is_media_field: + options.append(f"upload_to='{self.upload_path(attr_name, model_name)}'") + + return ", ".join(options) + + +@singleton +class AttributeFactory: + def __init__(self, associations: dict[str, FieldOptions], aliases: dict[str, str]): + self.registry = associations + self.aliases = aliases + + def field_options(self, kind: str, name: str) -> FieldOptions: + if kind == "": + return None + + options = self.registry.get(kind, None) + + if options is None: + alias = self.aliases.get(kind, None) + options = self.registry.get(alias, None) + + if options is None: + return None + + return options + + def parsed_fields(self, values: list[str]) -> dict[str, FieldOptions]: + pairs = dict(arg.split(':') for arg in values) + + fields = {} + for n, k in pairs.items(): + f = self.field_options(k, n) + fields[n] = f + + return fields + + +attribute_aliases = { + "bigint": "big", + "big-int": "big", + "integer": "int", + "boolean": "bool", + "string": "text", + "file-path": "filepath", + "photo": "image", + "ipaddress": "ip", + "ip-address": "ip", + "belongsto": "fk", + "belongs-to": "fk", + "foreignkey": "fk", + "foreign-key": "fk", + "one-to-one": "one", + "hasone": "one", + "has-one": "one", + "many": "hasmany", + "has-many": "hasmany", + "manytomany": "hasmany", + "many-to-many": "hasmany", +} + +field_registry = { + # Relationships + "fk": FieldOptions(kind="ForeignKey", options={"blank": False, "on_delete": "models.DO_NOTHING"}, is_fk_relationship=True), + "one": FieldOptions( + kind="OneToOneField", + options={"blank": False, "on_delete": "models.CASCADE", "primary_key": True}, is_fk_relationship=True + ), + "hasmany": FieldOptions( + kind="ManyToManyField", + options={"blank": True, "on_delete": "models.DO_NOTHING"}, + supports_admin=False, + is_many_relationship=True + ), + + # Boolean + "bool": FieldOptions(kind="BooleanField", options={"default": False}), + + # Numeric + "big": FieldOptions(kind="BigIntegerField"), + "decimal": FieldOptions(kind="DecimalField"), + "float": FieldOptions(kind="FloatField"), + "int": FieldOptions(kind="IntegerField"), + + # Text + "char": FieldOptions(kind="CharField", options={"max_length": 100, "blank": False}), + "email": FieldOptions(kind="EmailField"), + "slug": FieldOptions(kind="SlugField", options={"unique": True}), + "text": FieldOptions(kind="TextField", options={"blank": False}, supports_admin=False), + "url": FieldOptions(kind="URLField"), + "uuid": FieldOptions(kind="UUIDField", options={"default": "uuid.uuid4", "editable": False}), + + # Files + "file": FieldOptions(kind="FileField", options={"blank": False}, supports_admin=False), + "filepath": FieldOptions(kind="FilePathField", options={"blank": True}, supports_admin=False), + "image": FieldOptions(kind="ImageField", options={"blank": False}, supports_admin=False), + + # Date + "date": FieldOptions(kind="DateField", options={"auto_now": True}), + "datetime": FieldOptions(kind="DateTimeField", options={"auto_now": True}), + "duration": FieldOptions(kind="DurationField"), + "time": FieldOptions(kind="TimeField", options={"auto_now": True}), + + # Ip + "ip": FieldOptions(kind="GenericIPAddressField"), +} + +# Singleton +AttributeFactory(field_registry, attribute_aliases) diff --git a/src/cli/core/git/__init__.py b/django_clite/core/git/__init__.py similarity index 100% rename from src/cli/core/git/__init__.py rename to django_clite/core/git/__init__.py diff --git a/src/cli/core/git/git.py b/django_clite/core/git/git.py similarity index 100% rename from src/cli/core/git/git.py rename to django_clite/core/git/git.py diff --git a/src/cli/core/git/protocols.py b/django_clite/core/git/protocols.py similarity index 100% rename from src/cli/core/git/protocols.py rename to django_clite/core/git/protocols.py diff --git a/src/cli/core/logger.py b/django_clite/core/logger.py similarity index 54% rename from src/cli/core/logger.py rename to django_clite/core/logger.py index f5eb9d8..ef5106e 100644 --- a/src/cli/core/logger.py +++ b/django_clite/core/logger.py @@ -1,5 +1,5 @@ import logging -from cli.constants import CLI_NAME_KEY +from django_clite.constants import CLI_NAME_KEY logger = logging.getLogger(CLI_NAME_KEY) diff --git a/src/cli/decorators/__init__.py b/django_clite/decorators/__init__.py similarity index 100% rename from src/cli/decorators/__init__.py rename to django_clite/decorators/__init__.py diff --git a/src/cli/decorators/scope.py b/django_clite/decorators/scope.py similarity index 93% rename from src/cli/decorators/scope.py rename to django_clite/decorators/scope.py index 5a72f6e..1acd8b3 100644 --- a/src/cli/decorators/scope.py +++ b/django_clite/decorators/scope.py @@ -5,6 +5,7 @@ from enum import Enum from geny.core.filesystem.finder import core_project_files +from django_clite.constants import ENABLE_FORCE_KEY class Scope(Enum): @@ -17,8 +18,6 @@ def decorator(cmd: click.Command): def allowed_to_continue() -> bool: django_files = core_project_files(os.getcwd()) - # TODO: Handle forceful creation - # 1. Probably not inside a django project directory if len(django_files) == 0: return False @@ -40,7 +39,7 @@ def allowed_to_continue() -> bool: class ScopedCommand(click.Command): def invoke(self, ctx): - if allowed_to_continue(): + if ctx.obj.get(ENABLE_FORCE_KEY, False) or allowed_to_continue(): super().invoke(ctx) return diff --git a/src/cli/extensions/__init__.py b/django_clite/extensions/__init__.py similarity index 100% rename from src/cli/extensions/__init__.py rename to django_clite/extensions/__init__.py diff --git a/src/cli/extensions/aliased.py b/django_clite/extensions/aliased.py similarity index 100% rename from src/cli/extensions/aliased.py rename to django_clite/extensions/aliased.py diff --git a/src/cli/extensions/combined.py b/django_clite/extensions/combined.py similarity index 89% rename from src/cli/extensions/combined.py rename to django_clite/extensions/combined.py index f4b91a2..934fa25 100644 --- a/src/cli/extensions/combined.py +++ b/django_clite/extensions/combined.py @@ -1,4 +1,4 @@ -from cli.extensions.discoverable import DiscoverableGroup +from django_clite.extensions.discoverable import DiscoverableGroup class AliasedAndDiscoverableGroup(DiscoverableGroup): diff --git a/src/cli/extensions/discoverable.py b/django_clite/extensions/discoverable.py similarity index 89% rename from src/cli/extensions/discoverable.py rename to django_clite/extensions/discoverable.py index 322650e..f1b5b48 100644 --- a/src/cli/extensions/discoverable.py +++ b/django_clite/extensions/discoverable.py @@ -1,6 +1,7 @@ import os from click import MultiCommand -from cli import COMMANDS_FOLDER, PLUGINS_FOLDER +from django_clite import COMMANDS_FOLDER, PLUGINS_FOLDER +from django_clite.constants import DEV_MODE_ENV_VAR def execute_command(command, ns): @@ -63,6 +64,8 @@ def list_commands(self, ctx): to have been added as sub-commands of `read` already. """ + debug_mode = (os.getenv(DEV_MODE_ENV_VAR, 'false').lower() == 'true') + for func, path in commands.items(): # Skip packages beginning or ending in underscores (_) command = path.split("/")[-1] @@ -71,7 +74,14 @@ def list_commands(self, ctx): ): rv.append(func) + if debug_mode: + print(f"{func} from {path}") + rv.sort() + + # if debug_mode: + # print(rv) + return rv def get_command(self, ctx, name): diff --git a/src/cli/template_files/__init__.py b/django_clite/template_files/__init__.py similarity index 100% rename from src/cli/template_files/__init__.py rename to django_clite/template_files/__init__.py diff --git a/src/cli/template_files/admin/admin.tpl b/django_clite/template_files/admin/admin.tpl similarity index 91% rename from src/cli/template_files/admin/admin.tpl rename to django_clite/template_files/admin/admin.tpl index 19986bd..3cc9148 100644 --- a/src/cli/template_files/admin/admin.tpl +++ b/django_clite/template_files/admin/admin.tpl @@ -5,8 +5,8 @@ from {{ project }}.{{ app }}.models import {{ classname }} @admin.register({{ classname }}) class {{ classname }}Admin(admin.ModelAdmin): {%- if fields %} - list_display = [{% for f in fields %} - '{{ f.name }}', + list_display = [{% for attr_name in fields %} + '{{ attr_name }}', {%- endfor %} ] {% else %} diff --git a/src/cli/template_files/admin/inline.tpl b/django_clite/template_files/admin/inline.tpl similarity index 100% rename from src/cli/template_files/admin/inline.tpl rename to django_clite/template_files/admin/inline.tpl diff --git a/src/cli/template_files/app/404.tpl b/django_clite/template_files/app/404.tpl similarity index 100% rename from src/cli/template_files/app/404.tpl rename to django_clite/template_files/app/404.tpl diff --git a/src/cli/template_files/app/500.tpl b/django_clite/template_files/app/500.tpl similarity index 100% rename from src/cli/template_files/app/500.tpl rename to django_clite/template_files/app/500.tpl diff --git a/src/cli/template_files/app/LICENSE.tpl b/django_clite/template_files/app/LICENSE.tpl similarity index 100% rename from src/cli/template_files/app/LICENSE.tpl rename to django_clite/template_files/app/LICENSE.tpl diff --git a/src/cli/template_files/app/MANIFEST.tpl b/django_clite/template_files/app/MANIFEST.tpl similarity index 100% rename from src/cli/template_files/app/MANIFEST.tpl rename to django_clite/template_files/app/MANIFEST.tpl diff --git a/src/cli/template_files/app/README.tpl b/django_clite/template_files/app/README.tpl similarity index 100% rename from src/cli/template_files/app/README.tpl rename to django_clite/template_files/app/README.tpl diff --git a/src/cli/template_files/app/__init__.py b/django_clite/template_files/app/__init__.py similarity index 100% rename from src/cli/template_files/app/__init__.py rename to django_clite/template_files/app/__init__.py diff --git a/src/cli/template_files/app/admin-init.tpl b/django_clite/template_files/app/admin-init.tpl similarity index 100% rename from src/cli/template_files/app/admin-init.tpl rename to django_clite/template_files/app/admin-init.tpl diff --git a/src/cli/template_files/app/api.tpl b/django_clite/template_files/app/api.tpl similarity index 100% rename from src/cli/template_files/app/api.tpl rename to django_clite/template_files/app/api.tpl diff --git a/src/cli/template_files/app/apps.tpl b/django_clite/template_files/app/apps.tpl similarity index 100% rename from src/cli/template_files/app/apps.tpl rename to django_clite/template_files/app/apps.tpl diff --git a/src/cli/template_files/app/gitignore.tpl b/django_clite/template_files/app/gitignore.tpl similarity index 100% rename from src/cli/template_files/app/gitignore.tpl rename to django_clite/template_files/app/gitignore.tpl diff --git a/src/cli/template_files/app/init.tpl b/django_clite/template_files/app/init.tpl similarity index 100% rename from src/cli/template_files/app/init.tpl rename to django_clite/template_files/app/init.tpl diff --git a/src/cli/template_files/app/pyproject.tpl b/django_clite/template_files/app/pyproject.tpl similarity index 100% rename from src/cli/template_files/app/pyproject.tpl rename to django_clite/template_files/app/pyproject.tpl diff --git a/src/cli/template_files/app/router.tpl b/django_clite/template_files/app/router.tpl similarity index 100% rename from src/cli/template_files/app/router.tpl rename to django_clite/template_files/app/router.tpl diff --git a/src/cli/template_files/app/router_init.tpl b/django_clite/template_files/app/router_init.tpl similarity index 100% rename from src/cli/template_files/app/router_init.tpl rename to django_clite/template_files/app/router_init.tpl diff --git a/src/cli/template_files/app/routes.tpl b/django_clite/template_files/app/routes.tpl similarity index 100% rename from src/cli/template_files/app/routes.tpl rename to django_clite/template_files/app/routes.tpl diff --git a/src/cli/template_files/app/urls.tpl b/django_clite/template_files/app/urls.tpl similarity index 100% rename from src/cli/template_files/app/urls.tpl rename to django_clite/template_files/app/urls.tpl diff --git a/src/cli/template_files/docker/docker-compose.tpl b/django_clite/template_files/docker/docker-compose.tpl similarity index 100% rename from src/cli/template_files/docker/docker-compose.tpl rename to django_clite/template_files/docker/docker-compose.tpl diff --git a/src/cli/template_files/docker/docker-entrypoint.tpl b/django_clite/template_files/docker/docker-entrypoint.tpl similarity index 100% rename from src/cli/template_files/docker/docker-entrypoint.tpl rename to django_clite/template_files/docker/docker-entrypoint.tpl diff --git a/src/cli/template_files/docker/dockerfile.tpl b/django_clite/template_files/docker/dockerfile.tpl similarity index 100% rename from src/cli/template_files/docker/dockerfile.tpl rename to django_clite/template_files/docker/dockerfile.tpl diff --git a/src/cli/template_files/docker/dockerignore.tpl b/django_clite/template_files/docker/dockerignore.tpl similarity index 100% rename from src/cli/template_files/docker/dockerignore.tpl rename to django_clite/template_files/docker/dockerignore.tpl diff --git a/django_clite/template_files/fixture.tpl b/django_clite/template_files/fixture.tpl new file mode 100644 index 0000000..73aaca9 --- /dev/null +++ b/django_clite/template_files/fixture.tpl @@ -0,0 +1,13 @@ +[ + {% for i in range(total) -%} + { + "pk": {{ loop.index }}, + "model": "{{ app }}.{{ classname }}", + "fields": { + {% for attr_name, f in fields.items() -%} + "{{ attr_name }}": "{{ f.example_value }}"{% if loop.index < loop.length %}, {%- endif -%} + {% endfor %} + } + }{% if loop.index < loop.length %},{% endif -%} + {% endfor %} +] diff --git a/src/cli/template_files/form.tpl b/django_clite/template_files/form.tpl similarity index 100% rename from src/cli/template_files/form.tpl rename to django_clite/template_files/form.tpl diff --git a/src/cli/template_files/github/ci.tpl b/django_clite/template_files/github/ci.tpl similarity index 100% rename from src/cli/template_files/github/ci.tpl rename to django_clite/template_files/github/ci.tpl diff --git a/src/cli/template_files/github/pull_request_template.tpl b/django_clite/template_files/github/pull_request_template.tpl similarity index 100% rename from src/cli/template_files/github/pull_request_template.tpl rename to django_clite/template_files/github/pull_request_template.tpl diff --git a/src/cli/template_files/kubernetes/configmap.tpl b/django_clite/template_files/kubernetes/configmap.tpl similarity index 100% rename from src/cli/template_files/kubernetes/configmap.tpl rename to django_clite/template_files/kubernetes/configmap.tpl diff --git a/src/cli/template_files/kubernetes/deployment.tpl b/django_clite/template_files/kubernetes/deployment.tpl similarity index 100% rename from src/cli/template_files/kubernetes/deployment.tpl rename to django_clite/template_files/kubernetes/deployment.tpl diff --git a/src/cli/template_files/kubernetes/ingress.tpl b/django_clite/template_files/kubernetes/ingress.tpl similarity index 100% rename from src/cli/template_files/kubernetes/ingress.tpl rename to django_clite/template_files/kubernetes/ingress.tpl diff --git a/src/cli/template_files/kubernetes/service.tpl b/django_clite/template_files/kubernetes/service.tpl similarity index 100% rename from src/cli/template_files/kubernetes/service.tpl rename to django_clite/template_files/kubernetes/service.tpl diff --git a/src/cli/template_files/management.tpl b/django_clite/template_files/management.tpl similarity index 100% rename from src/cli/template_files/management.tpl rename to django_clite/template_files/management.tpl diff --git a/src/cli/template_files/models/manager.tpl b/django_clite/template_files/models/manager.tpl similarity index 100% rename from src/cli/template_files/models/manager.tpl rename to django_clite/template_files/models/manager.tpl diff --git a/src/cli/template_files/models/model.tpl b/django_clite/template_files/models/model.tpl similarity index 78% rename from src/cli/template_files/models/model.tpl rename to django_clite/template_files/models/model.tpl index 2104cb5..b19bd3c 100644 --- a/src/cli/template_files/models/model.tpl +++ b/django_clite/template_files/models/model.tpl @@ -3,13 +3,13 @@ from django.db import models from django.urls import reverse from django.utils.translation import gettext_lazy as _ from django.template.defaultfilters import slugify -{% for field in imports -%} -from {{ project }}.{{ app }}.models.{{ field.module_name }} import {{ field.klass_name }} +{% for attr_name, field in imports.items() -%} +from {{ project }}.{{ app }}.models.{{ field.module_name(attr_name) }} import {{ field.klass_name(attr_name) }} {% endfor %} class {{ classname }}(models.Model): - {% for f in fields -%} - {{f.name}} = models.{{f.kind}}({{ f.options }}) + {% for attr_name, f in fields.items() -%} + {{attr_name}} = models.{{f.kind}}({{ f.field_options(attr_name, classname) }}) {% endfor %} # Default fields. Used for record-keeping. uuid = models.UUIDField(default=uuid.uuid4, editable=False) diff --git a/src/cli/template_files/models/signal.tpl b/django_clite/template_files/models/signal.tpl similarity index 100% rename from src/cli/template_files/models/signal.tpl rename to django_clite/template_files/models/signal.tpl diff --git a/src/cli/template_files/models/test.tpl b/django_clite/template_files/models/test.tpl similarity index 100% rename from src/cli/template_files/models/test.tpl rename to django_clite/template_files/models/test.tpl diff --git a/src/cli/template_files/models/validator.tpl b/django_clite/template_files/models/validator.tpl similarity index 100% rename from src/cli/template_files/models/validator.tpl rename to django_clite/template_files/models/validator.tpl diff --git a/src/cli/template_files/project/README.tpl b/django_clite/template_files/project/README.tpl similarity index 100% rename from src/cli/template_files/project/README.tpl rename to django_clite/template_files/project/README.tpl diff --git a/src/cli/template_files/project/__init__.py b/django_clite/template_files/project/__init__.py similarity index 100% rename from src/cli/template_files/project/__init__.py rename to django_clite/template_files/project/__init__.py diff --git a/src/cli/template_files/project/api.tpl b/django_clite/template_files/project/api.tpl similarity index 100% rename from src/cli/template_files/project/api.tpl rename to django_clite/template_files/project/api.tpl diff --git a/src/cli/template_files/project/app_json.tpl b/django_clite/template_files/project/app_json.tpl similarity index 100% rename from src/cli/template_files/project/app_json.tpl rename to django_clite/template_files/project/app_json.tpl diff --git a/src/cli/template_files/project/celery.tpl b/django_clite/template_files/project/celery.tpl similarity index 100% rename from src/cli/template_files/project/celery.tpl rename to django_clite/template_files/project/celery.tpl diff --git a/src/cli/template_files/project/celery_init.tpl b/django_clite/template_files/project/celery_init.tpl similarity index 100% rename from src/cli/template_files/project/celery_init.tpl rename to django_clite/template_files/project/celery_init.tpl diff --git a/src/cli/template_files/project/celery_tasks.tpl b/django_clite/template_files/project/celery_tasks.tpl similarity index 100% rename from src/cli/template_files/project/celery_tasks.tpl rename to django_clite/template_files/project/celery_tasks.tpl diff --git a/src/cli/template_files/project/cli-config_json.tpl b/django_clite/template_files/project/cli-config_json.tpl similarity index 100% rename from src/cli/template_files/project/cli-config_json.tpl rename to django_clite/template_files/project/cli-config_json.tpl diff --git a/src/cli/template_files/project/cli-config_yaml.tpl b/django_clite/template_files/project/cli-config_yaml.tpl similarity index 100% rename from src/cli/template_files/project/cli-config_yaml.tpl rename to django_clite/template_files/project/cli-config_yaml.tpl diff --git a/src/cli/template_files/project/dokku_checks.tpl b/django_clite/template_files/project/dokku_checks.tpl similarity index 100% rename from src/cli/template_files/project/dokku_checks.tpl rename to django_clite/template_files/project/dokku_checks.tpl diff --git a/src/cli/template_files/project/dokku_scale.tpl b/django_clite/template_files/project/dokku_scale.tpl similarity index 100% rename from src/cli/template_files/project/dokku_scale.tpl rename to django_clite/template_files/project/dokku_scale.tpl diff --git a/src/cli/template_files/project/env.tpl b/django_clite/template_files/project/env.tpl similarity index 100% rename from src/cli/template_files/project/env.tpl rename to django_clite/template_files/project/env.tpl diff --git a/src/cli/template_files/project/github_ci.tpl b/django_clite/template_files/project/github_ci.tpl similarity index 100% rename from src/cli/template_files/project/github_ci.tpl rename to django_clite/template_files/project/github_ci.tpl diff --git a/src/cli/template_files/project/gitignore.tpl b/django_clite/template_files/project/gitignore.tpl similarity index 100% rename from src/cli/template_files/project/gitignore.tpl rename to django_clite/template_files/project/gitignore.tpl diff --git a/src/cli/template_files/project/pipfile.tpl b/django_clite/template_files/project/pipfile.tpl similarity index 100% rename from src/cli/template_files/project/pipfile.tpl rename to django_clite/template_files/project/pipfile.tpl diff --git a/src/cli/template_files/project/procfile.tpl b/django_clite/template_files/project/procfile.tpl similarity index 100% rename from src/cli/template_files/project/procfile.tpl rename to django_clite/template_files/project/procfile.tpl diff --git a/src/cli/template_files/project/requirements.tpl b/django_clite/template_files/project/requirements.tpl similarity index 100% rename from src/cli/template_files/project/requirements.tpl rename to django_clite/template_files/project/requirements.tpl diff --git a/src/cli/template_files/project/settings.tpl b/django_clite/template_files/project/settings.tpl similarity index 100% rename from src/cli/template_files/project/settings.tpl rename to django_clite/template_files/project/settings.tpl diff --git a/src/cli/template_files/project/storage.tpl b/django_clite/template_files/project/storage.tpl similarity index 100% rename from src/cli/template_files/project/storage.tpl rename to django_clite/template_files/project/storage.tpl diff --git a/src/cli/template_files/project/travis-yml.tpl b/django_clite/template_files/project/travis-yml.tpl similarity index 100% rename from src/cli/template_files/project/travis-yml.tpl rename to django_clite/template_files/project/travis-yml.tpl diff --git a/src/cli/template_files/project/urls.tpl b/django_clite/template_files/project/urls.tpl similarity index 100% rename from src/cli/template_files/project/urls.tpl rename to django_clite/template_files/project/urls.tpl diff --git a/src/cli/template_files/serializer.tpl b/django_clite/template_files/serializer.tpl similarity index 100% rename from src/cli/template_files/serializer.tpl rename to django_clite/template_files/serializer.tpl diff --git a/src/cli/template_files/shared/init.tpl b/django_clite/template_files/shared/init.tpl similarity index 100% rename from src/cli/template_files/shared/init.tpl rename to django_clite/template_files/shared/init.tpl diff --git a/src/cli/template_files/templates/create.tpl b/django_clite/template_files/templates/create.tpl similarity index 100% rename from src/cli/template_files/templates/create.tpl rename to django_clite/template_files/templates/create.tpl diff --git a/src/cli/template_files/templates/detail.tpl b/django_clite/template_files/templates/detail.tpl similarity index 100% rename from src/cli/template_files/templates/detail.tpl rename to django_clite/template_files/templates/detail.tpl diff --git a/src/cli/template_files/templates/list.tpl b/django_clite/template_files/templates/list.tpl similarity index 100% rename from src/cli/template_files/templates/list.tpl rename to django_clite/template_files/templates/list.tpl diff --git a/src/cli/template_files/templates/template.tpl b/django_clite/template_files/templates/template.tpl similarity index 100% rename from src/cli/template_files/templates/template.tpl rename to django_clite/template_files/templates/template.tpl diff --git a/src/cli/template_files/templates/update.tpl b/django_clite/template_files/templates/update.tpl similarity index 100% rename from src/cli/template_files/templates/update.tpl rename to django_clite/template_files/templates/update.tpl diff --git a/src/cli/template_files/templatetags/tag.tpl b/django_clite/template_files/templatetags/tag.tpl similarity index 100% rename from src/cli/template_files/templatetags/tag.tpl rename to django_clite/template_files/templatetags/tag.tpl diff --git a/src/cli/template_files/views/create.tpl b/django_clite/template_files/views/create.tpl similarity index 100% rename from src/cli/template_files/views/create.tpl rename to django_clite/template_files/views/create.tpl diff --git a/src/cli/template_files/views/detail.tpl b/django_clite/template_files/views/detail.tpl similarity index 100% rename from src/cli/template_files/views/detail.tpl rename to django_clite/template_files/views/detail.tpl diff --git a/src/cli/template_files/views/list.tpl b/django_clite/template_files/views/list.tpl similarity index 100% rename from src/cli/template_files/views/list.tpl rename to django_clite/template_files/views/list.tpl diff --git a/src/cli/template_files/views/update.tpl b/django_clite/template_files/views/update.tpl similarity index 100% rename from src/cli/template_files/views/update.tpl rename to django_clite/template_files/views/update.tpl diff --git a/src/cli/template_files/views/view.tpl b/django_clite/template_files/views/view.tpl similarity index 100% rename from src/cli/template_files/views/view.tpl rename to django_clite/template_files/views/view.tpl diff --git a/src/cli/template_files/viewsets/test.tpl b/django_clite/template_files/viewsets/test.tpl similarity index 100% rename from src/cli/template_files/viewsets/test.tpl rename to django_clite/template_files/viewsets/test.tpl diff --git a/src/cli/template_files/viewsets/viewset.tpl b/django_clite/template_files/viewsets/viewset.tpl similarity index 100% rename from src/cli/template_files/viewsets/viewset.tpl rename to django_clite/template_files/viewsets/viewset.tpl diff --git a/package.json b/package.json deleted file mode 100644 index 182ab06..0000000 --- a/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "django-clite", - "version": "1.0.0", - "main": "index.js", - "author": "Leo Neto ", - "license": "BSD-3-Clause", - "devDependencies": { - "husky": "^7.0.0", - "standard-version": "^9.3.2" - }, - "scripts": { - "prepare": "husky install", - "release": "standard-version", - "release:minor": "standard-version --release-as minor", - "release:patch": "standard-version --release-as patch", - "release:major": "standard-version --release-as major" - }, - "dependencies": { - "@commitlint/cli": "^17.8.1", - "@commitlint/config-conventional": "^15.0.0" - } -} diff --git a/pyproject.toml b/pyproject.toml index cbbffb4..a8ec4e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ maintainers = [{ name = "Leo Neto", email = "leo@ekletik.com" }] requires-python = ">=3.8" license = { file = "LICENSE" } readme = "README.md" -version = "0.19.0" +version = "0.19.1" keywords = [ "django", "automate", @@ -44,7 +44,7 @@ dependencies = [ "inquirer>=3.1.3", "inflection>=0.5.1", "rich>=13.3", - "geny==0.1.0", + "geny==0.1.2", ] [project.urls] @@ -52,4 +52,7 @@ Homepage = "https://github.com/oleoneto/django-clite" Issues = "https://github.com/oleoneto/django-clite/issues" [project.scripts] -django-clite = "cli.app:cli" +django-clite = "django_clite.cli.cli:cli" + +[tool.setuptools] +packages = ["django_clite"] diff --git a/src/__init__.py b/src/__init__.py deleted file mode 100644 index a0b92f8..0000000 --- a/src/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# django-clite diff --git a/src/cli/commands/callbacks.py b/src/cli/commands/callbacks.py deleted file mode 100644 index 88df74f..0000000 --- a/src/cli/commands/callbacks.py +++ /dev/null @@ -1,29 +0,0 @@ -from cli.utils import sanitized_string -from cli.core.field_parser.factory import make_field - - -def sanitized_string_callback(ctx, param, value): - return sanitized_string(value) - - -def fields_callback(ctx, param, value): - model_fields = [] - model_relationships = [] - - model = ctx.params.get("name") - - for pair in value: - # Ignore malformed field pairs - if pair.find(":") == -1: - continue - - kind, name = pair.split(":") - - f = make_field(kind=kind, name=name, model=model) - if f.kind is not None: - model_fields.append(f) - - if f.is_relationship: - model_relationships.append(f) - - return model_fields, model_relationships diff --git a/src/cli/commands/destroy/main.py b/src/cli/commands/destroy/main.py deleted file mode 100644 index 08bd276..0000000 --- a/src/cli/commands/destroy/main.py +++ /dev/null @@ -1,50 +0,0 @@ -# cli:commands:destroy -import click - -from cli.commands.destroy.admin import admin, admin_inline as admin_inline -from cli.commands.destroy.fixtures import fixture -from cli.commands.destroy.forms import form -from cli.commands.destroy.management import management -from cli.commands.destroy.managers import manager -from cli.commands.destroy.models import model, scaffold -from cli.commands.destroy.serializers import serializer -from cli.commands.destroy.signals import signal -from cli.commands.destroy.tags import tag -from cli.commands.destroy.template import template -from cli.commands.destroy.tests import test -from cli.commands.destroy.validators import validator -from cli.commands.destroy.views import view -from cli.commands.destroy.viewsets import viewset - - -@click.group() -@click.pass_context -def destroy(ctx): - """ - Destroy application resources. - """ - - ctx.ensure_object(dict) - - -[ - destroy.add_command(cmd) - for cmd in [ - admin, - admin_inline, - fixture, - form, - management, - manager, - model, - scaffold, - serializer, - signal, - tag, - template, - test, - validator, - view, - viewset, - ] -] diff --git a/src/cli/commands/generate/main.py b/src/cli/commands/generate/main.py deleted file mode 100644 index e15e036..0000000 --- a/src/cli/commands/generate/main.py +++ /dev/null @@ -1,54 +0,0 @@ -# cli:commands:generate -import click - -from cli.commands.generate.admin import admin, admin_inline as admin_inline -from cli.commands.generate.dockerfile import dockerfile -from cli.commands.generate.fixtures import fixture -from cli.commands.generate.forms import form -from cli.commands.generate.management import management -from cli.commands.generate.managers import manager -from cli.commands.generate.models import model, scaffold -from cli.commands.generate.serializers import serializer -from cli.commands.generate.signals import signal -from cli.commands.generate.tags import tag -from cli.commands.generate.template import template -from cli.commands.generate.tests import test -from cli.commands.generate.validators import validator -from cli.commands.generate.views import view -from cli.commands.generate.viewsets import viewset - - -@click.group() -@click.option("--project", type=click.Path(), help="Project name.") -@click.option("--app", type=click.Path(), help="Application name.") -@click.pass_context -def generate(ctx, project, app): - """ - Create application resources. - """ - - ctx.ensure_object(dict) - - -[ - generate.add_command(cmd) - for cmd in [ - admin, - admin_inline, - dockerfile, - fixture, - form, - management, - manager, - model, - scaffold, - serializer, - signal, - tag, - template, - test, - validator, - view, - viewset, - ] -] diff --git a/src/cli/core/field_parser/factory.py b/src/cli/core/field_parser/factory.py deleted file mode 100644 index 0f0546d..0000000 --- a/src/cli/core/field_parser/factory.py +++ /dev/null @@ -1,189 +0,0 @@ -# cli:core:field_parser -import inflection - -json_compatible_fields = { - "BigIntegerField": "pyint", - "BooleanField": "pybool", - "CharField": "text", - "DateField": "future_date", - "DateTimeField": "iso8601", - "DecimalField": "pydecimal", - "EmailField": "safe_email", - "GenericIPAddressField": "ipv4", - "FileField": "file_path", - "FilePathField": "file_path", - "FloatField": "pyfloat", - "ImageField": "image_url", - "IntegerField": "pyint", - "SlugField": "slug", - "TextField": "text", - "TimeField": "time", - "URLField": "url", - "UUIDField": "uuid4", -} - - -class Field: - def __init__( - self, - kind: str, - name: str = "", - model: str = "", - options: dict = None, - supports_admin: bool = True, - is_fk_relationship: bool = False, - is_many_to_many_relationship: bool = False, - ): - self.model = model - self.kind = kind - self.name = name - self.supported_in_admin = supports_admin - self.is_fk_relationship = is_fk_relationship - self.is_many_relationship = is_many_to_many_relationship - self._options = {} if options is None else options - - @property - def is_relationship(self) -> bool: - return self.is_fk_relationship or self.is_many_relationship - - @property - def is_media_field(self) -> bool: - return self.kind in ["FileField", "FilePathField", "ImageField"] - - @property - def klass_name(self): - return inflection.camelize(inflection.singularize(self.name)).strip() - - @property - def module_name(self): - return inflection.singularize(self.name).strip() - - @property - def upload_path(self) -> str: - if not self.is_media_field: - return "" - - return f"uploads/{inflection.pluralize(self.model.lower())}/{inflection.pluralize(self.name)}/" - - @property - def example_value(self): - if json_compatible_fields.get(self.kind): - return "" - return None - - @property - def options(self): - if self.is_relationship: - self._options.update( - {"related_name": f"'{inflection.singularize(self.model.lower())}'"} - ) - - options = [self.klass_name] if self.is_relationship else [] - options.append(f"_('{self.name}')") - - for k, v in self._options.items(): - options.append(f"{k}={v}") - - if self.is_media_field: - options.append(f"upload_to='{self.upload_path}'") - - return ", ".join(options) - - -__many_to_many_relationship_field = Field( - kind="ManyToManyField", - options={"blank": True, "on_delete": "models.DO_NOTHING"}, - supports_admin=False, -) - -__foreign_key_relationship_field = Field( - kind="ForeignKey", options={"blank": False, "on_delete": "models.DO_NOTHING"} -) - -__one_to_one_relationship_field = Field( - kind="OneToOneField", - options={"blank": False, "on_delete": "models.CASCADE", "primary_key": True}, -) - -__big_integer_field = Field(kind="BigIntegerField") - -__generic_ip_field = Field(kind="GenericIPAddressField") - -__fields = { - # Numeric - "big": __big_integer_field, - "bigint": __big_integer_field, - "big-int": __big_integer_field, - "decimal": Field(kind="DecimalField"), - "float": Field(kind="FloatField"), - "int": Field(kind="IntegerField"), - "integer": Field(kind="IntegerField"), - # Boolean - "bool": Field(kind="BooleanField", options={"default": False}), - "boolean": Field(kind="BooleanField", options={"default": False}), - # Text - "char": Field(kind="CharField", options={"max_length": 100, "blank": False}), - "email": Field(kind="EmailField"), - "slug": Field(kind="SlugField", options={"unique": True}), - "string": Field(kind="TextField", options={"blank": False}, supports_admin=False), - "text": Field(kind="TextField", options={"blank": False}, supports_admin=False), - "url": Field(kind="URLField"), - "uuid": Field( - kind="UUIDField", options={"default": "uuid.uuid4", "editable": False} - ), - # Files - "file": Field(kind="FileField", options={"blank": False}, supports_admin=False), - "filepath": Field( - kind="FilePathField", options={"blank": True}, supports_admin=False - ), - "file-path": Field( - kind="FilePathField", options={"blank": True}, supports_admin=False - ), - "image": Field(kind="ImageField", options={"blank": False}, supports_admin=False), - "photo": Field(kind="ImageField", options={"blank": False}, supports_admin=False), - # Date - "date": Field(kind="DateField", options={"auto_now": True}), - "datetime": Field(kind="DateTimeField", options={"auto_now": True}), - "duration": Field(kind="DurationField"), - "time": Field(kind="TimeField", options={"auto_now": True}), - # Ip - "ip": __generic_ip_field, - "ipaddress": __generic_ip_field, - "ip-address": __generic_ip_field, - # Relationships - # - FK - "belongsto": __foreign_key_relationship_field, - "belongs-to": __foreign_key_relationship_field, - "fk": __foreign_key_relationship_field, - "foreignkey": __foreign_key_relationship_field, - "foreign-key": __foreign_key_relationship_field, - # - One - "one": __one_to_one_relationship_field, - "one-to-one": __one_to_one_relationship_field, - "hasone": __one_to_one_relationship_field, - "has-one": __one_to_one_relationship_field, - # - Many - "hasmany": __many_to_many_relationship_field, - "has-many": __many_to_many_relationship_field, - "many": __many_to_many_relationship_field, - "manytomany": __many_to_many_relationship_field, - "many-to-many": __many_to_many_relationship_field, -} - - -def make_field(kind, name, model) -> Field: - f = __fields.get(kind) - - if f is not None: - f.name = name - f.model = model - - if f.kind in ["ForeignKey", "OneToOneField"]: - f.is_fk_relationship = True - - if f.kind in ["ManyToManyField"]: - f.is_many_relationship = True - - return f - - return Field(kind=None, name=None, model=None, options=None) diff --git a/src/cli/template_files/fixture.tpl b/src/cli/template_files/fixture.tpl deleted file mode 100644 index b7efe88..0000000 --- a/src/cli/template_files/fixture.tpl +++ /dev/null @@ -1,13 +0,0 @@ -[ - {% for i in range(total) -%} - { - "pk": {{ loop.index }}, - "model": "{{ app }}.{{ classname }}", - "fields": { - {% for f in fields -%} - "{{ f.name }}": {{ f.example_value }}, - {% endfor %} - } - }{% if loop.index == loop.length %}{% else %},{% endif %} - {% endfor %} -] diff --git a/tests/__init__.py b/tests/__init__.py index 4ba8112..625e09d 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -3,8 +3,8 @@ from pathlib import Path -from cli import template_files from geny.core.templates.template import TemplateParser +from django_clite import template_files parser = TemplateParser( templates_dir=[Path(template_files.__file__).resolve().parent], diff --git a/tests/commands/destroy/test_destroyer.py b/tests/commands/destroy/test_destroyer.py new file mode 100644 index 0000000..6f063e1 --- /dev/null +++ b/tests/commands/destroy/test_destroyer.py @@ -0,0 +1,52 @@ +import unittest +from click.testing import CliRunner + + +runner = CliRunner() + + +class DestroyerTestCase(unittest.TestCase): + def test_destroy_admin(self): + pass + + def test_destroy_admin_inline(self): + pass + + def test_destroy_dockerfile(self): + pass + + def test_destroy_fixture(self): + pass + + def test_destroy_form(self): + pass + + def test_destroy_management(self): + pass + + def test_destroy_manager(self): + pass + + def test_destroy_model(self): + pass + + def test_destroy_serializer(self): + pass + + def test_destroy_signal(self): + pass + + def test_destroy_tag(self): + pass + + def test_destroy_templates(self): + pass + + def test_destroy_validator(self): + pass + + def test_destroy_views(self): + pass + + def test_destroy_viewset(self): + pass diff --git a/tests/commands/generate/test_generator.py b/tests/commands/generate/test_generator.py index 00f0960..6732ee4 100644 --- a/tests/commands/generate/test_generator.py +++ b/tests/commands/generate/test_generator.py @@ -5,7 +5,7 @@ from django.core.management.commands import startapp, startproject from geny.core.filesystem.filesystem import working_directory -from cli.commands.generate.main import generate +from django_clite.commands.generate.main import generate runner = CliRunner() @@ -31,8 +31,9 @@ def test_generators_require_app_dir(self): } for command in commands: - res = runner.invoke(generate, [command, "article"]) + res = runner.invoke(generate, [command, "article"]) # noqa self.assertNotEqual(0, res.exit_code) + self.assertIn("app was not detected", res.output) # App-level resources @@ -43,7 +44,7 @@ def test_generate_admin(self): call_command(startapp.Command(), app, verbosity=0) with working_directory(app): - res = runner.invoke(generate, ["admin", "article"]) + res = runner.invoke(generate, ["admin", "article"]) # noqa self.assertEqual(0, res.exit_code) self.assertIn("admin", os.listdir()) @@ -55,7 +56,7 @@ def test_generate_admin_inline(self): call_command(startapp.Command(), app, verbosity=0) with working_directory(app): - res = runner.invoke(generate, ["admin-inline", "article"]) + res = runner.invoke(generate, ["admin-inline", "article"]) # noqa self.assertEqual(0, res.exit_code) self.assertIn("admin", os.listdir()) @@ -72,7 +73,7 @@ def test_generate_fixture(self): command = "fixture" resource_dir = "fixtures" - res = runner.invoke(generate, [command, resource]) + res = runner.invoke(generate, [command, resource]) # noqa self.assertEqual(0, res.exit_code) self.assertIn(resource_dir, os.listdir()) @@ -88,7 +89,7 @@ def test_generate_form(self): command = "form" resource_dir = "forms" - res = runner.invoke(generate, [command, resource]) + res = runner.invoke(generate, [command, resource]) # noqa self.assertEqual(0, res.exit_code) self.assertIn(resource_dir, os.listdir()) @@ -104,7 +105,7 @@ def test_generate_manager(self): command = "manager" resource_dir = "managers" - res = runner.invoke(generate, [command, resource]) + res = runner.invoke(generate, [command, resource]) # noqa self.assertEqual(0, res.exit_code) self.assertIn(resource_dir, os.listdir("models")) @@ -120,7 +121,7 @@ def test_generate_model(self): command = "model" resource_dir = "models" - res = runner.invoke(generate, [command, resource]) + res = runner.invoke(generate, [command, resource]) # noqa self.assertEqual(0, res.exit_code) self.assertIn(resource_dir, os.listdir()) @@ -136,7 +137,7 @@ def test_generate_serializer(self): command = "serializer" resource_dir = "serializers" - res = runner.invoke(generate, [command, resource]) + res = runner.invoke(generate, [command, resource]) # noqa self.assertEqual(0, res.exit_code) self.assertIn(resource_dir, os.listdir()) @@ -152,7 +153,7 @@ def test_generate_signal(self): command = "signal" resource_dir = "signals" - res = runner.invoke(generate, [command, resource]) + res = runner.invoke(generate, [command, resource]) # noqa self.assertEqual(0, res.exit_code) self.assertIn(resource_dir, os.listdir("models")) @@ -170,7 +171,7 @@ def test_generate_tag(self): command = "tag" resource_dir = "templatetags" - res = runner.invoke(generate, [command, resource]) + res = runner.invoke(generate, [command, resource]) # noqa self.assertEqual(0, res.exit_code) self.assertIn(resource_dir, os.listdir()) @@ -186,7 +187,7 @@ def test_generate_templates(self): command = "template" resource_dir = "templates" - res = runner.invoke(generate, [command, resource, "--full"]) + res = runner.invoke(generate, [command, resource, "--full"]) # noqa self.assertEqual(0, res.exit_code) self.assertIn(resource_dir, os.listdir()) @@ -210,7 +211,7 @@ def test_generate_tests(self): command = "test" resource_dir = "models" - res = runner.invoke(generate, [command, resource, "--scope", "model"]) + res = runner.invoke(generate, [command, resource, "--scope", "model"]) # noqa self.assertEqual(0, res.exit_code) self.assertIn(resource_dir, os.listdir("tests")) @@ -221,7 +222,7 @@ def test_generate_tests(self): command = "test" resource_dir = "viewsets" - res = runner.invoke(generate, [command, resource, "--scope", "viewset"]) + res = runner.invoke(generate, [command, resource, "--scope", "viewset"]) # noqa self.assertEqual(0, res.exit_code) self.assertIn(resource_dir, os.listdir("tests")) @@ -237,7 +238,7 @@ def test_generate_validator(self): command = "validator" resource_dir = "validators" - res = runner.invoke(generate, [command, resource]) + res = runner.invoke(generate, [command, resource]) # noqa self.assertEqual(0, res.exit_code) self.assertIn(resource_dir, os.listdir("models")) @@ -253,7 +254,7 @@ def test_generate_views(self): command = "view" resource_dir = "views" - res = runner.invoke(generate, [command, resource]) + res = runner.invoke(generate, [command, resource]) # noqa self.assertEqual(0, res.exit_code) self.assertIn(resource_dir, os.listdir()) @@ -269,7 +270,7 @@ def test_generate_viewset(self): command = "viewset" resource_dir = "viewsets" - res = runner.invoke(generate, [command, resource]) + res = runner.invoke(generate, [command, resource]) # noqa self.assertEqual(0, res.exit_code) self.assertIn(resource_dir, os.listdir()) @@ -278,7 +279,7 @@ def test_generate_viewset(self): # Project-level resource def test_generate_dockerfile_requires_project_dir(self): - res = runner.invoke(generate, ["dockerfile"]) + res = runner.invoke(generate, ["dockerfile"]) # noqa self.assertNotEqual(0, res.exit_code) self.assertIn("project was not detected", res.output) @@ -288,7 +289,7 @@ def test_generate_dockerfile_requires_non_app_dir(self): call_command(startapp.Command(), app, verbosity=0) with working_directory(app): - res = runner.invoke(generate, ["dockerfile"]) + res = runner.invoke(generate, ["dockerfile"]) # noqa self.assertNotEqual(0, res.exit_code) self.assertIn("project was not detected", res.output) @@ -298,7 +299,7 @@ def test_generate_dockerfile(self): call_command(startproject.Command(), project, verbosity=0) with working_directory(project): - res = runner.invoke(generate, ["dockerfile"]) + res = runner.invoke(generate, ["dockerfile"]) # noqa self.assertEqual(0, res.exit_code) self.assertIn("Dockerfile", os.listdir()) @@ -308,7 +309,7 @@ def test_generate_dockerfile(self): call_command(startproject.Command(), project, verbosity=0) with working_directory(project): - res = runner.invoke(generate, ["dockerfile", "--docker-compose"]) + res = runner.invoke(generate, ["dockerfile", "--docker-compose"]) # noqa self.assertEqual(0, res.exit_code) self.assertIn("Dockerfile", os.listdir()) diff --git a/tests/commands/new/test_new.py b/tests/commands/new/test_new.py new file mode 100644 index 0000000..6f7d869 --- /dev/null +++ b/tests/commands/new/test_new.py @@ -0,0 +1,13 @@ +import unittest +from click.testing import CliRunner + + +runner = CliRunner() + + +class CreatorTestCase(unittest.TestCase): + def test_new_project(self): + pass + + def test_new_app(self): + pass diff --git a/tests/core/field_parser/test_factory.py b/tests/core/field_parser/test_factory.py index 562d8a4..91a5513 100644 --- a/tests/core/field_parser/test_factory.py +++ b/tests/core/field_parser/test_factory.py @@ -1,5 +1,5 @@ import unittest -from cli.core.field_parser.factory import make_field +from django_clite.core.field_parser.factory import AttributeFactory class FieldFactoryTestCase(unittest.TestCase): @@ -7,347 +7,347 @@ class FieldFactoryTestCase(unittest.TestCase): def test_make_bigint_field(self): aliases = ["big", "bigint", "big-int"] + attr_name = 'id' + model_name = 'Album' for alias in aliases: - field = make_field(alias, "id", "Album") + field = AttributeFactory().field_options(alias, attr_name) self.assertEqual("BigIntegerField", field.kind) - self.assertEqual("id", field.name) - self.assertEqual("Album", field.model) - self.assertEqual("_('id')", field.options) + self.assertEqual("_('id')", field.field_options(attr_name, model_name)) self.assertFalse(field.is_media_field) self.assertFalse(field.is_relationship) self.assertFalse(field.is_fk_relationship) self.assertFalse(field.is_many_relationship) - self.assertTrue(field.supported_in_admin) + self.assertTrue(field.supports_admin) def test_make_decimal_field(self): aliases = ["decimal"] + attr_name = 'price' + model_name = 'Album' for alias in aliases: - field = make_field(alias, "price", "Album") + field = AttributeFactory().field_options(alias, attr_name) self.assertEqual("DecimalField", field.kind) - self.assertEqual("price", field.name) - self.assertEqual("Album", field.model) - self.assertEqual("_('price')", field.options) + self.assertEqual("_('price')", field.field_options(attr_name, model_name)) self.assertFalse(field.is_media_field) self.assertFalse(field.is_relationship) self.assertFalse(field.is_fk_relationship) self.assertFalse(field.is_many_relationship) - self.assertTrue(field.supported_in_admin) + self.assertTrue(field.supports_admin) def test_make_float_field(self): aliases = ["float"] + attr_name = 'price' + model_name = 'Album' for alias in aliases: - field = make_field(alias, "price", "Album") + field = AttributeFactory().field_options(alias, attr_name) self.assertEqual("FloatField", field.kind) - self.assertEqual("price", field.name) - self.assertEqual("Album", field.model) - self.assertEqual("_('price')", field.options) + self.assertEqual("_('price')", field.field_options(attr_name, model_name)) self.assertFalse(field.is_media_field) self.assertFalse(field.is_relationship) self.assertFalse(field.is_fk_relationship) self.assertFalse(field.is_many_relationship) - self.assertTrue(field.supported_in_admin) + self.assertTrue(field.supports_admin) def test_make_integer_field(self): aliases = ["int", "integer"] + attr_name = 'tracks' + model_name = 'Album' for alias in aliases: - field = make_field(alias, "tracks", "Album") + field = AttributeFactory().field_options(alias, attr_name) self.assertEqual("IntegerField", field.kind) - self.assertEqual("tracks", field.name) - self.assertEqual("Album", field.model) - self.assertEqual("_('tracks')", field.options) + self.assertEqual("_('tracks')", field.field_options(attr_name, model_name)) self.assertFalse(field.is_media_field) self.assertFalse(field.is_relationship) self.assertFalse(field.is_fk_relationship) self.assertFalse(field.is_many_relationship) - self.assertTrue(field.supported_in_admin) + self.assertTrue(field.supports_admin) # Boolean def test_make_boolean_field(self): aliases = ["bool", "boolean"] + attr_name = 'is_compilation' + model_name = 'Album' for alias in aliases: - field = make_field(alias, "is_compilation", "Album") + field = AttributeFactory().field_options(alias, attr_name) self.assertEqual("BooleanField", field.kind) - self.assertEqual("is_compilation", field.name) - self.assertEqual("Album", field.model) - self.assertEqual("_('is_compilation'), default=False", field.options) + self.assertEqual("_('is_compilation'), default=False", field.field_options(attr_name, model_name)) self.assertFalse(field.is_media_field) self.assertFalse(field.is_relationship) self.assertFalse(field.is_fk_relationship) self.assertFalse(field.is_many_relationship) - self.assertTrue(field.supported_in_admin) + self.assertTrue(field.supports_admin) # Text and Strings def test_make_char_field(self): aliases = ["char"] + attr_name = 'title' + model_name = 'Album' for alias in aliases: - field = make_field(alias, "title", "Album") + field = AttributeFactory().field_options(alias, attr_name) self.assertEqual("CharField", field.kind) - self.assertEqual("title", field.name) - self.assertEqual("Album", field.model) - self.assertEqual("_('title'), max_length=100, blank=False", field.options) + self.assertEqual("_('title'), max_length=100, blank=False", field.field_options(attr_name, model_name)) self.assertFalse(field.is_media_field) self.assertFalse(field.is_relationship) self.assertFalse(field.is_fk_relationship) self.assertFalse(field.is_many_relationship) - self.assertTrue(field.supported_in_admin) + self.assertTrue(field.supports_admin) def test_make_slug_field(self): aliases = ["slug"] + attr_name = "slug" + model_name = "Album" for alias in aliases: - field = make_field(alias, "slug", "Album") + field = AttributeFactory().field_options(alias, attr_name) self.assertEqual("SlugField", field.kind) - self.assertEqual("slug", field.name) - self.assertEqual("Album", field.model) - self.assertEqual("_('slug'), unique=True", field.options) + self.assertEqual("_('slug'), unique=True", field.field_options(attr_name, model_name)) self.assertFalse(field.is_media_field) self.assertFalse(field.is_relationship) self.assertFalse(field.is_fk_relationship) self.assertFalse(field.is_many_relationship) - self.assertTrue(field.supported_in_admin) + self.assertTrue(field.supports_admin) def test_make_string_field(self): aliases = ["string", "text"] + attr_name = "description" + model_name = "Album" for alias in aliases: - field = make_field(alias, "description", "Album") + field = AttributeFactory().field_options(alias, attr_name) self.assertEqual("TextField", field.kind) - self.assertEqual("description", field.name) - self.assertEqual("Album", field.model) - self.assertEqual("_('description'), blank=False", field.options) + self.assertEqual("_('description'), blank=False", field.field_options(attr_name, model_name)) self.assertFalse(field.is_media_field) self.assertFalse(field.is_relationship) self.assertFalse(field.is_fk_relationship) self.assertFalse(field.is_many_relationship) - self.assertFalse(field.supported_in_admin) + self.assertFalse(field.supports_admin) def test_make_uuid_field(self): aliases = ["uuid"] + attr_name = "id" + model_name = "Album" for alias in aliases: - field = make_field(alias, "id", "Album") + field = AttributeFactory().field_options(alias, attr_name) self.assertEqual("UUIDField", field.kind) - self.assertEqual("id", field.name) - self.assertEqual("Album", field.model) self.assertEqual( - "_('id'), default=uuid.uuid4, editable=False", field.options + "_('id'), default=uuid.uuid4, editable=False", field.field_options(attr_name, model_name) ) self.assertFalse(field.is_media_field) self.assertFalse(field.is_relationship) self.assertFalse(field.is_fk_relationship) self.assertFalse(field.is_many_relationship) - self.assertTrue(field.supported_in_admin) + self.assertTrue(field.supports_admin) # Files def test_make_file_field(self): aliases = ["file"] + attr_name = "file" + model_name = "Album" for alias in aliases: - field = make_field(alias, "file", "Album") + field = AttributeFactory().field_options(alias, attr_name) self.assertEqual("FileField", field.kind) - self.assertEqual("file", field.name) - self.assertEqual("Album", field.model) self.assertEqual( "_('file'), blank=False, upload_to='uploads/albums/files/'", - field.options, + field.field_options(attr_name, model_name), ) self.assertTrue(field.is_media_field) self.assertFalse(field.is_relationship) self.assertFalse(field.is_fk_relationship) self.assertFalse(field.is_many_relationship) - self.assertFalse(field.supported_in_admin) + self.assertFalse(field.supports_admin) def test_make_filepath_field(self): aliases = ["filepath", "file-path"] + attr_name = "file" + model_name = "Album" for alias in aliases: - field = make_field(alias, "file", "Album") + field = AttributeFactory().field_options(alias, attr_name) self.assertEqual("FilePathField", field.kind) - self.assertEqual("file", field.name) - self.assertEqual("Album", field.model) self.assertEqual( "_('file'), blank=True, upload_to='uploads/albums/files/'", - field.options, + field.field_options(attr_name, model_name), ) self.assertTrue(field.is_media_field) self.assertFalse(field.is_relationship) self.assertFalse(field.is_fk_relationship) self.assertFalse(field.is_many_relationship) - self.assertFalse(field.supported_in_admin) + self.assertFalse(field.supports_admin) def test_make_image_field(self): aliases = ["image", "photo"] + attr_name = "image" + model_name = "Album" for alias in aliases: - field = make_field(alias, "image", "Album") + field = AttributeFactory().field_options(alias, "image") self.assertEqual("ImageField", field.kind) - self.assertEqual("image", field.name) - self.assertEqual("Album", field.model) self.assertEqual( "_('image'), blank=False, upload_to='uploads/albums/images/'", - field.options, + field.field_options(attr_name, model_name), ) self.assertTrue(field.is_media_field) self.assertFalse(field.is_relationship) self.assertFalse(field.is_fk_relationship) self.assertFalse(field.is_many_relationship) - self.assertFalse(field.supported_in_admin) + self.assertFalse(field.supports_admin) # Internet def test_make_email_field(self): aliases = ["email"] + attr_name = "email_address" + model_name = "Person" for alias in aliases: - field = make_field(alias, "email_address", "Person") + field = AttributeFactory().field_options(alias, attr_name) self.assertEqual("EmailField", field.kind) - self.assertEqual("email_address", field.name) - self.assertEqual("Person", field.model) - self.assertEqual("_('email_address')", field.options) + self.assertEqual("_('email_address')", field.field_options(attr_name, model_name)) self.assertFalse(field.is_media_field) self.assertFalse(field.is_relationship) self.assertFalse(field.is_fk_relationship) self.assertFalse(field.is_many_relationship) - self.assertTrue(field.supported_in_admin) + self.assertTrue(field.supports_admin) def test_make_url_field(self): aliases = ["url"] + attr_name = "url" + model_name = "Album" for alias in aliases: - field = make_field(alias, "url", "Album") + field = AttributeFactory().field_options(alias, attr_name) self.assertEqual("URLField", field.kind) - self.assertEqual("url", field.name) - self.assertEqual("Album", field.model) - self.assertEqual("_('url')", field.options) + self.assertEqual("_('url')", field.field_options(attr_name, model_name)) self.assertFalse(field.is_media_field) self.assertFalse(field.is_relationship) self.assertFalse(field.is_fk_relationship) self.assertFalse(field.is_many_relationship) - self.assertTrue(field.supported_in_admin) + self.assertTrue(field.supports_admin) def test_make_ip_field(self): aliases = ["ip", "ipaddress", "ip-address"] + attr_name = "ip" + model_name = "Album" for alias in aliases: - field = make_field(alias, "ip", "Album") + field = AttributeFactory().field_options(alias, attr_name) self.assertEqual("GenericIPAddressField", field.kind) - self.assertEqual("ip", field.name) - self.assertEqual("Album", field.model) - self.assertEqual("_('ip')", field.options) + self.assertEqual("_('ip')", field.field_options(attr_name, model_name)) self.assertFalse(field.is_media_field) self.assertFalse(field.is_relationship) self.assertFalse(field.is_fk_relationship) self.assertFalse(field.is_many_relationship) - self.assertTrue(field.supported_in_admin) + self.assertTrue(field.supports_admin) # Dates and Time def test_make_date_field(self): aliases = ["date"] + attr_name = "date" + model_name = "Album" for alias in aliases: - field = make_field(alias, "date", "Album") + field = AttributeFactory().field_options(alias, attr_name) self.assertEqual("DateField", field.kind) - self.assertEqual("date", field.name) - self.assertEqual("Album", field.model) - self.assertEqual("_('date'), auto_now=True", field.options) + self.assertEqual("_('date'), auto_now=True", field.field_options(attr_name, model_name)) self.assertFalse(field.is_media_field) self.assertFalse(field.is_relationship) self.assertFalse(field.is_fk_relationship) self.assertFalse(field.is_many_relationship) - self.assertTrue(field.supported_in_admin) + self.assertTrue(field.supports_admin) def test_make_datetime_field(self): aliases = ["datetime"] + attr_name = "datetime" + model_name = "Album" for alias in aliases: - field = make_field(alias, "datetime", "Album") + field = AttributeFactory().field_options(alias, attr_name) self.assertEqual("DateTimeField", field.kind) - self.assertEqual("datetime", field.name) - self.assertEqual("Album", field.model) - self.assertEqual("_('datetime'), auto_now=True", field.options) + self.assertEqual("_('datetime'), auto_now=True", field.field_options(attr_name, model_name)) self.assertFalse(field.is_media_field) self.assertFalse(field.is_relationship) self.assertFalse(field.is_fk_relationship) self.assertFalse(field.is_many_relationship) - self.assertTrue(field.supported_in_admin) + self.assertTrue(field.supports_admin) def test_make_duration_field(self): aliases = ["duration"] + attr_name = "duration" + model_name = "Album" for alias in aliases: - field = make_field(alias, "duration", "Album") + field = AttributeFactory().field_options(alias, attr_name) self.assertEqual("DurationField", field.kind) - self.assertEqual("duration", field.name) - self.assertEqual("Album", field.model) - self.assertEqual("_('duration')", field.options) + self.assertEqual("_('duration')", field.field_options(attr_name, model_name)) self.assertFalse(field.is_media_field) self.assertFalse(field.is_relationship) self.assertFalse(field.is_fk_relationship) self.assertFalse(field.is_many_relationship) - self.assertTrue(field.supported_in_admin) + self.assertTrue(field.supports_admin) def test_make_time_field(self): aliases = ["time"] + attr_name = "time" + model_name = "Album" for alias in aliases: - field = make_field(alias, "time", "Album") + field = AttributeFactory().field_options(alias, attr_name) self.assertEqual("TimeField", field.kind) - self.assertEqual("time", field.name) - self.assertEqual("Album", field.model) - self.assertEqual("_('time'), auto_now=True", field.options) + self.assertEqual("_('time'), auto_now=True", field.field_options(attr_name, model_name)) self.assertFalse(field.is_media_field) self.assertFalse(field.is_relationship) self.assertFalse(field.is_fk_relationship) self.assertFalse(field.is_many_relationship) - self.assertTrue(field.supported_in_admin) + self.assertTrue(field.supports_admin) # Relationships # ============= @@ -355,64 +355,64 @@ def test_make_time_field(self): # Foreign Key def test_make_foreign_key_field(self): aliases = ["belongsto", "belongs-to", "fk", "foreignkey", "foreign-key"] + attr_name = "singer" + model_name = "Album" for alias in aliases: - field = make_field(alias, "singer", "Album") + field = AttributeFactory().field_options(alias, attr_name) self.assertEqual("ForeignKey", field.kind) - self.assertEqual("singer", field.name) - self.assertEqual("Album", field.model) self.assertEqual( "Singer, _('singer'), blank=False, on_delete=models.DO_NOTHING, related_name='album'", - field.options, + field.field_options(attr_name, model_name), ) self.assertFalse(field.is_media_field) self.assertTrue(field.is_relationship) self.assertTrue(field.is_fk_relationship) self.assertFalse(field.is_many_relationship) - self.assertTrue(field.supported_in_admin) + self.assertTrue(field.supports_admin) # One-To-One def test_make_one_to_one_field(self): aliases = ["one", "hasone", "has-one", "one-to-one"] + attr_name = 'ssn' + model_name = 'Person' for alias in aliases: - field = make_field(alias, "ssn", "Person") + field = AttributeFactory().field_options(alias, attr_name) self.assertEqual("OneToOneField", field.kind) - self.assertEqual("Person", field.model) - self.assertEqual("ssn", field.name) self.assertEqual( "Ssn, _('ssn'), blank=False, on_delete=models.CASCADE, primary_key=True, related_name='person'", - field.options, + field.field_options(attr_name, model_name), ) self.assertFalse(field.is_media_field) self.assertTrue(field.is_relationship) self.assertTrue(field.is_fk_relationship) self.assertFalse(field.is_many_relationship) - self.assertTrue(field.supported_in_admin) + self.assertTrue(field.supports_admin) # Many def test_make_many_to_many_field(self): aliases = ["hasmany", "has-many", "many", "manytomany", "many-to-many"] + attr_name = "songs" + model_name = "Album" for alias in aliases: - field = make_field(alias, "songs", "Album") + field = AttributeFactory().field_options(alias, attr_name) self.assertEqual("ManyToManyField", field.kind) - self.assertEqual("Album", field.model) - self.assertEqual("songs", field.name) self.assertEqual( "Song, _('songs'), blank=True, on_delete=models.DO_NOTHING, related_name='album'", - field.options, + field.field_options(attr_name, model_name), ) self.assertFalse(field.is_media_field) self.assertTrue(field.is_relationship) self.assertFalse(field.is_fk_relationship) self.assertTrue(field.is_many_relationship) - self.assertFalse(field.supported_in_admin) + self.assertFalse(field.supports_admin) diff --git a/tests/core/field_parser/test_field_parser.py b/tests/core/field_parser/test_field_parser.py new file mode 100644 index 0000000..5080270 --- /dev/null +++ b/tests/core/field_parser/test_field_parser.py @@ -0,0 +1,23 @@ +import unittest +from django_clite.core.field_parser.factory import AttributeFactory + + +class CallbackTestCase(unittest.TestCase): + def test_field_parser(self): + fields = AttributeFactory().parsed_fields( + [ + "name:char", + "title:char", + "user:fk", + "desc:char", + "rating:int", + "owner:fk", + "total:int", + ] + ) + + field_names = sorted(fields.keys()) + self.assertEqual(['desc', 'name', 'owner', 'rating', 'title', 'total', 'user'], field_names) + + field_values = list(map(lambda x: x.kind, fields.values())) + self.assertEqual(['CharField', 'CharField', 'ForeignKey', 'CharField', 'IntegerField', 'ForeignKey', 'IntegerField'], field_values) diff --git a/tests/test_utils.py b/tests/test_utils.py index 5bc4379..af2da7e 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,5 +1,5 @@ import unittest -from cli.utils import inflect, sanitized_string +from django_clite.cli.utils import inflect, sanitized_string class UtilsTestCase(unittest.TestCase):