This commit is contained in:
Flatlogic Bot 2026-01-28 04:47:18 +00:00
parent e6c45971eb
commit 0beefaf8a8
9 changed files with 167 additions and 4 deletions

15
.dockerignore Normal file
View File

@ -0,0 +1,15 @@
.git
.gitignore
.env
.venv
venv/
__pycache__
*.pyc
*.pyo
*.pyd
.DS_Store
db.sqlite3
staticfiles/
media/
.idea
.vscode

42
Dockerfile Normal file
View File

@ -0,0 +1,42 @@
FROM python:3.11-slim-bookworm
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
# Set work directory
WORKDIR /app
# Install system dependencies
# WeasyPrint needs: libpango-1.0-0 libpangoft2-1.0-0 libharfbuzz-subset0 libjpeg-dev libopenjp2-7-dev libxcb1
# MySQLclient needs: default-libmysqlclient-dev gcc pkg-config
RUN apt-get update && apt-get install -y \
gcc \
pkg-config \
default-libmysqlclient-dev \
libpango-1.0-0 \
libpangoft2-1.0-0 \
libharfbuzz-subset0 \
libjpeg-dev \
libopenjp2-7-dev \
libxcb1 \
curl \
&& rm -rf /var/lib/apt/lists/*
# Install python dependencies
COPY requirements.txt /app/
RUN pip install --upgrade pip && pip install --no-cache-dir -r requirements.txt
# Copy project
COPY . /app/
# Copy entrypoint script and make it executable
COPY entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh
# Expose port
EXPOSE 8000
# Entrypoint
ENTRYPOINT ["/app/entrypoint.sh"]

View File

@ -59,6 +59,7 @@ INSTALLED_APPS = [
'rest_framework',
'rest_framework.authtoken',
'drf_yasg',
'rangefilter',
'core',
]
@ -242,6 +243,7 @@ JAZZMIN_SETTINGS = {
"user_avatar": None,
"topmenu_links": [
{"name": "Home", "url": "admin:index", "permissions": ["auth.view_user"]},
{"name": "View Website", "url": "index", "new_window": True},
{"model": "auth.User"},
{"app": "core"},
],
@ -306,4 +308,4 @@ REST_FRAMEWORK = {
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
],
}
}

View File

@ -13,6 +13,9 @@ from .mail import send_html_email
import logging
import csv
from django.http import HttpResponse, HttpResponseRedirect
from rangefilter.filters import DateRangeFilter
from django.template.loader import render_to_string
import weasyprint
class ProfileInline(admin.StackedInline):
model = Profile
@ -92,9 +95,13 @@ class CustomUserAdmin(UserAdmin):
class ParcelAdmin(admin.ModelAdmin):
list_display = ('tracking_number', 'shipper', 'carrier', 'price', 'status', 'payment_status', 'created_at')
list_filter = ('status', 'payment_status', 'created_at')
list_filter = (
'status',
'payment_status',
('created_at', DateRangeFilter),
)
search_fields = ('tracking_number', 'shipper__username', 'receiver_name', 'carrier__username')
actions = ['export_as_csv']
actions = ['export_as_csv', 'print_parcels', 'export_pdf']
def export_as_csv(self, request, queryset):
response = HttpResponse(content_type='text/csv')
@ -118,6 +125,21 @@ class ParcelAdmin(admin.ModelAdmin):
return response
export_as_csv.short_description = _("Export Selected to CSV")
def print_parcels(self, request, queryset):
return render(request, 'admin/core/parcel/parcel_list_print.html', {'parcels': queryset, 'is_pdf': False})
print_parcels.short_description = _("Print Selected Parcels")
def export_pdf(self, request, queryset):
html_string = render_to_string('admin/core/parcel/parcel_list_print.html', {'parcels': queryset, 'is_pdf': True})
html = weasyprint.HTML(string=html_string, base_url=request.build_absolute_uri())
result = html.write_pdf()
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="parcels_list.pdf"'
response.write(result)
return response
export_pdf.short_description = _("Download Selected as PDF")
class PlatformProfileAdmin(admin.ModelAdmin):
fieldsets = (
(_('General Info'), {
@ -260,4 +282,4 @@ class NotificationTemplateAdmin(admin.ModelAdmin):
def has_delete_permission(self, request, obj=None):
return False
admin.site.register(NotificationTemplate, NotificationTemplateAdmin)
admin.site.register(NotificationTemplate, NotificationTemplateAdmin)

View File

@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Parcel List</title>
<style>
body { font-family: sans-serif; font-size: 12px; }
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
th, td { border: 1px solid #ddd; padding: 6px; text-align: left; }
th { background-color: #f2f2f2; }
.header { text-align: center; margin-bottom: 20px; }
.meta { margin-bottom: 10px; font-size: 10px; color: #666; }
@media print {
.no-print { display: none; }
table { font-size: 10px; }
}
</style>
</head>
<body>
<div class="header">
<h1>Parcel List</h1>
<p class="meta">Generated on: {% now "Y-m-d H:i" %}</p>
</div>
{% if not is_pdf %}
<div class="no-print" style="margin-bottom: 20px; text-align: center;">
<button onclick="window.print()" style="padding: 10px 20px; font-size: 16px; cursor: pointer;">Print List</button>
<button onclick="window.history.back()" style="padding: 10px 20px; font-size: 16px; cursor: pointer; margin-left: 10px;">Back</button>
</div>
{% endif %}
<table>
<thead>
<tr>
<th>Tracking #</th>
<th>Shipper</th>
<th>Carrier</th>
<th>Receiver</th>
<th>From</th>
<th>To</th>
<th>Status</th>
<th>Price</th>
<th>Created At</th>
</tr>
</thead>
<tbody>
{% for parcel in parcels %}
<tr>
<td>{{ parcel.tracking_number }}</td>
<td>{{ parcel.shipper.username|default:"-" }}</td>
<td>{{ parcel.carrier.username|default:"-" }}</td>
<td>{{ parcel.receiver_name }}</td>
<td>{{ parcel.pickup_city.name_en|default:"-" }}</td>
<td>{{ parcel.delivery_city.name_en|default:"-" }}</td>
<td>{{ parcel.get_status_display }}</td>
<td>{{ parcel.price }}</td>
<td>{{ parcel.created_at|date:"Y-m-d H:i" }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>

16
entrypoint.sh Normal file
View File

@ -0,0 +1,16 @@
#!/bin/sh
# Exit immediately if a command exits with a non-zero status
set -e
# Apply database migrations
echo "Applying database migrations..."
python manage.py migrate
# Collect static files
echo "Collecting static files..."
python manage.py collectstatic --noinput
# Start Gunicorn
echo "Starting Gunicorn..."
exec gunicorn config.wsgi:application --bind 0.0.0.0:8000 --workers 3

View File

@ -7,3 +7,6 @@ qrcode
django-jazzmin==3.0.1
djangorestframework==3.15.1
drf-yasg
gunicorn==22.0.0
django-cors-headers
django-admin-rangefilter