diff --git a/Libros/Dockerfile b/Libros/Dockerfile index f5372dc..0fcdbaf 100644 --- a/Libros/Dockerfile +++ b/Libros/Dockerfile @@ -1,8 +1,9 @@ # syntax=docker/dockerfile:1 +################## +# BUILDER # +################## -FROM python:3.11.4-slim-buster - -#FROM python:3.9.19-slim-bullseye +FROM python:3.11.4-slim-buster AS builder # set work directory WORKDIR /app @@ -12,7 +13,26 @@ ENV PYTHONDONTWRITEBYTECODE=1 ENV PYTHONUNBUFFERED=1 # install system dependencies -RUN apt-get update && apt-get install -y sqlite3 netcat +RUN apt-get update && \ + apt-get install -y --no-install-recommends gcc + +# lint +RUN pip install --upgrade pip +RUN pip install flake8==6.0.0 +COPY . /app/ +RUN flake8 --ignore=E501,F401,E126 . + +COPY ./requirements.txt . + + +RUN pip wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt + +################## +# FINAL # +################## + +FROM python:3.11.4-slim-buster + # create directory for the app user RUN mkdir -p /app @@ -27,9 +47,13 @@ RUN mkdir -p $APP_HOME #RUN mkdir -p $APP_HOME/mediafiles WORKDIR $APP_HOME +# install system dependencies +RUN apt-get update && apt-get install -y sqlite3 netcat +COPY --from=builder /app/wheels /wheels +COPY --from=builder /app/requirements.txt . + RUN pip install --upgrade pip -COPY ./requirements.txt . -RUN pip install -r /app/requirements.txt +RUN pip install --no-cache /wheels/* # copy entrypoint.sh COPY ./entrypoint.sh . diff --git a/Libros/K8S/Makefile b/Libros/K8S/Makefile index 8c0d701..505a3cf 100644 --- a/Libros/K8S/Makefile +++ b/Libros/K8S/Makefile @@ -1,5 +1,5 @@ export ARQUITECTURA := $(shell lscpu |grep itectur | tr -d ' '| cut -f2 -d':') -export IMG_VERSION = 1.29 +export IMG_VERSION = 1.30 export IMG_NGINX_VERSION = 1.17 # limpia todo diff --git a/Libros/biblioteca/accounts/urls.py b/Libros/biblioteca/accounts/urls.py index 52f72bf..cb6be4a 100644 --- a/Libros/biblioteca/accounts/urls.py +++ b/Libros/biblioteca/accounts/urls.py @@ -9,4 +9,4 @@ urlpatterns = [ path("signup/", SignUpView.as_view(), name="signup"), path('login/', auth_views.LoginView.as_view(), name='login'), path('logout/', auth_views.LogoutView.as_view(), name='logout'), -] \ No newline at end of file +] diff --git a/Libros/biblioteca/accounts/views.py b/Libros/biblioteca/accounts/views.py index 6517d3e..824229b 100644 --- a/Libros/biblioteca/accounts/views.py +++ b/Libros/biblioteca/accounts/views.py @@ -1,11 +1,12 @@ # accounts/views.py -#from django.contrib.auth.forms import UserCreationForm +# from django.contrib.auth.forms import UserCreationForm from django.urls import reverse_lazy from django.views.generic import CreateView from gestion.forms import ReyMotaUserCreationForm + class SignUpView(CreateView): form_class = ReyMotaUserCreationForm success_url = reverse_lazy("login") - template_name = "registration/signup.html" \ No newline at end of file + template_name = "registration/signup.html" diff --git a/Libros/biblioteca/biblioteca/settings.py b/Libros/biblioteca/biblioteca/settings.py index 4a6b8bd..776e14a 100644 --- a/Libros/biblioteca/biblioteca/settings.py +++ b/Libros/biblioteca/biblioteca/settings.py @@ -21,11 +21,11 @@ BASE_DIR = Path(__file__).resolve().parent.parent # See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -#SECRET_KEY = 'django-insecure-q0bxrr=*ab*b8oa#=+-@_4cu-cp6)c3lz#+!c_%51$*!bnz%4!' + SECRET_KEY = os.environ.get("SECRET_KEY") # SECURITY WARNING: don't run with debug turned on in production! -#DEBUG = True +# DEBUG = True DEBUG = bool(os.environ.get("DEBUG", default=0)) @@ -88,8 +88,8 @@ DATABASES = { } } -#print("DB:",DATABASES) -#print("BASE_DIR:",BASE_DIR) +# print("DB:",DATABASES) +# print("BASE_DIR:",BASE_DIR) # Password validation # https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators @@ -139,26 +139,26 @@ LOGOUT_REDIRECT_URL = 'principal' AUTH_USER_MODEL = "gestion.ReyMotaUser" -MEDIA_ROOT = os.path.join(BASE_DIR, 'mediafiles/') # 'media' is my media folder +MEDIA_ROOT = os.path.join(BASE_DIR, 'mediafiles/') LOGGING = { - 'version': 1, - 'disable_existing_loggers': False, - 'handlers': { - 'file': { - 'level': 'DEBUG', - 'class': 'logging.FileHandler', - 'filename': '/tmp/debug.log', - }, - }, - 'loggers': { - 'django': { - 'handlers': ['file'], - 'level': 'DEBUG', - 'propagate': True, - }, - }, + 'version': 1, + 'disable_existing_loggers': False, + 'handlers': { + 'file': { + 'level': 'DEBUG', + 'class': 'logging.FileHandler', + 'filename': '/tmp/debug.log', + }, + }, + 'loggers': { + 'django': { + 'handlers': ['file'], + 'level': 'DEBUG', + 'propagate': True, + }, + }, } CSRF_TRUSTED_ORIGINS = ["http://localhost:30443"] diff --git a/Libros/biblioteca/biblioteca/urls.py b/Libros/biblioteca/biblioteca/urls.py index a49712d..691645c 100644 --- a/Libros/biblioteca/biblioteca/urls.py +++ b/Libros/biblioteca/biblioteca/urls.py @@ -20,6 +20,8 @@ from django.conf import settings from django.conf.urls.static import static from django.views.generic.base import TemplateView # new +admin.site.site_title = "Demo django con nginx" + urlpatterns = [ path('obreros/', admin.site.urls), path('gestion/', include('gestion.urls')), @@ -28,9 +30,11 @@ urlpatterns = [ path("accounts/", include("django.contrib.auth.urls")), - path("", TemplateView.as_view(template_name="gestion/index.html"), name="principal"), # new + path("", TemplateView.as_view(template_name="gestion/index.html"), + name="principal"), # new ] if settings.DEBUG: - urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + urlpatterns += static(settings.MEDIA_URL, + document_root=settings.MEDIA_ROOT) diff --git a/Libros/biblioteca/gestion/admin.py b/Libros/biblioteca/gestion/admin.py index 02e8944..1c803db 100644 --- a/Libros/biblioteca/gestion/admin.py +++ b/Libros/biblioteca/gestion/admin.py @@ -10,6 +10,7 @@ from .forms import ReyMotaUserCreationForm, ReyMotaUserChangeForm admin.site.register(Autor) admin.site.register(Libro) + class ReyMotaUserAdmin(UserAdmin): add_form = ReyMotaUserCreationForm form = ReyMotaUserChangeForm @@ -19,21 +20,27 @@ class ReyMotaUserAdmin(UserAdmin): fieldsets = ( (None, {"fields": ("email", "password")}), ("Personal", {"fields": ("nombre",)}), - ("Permissions", {"fields": ("is_staff", "is_active", "groups", "user_permissions")}), + ("Permissions", {"fields": ("is_staff", "is_active", "groups", + "user_permissions")}), ("Varios", {"fields": ("foto",)}), ) add_fieldsets = ( - (None, { - "classes": ("wide",), - "fields": ( - "email", "password1", "password2", "is_staff", - "is_active", "groups", "user_permissions" - )} + ( + None, + { + "classes": ("wide",), + "fields": ( + "email", "password1", "password2", "is_staff", + "is_active", "groups", "user_permissions" + ) + } ), + ("Personal", {"fields": ("nombre",)}), ("Varios", {"fields": ("foto",)}), ) search_fields = ("email",) ordering = ("email",) -admin.site.register(ReyMotaUser, ReyMotaUserAdmin) \ No newline at end of file + +admin.site.register(ReyMotaUser, ReyMotaUserAdmin) diff --git a/Libros/biblioteca/gestion/forms.py b/Libros/biblioteca/gestion/forms.py index a89c241..e0d0ad1 100644 --- a/Libros/biblioteca/gestion/forms.py +++ b/Libros/biblioteca/gestion/forms.py @@ -3,26 +3,34 @@ from django.contrib.auth.forms import UserCreationForm, UserChangeForm from .models import Autor, Libro, ReyMotaUser + class AutorForm(forms.ModelForm): class Meta: model = Autor fields = ['nombre', 'biografia', 'foto'] - nombre = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control'})) - biografia = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control'})) + nombre = forms.CharField( + widget=forms.TextInput(attrs={'class': 'form-control'})) + biografia = forms.CharField( + widget=forms.TextInput(attrs={'class': 'form-control'})) + class LibroForm(forms.ModelForm): class Meta: model = Libro - fields = ['titulo', 'autor', 'fecha_publicacion', 'descripcion', 'archivo', 'portada'] + fields = ['titulo', 'autor', 'fecha_publicacion', 'descripcion', + 'archivo', 'portada'] - titulo = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control'})) - descripcion = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control'})) + titulo = forms.CharField( + widget=forms.TextInput(attrs={'class': 'form-control'})) + descripcion = forms.CharField( + widget=forms.TextInput(attrs={'class': 'form-control'})) autor = forms.ModelChoiceField( queryset=Autor.objects.all(), widget=forms.Select(attrs={'class': 'form-control'})) + class ReyMotaUserCreationForm(UserCreationForm): class Meta: @@ -30,6 +38,7 @@ class ReyMotaUserCreationForm(UserCreationForm): fields = ("email", "nombre", "foto") labels = {'email': 'Dirección de correo'} + class ReyMotaUserChangeForm(UserChangeForm): class Meta: diff --git a/Libros/biblioteca/gestion/managers.py b/Libros/biblioteca/gestion/managers.py index a42b5cc..0b8128d 100644 --- a/Libros/biblioteca/gestion/managers.py +++ b/Libros/biblioteca/gestion/managers.py @@ -31,4 +31,4 @@ class ReyMotaUserManager(BaseUserManager): raise ValueError(_("Superuser must have is_staff=True.")) if extra_fields.get("is_superuser") is not True: raise ValueError(_("Superuser must have is_superuser=True.")) - return self.create_user(email, password, **extra_fields) \ No newline at end of file + return self.create_user(email, password, **extra_fields) diff --git a/Libros/biblioteca/gestion/models.py b/Libros/biblioteca/gestion/models.py index 2515b4f..b715271 100644 --- a/Libros/biblioteca/gestion/models.py +++ b/Libros/biblioteca/gestion/models.py @@ -6,6 +6,7 @@ from django.utils.translation import gettext_lazy as _ from .managers import ReyMotaUserManager + def current_year(): return datetime.date.today().year @@ -13,6 +14,7 @@ def current_year(): def max_value_current_year(value): return MaxValueValidator(current_year())(value) + class Autor(models.Model): nombre = models.CharField(max_length=200) biografia = models.TextField(blank=True, null=True) @@ -21,6 +23,7 @@ class Autor(models.Model): def __str__(self): return self.nombre + class Libro(models.Model): titulo = models.CharField(max_length=200) autor = models.ForeignKey(Autor, on_delete=models.CASCADE) @@ -46,4 +49,4 @@ class ReyMotaUser(AbstractBaseUser, PermissionsMixin): objects = ReyMotaUserManager() def __str__(self): - return self.email \ No newline at end of file + return self.email diff --git a/Libros/biblioteca/gestion/templatetags/filtros_de_entorno.py b/Libros/biblioteca/gestion/templatetags/filtros_de_entorno.py index c3a307b..4a6a015 100644 --- a/Libros/biblioteca/gestion/templatetags/filtros_de_entorno.py +++ b/Libros/biblioteca/gestion/templatetags/filtros_de_entorno.py @@ -1,8 +1,9 @@ -import os +import os from django import template register = template.Library() + @register.filter def muestra_version(clave): return os.getenv(clave, '') diff --git a/Libros/biblioteca/gestion/urls.py b/Libros/biblioteca/gestion/urls.py index d5cb7ee..9cab83f 100644 --- a/Libros/biblioteca/gestion/urls.py +++ b/Libros/biblioteca/gestion/urls.py @@ -8,7 +8,7 @@ urlpatterns = [ path('autores//', views.detalle_autor, name='detalle_autor'), path('autores//editar/', views.editar_autor, name='editar_autor'), path('autores//eliminar/', views.eliminar_autor, name='eliminar_autor'), - + path('libros/', views.lista_libros, name='lista_libros'), path('libros/nuevo/', views.nuevo_libro, name='nuevo_libro'), path('libros//', views.detalle_libro, name='detalle_libro'), diff --git a/Libros/biblioteca/gestion/views.py b/Libros/biblioteca/gestion/views.py index 7defaed..57044cd 100644 --- a/Libros/biblioteca/gestion/views.py +++ b/Libros/biblioteca/gestion/views.py @@ -5,18 +5,20 @@ from django.views.generic import CreateView from django.contrib.auth.forms import UserCreationForm from django.urls import reverse_lazy - from .models import Autor, Libro from .forms import AutorForm, LibroForm + def principal(request): return render(request, 'gestion/index.html') + # Vistas para los autores def lista_autores(request): autores = Autor.objects.all() return render(request, 'gestion/lista_autores.html', {'autores': autores}) + def detalle_autor(request, autor_id): autor = get_object_or_404(Autor, pk=autor_id) @@ -24,6 +26,7 @@ def detalle_autor(request, autor_id): return render(request, 'gestion/detalle_autor.html', {'autor': autor, 'libros': libros}) + @login_required def nuevo_autor(request): if request.method == 'POST': @@ -35,6 +38,7 @@ def nuevo_autor(request): form = AutorForm() return render(request, 'gestion/form_autor.html', {'form': form}) + @login_required def editar_autor(request, autor_id): autor = get_object_or_404(Autor, pk=autor_id) @@ -47,21 +51,25 @@ def editar_autor(request, autor_id): form = AutorForm(instance=autor) return render(request, 'gestion/form_autor.html', {'form': form}) + @login_required def eliminar_autor(request, autor_id): autor = get_object_or_404(Autor, pk=autor_id) autor.delete() return redirect('lista_autores') + # Vistas para los libros def lista_libros(request): libros = Libro.objects.all() return render(request, 'gestion/lista_libros.html', {'libros': libros}) + def detalle_libro(request, libro_id): libro = get_object_or_404(Libro, pk=libro_id) return render(request, 'gestion/detalle_libro.html', {'libro': libro}) + @login_required def nuevo_libro(request): if request.method == 'POST': @@ -73,6 +81,7 @@ def nuevo_libro(request): form = LibroForm() return render(request, 'gestion/form_libro.html', {'form': form}) + @login_required def editar_libro(request, libro_id): libro = get_object_or_404(Libro, pk=libro_id) @@ -85,6 +94,7 @@ def editar_libro(request, libro_id): form = LibroForm(instance=libro) return render(request, 'gestion/form_libro.html', {'form': form}) + @login_required def eliminar_libro(request, libro_id): libro = get_object_or_404(Libro, pk=libro_id) @@ -95,4 +105,4 @@ def eliminar_libro(request, libro_id): class SignUpView(CreateView): form_class = UserCreationForm success_url = reverse_lazy("login") - template_name = "registration/signup.html" \ No newline at end of file + template_name = "registration/signup.html" diff --git a/Libros/requirements.txt b/Libros/requirements.txt index e4ba69b..9303601 100644 --- a/Libros/requirements.txt +++ b/Libros/requirements.txt @@ -1,7 +1,12 @@ asgiref==3.8.1 Django==4.2 +flake8==7.1.1 +gunicorn==22.0.0 +mccabe==0.7.0 +packaging==24.1 pillow==10.4.0 +psycopg2-binary==2.9.6 +pycodestyle==2.12.1 +pyflakes==3.2.0 sqlparse==0.5.1 typing_extensions==4.12.2 -gunicorn==22.0.0 -psycopg2-binary==2.9.6