Fixed static and template email files for container

This commit is contained in:
2025-12-09 10:45:25 +01:00
parent ec7ae4a48e
commit a9587776e8
8 changed files with 47 additions and 30 deletions

View File

@@ -154,4 +154,4 @@ EMAIL_PORT = 587
EMAIL_USE_TLS = True EMAIL_USE_TLS = True
EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend" EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend"
EMAIL_FILE_PATH = "/tmp/app-messages" # change this to a proper location EMAIL_FILE_PATH = "/mnt/d/Test/flightslot-mail" # change this to a proper location

View File

@@ -75,7 +75,7 @@ ROOT_URLCONF = 'cntmanage.urls'
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': ['/var/www/templates'], 'DIRS': ['/var/www/templates', '/app/templates'],
'APP_DIRS': True, 'APP_DIRS': True,
'OPTIONS': { 'OPTIONS': {
'context_processors': [ 'context_processors': [
@@ -139,6 +139,9 @@ USE_TZ = True
# https://docs.djangoproject.com/en/5.1/howto/static-files/ # https://docs.djangoproject.com/en/5.1/howto/static-files/
STATIC_URL = "static/" STATIC_URL = "static/"
STATIC_ROOT = "/var/www/static/" STATIC_ROOT = "/var/www/static/"
STATICFILES_DIRS = [
"/app/static/"
]
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage" STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
# Default primary key field type # Default primary key field type
@@ -155,4 +158,4 @@ EMAIL_PORT = 587
EMAIL_USE_TLS = True EMAIL_USE_TLS = True
# Use dummy backed for testing # Use dummy backed for testing
EMAIL_BACKEND = "django.core.mail.backends..dummy.EmailBackend" EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"

View File

@@ -19,11 +19,12 @@ FROM python:3.12-slim AS deploy
WORKDIR /app WORKDIR /app
# Copy application custom static files # Copy application custom static files
RUN mkdir -p static RUN mkdir -p static
COPY ./static/cantorair.jpg ./static COPY ./static/* ./static
COPY ./static/cantorair_blue.jpg ./static
# Copy application custom templates for admin page # Copy application custom templates for admin page
RUN mkdir -p /templates/admin RUN mkdir -p /templates/admin
RUN mkdir -p /templates/email
COPY ./templates/admin/* ./templates/admin/ COPY ./templates/admin/* ./templates/admin/
COPY ./templates/email/* ./templates/email/
# Copy and install application wheel package # Copy and install application wheel package
COPY --from=builder /build/dist/*.whl ./ COPY --from=builder /build/dist/*.whl ./
RUN pip install --no-cache-dir *.whl RUN pip install --no-cache-dir *.whl

View File

@@ -1,6 +1,6 @@
from django.conf import settings from django.contrib.staticfiles import finders
from django.contrib import messages from django.contrib import messages
from django.core.mail import EmailMultiAlternatives from django.core.mail import EmailMultiAlternatives, get_connection
from django.http import HttpRequest from django.http import HttpRequest
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from django.utils.safestring import SafeText from django.utils.safestring import SafeText
@@ -11,24 +11,27 @@ from ..models.students import Student
from smtplib import SMTPException from smtplib import SMTPException
from email.mime.image import MIMEImage from email.mime.image import MIMEImage
import os from typing import List
def send_mail_password(request: HttpRequest, queryset: QuerySet[Student]) -> None: def send_mail_password(request: HttpRequest, queryset: QuerySet[Student]) -> None:
img: MIMEImage | None = None img: MIMEImage | None = None
try: filename: str
for d in settings.STATICFILES_DIRS: candidates = finders.find("cantorair.png")
filename = os.path.join(d, "cantorair.png") if not candidates:
if not os.path.exists(filename): messages.error(request=request, message="Cannot Load CantorAir Logo")
continue return
elif isinstance(candidates, list):
filename = candidates.pop()
else:
filename = candidates
with open(filename, "rb") as f: with open(filename, "rb") as f:
img = MIMEImage(f.read()) img = MIMEImage(f.read())
img.add_header("Content-ID", "logo_image") img.add_header("Content-ID", "logo_image")
img.add_header("Content-Disposition", "inline", filename="cantorair.png") img.add_header("Content-Disposition", "inline", filename="cantorair.png")
break
except:
messages.error(request=request, message="Cannot Load CantorAir Logo")
return
# build mail list filling template
mails: List[EmailMultiAlternatives] = []
for student in queryset: for student in queryset:
if not student.user or not student.email: # skip student if has not an associated user if not student.user or not student.email: # skip student if has not an associated user
continue continue
@@ -52,11 +55,19 @@ def send_mail_password(request: HttpRequest, queryset: QuerySet[Student]) -> Non
) )
mail.attach(filename=img) mail.attach(filename=img)
mail.attach_alternative(content=html_message, mimetype="text/html") mail.attach_alternative(content=html_message, mimetype="text/html")
mail.send() mails.append(mail)
except SMTPException as e:
messages.error(request=request, message=f"Send Mail error: {e.strerror}")
except Exception as e: except Exception as e:
messages.error(request=request, message=f"General Error: {e}") messages.error(request=request, message=f"General Error: {e}")
# Open only one conenction and send mass email
try:
with get_connection() as conn:
conn.send_messages(mails)
except SMTPException as e:
messages.error(request=request, message=f"Send Mail SMTP error: {e.strerror}")
except Exception as e:
messages.error(request=request, message=f"Send Mail General error: {e}")
else: else:
messages.success(request=request, message=f"Email Sent To: {student.surname} {student.name[0].upper()}. -> {mail.to.pop()}") messages.success(request=request, message=f"Successfully sent {len(mails)} messages")
return return

View File

@@ -115,9 +115,9 @@ class StudentAdmin(ImportMixin, AdminConfirmMixin, AdminActionFormsMixin, admin.
i, ac_types = assign_aircraft(queryset=queryset, data=data) i, ac_types = assign_aircraft(queryset=queryset, data=data)
messages.success(request, f"{i} Students updated to {ac_types}") messages.success(request, f"{i} Students updated to {ac_types}")
@admin.action(description="Send Access Credentials e-mail")
@confirm_action @confirm_action
def send_mail(self, request: HttpRequest, queryset: QuerySet[Student], data: Any) -> None: @admin.action(description="Send Access Credentials e-mail")
def send_mail(self, request: HttpRequest, queryset: QuerySet[Student], *args: Any) -> None:
send_mail_password(request=request, queryset=queryset) send_mail_password(request=request, queryset=queryset)
# Return the initial form for import confirmations, request course to user # Return the initial form for import confirmations, request course to user

View File

@@ -1 +0,0 @@
admin: CantorAir2k25

View File

@@ -8,8 +8,8 @@
<h1 id="site-name"> <h1 id="site-name">
<a href="{% url 'admin:index' %}" style="color: #0b1728;"> <a href="{% url 'admin:index' %}" style="color: #0b1728;">
<img src="{% static 'cantorair_blue.jpg' %}" <img src="{% static 'cantorair.png' %}"
height="60px" height="70px"
style="margin-right: 20px;"/> style="margin-right: 20px;"/>
</a> </a>
</h1> </h1>

View File

@@ -3,3 +3,6 @@ OK aereo assegnato allo studente di fianco al numero della missione per PPL, inv
OK le missioni ripetute su piu' giorni hanno una cella per giorno (non unite) OK le missioni ripetute su piu' giorni hanno una cella per giorno (non unite)
OK lo studente vede solo le missioni della sua fase PPL->PPL ATPL-> tutto OK lo studente vede solo le missioni della sua fase PPL->PPL ATPL-> tutto
OK ogni richiesta ha un colore diverso che cicla con delle tinte pastello OK ogni richiesta ha un colore diverso che cicla con delle tinte pastello
password: CantorAir2k25