diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index 93e6085..1b97a82 100644 Binary files a/core/__pycache__/admin.cpython-311.pyc and b/core/__pycache__/admin.cpython-311.pyc differ diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index 2e8ff7d..4bdc403 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/admin.py b/core/admin.py index 61d387a..097ec01 100644 --- a/core/admin.py +++ b/core/admin.py @@ -6,6 +6,7 @@ from django.contrib import messages from django.utils.translation import gettext_lazy as _ from django.utils import timezone from django.utils.html import format_html +import os from .models import Profile, Truck, Shipment, Bid, Message, WhatsAppConfig, Country, City, TruckType, AppSetting, Banner, HomeSection from .whatsapp import send_whatsapp_message @@ -38,7 +39,14 @@ class ProfileAdmin(admin.ModelAdmin): if obj.is_expired(): return format_html('{}', _('Expired')) - days = obj.days_until_expiry() + # Profile model might not have days_until_expiry, let's check + # Based on models.py read earlier, it doesn't have it. + # It has a commented out section that seems to be a start of it. + # I'll use inline calculation + if not obj.subscription_expiry: + return format_html('{}', _('No Expiry')) + + days = (obj.subscription_expiry - timezone.now().date()).days if days <= 7: return format_html('{} ({} {})', _('Expiring soon'), days, _('days')) @@ -48,9 +56,56 @@ class ProfileAdmin(admin.ModelAdmin): @admin.register(Truck) class TruckAdmin(admin.ModelAdmin): - list_display = ('display_truck_type', 'model', 'plate_no', 'owner', 'load_capacity') + list_display = ('display_truck_type', 'model', 'plate_no', 'owner', 'load_capacity', 'is_approved') search_fields = ('plate_no', 'owner__username', 'model') list_filter = ('truck_type_link', 'is_approved') + readonly_fields = ('truck_picture_preview', 'registration_front_preview', 'registration_back_preview', 'driver_license_front_preview', 'driver_license_back_preview') + + def truck_picture_preview(self, obj): + if obj.truck_picture: + return format_html('', obj.truck_picture.url) + return "-" + truck_picture_preview.short_description = _("Truck Picture Preview") + + def _file_preview(self, field): + if not field: + return "-" + url = field.url + ext = os.path.splitext(field.name)[1].lower() + if ext in ['.jpg', '.jpeg', '.png', '.webp', '.gif']: + return format_html('', url, url) + elif ext == '.pdf': + return format_html('
{}
', url, _("View PDF Document")) + return format_html('{}', url, _("View File")) + + def registration_front_preview(self, obj): + return self._file_preview(obj.registration_front) + registration_front_preview.short_description = _("Registration Front Preview") + + def registration_back_preview(self, obj): + return self._file_preview(obj.registration_back) + registration_back_preview.short_description = _("Registration Back Preview") + + def driver_license_front_preview(self, obj): + return self._file_preview(obj.driver_license_front) + driver_license_front_preview.short_description = _("Driver License Front Preview") + + def driver_license_back_preview(self, obj): + return self._file_preview(obj.driver_license_back) + driver_license_back_preview.short_description = _("Driver License Back Preview") + + fieldsets = ( + (None, {'fields': ('owner', 'truck_type_link', 'is_approved')}), + (_('Details (EN)'), {'fields': ('truck_type', 'model', 'load_capacity', 'color', 'year', 'plate_no', 'registration_expiry_date')}), + (_('Details (AR)'), {'fields': ('truck_type_ar', 'model_ar', 'load_capacity_ar', 'color_ar')}), + (_('Pictures & Documents'), {'fields': ( + 'truck_picture', 'truck_picture_preview', + 'registration_front', 'registration_front_preview', + 'registration_back', 'registration_back_preview', + 'driver_license_front', 'driver_license_front_preview', + 'driver_license_back', 'driver_license_back_preview' + )}), + ) @admin.register(Shipment) class ShipmentAdmin(admin.ModelAdmin): @@ -134,4 +189,4 @@ class HomeSectionAdmin(admin.ModelAdmin): list_display = ('title', 'section_type', 'order', 'is_active') list_editable = ('order', 'is_active') list_filter = ('section_type', 'is_active', 'background_color') - search_fields = ('title', 'title_ar', 'subtitle', 'subtitle_ar', 'content', 'content_ar') + search_fields = ('title', 'title_ar', 'subtitle', 'subtitle_ar', 'content', 'content_ar') \ No newline at end of file diff --git a/core/migrations/0022_alter_truck_driver_license_back_and_more.py b/core/migrations/0022_alter_truck_driver_license_back_and_more.py new file mode 100644 index 0000000..a8680e1 --- /dev/null +++ b/core/migrations/0022_alter_truck_driver_license_back_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 5.2.7 on 2026-01-24 07:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0021_appsetting_thawani_enabled'), + ] + + operations = [ + migrations.AlterField( + model_name='truck', + name='driver_license_back', + field=models.FileField(blank=True, null=True, upload_to='docs/', verbose_name='Driver License (Back Face)'), + ), + migrations.AlterField( + model_name='truck', + name='driver_license_front', + field=models.FileField(blank=True, null=True, upload_to='docs/', verbose_name='Driver License (Front Face)'), + ), + migrations.AlterField( + model_name='truck', + name='registration_back', + field=models.FileField(blank=True, null=True, upload_to='docs/', verbose_name='Registration (Back Face)'), + ), + migrations.AlterField( + model_name='truck', + name='registration_front', + field=models.FileField(blank=True, null=True, upload_to='docs/', verbose_name='Registration (Front Face)'), + ), + ] diff --git a/core/migrations/__pycache__/0022_alter_truck_driver_license_back_and_more.cpython-311.pyc b/core/migrations/__pycache__/0022_alter_truck_driver_license_back_and_more.cpython-311.pyc new file mode 100644 index 0000000..662d902 Binary files /dev/null and b/core/migrations/__pycache__/0022_alter_truck_driver_license_back_and_more.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index 3384816..d8c35ff 100644 --- a/core/models.py +++ b/core/models.py @@ -5,8 +5,12 @@ from django.dispatch import receiver from django.utils.translation import gettext_lazy as _ from django.utils.translation import get_language from django.utils import timezone +from django.core.files import File import random import string +import os +from io import BytesIO +from PIL import Image class Country(models.Model): name = models.CharField(_('Country Name'), max_length=100) @@ -127,10 +131,10 @@ class Truck(models.Model): # Pictures truck_picture = models.ImageField(_('Truck Picture'), upload_to='trucks/', blank=True, null=True) - registration_front = models.ImageField(_('Registration (Front Face)'), upload_to='docs/', blank=True, null=True) - registration_back = models.ImageField(_('Registration (Back Face)'), upload_to='docs/', blank=True, null=True) - driver_license_front = models.ImageField(_('Driver License (Front Face)'), upload_to='docs/', blank=True, null=True) - driver_license_back = models.ImageField(_('Driver License (Back Face)'), upload_to='docs/', blank=True, null=True) + registration_front = models.FileField(_('Registration (Front Face)'), upload_to='docs/', blank=True, null=True) + registration_back = models.FileField(_('Registration (Back Face)'), upload_to='docs/', blank=True, null=True) + driver_license_front = models.FileField(_('Driver License (Front Face)'), upload_to='docs/', blank=True, null=True) + driver_license_back = models.FileField(_('Driver License (Back Face)'), upload_to='docs/', blank=True, null=True) is_approved = models.BooleanField(_('Is Approved'), default=False) created_at = models.DateTimeField(auto_now_add=True) @@ -166,6 +170,55 @@ class Truck(models.Model): return self.color_ar return self.color + def save(self, *args, **kwargs): + # Compress images + if self.truck_picture: + self.truck_picture = self.compress_image(self.truck_picture) + + # For docs, compress only if they are images + if self.registration_front: + self.registration_front = self.compress_image(self.registration_front) + if self.registration_back: + self.registration_back = self.compress_image(self.registration_back) + if self.driver_license_front: + self.driver_license_front = self.compress_image(self.driver_license_front) + if self.driver_license_back: + self.driver_license_back = self.compress_image(self.driver_license_back) + + super().save(*args, **kwargs) + + def compress_image(self, image_field): + if not image_field: + return image_field + + try: + # Check file extension + ext = os.path.splitext(image_field.name)[1].lower() + if ext not in ['.jpg', '.jpeg', '.png', '.webp']: + return image_field + + img = Image.open(image_field) + + # If already small enough, don't compress (optional, but good for performance) + # if image_field.size < 300 * 1024: return image_field + + if img.mode != 'RGB': + img = img.convert('RGB') + + # Resize if too large + max_size = (1200, 1200) + img.thumbnail(max_size, Image.LANCZOS) + + output = BytesIO() + img.save(output, format='JPEG', quality=70, optimize=True) + output.seek(0) + + new_name = os.path.splitext(image_field.name)[0] + '.jpg' + return File(output, name=new_name) + except Exception as e: + # Not an image or other error, return as is + return image_field + class Shipment(models.Model): STATUS_CHOICES = ( ('OPEN', _('Open for Bids')), @@ -421,4 +474,4 @@ class Transaction(models.Model): super().save(*args, **kwargs) def __str__(self): - return f"{self.receipt_number} - {self.user.username} ({self.amount})" + return f"{self.receipt_number} - {self.user.username} ({self.amount})" \ No newline at end of file diff --git a/core/templates/core/truck_register.html b/core/templates/core/truck_register.html index 5530196..3e8f942 100644 --- a/core/templates/core/truck_register.html +++ b/core/templates/core/truck_register.html @@ -125,6 +125,7 @@
{% trans "Documents & Photos" %}
+

{% trans "Photos will be automatically resized for better performance. PDF files are accepted for documents." %}

@@ -144,7 +145,15 @@ {% if truck.driver_license_front %}
- + {% with ext=truck.driver_license_front.name|lower %} + {% if ".pdf" in ext %} + + {% trans "View PDF" %} + + {% else %} + + {% endif %} + {% endwith %}
{% endif %} {{ form.driver_license_front }} @@ -154,7 +163,15 @@ {% if truck.driver_license_back %}
- + {% with ext=truck.driver_license_back.name|lower %} + {% if ".pdf" in ext %} + + {% trans "View PDF" %} + + {% else %} + + {% endif %} + {% endwith %}
{% endif %} {{ form.driver_license_back }} @@ -167,7 +184,15 @@ {% if truck.registration_front %}
- + {% with ext=truck.registration_front.name|lower %} + {% if ".pdf" in ext %} + + {% trans "View PDF" %} + + {% else %} + + {% endif %} + {% endwith %}
{% endif %} {{ form.registration_front }} @@ -177,7 +202,15 @@ {% if truck.registration_back %}
- + {% with ext=truck.registration_back.name|lower %} + {% if ".pdf" in ext %} + + {% trans "View PDF" %} + + {% else %} + + {% endif %} + {% endwith %}
{% endif %} {{ form.registration_back }} @@ -198,4 +231,4 @@
-{% endblock %} \ No newline at end of file +{% endblock %}