diff --git a/.DS_Store b/.DS_Store index ba81c8bd..6964535e 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/haikalbot/migrations/0001_initial.py b/haikalbot/migrations/0001_initial.py deleted file mode 100644 index 9f36e19a..00000000 --- a/haikalbot/migrations/0001_initial.py +++ /dev/null @@ -1,46 +0,0 @@ -# Generated by Django 5.2.4 on 2025-07-22 08:37 - -import django.db.models.deletion -import django.utils.timezone -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='ChatLog', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('user_message', models.TextField()), - ('chatbot_response', models.TextField()), - ('timestamp', models.DateTimeField(auto_now_add=True, db_index=True)), - ], - options={ - 'ordering': ['-timestamp'], - }, - ), - migrations.CreateModel( - name='AnalysisCache', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('prompt_hash', models.CharField(db_index=True, max_length=64)), - ('dealer_id', models.IntegerField(blank=True, db_index=True, null=True)), - ('created_at', models.DateTimeField(default=django.utils.timezone.now)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('expires_at', models.DateTimeField(db_index=True)), - ('result', models.JSONField()), - ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - options={ - 'verbose_name_plural': 'Analysis caches', - }, - ), - ] diff --git a/haikalbot/migrations/0002_initial.py b/haikalbot/migrations/0002_initial.py deleted file mode 100644 index cee8307b..00000000 --- a/haikalbot/migrations/0002_initial.py +++ /dev/null @@ -1,34 +0,0 @@ -# Generated by Django 5.2.4 on 2025-07-22 08:37 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('haikalbot', '0001_initial'), - ('inventory', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='chatlog', - name='dealer', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='chatlogs', to='inventory.dealer'), - ), - migrations.AddIndex( - model_name='analysiscache', - index=models.Index(fields=['prompt_hash', 'dealer_id'], name='haikalbot_a_prompt__b98e1e_idx'), - ), - migrations.AddIndex( - model_name='analysiscache', - index=models.Index(fields=['expires_at'], name='haikalbot_a_expires_e790cd_idx'), - ), - migrations.AddIndex( - model_name='chatlog', - index=models.Index(fields=['dealer', 'timestamp'], name='haikalbot_c_dealer__6f8d63_idx'), - ), - ] diff --git a/inventory/management/commands/db2json.py b/inventory/management/commands/db2json.py index cfb514c6..8bac680e 100644 --- a/inventory/management/commands/db2json.py +++ b/inventory/management/commands/db2json.py @@ -10,7 +10,7 @@ db_config = { "host": "localhost", "user": "root", "password": "Kfsh&rc9788", - "database": "car2db_june", + "database": "car2db_20250701", } EXCLUDED_TABLES = {"car_serie", "car_generation"} # Tables to exclude from direct dump diff --git a/inventory/middleware.py b/inventory/middleware.py index dda82b5f..c42a6132 100644 --- a/inventory/middleware.py +++ b/inventory/middleware.py @@ -1,6 +1,8 @@ import logging import time +from django.http import Http404 + # from django.http import Http404, HttpResponseForbidden # from django.shortcuts import redirect # from inventory import models diff --git a/inventory/migrations/0001_initial.py b/inventory/migrations/0001_initial.py deleted file mode 100644 index daaff725..00000000 --- a/inventory/migrations/0001_initial.py +++ /dev/null @@ -1,1435 +0,0 @@ -# Generated by Django 5.2.4 on 2025-07-22 08:37 - -import datetime -import django.core.serializers.json -import django.core.validators -import django.db.models.deletion -import django.utils.timezone -import inventory.mixins -import inventory.models -import phonenumber_field.modelfields -import uuid -from decimal import Decimal -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('appointment', '0001_initial'), - ('auth', '0012_alter_user_first_name_max_length'), - ('contenttypes', '0002_remove_content_type_name'), - ('django_ledger', '0022_alter_billmodel_bill_items_and_more'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='CarEquipment', - fields=[ - ('id_car_equipment', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(blank=True, max_length=255, null=True)), - ('arabic_name', models.CharField(blank=True, max_length=255, null=True)), - ('year_begin', models.IntegerField(blank=True, null=True)), - ('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True)), - ], - options={ - 'verbose_name': 'Equipment', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.CreateModel( - name='CarMake', - fields=[ - ('id_car_make', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(blank=True, max_length=255, null=True)), - ('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True)), - ('arabic_name', models.CharField(blank=True, max_length=255, null=True)), - ('logo', models.ImageField(blank=True, default='user-logo.png', null=True, upload_to='car_make', verbose_name='logo')), - ('is_sa_import', models.BooleanField(default=False)), - ('car_type', models.SmallIntegerField(blank=True, choices=[(1, 'Car'), (2, 'Light Commercial'), (3, 'Heavy-Duty Tractors'), (4, 'Trailers'), (5, 'Medium Trucks'), (6, 'Buses'), (20, 'Motorcycles'), (21, 'Buggy'), (22, 'Moto ATV'), (23, 'Scooters'), (24, 'Karting'), (25, 'ATV'), (26, 'Snowmobiles')], null=True)), - ], - options={ - 'verbose_name': 'Make', - 'indexes': [models.Index(fields=['name'], name='car_make_name_idx'), models.Index(fields=['is_sa_import'], name='car_make_sa_import_idx'), models.Index(fields=['car_type'], name='car_make_type_idx')], - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.CreateModel( - name='CarModel', - fields=[ - ('id_car_model', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(blank=True, max_length=255, null=True)), - ('arabic_name', models.CharField(blank=True, max_length=255, null=True)), - ('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True)), - ('id_car_make', models.ForeignKey(db_column='id_car_make', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmake')), - ], - options={ - 'verbose_name': 'Model', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.CreateModel( - name='Car', - fields=[ - ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True, verbose_name='Primary Key')), - ('slug', models.SlugField(blank=True, help_text='Slug for the object. If not provided, it will be generated automatically.', null=True, unique=True, verbose_name='Slug')), - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated At')), - ('vin', models.CharField(max_length=17, unique=True, verbose_name='VIN')), - ('year', models.IntegerField(verbose_name='Year')), - ('status', models.CharField(choices=[('available', 'Available'), ('sold', 'Sold'), ('hold', 'Hold'), ('damaged', 'Damaged'), ('reserved', 'Reserved'), ('transfer', 'Transfer')], default='available', max_length=10, verbose_name='Status')), - ('stock_type', models.CharField(choices=[('new', 'New'), ('used', 'Used')], default='new', max_length=10, verbose_name='Stock Type')), - ('remarks', models.TextField(blank=True, null=True, verbose_name='Remarks')), - ('mileage', models.IntegerField(blank=True, null=True, verbose_name='Mileage')), - ('receiving_date', models.DateTimeField(verbose_name='Receiving Date')), - ('hash', models.CharField(blank=True, max_length=64, null=True, verbose_name='Hash')), - ('item_model', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='django_ledger.itemmodel', verbose_name='Item Model')), - ('id_car_make', models.ForeignKey(blank=True, db_column='id_car_make', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmake', verbose_name='Make')), - ('id_car_model', models.ForeignKey(blank=True, db_column='id_car_model', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel', verbose_name='Model')), - ], - options={ - 'verbose_name': 'Car', - 'verbose_name_plural': 'Cars', - }, - ), - migrations.CreateModel( - name='CarOption', - fields=[ - ('id_car_option', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(blank=True, max_length=255, null=True)), - ('arabic_name', models.CharField(blank=True, max_length=255, null=True)), - ('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True)), - ('id_parent', models.ForeignKey(blank=True, db_column='id_parent', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.caroption')), - ], - options={ - 'verbose_name': 'Option', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.CreateModel( - name='CarOptionValue', - fields=[ - ('id_car_option_value', models.AutoField(primary_key=True, serialize=False)), - ('value', models.CharField(max_length=500)), - ('unit', models.CharField(blank=True, max_length=255, null=True)), - ('is_base', models.IntegerField()), - ('id_car_equipment', models.ForeignKey(db_column='id_car_equipment', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carequipment')), - ('id_car_option', models.ForeignKey(db_column='id_car_option', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.caroption')), - ], - options={ - 'verbose_name': 'Option Value', - }, - ), - migrations.CreateModel( - name='CarRegistration', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('plate_number', models.IntegerField(verbose_name='Plate Number')), - ('text1', models.CharField(max_length=1, verbose_name='Text 1')), - ('text2', models.CharField(blank=True, max_length=1, null=True, verbose_name='Text 2')), - ('text3', models.CharField(blank=True, max_length=1, null=True, verbose_name='Text 3')), - ('registration_date', models.DateTimeField(verbose_name='Registration Date')), - ('car', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='registrations', to='inventory.car', verbose_name='Car')), - ], - options={ - 'verbose_name': 'Registration', - 'verbose_name_plural': 'Registrations', - }, - ), - migrations.CreateModel( - name='CarReservation', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('reserved_at', models.DateTimeField(auto_now_add=True, verbose_name='Reserved At')), - ('reserved_until', models.DateTimeField(verbose_name='Reserved Until')), - ('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reservations', to='inventory.car', verbose_name='Car')), - ('reserved_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reservations', to=settings.AUTH_USER_MODEL, verbose_name='Reserved By')), - ], - options={ - 'verbose_name': 'Car Reservation', - 'verbose_name_plural': 'Car Reservations', - 'ordering': ['-reserved_at'], - }, - ), - migrations.CreateModel( - name='CarSerie', - fields=[ - ('id_car_serie', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(blank=True, max_length=255, null=True)), - ('arabic_name', models.CharField(blank=True, max_length=255, null=True)), - ('year_begin', models.IntegerField(blank=True, null=True)), - ('year_end', models.IntegerField(blank=True, null=True)), - ('generation_name', models.CharField(blank=True, max_length=255, null=True)), - ('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True)), - ('id_car_model', models.ForeignKey(db_column='id_car_model', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel')), - ], - options={ - 'verbose_name': 'Series', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.AddField( - model_name='car', - name='id_car_serie', - field=models.ForeignKey(blank=True, db_column='id_car_serie', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carserie', verbose_name='Series'), - ), - migrations.CreateModel( - name='CarSpecification', - fields=[ - ('id_car_specification', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(max_length=255)), - ('arabic_name', models.CharField(max_length=255)), - ('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True)), - ('id_parent', models.ForeignKey(blank=True, db_column='id_parent', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carspecification')), - ], - options={ - 'verbose_name': 'Specification', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.CreateModel( - name='CarTrim', - fields=[ - ('id_car_trim', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(blank=True, max_length=255, null=True)), - ('arabic_name', models.CharField(blank=True, max_length=255, null=True)), - ('start_production_year', models.IntegerField(blank=True, null=True)), - ('end_production_year', models.IntegerField(blank=True, null=True)), - ('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True)), - ('id_car_serie', models.ForeignKey(db_column='id_car_serie', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carserie')), - ], - options={ - 'verbose_name': 'Trim', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.CreateModel( - name='CarSpecificationValue', - fields=[ - ('id_car_specification_value', models.AutoField(primary_key=True, serialize=False)), - ('value', models.CharField(max_length=500)), - ('unit', models.CharField(blank=True, max_length=255, null=True)), - ('id_car_specification', models.ForeignKey(db_column='id_car_specification', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carspecification')), - ('id_car_trim', models.ForeignKey(db_column='id_car_trim', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.cartrim')), - ], - options={ - 'verbose_name': 'Specification Value', - }, - ), - migrations.AddField( - model_name='carequipment', - name='id_car_trim', - field=models.ForeignKey(db_column='id_car_trim', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.cartrim'), - ), - migrations.AddField( - model_name='car', - name='id_car_trim', - field=models.ForeignKey(blank=True, db_column='id_car_trim', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.cartrim', verbose_name='Trim'), - ), - migrations.CreateModel( - name='CustomCard', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('custom_number', models.CharField(max_length=255, verbose_name='Custom Number')), - ('custom_date', models.DateField(verbose_name='Custom Date')), - ('car', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='custom_cards', to='inventory.car', verbose_name='Car')), - ], - options={ - 'verbose_name': 'Custom Card', - 'verbose_name_plural': 'Custom Cards', - }, - ), - migrations.CreateModel( - name='Dealer', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('crn', models.CharField(blank=True, max_length=10, null=True, verbose_name='Commercial Registration Number')), - ('vrn', models.CharField(blank=True, max_length=15, null=True, verbose_name='VAT Registration Number')), - ('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')), - ('name', models.CharField(max_length=255, verbose_name='English Name')), - ('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')), - ('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')), - ('logo', models.ImageField(blank=True, default='user-logo.png', null=True, upload_to='logos/users', verbose_name='Logo')), - ('joined_at', models.DateTimeField(auto_now_add=True, verbose_name='Joined At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated At')), - ('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True)), - ('entity', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='django_ledger.entitymodel')), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='dealer', to=settings.AUTH_USER_MODEL)), - ], - options={ - 'verbose_name': 'Dealer', - 'verbose_name_plural': 'Dealers', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - managers=[ - ('objects', inventory.models.DealerUserManager()), - ], - ), - migrations.CreateModel( - name='CustomGroup', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=100)), - ('group', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='auth.group', verbose_name='Group')), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='groups', to='inventory.dealer')), - ], - options={ - 'verbose_name': 'Custom Group', - 'verbose_name_plural': 'Custom Groups', - }, - ), - migrations.CreateModel( - name='Customer', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.CharField(choices=[('mr', 'Mr'), ('mrs', 'Mrs'), ('ms', 'Ms'), ('miss', 'Miss'), ('dr', 'Dr'), ('prof', 'Prof'), ('prince', 'Prince'), ('princess', 'Princess'), ('company', 'Company'), ('na', 'N/A')], default='na', max_length=10, verbose_name='Title')), - ('first_name', models.CharField(max_length=50, verbose_name='First Name')), - ('last_name', models.CharField(max_length=50, verbose_name='Last Name')), - ('gender', models.CharField(choices=[('m', 'Male'), ('f', 'Female')], max_length=1, verbose_name='Gender')), - ('dob', models.DateField(blank=True, null=True, verbose_name='Date of Birth')), - ('email', models.EmailField(max_length=254, unique=True, verbose_name='Email')), - ('national_id', models.CharField(blank=True, max_length=10, null=True, unique=True, verbose_name='National ID')), - ('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', unique=True, verbose_name='Phone Number')), - ('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')), - ('active', models.BooleanField(default=True, verbose_name='Active')), - ('image', models.ImageField(blank=True, null=True, upload_to='customers/', verbose_name='Image')), - ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')), - ('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')), - ('slug', models.SlugField(blank=True, editable=False, max_length=255, null=True, unique=True)), - ('customer_model', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='django_ledger.customermodel')), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='customer_profile', to=settings.AUTH_USER_MODEL)), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='customers', to='inventory.dealer')), - ], - options={ - 'verbose_name': 'Customer', - 'verbose_name_plural': 'Customers', - }, - ), - migrations.CreateModel( - name='CarTransfer', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('transfer_date', models.DateTimeField(auto_now_add=True, verbose_name='Transfer Date')), - ('quantity', models.IntegerField(default=1, verbose_name='Quantity')), - ('remarks', models.TextField(blank=True, null=True, verbose_name='Remarks')), - ('status', models.CharField(default='draft', max_length=10, verbose_name=[('draft', 'Draft'), ('approved', 'Approved'), ('pending', 'Pending'), ('accepted', 'Accepted'), ('success', 'Success'), ('reject', 'Reject'), ('cancelled', 'Cancelled')])), - ('is_approved', models.BooleanField(default=False)), - ('active', models.BooleanField(default=True)), - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated At')), - ('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transfer_logs', to='inventory.car', verbose_name='Car')), - ('from_dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transfers_out', to='inventory.dealer', verbose_name='From Dealer')), - ('to_dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transfers_in', to='inventory.dealer', verbose_name='To Dealer')), - ], - options={ - 'verbose_name': 'Car Transfer Log', - 'verbose_name_plural': 'Car Transfer Logs', - }, - ), - migrations.CreateModel( - name='CarLocation', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('description', models.TextField(blank=True, help_text='Optional description about the showroom placement.', null=True, verbose_name='Description')), - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Updated')), - ('car', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='location', to='inventory.car', verbose_name='Car')), - ('owner', models.ForeignKey(help_text='Dealer who owns the car.', on_delete=django.db.models.deletion.CASCADE, related_name='owned_cars', to='inventory.dealer', verbose_name='Owner')), - ('showroom', models.ForeignKey(help_text='Dealer where the car is displayed (can be the owner).', on_delete=django.db.models.deletion.CASCADE, related_name='showroom_cars', to='inventory.dealer', verbose_name='Showroom')), - ], - options={ - 'verbose_name': 'Car Location', - 'verbose_name_plural': 'Car Locations', - }, - ), - migrations.AddField( - model_name='car', - name='dealer', - field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='cars', to='inventory.dealer', verbose_name='Dealer'), - ), - migrations.CreateModel( - name='AdditionalServices', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='Name')), - ('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')), - ('description', models.TextField(verbose_name='Description')), - ('price', models.DecimalField(decimal_places=2, max_digits=14, verbose_name='Price')), - ('taxable', models.BooleanField(default=False, verbose_name='taxable')), - ('uom', models.CharField(choices=[('EA', 'Each'), ('PR', 'Pair'), ('SET', 'Set'), ('GAL', 'Gallon'), ('L', 'Liter'), ('M', 'Meter'), ('KG', 'Kilogram'), ('HR', 'Hour'), ('BX', 'Box'), ('RL', 'Roll'), ('PKG', 'Package'), ('DZ', 'Dozen'), ('SQ_M', 'Square Meter'), ('PC', 'Piece'), ('BDL', 'Bundle')], max_length=10, verbose_name='Unit of Measurement')), - ('item', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='django_ledger.itemmodel', verbose_name='Item')), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.dealer', verbose_name='Dealer')), - ], - options={ - 'verbose_name': 'Additional Services', - 'verbose_name_plural': 'Additional Services', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.CreateModel( - name='Activity', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('object_id', models.PositiveIntegerField()), - ('activity_type', models.CharField(choices=[('call', 'Call'), ('sms', 'SMS'), ('email', 'Email'), ('meeting', 'Meeting'), ('whatsapp', 'WhatsApp'), ('visit', 'Visit'), ('negotiation', 'Negotiation'), ('follow_up', 'Follow Up'), ('won', 'Won'), ('lost', 'Lost'), ('closed', 'Closed'), ('converted', 'Converted'), ('transfer', 'Transfer'), ('add_car', 'Add Car'), ('sale_car', 'Sale Car'), ('reserve_car', 'Reserve Car'), ('transfer_car', 'Transfer Car'), ('remove_car', 'Remove Car'), ('create_quotation', 'Create Quotation'), ('cancel_quotation', 'Cancel Quotation'), ('create_order', 'Create Order'), ('cancel_order', 'Cancel Order'), ('create_invoice', 'Create Invoice'), ('cancel_invoice', 'Cancel Invoice')], max_length=50, verbose_name='Activity Type')), - ('notes', models.TextField(blank=True, null=True, verbose_name='Notes')), - ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')), - ('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')), - ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='contenttypes.contenttype')), - ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='activities_created_by', to=settings.AUTH_USER_MODEL)), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='activities', to='inventory.dealer')), - ], - options={ - 'verbose_name': 'Activity', - 'verbose_name_plural': 'Activities', - }, - ), - migrations.CreateModel( - name='DealerSettings', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('additional_info', models.JSONField(blank=True, default=dict, null=True)), - ('bill_cash_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bill_cash', to='django_ledger.accountmodel')), - ('bill_prepaid_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bill_prepaid', to='django_ledger.accountmodel')), - ('bill_unearned_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bill_unearned', to='django_ledger.accountmodel')), - ('dealer', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='settings', to='inventory.dealer')), - ('invoice_cash_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_cash', to='django_ledger.accountmodel')), - ('invoice_prepaid_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_prepaid', to='django_ledger.accountmodel')), - ('invoice_unearned_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_unearned', to='django_ledger.accountmodel')), - ], - ), - migrations.CreateModel( - name='DealersMake', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('added_at', models.DateTimeField(auto_now_add=True)), - ('car_make', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='car_dealers', to='inventory.carmake')), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='dealer_makes', to='inventory.dealer')), - ], - ), - migrations.CreateModel( - name='Email', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('object_id', models.UUIDField()), - ('from_email', models.TextField(blank=True, null=True, verbose_name='From Email')), - ('to_email', models.TextField(blank=True, null=True, verbose_name='To Email')), - ('subject', models.TextField(blank=True, null=True, verbose_name='Subject')), - ('message', models.TextField(blank=True, null=True, verbose_name='Message')), - ('status', models.CharField(choices=[('SENT', 'Sent'), ('FAILED', 'Failed'), ('DELIVERED', 'Delivered'), ('OPEN', 'Open'), ('DRAFT', 'Draft')], default='OPEN', max_length=20, verbose_name='Status')), - ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')), - ('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')), - ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')), - ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='emails_created', to=settings.AUTH_USER_MODEL)), - ], - options={ - 'verbose_name': 'Email', - 'verbose_name_plural': 'Emails', - }, - ), - migrations.CreateModel( - name='ExteriorColors', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='Name')), - ('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')), - ('rgb', models.CharField(blank=True, max_length=24, null=True, verbose_name='RGB')), - ], - options={ - 'verbose_name': 'Exterior Colors', - 'verbose_name_plural': 'Exterior Colors', - 'indexes': [models.Index(fields=['name'], name='exterior_color_name_idx'), models.Index(fields=['arabic_name'], name='exterior_color_arabic_name_idx')], - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.CreateModel( - name='ExtraInfo', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('object_id', models.CharField(blank=True, max_length=255, null=True)), - ('related_object_id', models.CharField(blank=True, max_length=255, null=True)), - ('data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='extra_info_primary', to='contenttypes.contenttype')), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_extra_info', to=settings.AUTH_USER_MODEL)), - ('dealer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='extra_info', to='inventory.dealer')), - ('related_content_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='extra_info_secondary', to='contenttypes.contenttype')), - ], - options={ - 'verbose_name': 'Extra Info', - 'verbose_name_plural': 'Extra Info', - }, - ), - migrations.CreateModel( - name='InteriorColors', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='Name')), - ('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')), - ('rgb', models.CharField(blank=True, max_length=24, null=True, verbose_name='RGB')), - ], - options={ - 'verbose_name': 'Interior Colors', - 'verbose_name_plural': 'Interior Colors', - 'indexes': [models.Index(fields=['name'], name='interior_color_name_idx'), models.Index(fields=['arabic_name'], name='interior_color_arabic_name_idx')], - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.CreateModel( - name='CarColors', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('car', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='colors', to='inventory.car')), - ('exterior', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='colors', to='inventory.exteriorcolors')), - ('interior', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='colors', to='inventory.interiorcolors')), - ], - options={ - 'verbose_name': 'Color', - 'verbose_name_plural': 'Colors', - }, - ), - migrations.CreateModel( - name='Lead', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('first_name', models.CharField(max_length=50, verbose_name='First Name')), - ('last_name', models.CharField(max_length=50, verbose_name='Last Name')), - ('email', models.EmailField(max_length=254, verbose_name='Email')), - ('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')), - ('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')), - ('lead_type', models.CharField(choices=[('customer', 'Customer'), ('organization', 'Organization')], default='customer', max_length=50, verbose_name='Lead Type')), - ('source', models.CharField(choices=[('referrals', 'Referrals'), ('whatsapp', 'WhatsApp'), ('showroom', 'Showroom'), ('tiktok', 'TikTok'), ('instagram', 'Instagram'), ('x', 'X'), ('facebook', 'Facebook'), ('motory', 'Motory'), ('influencers', 'Influencers'), ('youtube', 'Youtube'), ('campaign', 'Campaign')], max_length=50, verbose_name='Source')), - ('channel', models.CharField(choices=[('walk_in', 'Walk In'), ('toll_free', 'Toll Free'), ('website', 'Website'), ('email', 'Email'), ('form', 'Form')], max_length=50, verbose_name='Channel')), - ('status', models.CharField(choices=[('new', 'New'), ('contacted', 'Contacted'), ('qualified', 'Qualified'), ('unqualified', 'Unqualified'), ('converted', 'Converted')], db_index=True, default='new', max_length=50, verbose_name='Status')), - ('next_action', models.CharField(blank=True, max_length=255, null=True, verbose_name='Next Action')), - ('next_action_date', models.DateTimeField(blank=True, null=True, verbose_name='Next Action Date')), - ('is_converted', models.BooleanField(default=False)), - ('converted_at', models.DateTimeField(blank=True, null=True)), - ('created', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='Created')), - ('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')), - ('slug', models.SlugField(blank=True, null=True, unique=True)), - ('customer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='customer_leads', to='inventory.customer')), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='leads', to='inventory.dealer')), - ('id_car_make', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmake', verbose_name='Make')), - ('id_car_model', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel', verbose_name='Model')), - ], - options={ - 'verbose_name': 'Lead', - 'verbose_name_plural': 'Leads', - }, - ), - migrations.CreateModel( - name='Notes', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('object_id', models.UUIDField()), - ('note', models.TextField(verbose_name='Note')), - ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')), - ('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')), - ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')), - ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='notes_created', to=settings.AUTH_USER_MODEL)), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='inventory.dealer')), - ], - options={ - 'verbose_name': 'Note', - 'verbose_name_plural': 'Notes', - }, - ), - migrations.CreateModel( - name='Notification', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('message', models.CharField(max_length=255, verbose_name='Message')), - ('is_read', models.BooleanField(default=False, verbose_name='Is Read')), - ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notifications', to=settings.AUTH_USER_MODEL)), - ], - options={ - 'verbose_name': 'Notification', - 'verbose_name_plural': 'Notifications', - 'ordering': ['-created'], - }, - ), - migrations.CreateModel( - name='Organization', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='Name')), - ('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')), - ('crn', models.CharField(max_length=15, verbose_name='Commercial Registration Number')), - ('vrn', models.CharField(max_length=15, verbose_name='VAT Registration Number')), - ('email', models.EmailField(max_length=254, verbose_name='Email')), - ('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')), - ('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')), - ('logo', models.ImageField(blank=True, default='user-logo.png', null=True, upload_to='logos', verbose_name='Logo')), - ('active', models.BooleanField(default=True, verbose_name='Active')), - ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')), - ('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')), - ('slug', models.SlugField(blank=True, editable=False, max_length=255, null=True, unique=True)), - ('customer_model', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='django_ledger.customermodel')), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='organizations', to='inventory.dealer')), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='organization_profile', to=settings.AUTH_USER_MODEL)), - ], - options={ - 'verbose_name': 'Organization', - 'verbose_name_plural': 'Organizations', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.CreateModel( - name='Opportunity', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('crn', models.CharField(blank=True, max_length=20, null=True, verbose_name='CRN')), - ('vrn', models.CharField(blank=True, max_length=20, null=True, verbose_name='VRN')), - ('salary', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='Salary')), - ('priority', models.CharField(choices=[('high', 'High'), ('medium', 'Medium'), ('low', 'Low')], default='medium', max_length=20, verbose_name='Priority')), - ('stage', models.CharField(choices=[('qualification', 'Qualification'), ('test_drive', 'Test Drive'), ('quotation', 'Quotation'), ('negotiation', 'Negotiation'), ('financing', 'Financing'), ('closed_won', 'Closed Won'), ('closed_lost', 'Closed Lost'), ('on_hold', 'On Hold')], max_length=20, verbose_name='Stage')), - ('probability', models.PositiveIntegerField(validators=[inventory.models.validate_probability])), - ('amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Amount')), - ('expected_revenue', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='Expected Revenue')), - ('vehicle_of_interest_make', models.CharField(blank=True, max_length=50, null=True)), - ('vehicle_of_interest_model', models.CharField(blank=True, max_length=100, null=True)), - ('expected_close_date', models.DateField(blank=True, null=True)), - ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')), - ('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')), - ('slug', models.SlugField(blank=True, help_text='Unique slug for the opportunity.', null=True, unique=True, verbose_name='Slug')), - ('loss_reason', models.CharField(blank=True, max_length=255, null=True)), - ('car', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='inventory.car', verbose_name='Car')), - ('customer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='opportunities', to='inventory.customer')), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='opportunities', to='inventory.dealer')), - ('estimate', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='opportunity', to='django_ledger.estimatemodel')), - ('lead', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='opportunity', to='inventory.lead')), - ('organization', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='inventory.organization', verbose_name='Organization')), - ], - options={ - 'verbose_name': 'Opportunity', - 'verbose_name_plural': 'Opportunities', - }, - ), - migrations.AddField( - model_name='lead', - name='organization', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='organization_leads', to='inventory.organization'), - ), - migrations.CreateModel( - name='Payment', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='amount')), - ('payment_method', models.CharField(choices=[('cash', 'cash'), ('credit', 'credit'), ('transfer', 'transfer'), ('debit', 'debit'), ('sadad', 'SADAD')], max_length=50, verbose_name='method')), - ('reference_number', models.CharField(blank=True, max_length=100, null=True, verbose_name='reference number')), - ('payment_date', models.DateField(auto_now_add=True, verbose_name='date')), - ('invoice', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='payments', to='django_ledger.invoicemodel', verbose_name='invoice')), - ], - options={ - 'verbose_name': 'payment', - 'verbose_name_plural': 'payments', - }, - ), - migrations.CreateModel( - name='PaymentHistory', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('user_data', models.JSONField(blank=True, null=True)), - ('amount', models.DecimalField(decimal_places=2, max_digits=10, validators=[django.core.validators.MinValueValidator(0.01)])), - ('currency', models.CharField(default='SAR', max_length=3)), - ('payment_date', models.DateTimeField(default=django.utils.timezone.now)), - ('status', models.CharField(choices=[('initiated', 'initiated'), ('pending', 'Pending'), ('completed', 'Completed'), ('paid', 'Paid'), ('failed', 'Failed'), ('refunded', 'Refunded'), ('cancelled', 'Cancelled')], default='pending', max_length=10)), - ('payment_method', models.CharField(choices=[('credit_card', 'Credit Card'), ('debit_card', 'Debit Card'), ('paypal', 'PayPal'), ('bank_transfer', 'Bank Transfer'), ('crypto', 'Cryptocurrency'), ('other', 'Other')], max_length=20)), - ('transaction_id', models.CharField(blank=True, max_length=100, null=True, unique=True)), - ('invoice_number', models.CharField(blank=True, max_length=50, null=True)), - ('order_reference', models.CharField(blank=True, max_length=100, null=True)), - ('gateway_response', models.JSONField(blank=True, null=True)), - ('gateway_name', models.CharField(blank=True, max_length=50, null=True)), - ('description', models.TextField(blank=True, null=True)), - ('is_recurring', models.BooleanField(default=False)), - ('billing_email', models.EmailField(blank=True, max_length=254, null=True)), - ('billing_address', models.TextField(blank=True, null=True)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to=settings.AUTH_USER_MODEL)), - ], - options={ - 'verbose_name': 'Payment History', - 'verbose_name_plural': 'Payment Histories', - 'ordering': ['-payment_date'], - }, - ), - migrations.CreateModel( - name='PoItemsUploaded', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('status', models.CharField(blank=True, max_length=100, null=True)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('dealer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='inventory.dealer')), - ('item', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='po_items', to='django_ledger.itemtransactionmodel')), - ('po', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='items', to='django_ledger.purchaseordermodel')), - ], - options={ - 'verbose_name': 'PO Items', - 'verbose_name_plural': 'PO Items', - }, - ), - migrations.CreateModel( - name='Refund', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='amount')), - ('reason', models.TextField(blank=True, verbose_name='reason')), - ('refund_date', models.DateField(auto_now_add=True, verbose_name='refund date')), - ('payment', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='refund', to='inventory.payment')), - ], - options={ - 'verbose_name': 'refund', - 'verbose_name_plural': 'refunds', - }, - ), - migrations.CreateModel( - name='Representative', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='Name')), - ('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')), - ('id_number', models.CharField(max_length=10, unique=True, verbose_name='ID Number')), - ('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')), - ('email', models.EmailField(max_length=255, verbose_name='Email Address')), - ('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='representatives', to='inventory.dealer')), - ('organization', models.ManyToManyField(related_name='representatives', to='inventory.organization')), - ], - options={ - 'verbose_name': 'Representative', - 'verbose_name_plural': 'Representatives', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.CreateModel( - name='SaleOrder', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('comments', models.TextField(blank=True, null=True)), - ('formatted_order_id', models.CharField(editable=False, max_length=10, unique=True)), - ('status', models.CharField(choices=[('PENDING_APPROVAL', 'Pending Approval'), ('APPROVED', 'Approved'), ('IN_FINANCING', 'In Financing'), ('PARTIALLY_PAID', 'Partially Paid'), ('FULLY_PAID', 'Fully Paid'), ('PENDING_DELIVERY', 'Pending Delivery'), ('DELIVERED', 'Delivered'), ('CANCELLED', 'Cancelled')], default='PENDING_APPROVAL', help_text='Current status of the sales order.', max_length=20)), - ('order_date', models.DateTimeField(default=django.utils.timezone.now, help_text='The date and time the sales order was created.')), - ('expected_delivery_date', models.DateField(blank=True, help_text='The planned date for vehicle delivery.', null=True)), - ('actual_delivery_date', models.DateTimeField(blank=True, help_text='The actual date and time the vehicle was delivered.', null=True)), - ('cancelled_date', models.DateTimeField(blank=True, help_text='The date and time the order was cancelled, if applicable.', null=True)), - ('cancellation_reason', models.TextField(blank=True, help_text='Reason for cancellation, if applicable.', null=True)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('created_by', models.ForeignKey(blank=True, help_text='The user who created this sales order.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_sales_orders', to=settings.AUTH_USER_MODEL)), - ('customer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='sale_orders', to='inventory.customer', verbose_name='Customer')), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sale_orders', to='inventory.dealer')), - ('estimate', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='sale_orders', to='django_ledger.estimatemodel', verbose_name='Estimate')), - ('invoice', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='sale_orders', to='django_ledger.invoicemodel', verbose_name='Invoice')), - ('last_modified_by', models.ForeignKey(blank=True, help_text='The user who last modified this sales order.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='modified_sales_orders', to=settings.AUTH_USER_MODEL)), - ('opportunity', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='sale_orders', to='inventory.opportunity', verbose_name='Opportunity')), - ], - options={ - 'verbose_name': 'Sales Order', - 'verbose_name_plural': 'Sales Orders', - 'ordering': ['-order_date'], - }, - ), - migrations.CreateModel( - name='Schedule', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('object_id', models.PositiveIntegerField()), - ('purpose', models.CharField(choices=[('product_demo', 'Product Demo'), ('follow_up_call', 'Follow-Up Call'), ('contract_discussion', 'Contract Discussion'), ('sales_meeting', 'Sales Meeting'), ('support_call', 'Support Call'), ('other', 'Other')], max_length=200)), - ('scheduled_at', models.DateTimeField()), - ('scheduled_type', models.CharField(choices=[('call', 'Call'), ('meeting', 'Meeting'), ('email', 'Email')], default='Call', max_length=200)), - ('completed', models.BooleanField(default=False, verbose_name='Completed')), - ('duration', models.DurationField(default=datetime.timedelta(seconds=300))), - ('notes', models.TextField(blank=True, null=True)), - ('status', models.CharField(choices=[('scheduled', 'Scheduled'), ('completed', 'Completed'), ('canceled', 'Canceled')], default='Scheduled', max_length=200)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')), - ('customer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='schedules', to='django_ledger.customermodel')), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.dealer')), - ('scheduled_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - options={ - 'verbose_name': 'Schedule', - 'verbose_name_plural': 'Schedules', - 'ordering': ['-scheduled_at'], - }, - ), - migrations.CreateModel( - name='Staff', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='Name')), - ('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')), - ('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')), - ('staff_type', models.CharField(choices=[('inventory', 'Inventory'), ('accountant', 'Accountant'), ('sales', 'Sales')], max_length=255, verbose_name='Staff Type')), - ('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')), - ('logo', models.ImageField(blank=True, default='user-logo.png', null=True, upload_to='logos/staff', verbose_name='Image')), - ('active', models.BooleanField(default=True, verbose_name='Active')), - ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')), - ('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')), - ('slug', models.SlugField(blank=True, editable=False, max_length=255, null=True, unique=True)), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='staff', to='inventory.dealer')), - ('staff_member', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='staff', to='appointment.staffmember')), - ], - options={ - 'verbose_name': 'Staff', - 'verbose_name_plural': 'Staff', - 'permissions': [], - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - managers=[ - ('objects', inventory.models.StaffUserManager()), - ], - ), - migrations.AddField( - model_name='opportunity', - name='staff', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='owner', to='inventory.staff', verbose_name='Owner'), - ), - migrations.CreateModel( - name='LeadStatusHistory', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('old_status', models.CharField(choices=[('new', 'New'), ('contacted', 'Contacted'), ('qualified', 'Qualified'), ('unqualified', 'Unqualified'), ('converted', 'Converted')], max_length=50, verbose_name='Old Status')), - ('new_status', models.CharField(choices=[('new', 'New'), ('contacted', 'Contacted'), ('qualified', 'Qualified'), ('unqualified', 'Unqualified'), ('converted', 'Converted')], max_length=50, verbose_name='New Status')), - ('changed_at', models.DateTimeField(auto_now_add=True, verbose_name='Changed At')), - ('lead', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='status_history', to='inventory.lead')), - ('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='status_changes', to='inventory.staff')), - ], - options={ - 'verbose_name': 'Lead Status History', - 'verbose_name_plural': 'Lead Status Histories', - }, - ), - migrations.AddField( - model_name='lead', - name='staff', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assigned', to='inventory.staff', verbose_name='Assigned'), - ), - migrations.CreateModel( - name='Tasks', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('object_id', models.UUIDField()), - ('title', models.CharField(max_length=255, verbose_name='Title')), - ('description', models.TextField(blank=True, null=True, verbose_name='Description')), - ('due_date', models.DateField(verbose_name='Due Date')), - ('completed', models.BooleanField(default=False, verbose_name='Completed')), - ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')), - ('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')), - ('assigned_to', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='tasks_assigned', to=settings.AUTH_USER_MODEL)), - ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')), - ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='tasks_created', to=settings.AUTH_USER_MODEL)), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tasks', to='inventory.dealer')), - ], - options={ - 'verbose_name': 'Task', - 'verbose_name_plural': 'Tasks', - }, - ), - migrations.CreateModel( - name='UserActivityLog', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('action', models.TextField()), - ('timestamp', models.DateTimeField(auto_now_add=True)), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - options={ - 'verbose_name': 'User Activity Log', - 'verbose_name_plural': 'User Activity Logs', - 'ordering': ['-timestamp'], - }, - ), - migrations.CreateModel( - name='VatRate', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('rate', models.DecimalField(decimal_places=2, default=Decimal('0.15'), max_digits=5)), - ('is_active', models.BooleanField(default=True)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.dealer')), - ], - ), - migrations.CreateModel( - name='Vendor', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('crn', models.CharField(max_length=10, unique=True, verbose_name='Commercial Registration Number')), - ('vrn', models.CharField(max_length=15, unique=True, verbose_name='VAT Registration Number')), - ('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')), - ('name', models.CharField(max_length=255, verbose_name='English Name')), - ('contact_person', models.CharField(max_length=100, verbose_name='Contact Person')), - ('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')), - ('email', models.EmailField(max_length=255, verbose_name='Email Address')), - ('address', models.CharField(max_length=200, verbose_name='Address')), - ('logo', models.ImageField(blank=True, default='user-logo.png', null=True, upload_to='logos/vendors', verbose_name='Logo')), - ('active', models.BooleanField(default=True, verbose_name='Active')), - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True, verbose_name='Slug')), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vendors', to='inventory.dealer')), - ('vendor_model', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='django_ledger.vendormodel', verbose_name='Vendor Model')), - ], - options={ - 'verbose_name': 'Vendor', - 'verbose_name_plural': 'Vendors', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.AddField( - model_name='car', - name='vendor', - field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='cars', to='inventory.vendor', verbose_name='Vendor'), - ), - migrations.CreateModel( - name='CarFinance', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('cost_price', models.DecimalField(decimal_places=2, max_digits=14, verbose_name='Cost Price')), - ('selling_price', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Selling Price')), - ('marked_price', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Marked Price')), - ('discount_amount', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Discount Amount')), - ('is_sold', models.BooleanField(default=False)), - ('additional_services', models.ManyToManyField(blank=True, related_name='additional_finances', to='inventory.additionalservices')), - ('car', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='finances', to='inventory.car')), - ], - options={ - 'verbose_name': 'Car Financial Details', - 'verbose_name_plural': 'Car Financial Details', - 'indexes': [models.Index(fields=['car'], name='car_finance_car_idx'), models.Index(fields=['cost_price'], name='car_finance_cost_price_idx'), models.Index(fields=['selling_price'], name='car_finance_selling_price_idx'), models.Index(fields=['marked_price'], name='car_finance_marked_price_idx'), models.Index(fields=['discount_amount'], name='car_finance_discount_idx')], - }, - ), - migrations.AddIndex( - model_name='carmodel', - index=models.Index(fields=['id_car_make'], name='car_model_make_idx'), - ), - migrations.AddIndex( - model_name='carmodel', - index=models.Index(fields=['name'], name='car_model_name_idx'), - ), - migrations.AddIndex( - model_name='carmodel', - index=models.Index(fields=['id_car_make', 'name'], name='car_model_make_name_idx'), - ), - migrations.AddIndex( - model_name='caroption', - index=models.Index(fields=['id_parent'], name='car_option_parent_idx'), - ), - migrations.AddIndex( - model_name='caroption', - index=models.Index(fields=['name'], name='car_option_name_idx'), - ), - migrations.AddIndex( - model_name='caroptionvalue', - index=models.Index(fields=['id_car_option'], name='car_opt_val_option_idx'), - ), - migrations.AddIndex( - model_name='caroptionvalue', - index=models.Index(fields=['id_car_equipment'], name='car_opt_val_equipment_idx'), - ), - migrations.AddIndex( - model_name='caroptionvalue', - index=models.Index(fields=['is_base'], name='car_opt_val_is_base_idx'), - ), - migrations.AddIndex( - model_name='caroptionvalue', - index=models.Index(fields=['id_car_option', 'id_car_equipment'], name='cov_option_equipment_idx'), - ), - migrations.AlterUniqueTogether( - name='carreservation', - unique_together={('car', 'reserved_until')}, - ), - migrations.AddIndex( - model_name='carserie', - index=models.Index(fields=['id_car_model'], name='car_serie_model_idx'), - ), - migrations.AddIndex( - model_name='carserie', - index=models.Index(fields=['year_begin', 'year_end'], name='car_serie_years_idx'), - ), - migrations.AddIndex( - model_name='carserie', - index=models.Index(fields=['name'], name='car_serie_name_idx'), - ), - migrations.AddIndex( - model_name='carserie', - index=models.Index(fields=['generation_name'], name='car_serie_generation_idx'), - ), - migrations.AddIndex( - model_name='carspecification', - index=models.Index(fields=['id_parent'], name='car_spec_parent_idx'), - ), - migrations.AddIndex( - model_name='carspecification', - index=models.Index(fields=['name'], name='car_spec_name_idx'), - ), - migrations.AddIndex( - model_name='cartrim', - index=models.Index(fields=['id_car_serie'], name='car_trim_serie_idx'), - ), - migrations.AddIndex( - model_name='cartrim', - index=models.Index(fields=['start_production_year', 'end_production_year'], name='car_trim_prod_years_idx'), - ), - migrations.AddIndex( - model_name='cartrim', - index=models.Index(fields=['name'], name='car_trim_name_idx'), - ), - migrations.AddIndex( - model_name='carspecificationvalue', - index=models.Index(fields=['id_car_trim'], name='car_spec_val_trim_idx'), - ), - migrations.AddIndex( - model_name='carspecificationvalue', - index=models.Index(fields=['id_car_specification'], name='car_spec_val_spec_idx'), - ), - migrations.AddIndex( - model_name='carspecificationvalue', - index=models.Index(fields=['id_car_trim', 'id_car_specification'], name='car_spec_val_trim_spec_idx'), - ), - migrations.AddIndex( - model_name='carequipment', - index=models.Index(fields=['id_car_trim'], name='car_equipment_trim_idx'), - ), - migrations.AddIndex( - model_name='carequipment', - index=models.Index(fields=['year_begin'], name='car_equipment_year_idx'), - ), - migrations.AddIndex( - model_name='carequipment', - index=models.Index(fields=['name'], name='car_equipment_name_idx'), - ), - migrations.AddIndex( - model_name='dealer', - index=models.Index(fields=['name'], name='inventory_d_name_c4ba31_idx'), - ), - migrations.AddIndex( - model_name='customgroup', - index=models.Index(fields=['name'], name='inventory_c_name_65f272_idx'), - ), - migrations.AddIndex( - model_name='customgroup', - index=models.Index(fields=['dealer'], name='inventory_c_dealer__313a5a_idx'), - ), - migrations.AddIndex( - model_name='customgroup', - index=models.Index(fields=['group'], name='inventory_c_group_i_5f1074_idx'), - ), - migrations.AddIndex( - model_name='customer', - index=models.Index(fields=['title'], name='inventory_c_title_f01e78_idx'), - ), - migrations.AddIndex( - model_name='customer', - index=models.Index(fields=['first_name'], name='inventory_c_first_n_fd5078_idx'), - ), - migrations.AddIndex( - model_name='customer', - index=models.Index(fields=['last_name'], name='inventory_c_last_na_4bf5bd_idx'), - ), - migrations.AddIndex( - model_name='customer', - index=models.Index(fields=['email'], name='inventory_c_email_c8fd29_idx'), - ), - migrations.AddIndex( - model_name='customer', - index=models.Index(fields=['phone_number'], name='inventory_c_phone_n_1a4571_idx'), - ), - migrations.AddIndex( - model_name='activity', - index=models.Index(fields=['created_by'], name='activity_created_by_idx'), - ), - migrations.AddIndex( - model_name='activity', - index=models.Index(fields=['content_type'], name='activity_content_type_idx'), - ), - migrations.AddIndex( - model_name='activity', - index=models.Index(fields=['content_type', 'object_id'], name='activity_content_object_idx'), - ), - migrations.AddIndex( - model_name='activity', - index=models.Index(fields=['created'], name='activity_created_date_idx'), - ), - migrations.AddIndex( - model_name='activity', - index=models.Index(fields=['updated'], name='activity_updated_date_idx'), - ), - migrations.AddIndex( - model_name='activity', - index=models.Index(fields=['content_type', 'object_id', 'created'], name='a_content_obj_created_idx'), - ), - migrations.AlterUniqueTogether( - name='dealersmake', - unique_together={('dealer', 'car_make')}, - ), - migrations.AddIndex( - model_name='email', - index=models.Index(fields=['created_by'], name='email_created_by_idx'), - ), - migrations.AddIndex( - model_name='email', - index=models.Index(fields=['content_type'], name='email_content_type_idx'), - ), - migrations.AddIndex( - model_name='email', - index=models.Index(fields=['content_type', 'object_id'], name='email_content_object_idx'), - ), - migrations.AddIndex( - model_name='email', - index=models.Index(fields=['created'], name='email_created_date_idx'), - ), - migrations.AddIndex( - model_name='email', - index=models.Index(fields=['updated'], name='email_updated_date_idx'), - ), - migrations.AddIndex( - model_name='email', - index=models.Index(fields=['content_type', 'object_id', 'created'], name='email_content_obj_created_idx'), - ), - migrations.AddIndex( - model_name='extrainfo', - index=models.Index(fields=['content_type', 'object_id'], name='inventory_e_content_2ecbed_idx'), - ), - migrations.AddIndex( - model_name='extrainfo', - index=models.Index(fields=['related_content_type', 'related_object_id'], name='inventory_e_related_8680bb_idx'), - ), - migrations.AddIndex( - model_name='carcolors', - index=models.Index(fields=['exterior'], name='car_colors_exterior_idx'), - ), - migrations.AddIndex( - model_name='carcolors', - index=models.Index(fields=['interior'], name='car_colors_interior_idx'), - ), - migrations.AddIndex( - model_name='carcolors', - index=models.Index(fields=['exterior', 'interior'], name='car_colors_ext_int_combo_idx'), - ), - migrations.AlterUniqueTogether( - name='carcolors', - unique_together={('car', 'exterior', 'interior')}, - ), - migrations.AddIndex( - model_name='notes', - index=models.Index(fields=['dealer'], name='note_dealer_idx'), - ), - migrations.AddIndex( - model_name='notes', - index=models.Index(fields=['created_by'], name='note_created_by_idx'), - ), - migrations.AddIndex( - model_name='notes', - index=models.Index(fields=['content_type'], name='note_content_type_idx'), - ), - migrations.AddIndex( - model_name='notes', - index=models.Index(fields=['content_type', 'object_id'], name='note_content_object_idx'), - ), - migrations.AddIndex( - model_name='notes', - index=models.Index(fields=['created'], name='note_created_date_idx'), - ), - migrations.AddIndex( - model_name='notes', - index=models.Index(fields=['updated'], name='note_updated_date_idx'), - ), - migrations.AddIndex( - model_name='notes', - index=models.Index(fields=['dealer', 'created'], name='note_dealer_created_idx'), - ), - migrations.AddIndex( - model_name='notes', - index=models.Index(fields=['content_type', 'object_id', 'created'], name='note_content_obj_created_idx'), - ), - migrations.AddIndex( - model_name='notification', - index=models.Index(fields=['user'], name='notification_user_idx'), - ), - migrations.AddIndex( - model_name='notification', - index=models.Index(fields=['is_read'], name='notification_is_read_idx'), - ), - migrations.AddIndex( - model_name='notification', - index=models.Index(fields=['created'], name='notification_created_date_idx'), - ), - migrations.AddIndex( - model_name='organization', - index=models.Index(fields=['name'], name='inventory_o_name_cc18e2_idx'), - ), - migrations.AddIndex( - model_name='organization', - index=models.Index(fields=['email'], name='inventory_o_email_d6e7dd_idx'), - ), - migrations.AddIndex( - model_name='organization', - index=models.Index(fields=['phone_number'], name='inventory_o_phone_n_7cb3d4_idx'), - ), - migrations.AddIndex( - model_name='paymenthistory', - index=models.Index(fields=['transaction_id'], name='inventory_p_transac_9469f3_idx'), - ), - migrations.AddIndex( - model_name='paymenthistory', - index=models.Index(fields=['user'], name='inventory_p_user_id_c31626_idx'), - ), - migrations.AddIndex( - model_name='paymenthistory', - index=models.Index(fields=['status'], name='inventory_p_status_abcb77_idx'), - ), - migrations.AddIndex( - model_name='paymenthistory', - index=models.Index(fields=['payment_date'], name='inventory_p_payment_b3068c_idx'), - ), - migrations.AddIndex( - model_name='poitemsuploaded', - index=models.Index(fields=['po'], name='inventory_p_po_id_762198_idx'), - ), - migrations.AddIndex( - model_name='poitemsuploaded', - index=models.Index(fields=['item'], name='inventory_p_item_id_6dae83_idx'), - ), - migrations.AddIndex( - model_name='saleorder', - index=models.Index(fields=['dealer'], name='inventory_s_dealer__9eeebf_idx'), - ), - migrations.AddIndex( - model_name='saleorder', - index=models.Index(fields=['estimate'], name='inventory_s_estimat_82cc8d_idx'), - ), - migrations.AddIndex( - model_name='saleorder', - index=models.Index(fields=['invoice'], name='inventory_s_invoice_972b40_idx'), - ), - migrations.AddIndex( - model_name='saleorder', - index=models.Index(fields=['opportunity'], name='inventory_s_opportu_e6990d_idx'), - ), - migrations.AddIndex( - model_name='saleorder', - index=models.Index(fields=['customer'], name='inventory_s_custome_d75f5f_idx'), - ), - migrations.AddIndex( - model_name='saleorder', - index=models.Index(fields=['status'], name='inventory_s_status_34054d_idx'), - ), - migrations.AddIndex( - model_name='saleorder', - index=models.Index(fields=['order_date'], name='inventory_s_order_d_24f877_idx'), - ), - migrations.AddIndex( - model_name='saleorder', - index=models.Index(fields=['expected_delivery_date'], name='inventory_s_expecte_6facc1_idx'), - ), - migrations.AddIndex( - model_name='saleorder', - index=models.Index(fields=['actual_delivery_date'], name='inventory_s_actual__bb0b80_idx'), - ), - migrations.AddIndex( - model_name='saleorder', - index=models.Index(fields=['cancelled_date'], name='inventory_s_cancell_cf8528_idx'), - ), - migrations.AddIndex( - model_name='schedule', - index=models.Index(fields=['dealer'], name='inventory_s_dealer__879e62_idx'), - ), - migrations.AddIndex( - model_name='schedule', - index=models.Index(fields=['customer'], name='inventory_s_custome_19a62e_idx'), - ), - migrations.AddIndex( - model_name='schedule', - index=models.Index(fields=['content_type', 'object_id'], name='inventory_s_content_d3912b_idx'), - ), - migrations.AddIndex( - model_name='schedule', - index=models.Index(fields=['scheduled_at'], name='inventory_s_schedul_04d173_idx'), - ), - migrations.AddIndex( - model_name='staff', - index=models.Index(fields=['name'], name='inventory_s_name_da615c_idx'), - ), - migrations.AddIndex( - model_name='staff', - index=models.Index(fields=['staff_type'], name='inventory_s_staff_t_680ea0_idx'), - ), - migrations.AddIndex( - model_name='opportunity', - index=models.Index(fields=['dealer'], name='inventory_o_dealer__74a272_idx'), - ), - migrations.AddIndex( - model_name='opportunity', - index=models.Index(fields=['customer'], name='inventory_o_custome_dffe96_idx'), - ), - migrations.AddIndex( - model_name='opportunity', - index=models.Index(fields=['car'], name='inventory_o_car_id_9b4f5f_idx'), - ), - migrations.AddIndex( - model_name='opportunity', - index=models.Index(fields=['lead'], name='inventory_o_lead_id_7b2f47_idx'), - ), - migrations.AddIndex( - model_name='opportunity', - index=models.Index(fields=['organization'], name='inventory_o_organiz_82d31b_idx'), - ), - migrations.AddIndex( - model_name='opportunity', - index=models.Index(fields=['created'], name='inventory_o_created_124e5d_idx'), - ), - migrations.AddIndex( - model_name='lead', - index=models.Index(fields=['dealer'], name='inventory_l_dealer__ce2dfd_idx'), - ), - migrations.AddIndex( - model_name='lead', - index=models.Index(fields=['customer'], name='inventory_l_custome_b7dfe3_idx'), - ), - migrations.AddIndex( - model_name='lead', - index=models.Index(fields=['organization'], name='inventory_l_organiz_990f08_idx'), - ), - migrations.AddIndex( - model_name='lead', - index=models.Index(fields=['staff'], name='inventory_l_staff_i_01ba72_idx'), - ), - migrations.AddIndex( - model_name='lead', - index=models.Index(fields=['first_name'], name='inventory_l_first_n_77bb1b_idx'), - ), - migrations.AddIndex( - model_name='lead', - index=models.Index(fields=['last_name'], name='inventory_l_last_na_730f4a_idx'), - ), - migrations.AddIndex( - model_name='lead', - index=models.Index(fields=['email'], name='inventory_l_email_e502f1_idx'), - ), - migrations.AddIndex( - model_name='lead', - index=models.Index(fields=['phone_number'], name='inventory_l_phone_n_d9dab8_idx'), - ), - migrations.AddIndex( - model_name='lead', - index=models.Index(fields=['created'], name='inventory_l_created_24d483_idx'), - ), - migrations.AddIndex( - model_name='tasks', - index=models.Index(fields=['dealer'], name='task_dealer_idx'), - ), - migrations.AddIndex( - model_name='tasks', - index=models.Index(fields=['created_by'], name='task_created_by_idx'), - ), - migrations.AddIndex( - model_name='tasks', - index=models.Index(fields=['content_type'], name='task_content_type_idx'), - ), - migrations.AddIndex( - model_name='tasks', - index=models.Index(fields=['content_type', 'object_id'], name='task_content_object_idx'), - ), - migrations.AddIndex( - model_name='tasks', - index=models.Index(fields=['created'], name='task_created_date_idx'), - ), - migrations.AddIndex( - model_name='tasks', - index=models.Index(fields=['updated'], name='task_updated_date_idx'), - ), - migrations.AddIndex( - model_name='tasks', - index=models.Index(fields=['dealer', 'created'], name='task_dealer_created_idx'), - ), - migrations.AddIndex( - model_name='tasks', - index=models.Index(fields=['content_type', 'object_id', 'created'], name='task_content_obj_created_idx'), - ), - migrations.AddIndex( - model_name='vendor', - index=models.Index(fields=['slug'], name='vendor_slug_idx'), - ), - migrations.AddIndex( - model_name='vendor', - index=models.Index(fields=['active'], name='vendor_active_idx'), - ), - migrations.AddIndex( - model_name='vendor', - index=models.Index(fields=['crn'], name='vendor_crn_idx'), - ), - migrations.AddIndex( - model_name='vendor', - index=models.Index(fields=['vrn'], name='vendor_vrn_idx'), - ), - migrations.AddIndex( - model_name='car', - index=models.Index(fields=['vin'], name='car_vin_idx'), - ), - migrations.AddIndex( - model_name='car', - index=models.Index(fields=['year'], name='car_year_idx'), - ), - migrations.AddIndex( - model_name='car', - index=models.Index(fields=['status'], name='car_status_idx'), - ), - migrations.AddIndex( - model_name='car', - index=models.Index(fields=['dealer'], name='car_dealer_idx'), - ), - migrations.AddIndex( - model_name='car', - index=models.Index(fields=['vendor'], name='car_vendor_idx'), - ), - migrations.AddIndex( - model_name='car', - index=models.Index(fields=['id_car_make'], name='car_make_idx'), - ), - migrations.AddIndex( - model_name='car', - index=models.Index(fields=['id_car_model'], name='car_model_idx'), - ), - migrations.AddIndex( - model_name='car', - index=models.Index(fields=['id_car_serie'], name='car_serie_idx'), - ), - migrations.AddIndex( - model_name='car', - index=models.Index(fields=['id_car_trim'], name='car_trim_idx'), - ), - migrations.AddIndex( - model_name='car', - index=models.Index(fields=['id_car_make', 'id_car_model'], name='car_make_model_idx'), - ), - migrations.AddIndex( - model_name='car', - index=models.Index(fields=['id_car_make', 'year'], name='car_make_year_idx'), - ), - migrations.AddIndex( - model_name='car', - index=models.Index(fields=['dealer', 'status'], name='car_dealer_status_idx'), - ), - migrations.AddIndex( - model_name='car', - index=models.Index(fields=['vendor', 'status'], name='car_vendor_status_idx'), - ), - migrations.AddIndex( - model_name='car', - index=models.Index(fields=['year', 'status'], name='car_year_status_idx'), - ), - migrations.AddIndex( - model_name='car', - index=models.Index(condition=models.Q(('status', 'available')), fields=['status'], name='car_active_status_idx'), - ), - ] diff --git a/load_initial_data_marwan.sh b/load_initial_data_marwan.sh index d1d35bcd..82a53075 100755 --- a/load_initial_data_marwan.sh +++ b/load_initial_data_marwan.sh @@ -2,36 +2,38 @@ echo "Loading initial data" echo "Loading carmake" -python manage.py loaddata carmake_with_slugs.json +python manage.py loaddata carmake_7.json echo "Loading carmodel" -python manage.py loaddata carmodel_with_slugs.json +python manage.py loaddata carmodel_7.json echo "Loading carserie" -python manage.py loaddata carserie_with_slugs.json +python manage.py loaddata carserie_7.json echo "Loading cartrim" -python manage.py loaddata cartrim_with_slugs.json +python manage.py loaddata cartrim_7.json echo "Loading carequipment" -python manage.py loaddata carequipment_with_slugs.json +python manage.py loaddata carequipment_7.json echo "Loading carspecification" -python manage.py loaddata carspecification.json +python manage.py loaddata carspecification_7.json echo "Loading carspecificationvalue" -python manage.py loaddata carspecificationvalue.json +python manage.py loaddata carspecificationvalue_7.json echo "Loading caroption" -python manage.py loaddata caroption_with_slugs.json +python manage.py loaddata caroption_7.json echo "Loading caroptionvalue" -python manage.py loaddata caroptionvalue.json - +python manage.py loaddata caroptionvalue_7.json echo "Populating colors" python manage.py populate_colors +echo "Loading Cars" +python manage.py loaddata car_7.json + echo "Populating Plans" python manage.py loaddata plan.json python manage.py loaddata pricing.json diff --git a/load_json_data.py b/load_json_data.py index 1c54bd75..a112824d 100644 --- a/load_json_data.py +++ b/load_json_data.py @@ -1,6 +1,8 @@ import os import django import json + +from django.utils.text import slugify from tqdm import tqdm os.environ.setdefault("DJANGO_SETTINGS_MODULE", "car_inventory.settings") @@ -24,9 +26,19 @@ def run(): data = json.load(file) print("Starting data loading...") + def generate_unique_slug(model, field_value, slug_field="slug"): + base_slug = slugify(field_value) + slug = base_slug + counter = 1 + while model.objects.filter(**{slug_field: slug}).exists(): + slug = f"{base_slug}-{counter}" + counter += 1 + return slug + # Step 1: Insert CarMake for item in tqdm(data["car_make"], desc="Inserting CarMake"): if not CarMake.objects.filter(id_car_make=item["id_car_make"]).exists(): + unique_slug = generate_unique_slug(CarMake, item["name"]) CarMake.objects.create( id_car_make=item["id_car_make"], name=item["name"], @@ -34,6 +46,7 @@ def run(): # arabic_name=item.get("arabic_name", ""), # logo=item.get("Logo", ""), # is_sa_import=item.get("is_sa_import", False), + slug=unique_slug ) # Step 2: Insert CarModel @@ -41,11 +54,13 @@ def run(): if not CarModel.objects.filter(id_car_model=item["id_car_model"]).exists(): # Check if related make exists if CarMake.objects.filter(id_car_make=item["id_car_make"]).exists(): + unique_slug = generate_unique_slug(CarModel, item["name"]) CarModel.objects.create( id_car_model=item["id_car_model"], id_car_make_id=item["id_car_make"], name=item["name"], # arabic_name=item.get("arabic_name", ""), + slug=unique_slug ) # Step 3: Insert CarSerie @@ -53,6 +68,7 @@ def run(): if not CarSerie.objects.filter(id_car_serie=item["id_car_serie"]).exists(): # Check if related model exists if CarModel.objects.filter(id_car_model=item["id_car_model"]).exists(): + unique_slug = generate_unique_slug(CarSerie, item["name"]) CarSerie.objects.create( id_car_serie=item["id_car_serie"], id_car_model_id=item["id_car_model"], @@ -61,6 +77,7 @@ def run(): year_begin=item.get("year_begin"), year_end=item.get("year_end"), generation_name=item.get("generation_name", ""), + slug=unique_slug ) # Step 4: Insert CarTrim @@ -68,6 +85,7 @@ def run(): if not CarTrim.objects.filter(id_car_trim=item["id_car_trim"]).exists(): # Check if related serie exists if CarSerie.objects.filter(id_car_serie=item["id_car_serie"]).exists(): + unique_slug = generate_unique_slug(CarTrim, item["name"]) CarTrim.objects.create( id_car_trim=item["id_car_trim"], id_car_serie_id=item["id_car_serie"], @@ -75,20 +93,22 @@ def run(): # arabic_name=item.get("arabic_name", ""), start_production_year=item["start_production_year"], end_production_year=item["end_production_year"], + slug=unique_slug, ) # Step 5: Insert CarEquipment + + for item in tqdm(data["car_equipment"], desc="Inserting CarEquipment"): - if not CarEquipment.objects.filter( - id_car_equipment=item["id_car_equipment"] - ).exists(): - # Check if related trim exists + if not CarEquipment.objects.filter(id_car_equipment=item["id_car_equipment"]).exists(): if CarTrim.objects.filter(id_car_trim=item["id_car_trim"]).exists(): + unique_slug = generate_unique_slug(CarEquipment, item["name"]) CarEquipment.objects.create( id_car_equipment=item["id_car_equipment"], id_car_trim_id=item["id_car_trim"], name=item["name"], year_begin=item.get("year"), + slug=unique_slug ) # Step 6: Insert CarSpecification (Parent specifications first) diff --git a/pyproject.toml b/pyproject.toml index cd473fa7..d2ae781b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ dependencies = [ "cattrs>=25.1.1", "certifi>=2025.6.15", "cffi>=1.17.1", + "channels>=4.2.2", "chardet>=5.2.0", "charset-normalizer>=3.4.2", "click>=7.1.2", @@ -43,6 +44,7 @@ dependencies = [ "contourpy>=1.3.2", "crispy-bootstrap5>=2025.6", "cryptography>=45.0.4", + "cssbeautifier>=1.15.4", "cssselect2>=0.8.0", "ctranslate2>=4.6.0", "cycler>=0.12.1", @@ -57,6 +59,7 @@ dependencies = [ "dj-rest-auth>=7.0.1", "django>=5.2.3", "django-allauth>=65.9.0", + "django-appconf>=1.1.0", "django-appointment>=3.6.0", "django-autoslug>=1.9.9", "django-background-tasks>=1.2.8", @@ -71,9 +74,11 @@ dependencies = [ "django-extensions>=4.1", "django-filter>=25.1", "django-formtools>=2.5.1", + "django-imagekit>=5.0.0", "django-import-export>=4.3.8", "django-js-asset>=3.1.2", "django-ledger==0.7.8", + "django-manager-utils>=3.1.5", "django-model-utils>=5.0.0", "django-money>=3.5.4", "django-next-url-mixin>=0.4.0", @@ -86,6 +91,7 @@ dependencies = [ "django-plans>=2.0.0", "django-prometheus>=2.4.1", "django-q2>=1.8.0", + "django-query-builder>=3.2.0", "django-schema-graph>=3.1.0", "django-sekizai>=4.1.0", "django-sequences>=3.0", @@ -102,9 +108,11 @@ dependencies = [ "djangorestframework-simplejwt>=5.5.0", "djangoviz>=0.1.1", "djhtml>=3.0.8", + "djlint>=0.3.4", "docopt>=0.6.2", "docutils>=0.21.2", "easy-thumbnails>=2.10", + "editorconfig>=0.17.1", "emoji>=2.14.1", "et-xmlfile>=2.0.0", "eval-type-backport>=0.2.2", @@ -114,6 +122,7 @@ dependencies = [ "fastavro>=1.11.1", "filelock>=3.18.0", "fire>=0.7.0", + "fleming>=0.7.0", "fonttools>=4.58.4", "fpdf>=1.7.2", "fpdf2>=2.8.3", @@ -151,6 +160,8 @@ dependencies = [ "jiter>=0.10.0", "jmespath>=1.0.1", "joblib>=1.5.1", + "jsbeautifier>=1.15.4", + "json5>=0.12.0", "jsonpatch>=1.33", "jsonpointer>=3.0.0", "jwt>=1.4.0", @@ -208,8 +219,10 @@ dependencies = [ "packaging>=24.2", "pandas>=2.3.0", "pango>=0.0.1", + "pathspec>=0.12.1", "pdfkit>=1.0.0", "phonenumbers>=9.0.8", + "pilkit>=3.0", "pillow>=11.2.1", "platformdirs>=4.3.8", "prometheus-client>=0.22.1", @@ -283,6 +296,7 @@ dependencies = [ "rich>=14.0.0", "rsa>=4.9.1", "rubicon-objc>=0.5.1", + "ruff>=0.12.4", "s3transfer>=0.13.0", "sacremoses>=0.1.1", "safetensors>=0.5.3", @@ -292,6 +306,7 @@ dependencies = [ "selenium>=4.32.0", "sentence-transformers>=4.1.0", "sentencepiece>=0.2.0", + "setuptools>=80.9.0", "shapely>=2.1.1", "simsimd>=6.4.9", "six>=1.17.0", diff --git a/requirements.txt b/requirements.txt index 1b2bd360..9233b311 100644 --- a/requirements.txt +++ b/requirements.txt @@ -86,7 +86,7 @@ django-sequences django-silk django-simple-history django-sms -django-sslserver +django-sslserver-v2 django-tables2 django-treebeard django-view-breadcrumbs diff --git a/requirements_dev.txt b/requirements_dev.txt index 6e4c567b..bc9e9456 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -1,132 +1,132 @@ -annotated-types==0.7.0 -anyio==4.9.0 -arrow==1.3.0 -asgiref==3.9.1 -attrs==25.3.0 -Babel==2.15.0 -beautifulsoup4==4.13.4 -blessed==1.21.0 -cattrs==25.1.1 -certifi==2025.7.9 -cffi==1.17.1 -charset-normalizer==3.4.2 -click==8.2.1 -colorama==0.4.6 -crispy-bootstrap5==2025.6 -cryptography==45.0.5 -cssbeautifier==1.15.4 -defusedxml==0.7.1 -diff-match-patch==20241021 -distro==1.9.0 -Django==5.2.4 -django-allauth==65.10.0 -django-appconf==1.1.0 -django-appointment==3.8.0 -django-background-tasks==1.2.8 -django-bootstrap5==25.1 -django-ckeditor==6.7.3 -django-cors-headers==4.7.0 -django-countries==7.6.1 -django-crispy-forms==2.4 -django-debug-toolbar==5.2.0 -django-easy-audit==1.3.7 -django-extensions==4.1 -django-filter==25.1 -django-imagekit==5.0.0 -django-import-export==4.3.8 -django-js-asset==3.1.2 -django-ledger==0.7.6.1 -django-manager-utils==3.1.5 -django-next-url-mixin==0.4.0 -django-ordered-model==3.7.4 -django-phonenumber-field==8.0.0 -django-picklefield==3.3 -django-plans==2.0.0 -django-q2==1.8.0 -django-query-builder==3.2.0 -django-schema-graph==3.1.0 -django-sequences==3.0 -django-tables2==2.7.5 -django-treebeard==4.7.1 -django-widget-tweaks==1.5.0 -djangorestframework==3.16.0 -djhtml==3.0.8 -djlint==1.36.4 -docopt==0.6.2 -EditorConfig==0.17.1 -Faker==37.4.0 -fleming==0.7.0 -fonttools==4.58.5 -fpdf==1.7.2 -fpdf2==2.8.3 -greenlet==3.2.3 -h11==0.16.0 -httpcore==1.0.9 -httpx==0.28.1 -icalendar==6.3.1 -idna==3.10 -jiter==0.10.0 -jsbeautifier==1.15.4 -json5==0.12.0 -jsonpatch==1.33 -jsonpointer==3.0.0 -jwt==1.4.0 -langchain==0.3.26 -langchain-core==0.3.68 -langchain-ollama==0.3.4 -langchain-text-splitters==0.3.8 -langsmith==0.4.4 -luhnchecker==0.0.12 -Markdown==3.8.2 -markdown-it-py==3.0.0 -mdurl==0.1.2 -num2words==0.5.14 -numpy==2.3.1 -ofxtools==0.9.5 -ollama==0.5.1 -openai==1.93.3 -opencv-python==4.11.0.86 -orjson==3.10.18 -packaging==24.2 -pandas==2.3.1 -pathspec==0.12.1 -phonenumbers==8.13.42 -pilkit==3.0 -pillow==10.4.0 -psycopg2-binary==2.9.10 -pycparser==2.22 -pydantic==2.11.7 -pydantic_core==2.33.2 -Pygments==2.19.2 -python-dateutil==2.9.0.post0 -python-slugify==8.0.4 -python-stdnum==2.1 -pytz==2025.2 -pyvin==0.0.2 -PyYAML==6.0.2 -pyzbar==0.1.9 -redis==6.2.0 -regex==2024.11.6 -requests==2.32.4 -requests-toolbelt==1.0.0 -rich==14.0.0 -ruff==0.12.2 -setuptools==80.9.0 -six==1.17.0 -sniffio==1.3.1 -soupsieve==2.7 -SQLAlchemy==2.0.41 -sqlparse==0.5.3 -suds==1.2.0 -swapper==1.3.0 -tablib==3.8.0 -tenacity==9.1.2 -text-unidecode==1.3 -tqdm==4.67.1 -types-python-dateutil==2.9.0.20250708 -typing-inspection==0.4.1 -typing_extensions==4.14.1 -tzdata==2025.2 -urllib3==2.5.0 -wcwidth==0.2.13 -zstandard==0.23.0 +annotated-types +anyio +arrow +asgiref +attrs +Babel +beautifulsoup4 +blessed +cattrs +certifi +cffi +charset-normalizer +click +colorama +crispy-bootstrap5 +cryptography +cssbeautifier +defusedxml +diff-match-patch +distro +Django +django-allauth +django-appconf +django-appointment +django-background-tasks +django-bootstrap5 +django-ckeditor +django-cors-headers +django-countries +django-crispy-forms +django-debug-toolbar +django-easy-audit +django-extensions +django-filter +django-imagekit +django-import-export +django-js-asset +django-ledger +django-manager-utils +django-next-url-mixin +django-ordered-model +django-phonenumber-field +django-picklefield +django-plans +django-q2 +django-query-builder +django-schema-graph +django-sequences +django-tables2 +django-treebeard +django-widget-tweaks +djangorestframework +djhtml +djlint +docopt +EditorConfig +Faker +fleming +fonttools +fpdf +fpdf2 +greenlet +h11 +httpcore +httpx +icalendar +idna +jiter +jsbeautifier +json5 +jsonpatch +jsonpointer +jwt +langchain +langchain-core +langchain-ollama +langchain-text-splitters +langsmith +luhnchecker +Markdown +markdown-it-py +mdurl +num2words +numpy +ofxtools +ollama +openai +opencv-python +orjson +packaging +pandas +pathspec +phonenumbers +pilkit +pillow +psycopg2-binary +pycparser +pydantic +pydantic_core +Pygments +python-dateutil +python-slugify +python-stdnum +pytz +pyvin +PyYAML +pyzbar +redis +regex +requests +requests-toolbelt +rich +ruff +setuptools +six +sniffio +soupsieve +SQLAlchemy +sqlparse +suds +swapper +tablib +tenacity +text-unidecode +tqdm +types-python-dateutil +typing-inspection +typing_extensions +tzdata +urllib3 +wcwidth +zstandard diff --git a/staticfiles/import_export/export.css b/staticfiles/import_export/export.css new file mode 100644 index 00000000..4a42e789 --- /dev/null +++ b/staticfiles/import_export/export.css @@ -0,0 +1,7 @@ +.selectable-field-export-row { + padding-left: 10px; +} + +.selectable-field-export-row > label{ + padding-left: 5px; +} diff --git a/staticfiles/import_export/export_selectable_fields.js b/staticfiles/import_export/export_selectable_fields.js new file mode 100644 index 00000000..2662394f --- /dev/null +++ b/staticfiles/import_export/export_selectable_fields.js @@ -0,0 +1,63 @@ +function hideUnselectedResourceFields(selectedResourceIndex) { + const fields = document.querySelectorAll("[resource-index]"); + + fields.forEach((field) => { + if (field.getAttribute("resource-index") !== selectedResourceIndex.toString()) { + // field is wrapped by div, change visibility on wrapper + field.style.display = "none"; + } + }); +} + +function showSelectedResourceFields(resourceIndex) { + const fields = document.querySelectorAll(`[resource-index="${resourceIndex}"]`); + + fields.forEach((field) => { + // field is wrapped by div, change visibility on wrapper + field.style.display = "block"; + }); +} + +function onResourceSelected(e) { + const resourceIndex = e.target.value; + + showSelectedResourceFields(resourceIndex); + + hideUnselectedResourceFields(resourceIndex); +} + +function onSelectToggleChange(e) { + /* + * Handles a checkbox click event to select / deselect all field checkboxes. + */ + const select = e.target; + const isChecked = select.checked; + + if (isChecked) { + document.querySelectorAll('.selectable-field-export-row input[type="checkbox"]').forEach((checkbox) => { + checkbox.checked = true; + }); + } else { + document.querySelectorAll('.selectable-field-export-row input[type="checkbox"]').forEach((checkbox) => { + checkbox.checked = false; + }); + } +} + +document.addEventListener("DOMContentLoaded", () => { + const resourceSelector = document.querySelector("#id_resource"); + + if (!resourceSelector) { + console.error("resource select input not found"); + return; + } + + // If selector is actually select input, get selected option. + // else selected resource index is 0 + const selectedResourceIndex = resourceSelector.tagName === "SELECT" ? resourceSelector.value : 0; + + resourceSelector.addEventListener("input", onResourceSelected); + + // initially hide unselected resource fields + hideUnselectedResourceFields(selectedResourceIndex); +}); diff --git a/staticfiles/import_export/guess_format.js b/staticfiles/import_export/guess_format.js new file mode 100644 index 00000000..cd1635fc --- /dev/null +++ b/staticfiles/import_export/guess_format.js @@ -0,0 +1,21 @@ +(function($) { + $().ready(function () { + $('input.guess_format[type="file"]').change(function () { + var files = this.files; + var dropdowns = $(this.form).find('select.guess_format'); + if(files.length > 0) { + var extension = files[0].name.split('.').pop().trim().toLowerCase(); + for(var i = 0; i < dropdowns.length; i++) { + var dropdown = dropdowns[i]; + dropdown.selectedIndex = 0; + for(var j = 0; j < dropdown.options.length; j++) { + if(extension === dropdown.options[j].text.trim().toLowerCase()) { + dropdown.selectedIndex = j; + break; + } + } + } + } + }); + }); +})(django.jQuery); diff --git a/staticfiles/import_export/import.css b/staticfiles/import_export/import.css new file mode 100644 index 00000000..d457a7a1 --- /dev/null +++ b/staticfiles/import_export/import.css @@ -0,0 +1,159 @@ +.import-preview .errors { + position: relative; +} + +.validation-error-count { + display: inline-block; + background-color: #e40000; + border-radius: 6px; + color: white; + font-size: 0.9em; + position: relative; + font-weight: bold; + margin-top: -2px; + padding: 0.2em 0.4em; +} + +.validation-error-container { + position: absolute; + opacity: 0; + pointer-events: none; + background-color: #ffc1c1; + padding: 14px 15px 10px; + top: 25px; + margin: 0 0 20px 0; + width: 200px; + z-index: 2; +} + +html[data-theme="light"] .validation-error-container { + background-color: #ffc1c1; +} + +table.import-preview tr.skip, html[data-theme="light"] table.import-preview tr.skip { + background-color: #d2d2d2; +} + +table.import-preview tr.new, html[data-theme="light"] table.import-preview tr.new { + background-color: #bdd8b2; +} + +table.import-preview tr.delete, html[data-theme="light"] table.import-preview tr.delete { + background-color: #f9bebf; +} + +table.import-preview tr.update, html[data-theme="light"] table.import-preview tr.update { + background-color: #fdfdcf; +} + +table.import-preview td ins, html[data-theme="light"] table.import-preview td ins { + background-color: #e6ffe6 !important; +} + +html[data-theme="light"] table.import-preview td del { + background-color: #ffe6e6 !important; +} + +.import-preview td:hover .validation-error-count { + z-index: 3; +} +.import-preview td:hover .validation-error-container { + opacity: 1; + pointer-events: auto; +} + +.validation-error-list { + margin: 0; + padding: 0; +} + +.validation-error-list li { + list-style: none; + margin: 0; +} + +.validation-error-list > li > ul { + margin: 8px 0; + padding: 0; +} + +.validation-error-list > li > ul > li { + padding: 0; + margin: 0 0 10px; + line-height: 1.28em; +} + +.validation-error-field-label { + display: block; + border-bottom: 1px solid #e40000; + color: #e40000; + text-transform: uppercase; + font-weight: bold; + font-size: 0.85em; +} + +@media (prefers-color-scheme: dark) { + table.import-preview tr.skip { + background-color: #2d2d2d; + } + + table.import-preview tr.new { + background-color: #42274d; + } + + table.import-preview tr.delete { + background-color: #064140; + } + + table.import-preview tr.update { + background-color: #020230; + } + + .validation-error-container { + background-color: #003e3e; + } + + /* + these declarations are necessary to forcibly override the + formatting applied by the diff-match-patch python library + */ + table.import-preview td ins { + background-color: #190019 !important; + } + + table.import-preview td del { + background-color: #001919 !important; + } +} + +html[data-theme="dark"] table.import-preview tr.skip { + background-color: #2d2d2d; +} + +html[data-theme="dark"] table.import-preview tr.new { + background-color: #42274d; +} + +html[data-theme="dark"] table.import-preview tr.delete { + background-color: #064140; +} + +html[data-theme="dark"] table.import-preview tr.update { + background-color: #020230; +} + +html[data-theme="dark"] .validation-error-container { + background-color: #003e3e; +} + +/* +these declarations are necessary to forcibly override the +formatting applied by the diff-match-patch python library + */ +html[data-theme="dark"] table.import-preview td ins { + background-color: #190019 !important; +} + +html[data-theme="dark"] table.import-preview td del { + background-color: #001919 !important; +} diff --git a/staticfiles/ordered_model/arrow-bottom.gif b/staticfiles/ordered_model/arrow-bottom.gif new file mode 100644 index 00000000..26b1381f Binary files /dev/null and b/staticfiles/ordered_model/arrow-bottom.gif differ diff --git a/staticfiles/ordered_model/arrow-down.gif b/staticfiles/ordered_model/arrow-down.gif new file mode 100644 index 00000000..a967b9fd Binary files /dev/null and b/staticfiles/ordered_model/arrow-down.gif differ diff --git a/staticfiles/ordered_model/arrow-top.gif b/staticfiles/ordered_model/arrow-top.gif new file mode 100644 index 00000000..d78c6e80 Binary files /dev/null and b/staticfiles/ordered_model/arrow-top.gif differ diff --git a/staticfiles/ordered_model/arrow-up.gif b/staticfiles/ordered_model/arrow-up.gif new file mode 100644 index 00000000..3fe48513 Binary files /dev/null and b/staticfiles/ordered_model/arrow-up.gif differ diff --git a/staticfiles/silk/css/components/cell.css b/staticfiles/silk/css/components/cell.css new file mode 100644 index 00000000..b0e60a96 --- /dev/null +++ b/staticfiles/silk/css/components/cell.css @@ -0,0 +1,32 @@ +.cell { + display: inline-block; + background-color: transparent; + padding: 10px; + margin-left: 10px; + margin-top: 10px; + border-radius: 4px; + transition: background-color 0.15s ease, color 0.15s ease; +} +.cell div { + margin: 2px; +} +.cell .timestamp-div { + margin-bottom: 15px; + font-size: 13px; +} +.cell .meta { + font-size: 12px; + color: #be5b43; +} +.cell .meta .unit { + font-size: 9px; + font-weight: lighter !important; +} +.cell .method-div { + font-weight: bold; + font-size: 20px; +} +.cell .path-div { + font-size: 18px; + margin-bottom: 15px; +} diff --git a/staticfiles/silk/css/components/colors.css b/staticfiles/silk/css/components/colors.css new file mode 100644 index 00000000..f8ec26c4 --- /dev/null +++ b/staticfiles/silk/css/components/colors.css @@ -0,0 +1,19 @@ +.very-good-font-color { + color: #bac54b; +} + +.good-font-color { + color: #c3a948; +} + +.ok-font-color { + color: #c08245; +} + +.bad-font-color { + color: #be5b43; +} + +.very-bad-font-color { + color: #b9424f; +} diff --git a/staticfiles/silk/css/components/fonts.css b/staticfiles/silk/css/components/fonts.css new file mode 100644 index 00000000..2a2c14fc --- /dev/null +++ b/staticfiles/silk/css/components/fonts.css @@ -0,0 +1,72 @@ +/** +* Fira Sans +*/ +@font-face { + font-family: FiraSans; + src: url(../../fonts/fira/FiraSans-Regular.woff); + font-weight: normal; +} +@font-face { + font-family: FiraSans; + src: url(../../fonts/fira/FiraSans-Medium.woff); + font-weight: bold; +} +@font-face { + font-family: FiraSans; + src: url(../../fonts/fira/FiraSans-Bold.woff); + font-weight: bolder; +} +@font-face { + font-family: FiraSans; + src: url(../../fonts/fira/FiraSans-Light.woff); + font-weight: lighter; +} +@font-face { + font-family: FiraSans; + src: url(../../fonts/fira/FiraSans-RegularItalic.woff); + font-weight: normal; + font-style: italic; +} +@font-face { + font-family: FiraSans; + src: url(../../fonts/fira/FiraSans-MediumItalic.woff); + font-weight: bold; + font-style: italic; +} +@font-face { + font-family: FiraSans; + src: url(../../fonts/fira/FiraSans-BoldItalic.woff); + font-weight: bolder; + font-style: italic; +} +@font-face { + font-family: FiraSans; + src: url(../../fonts/fira/FiraSans-LightItalic.woff); + font-weight: lighter; + font-style: italic; +} +/** +* Fantasque +*/ +@font-face { + font-family: Fantasque; + src: url(../../fonts/fantasque/FantasqueSansMono-Regular.woff); + font-weight: normal; +} +@font-face { + font-family: Fantasque; + src: url(../../fonts/fantasque/FantasqueSansMono-Bold.woff); + font-weight: bold; +} +@font-face { + font-family: Fantasque; + src: url(../../fonts/fantasque/FantasqueSansMono-RegItalic.woff); + font-weight: normal; + font-style: italic; +} +@font-face { + font-family: Fantasque; + src: url(../../fonts/fantasque/FantasqueSansMono-BoldItalic.woff); + font-weight: bold; + font-style: italic; +} diff --git a/staticfiles/silk/css/components/heading.css b/staticfiles/silk/css/components/heading.css new file mode 100644 index 00000000..ee5ae8df --- /dev/null +++ b/staticfiles/silk/css/components/heading.css @@ -0,0 +1,14 @@ +.heading { + width: 100%; + background-color: transparent; + height: 30px; + display: table; + font-weight: bold; + margin-top: 20px; +} +.heading .inner-heading { + display: table-cell; + text-align: left; + padding: 0; + vertical-align: middle; +} diff --git a/staticfiles/silk/css/components/numeric.css b/staticfiles/silk/css/components/numeric.css new file mode 100644 index 00000000..fe141081 --- /dev/null +++ b/staticfiles/silk/css/components/numeric.css @@ -0,0 +1,15 @@ +.numeric { + font-weight: normal; +} + +.unit { + font-weight: normal; +} + +.numeric .unit { + font-size: 12px; +} + +.numeric { + font-size: 20px; +} diff --git a/staticfiles/silk/css/components/row.css b/staticfiles/silk/css/components/row.css new file mode 100644 index 00000000..9acfcd8c --- /dev/null +++ b/staticfiles/silk/css/components/row.css @@ -0,0 +1,56 @@ +.row-wrapper { + display: table; + margin: 2rem; + width: 100%; + width: -moz-available; + width: -webkit-fill-available; + width: fill-available; +} +.row-wrapper .row { + display: table-row; + transition: background-color 0.15s ease, color 0.15s ease; +} +.row-wrapper .row div { + padding: 1rem; +} +.row-wrapper .row .col { + font-size: 20px; + display: table-cell; +} +.row-wrapper .row .timestamp-div { + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + margin-bottom: 15px; + font-size: 13px; +} +.row-wrapper .row .meta { + font-size: 12px; + color: #be5b43; +} +.row-wrapper .row .meta .unit { + font-size: 9px; + font-weight: lighter !important; +} +.row-wrapper .row .method-div { + font-weight: bold; + font-size: 20px; +} +.row-wrapper .row .path-div { + font-size: 18px; + margin-bottom: 15px; +} +.row-wrapper .row .num-queries-div { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} +.row-wrapper .row .spacing .numeric { + padding: 0 0.3rem; +} +.row-wrapper .row .spacing .meta { + padding: 0 0.3rem; +} +.row-wrapper .row:hover { + background-color: rgb(51, 51, 68); + color: white; + cursor: pointer; +} diff --git a/staticfiles/silk/css/components/summary.css b/staticfiles/silk/css/components/summary.css new file mode 100644 index 00000000..940bf9a5 --- /dev/null +++ b/staticfiles/silk/css/components/summary.css @@ -0,0 +1,23 @@ +#error-div { + margin: 10px; +} + +#query-div { + margin: auto; + width: 960px; + text-align: center; +} + +#code { + text-align: left; +} + +.name-div { + margin-top: 20px; + margin-bottom: 15px; + font-weight: bold; +} + +.description { + text-align: left; +} diff --git a/staticfiles/silk/css/pages/base.css b/staticfiles/silk/css/pages/base.css new file mode 100644 index 00000000..05432b5b --- /dev/null +++ b/staticfiles/silk/css/pages/base.css @@ -0,0 +1,118 @@ +body { + font-family: FiraSans, "Helvetica Neue", Arial, sans-serif; + background-color: #f3f3f3; + margin: 0; + font-weight: lighter; +} + +pre { + font-family: Fantasque; + background-color: white !important; + padding: 0.5em !important; + margin: 0 !important; + font-size: 14px; + text-align: left; +} + +code { + font-family: Fantasque; + background-color: white !important; + padding: 0 !important; + margin: 0 !important; + font-size: 14px; +} + +html { + margin: 0; +} + +#header { + height: 50px; + background-color: rgb(51, 51, 68); + width: 100%; + position: relative; + padding: 0; +} + +#header div { + display: inline-block; +} + +.menu { + height: 50px; + padding: 0; + margin: 0; +} + +.menu-item { + height: 50px; + padding-left: 10px; + padding-right: 10px; + margin: 0; + margin-right: -4px; + color: white; +} + +.menu-item a { + color: white !important; +} + +#filter .menu-item { + margin-right: 0px; +} + +.selectable-menu-item { + transition: background-color 0.15s ease, color 0.15s ease; +} + +.selectable-menu-item:hover { + background-color: #f3f3f3; + cursor: pointer; + color: black !important; +} + +.selectable-menu-item:hover a { + color: black !important; +} + +.menu-item-selected { + background-color: #f3f3f3; + color: black !important; +} + +.menu-item-selected a { + color: black !important; +} + +.menu-item-outer { + display: table !important; + height: 100%; + width: 100%; +} + +.menu-item-inner { + display: table-cell !important; + vertical-align: middle; + width: 100%; +} + +a:visited { + color: black; +} + +a { + color: black; + text-decoration: none; +} + +#filter { + height: 50px; + position: absolute; + right: 0; +} + +.description { + font-style: italic; + font-size: 14px; + margin-bottom: 5px; +} diff --git a/staticfiles/silk/css/pages/clear_db.css b/staticfiles/silk/css/pages/clear_db.css new file mode 100644 index 00000000..30fa2a38 --- /dev/null +++ b/staticfiles/silk/css/pages/clear_db.css @@ -0,0 +1,41 @@ +.wrapper { + width: 100%; + margin-bottom: 20px; +} + +.inner { + margin: auto; + width: 960px; +} + +.cleardb-form .cleardb-form-wrapper { + margin-bottom: 20px; +} + +.cleardb-form label { + display: block; + margin-bottom: 8px; +} + +.cleardb-form .btn { + background: #333344; + color: #fff; + padding: 10px 20px; + border-radius: 2px; + cursor: pointer; + box-shadow: none; + font-size: 16px; + line-height: 20px; + border: 0; + min-width: 150px; + text-align: center; +} + +.cleardb-form label :last-child { + margin-bottom: 0; +} + +.msg { + margin-top: 20px; + color: #bac54b; +} diff --git a/staticfiles/silk/css/pages/cprofile.css b/staticfiles/silk/css/pages/cprofile.css new file mode 100644 index 00000000..0953cad8 --- /dev/null +++ b/staticfiles/silk/css/pages/cprofile.css @@ -0,0 +1,52 @@ +#query-info-div { + margin-top: 15px; +} + +#query-info-div .timestamp-div { + font-size: 13px; +} + +#pyprofile-div { + display: block; + margin: auto; + width: 960px; +} + +.pyprofile { + text-align: left; +} + +a { + color: #45ADA8; +} + +a:visited { + color: #45ADA8; +} + +a:hover { + color: #547980; +} + +a:active { + color: #594F4F; +} + +#graph-div { + padding: 25px; + background-color: white; + display: block; + margin-left: auto; + margin-right: auto; + margin-top: 25px; + width: 960px; + text-align: center; +} + +#percent { + width: 20px; +} + +svg { + display: block; +} diff --git a/staticfiles/silk/css/pages/detail_base.css b/staticfiles/silk/css/pages/detail_base.css new file mode 100644 index 00000000..3605da30 --- /dev/null +++ b/staticfiles/silk/css/pages/detail_base.css @@ -0,0 +1,32 @@ +#traceback { + overflow: visible; +} + +#time-div { + text-align: center; + margin-bottom: 30px; +} + +#query-div { + text-align: center; + margin-bottom: 20px; +} + +#query { + text-align: left; + margin: 0 auto; + display: inline-block; +} + +.line { + width: 100%; + display: inline-block; +} + +.the-line { + background-color: #c3c3c3; +} + +pre { + margin: 0; +} diff --git a/staticfiles/silk/css/pages/profile_detail.css b/staticfiles/silk/css/pages/profile_detail.css new file mode 100644 index 00000000..0953cad8 --- /dev/null +++ b/staticfiles/silk/css/pages/profile_detail.css @@ -0,0 +1,52 @@ +#query-info-div { + margin-top: 15px; +} + +#query-info-div .timestamp-div { + font-size: 13px; +} + +#pyprofile-div { + display: block; + margin: auto; + width: 960px; +} + +.pyprofile { + text-align: left; +} + +a { + color: #45ADA8; +} + +a:visited { + color: #45ADA8; +} + +a:hover { + color: #547980; +} + +a:active { + color: #594F4F; +} + +#graph-div { + padding: 25px; + background-color: white; + display: block; + margin-left: auto; + margin-right: auto; + margin-top: 25px; + width: 960px; + text-align: center; +} + +#percent { + width: 20px; +} + +svg { + display: block; +} diff --git a/staticfiles/silk/css/pages/profiling.css b/staticfiles/silk/css/pages/profiling.css new file mode 100644 index 00000000..1e71fb2b --- /dev/null +++ b/staticfiles/silk/css/pages/profiling.css @@ -0,0 +1,16 @@ +.name-div { + font-weight: bold; +} + +.container { + padding: 0 1em; +} + +h2 { + margin-bottom: 10px; +} + +.pyprofile { + overflow: scroll; + max-height: 650px; +} diff --git a/staticfiles/silk/css/pages/raw.css b/staticfiles/silk/css/pages/raw.css new file mode 100644 index 00000000..37eb17a4 --- /dev/null +++ b/staticfiles/silk/css/pages/raw.css @@ -0,0 +1,31 @@ +pre { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + background-color: white !important; + white-space: pre-wrap; /* css-3 */ + white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ + /*noinspection CssInvalidElement*/ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ +} + +code { + font-family: Fantasque; + background-color: white !important; + width: 100% !important; + height: auto; + padding: 0 !important; +} + +body { + margin: 0; + padding: 0; +} + +html { + margin: 0; + padding: 0; +} diff --git a/staticfiles/silk/css/pages/request.css b/staticfiles/silk/css/pages/request.css new file mode 100644 index 00000000..fce76182 --- /dev/null +++ b/staticfiles/silk/css/pages/request.css @@ -0,0 +1,64 @@ +pre { + white-space: pre-wrap; /* css-3 */ + white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ + /*noinspection CssInvalidElement*/ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ +} + +.cell { + background-color: transparent; + margin-top: 15px; +} + +div.wrapper { + width: 100%; +} + +div.wrapper div#request-summary { + margin: auto; + text-align: center; + width: 100%; +} + +div.wrapper div#request-info { + width: 960px; + margin: auto auto 20px; +} + +a { + color: #45ADA8; +} + +a:visited { + color: #45ADA8; +} + +a:hover { + color: #547980; +} + +a:active { + color: #594F4F; +} + +.headers { + font-size: 12px; + font-family: Fantasque; + background-color: white; + width: 100%; +} + +.headers tr:hover { + background-color: #f4f4f4; +} + +.headers td { + padding-bottom: 5px; + padding-left: 5px; +} + +.headers .key { + font-weight: bold; +} diff --git a/staticfiles/silk/css/pages/requests.css b/staticfiles/silk/css/pages/requests.css new file mode 100644 index 00000000..d241cb40 --- /dev/null +++ b/staticfiles/silk/css/pages/requests.css @@ -0,0 +1,19 @@ +.container { + padding: 0 1em; +} + +.resizing-input input { + background-color: white; + padding-top: 2px; + color: black; + box-shadow: inset 0 0 3px black; +} + +.resizing-input input::placeholder { + color: #383838; + opacity: 1; +} + +.filter-section { + line-height: 2.3; +} diff --git a/staticfiles/silk/css/pages/root_base.css b/staticfiles/silk/css/pages/root_base.css new file mode 100644 index 00000000..5906b4f4 --- /dev/null +++ b/staticfiles/silk/css/pages/root_base.css @@ -0,0 +1,252 @@ +.cell:hover { + background-color: rgb(51, 51, 68); + color: white; + cursor: pointer; +} + +.cell { + text-align: center; +} + +/* General styles for all menus */ +.cbp-spmenu { + background: rgb(51, 51, 68); + position: fixed; +} + +h3 { + color: white; + font-size: 1.9em; + padding: 10px; + margin: 0; + font-weight: 300; + background: rgb(51, 51, 68); +} + +/*.cbp-spmenu div {*/ +/*display: block;*/ +/*color: #fff;*/ +/*font-size: 1.1em;*/ +/*font-weight: 300;*/ +/*}*/ +/* Orientation-dependent styles for the content of the menu */ +.cbp-spmenu-vertical { + width: 300px; + height: 100%; + top: 0; + z-index: 1000; +} + +/* Vertical menu that slides from the left or right */ +.cbp-spmenu-right { + right: -300px; +} + +.cbp-spmenu-right.cbp-spmenu-open { + right: 0px; +} + +/* Push classes applied to the body */ +.cbp-spmenu-push { + overflow-x: hidden; + position: relative; + left: 0; +} + +.cbp-spmenu-push-toleft { + left: -300px; +} + +/* Transitions */ +.cbp-spmenu, +.cbp-spmenu-push { + -webkit-transition: all 0.3s ease; + -moz-transition: all 0.3s ease; + transition: all 0.3s ease; +} + +/* Example media queries */ +@media screen and (max-width: 55.1875em) { + .cbp-spmenu-horizontal { + font-size: 75%; + height: 110px; + } +} +.active-filter { + font-size: 12px; + color: white; +} + +.active-filter td { + padding: 0; + margin: 0; +} + +#cbp-spmenu-s2 button { + color: white; + background: none; + border: none; +} + +#cbp-spmenu-s2 button:hover { + color: black; + background: white; + border: none; + cursor: pointer; +} + +#cbp-spmenu-s2 button:focus { + outline: none; +} + +#slider-label { + margin-top: 5px; +} + +#outer-slider { + text-align: center; + width: 80%; +} + +#seconds { + width: 200px; + height: 50px; +} + +#slider { + width: 80%; + margin: auto; + margin-top: 10px; +} + +input[type=number], +input[type=text] { + border: none; + padding: 10px; + width: 222px; +} + +.add-button { + font-size: 20px; + font-weight: bold; +} + +.add-button.disabled { + color: darkgray !important; +} + +.add-button.disabled:hover { + background: transparent !important; + cursor: default !important; +} + +.button-div { + display: inline-block; + text-align: center; + height: 50px; +} + +.apply-div { + display: inline-block; + background-color: white; + color: black; + margin: 10px; + padding: 10px; + border-radius: 5px; +} + +.apply-div:hover { + cursor: pointer; +} + +select { + border-radius: 0; + max-width: 100%; +} + +@media screen and (-webkit-min-device-pixel-ratio: 0) { + /*safari and chrome*/ + select { + line-height: 30px; + background: #f4f4f4; + } +} +select::-moz-focus-inner { + /*Remove button padding in FF*/ + border: 0; + padding: 0; +} + +#cbp-spmenu-s2 p { + margin-bottom: 10px; + margin-left: 10px; + margin-right: 10px; + margin-top: 0; + font-size: 12px; + color: white; + font-style: oblique; + line-height: 16px; +} + +#filter-item { + width: 24px; + height: 20px; + float: right; + display: inline; + background-image: url("../../filter.png"); + background-size: contain; + background-repeat: no-repeat; + text-align: right; +} + +#filter-item #num-filters { + position: relative; + top: 7px; + left: 1px; +} + +.resizing-input { + display: inline-block; +} + +.resizing-input input, .resizing-input span { + font-size: 12px; + font-family: FiraSans; + white-space: pre; + font-weight: 300; + background-color: transparent; + color: white; + padding: 0; + padding-left: 4px; + padding-right: 4px; + margin: 0; + letter-spacing: 1px; +} + +.resizing-input input:focus { + outline: none; +} + +h4 { + padding: 0; + margin: 3px; + margin-left: 10px; + color: white; + font-weight: lighter; + font-size: 20px; +} + +.filter-section { + color: white; + font-size: 12px; + line-height: 2.3; + padding: 10px; +} + +.filter-section select { + margin-bottom: 10px; +} + +.filter-section input { + padding: 4px; +} diff --git a/staticfiles/silk/css/pages/sql.css b/staticfiles/silk/css/pages/sql.css new file mode 100644 index 00000000..86f47b69 --- /dev/null +++ b/staticfiles/silk/css/pages/sql.css @@ -0,0 +1,53 @@ +.right-aligned { + text-align: right; +} + +.center-aligned { + text-align: center; +} + +.left-aligned { + text-align: left; +} + +#table-pagination { + text-align: center; + margin: 20px; +} + +table { + border-spacing: 0; + margin: auto; + max-width: 920px; +} + +tr { + height: 20px; +} + +tr.data-row:hover { + background-color: rgb(51, 51, 68); + color: white; + cursor: pointer; +} + +td { + padding: 5px; + padding-left: 20px; + padding-right: 20px; +} + +th { + height: 40px; + padding-left: 20px; + padding-right: 20px; +} + +#table-div { + width: 100%; + margin-top: 40px; +} + +#table-pagination div { + padding: 5px; +} diff --git a/staticfiles/silk/css/pages/sql_detail.css b/staticfiles/silk/css/pages/sql_detail.css new file mode 100644 index 00000000..c6af3acd --- /dev/null +++ b/staticfiles/silk/css/pages/sql_detail.css @@ -0,0 +1,59 @@ +#traceback { + width: 960px; + margin: auto; +} + +#traceback pre { + margin-top: 15px !important; + margin-bottom: 15px !important; +} + +#traceback .not-third-party { + font-weight: bold; +} + +a:hover { + color: #9dd0ff; +} + +a:active { + color: #594F4F; +} + +code { + background-color: transparent !important; +} + +#query-div pre { + background-color: transparent !important; +} + +#query-div { + padding-top: 15px; +} + +#query-info-div div { + padding-top: 5px; +} + +#query-plan-div { + text-align: left; + width: 960px; + margin: auto; +} + +#plan-div code { + margin: auto !important; + display: inline-block; +} + +#query-plan-head { + padding-top: 5px; + padding-bottom: 15px; + text-align: center; + margin: auto; +} + +.file-path { + font-size: 13px; +} diff --git a/staticfiles/silk/css/pages/summary.css b/staticfiles/silk/css/pages/summary.css new file mode 100644 index 00000000..65bbf7d5 --- /dev/null +++ b/staticfiles/silk/css/pages/summary.css @@ -0,0 +1,51 @@ +.wrapper { + width: 100%; + margin-bottom: 20px; +} + +.inner { + margin: auto; + width: 960px; +} + +.summary-cell { + display: inline-block; + text-align: center; + padding: 10px; + margin-left: 10px; + margin-top: 10px; +} + +.summary-cell .desc { + margin-top: 8px; + font-size: 12px; +} + +.no-data { + font-size: 12px; + font-style: oblique; + margin-left: 10px; +} + +h2 { + margin-bottom: 0; +} + +#filters { + margin-top: 10px; + font-size: 12px; +} + +#filters input, +#filters span { + color: black !important; + font-weight: bold; +} + +#filter-image { + width: 20px; +} + +#filter-cell { + padding-left: 5px; +} diff --git a/staticfiles/silk/favicon-16x16.png b/staticfiles/silk/favicon-16x16.png new file mode 100644 index 00000000..f8eca737 Binary files /dev/null and b/staticfiles/silk/favicon-16x16.png differ diff --git a/staticfiles/silk/favicon-32x32.png b/staticfiles/silk/favicon-32x32.png new file mode 100644 index 00000000..13cd8f31 Binary files /dev/null and b/staticfiles/silk/favicon-32x32.png differ diff --git a/staticfiles/silk/filter.png b/staticfiles/silk/filter.png new file mode 100644 index 00000000..486153bc Binary files /dev/null and b/staticfiles/silk/filter.png differ diff --git a/staticfiles/silk/filter2.png b/staticfiles/silk/filter2.png new file mode 100644 index 00000000..1bf4b0ed Binary files /dev/null and b/staticfiles/silk/filter2.png differ diff --git a/staticfiles/silk/fonts/fantasque/FantasqueSansMono-Bold.woff b/staticfiles/silk/fonts/fantasque/FantasqueSansMono-Bold.woff new file mode 100644 index 00000000..20e00fb5 Binary files /dev/null and b/staticfiles/silk/fonts/fantasque/FantasqueSansMono-Bold.woff differ diff --git a/staticfiles/silk/fonts/fantasque/FantasqueSansMono-BoldItalic.woff b/staticfiles/silk/fonts/fantasque/FantasqueSansMono-BoldItalic.woff new file mode 100644 index 00000000..13d252a9 Binary files /dev/null and b/staticfiles/silk/fonts/fantasque/FantasqueSansMono-BoldItalic.woff differ diff --git a/staticfiles/silk/fonts/fantasque/FantasqueSansMono-RegItalic.woff b/staticfiles/silk/fonts/fantasque/FantasqueSansMono-RegItalic.woff new file mode 100644 index 00000000..1a19705b Binary files /dev/null and b/staticfiles/silk/fonts/fantasque/FantasqueSansMono-RegItalic.woff differ diff --git a/staticfiles/silk/fonts/fantasque/FantasqueSansMono-Regular.woff b/staticfiles/silk/fonts/fantasque/FantasqueSansMono-Regular.woff new file mode 100644 index 00000000..02ebd95a Binary files /dev/null and b/staticfiles/silk/fonts/fantasque/FantasqueSansMono-Regular.woff differ diff --git a/staticfiles/silk/fonts/fira/FiraSans-Bold.woff b/staticfiles/silk/fonts/fira/FiraSans-Bold.woff new file mode 100644 index 00000000..415071c2 Binary files /dev/null and b/staticfiles/silk/fonts/fira/FiraSans-Bold.woff differ diff --git a/staticfiles/silk/fonts/fira/FiraSans-BoldItalic.woff b/staticfiles/silk/fonts/fira/FiraSans-BoldItalic.woff new file mode 100644 index 00000000..a4129c61 Binary files /dev/null and b/staticfiles/silk/fonts/fira/FiraSans-BoldItalic.woff differ diff --git a/staticfiles/silk/fonts/fira/FiraSans-Light.woff b/staticfiles/silk/fonts/fira/FiraSans-Light.woff new file mode 100644 index 00000000..f05e73a7 Binary files /dev/null and b/staticfiles/silk/fonts/fira/FiraSans-Light.woff differ diff --git a/staticfiles/silk/fonts/fira/FiraSans-LightItalic.woff b/staticfiles/silk/fonts/fira/FiraSans-LightItalic.woff new file mode 100644 index 00000000..43a73e88 Binary files /dev/null and b/staticfiles/silk/fonts/fira/FiraSans-LightItalic.woff differ diff --git a/staticfiles/silk/fonts/fira/FiraSans-Medium.woff b/staticfiles/silk/fonts/fira/FiraSans-Medium.woff new file mode 100644 index 00000000..56272277 Binary files /dev/null and b/staticfiles/silk/fonts/fira/FiraSans-Medium.woff differ diff --git a/staticfiles/silk/fonts/fira/FiraSans-MediumItalic.woff b/staticfiles/silk/fonts/fira/FiraSans-MediumItalic.woff new file mode 100644 index 00000000..f3814873 Binary files /dev/null and b/staticfiles/silk/fonts/fira/FiraSans-MediumItalic.woff differ diff --git a/staticfiles/silk/fonts/fira/FiraSans-Regular.woff b/staticfiles/silk/fonts/fira/FiraSans-Regular.woff new file mode 100644 index 00000000..9ff40445 Binary files /dev/null and b/staticfiles/silk/fonts/fira/FiraSans-Regular.woff differ diff --git a/staticfiles/silk/fonts/fira/FiraSans-RegularItalic.woff b/staticfiles/silk/fonts/fira/FiraSans-RegularItalic.woff new file mode 100644 index 00000000..074c220e Binary files /dev/null and b/staticfiles/silk/fonts/fira/FiraSans-RegularItalic.woff differ diff --git a/staticfiles/silk/fonts/glyphicons-halflings-regular.eot b/staticfiles/silk/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 00000000..b93a4953 Binary files /dev/null and b/staticfiles/silk/fonts/glyphicons-halflings-regular.eot differ diff --git a/staticfiles/silk/fonts/glyphicons-halflings-regular.svg b/staticfiles/silk/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 00000000..ff1de8e3 --- /dev/null +++ b/staticfiles/silk/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/staticfiles/silk/fonts/glyphicons-halflings-regular.ttf b/staticfiles/silk/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 00000000..1413fc60 Binary files /dev/null and b/staticfiles/silk/fonts/glyphicons-halflings-regular.ttf differ diff --git a/staticfiles/silk/fonts/glyphicons-halflings-regular.woff b/staticfiles/silk/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 00000000..9e612858 Binary files /dev/null and b/staticfiles/silk/fonts/glyphicons-halflings-regular.woff differ diff --git a/staticfiles/silk/fonts/glyphicons-halflings-regular.woff2 b/staticfiles/silk/fonts/glyphicons-halflings-regular.woff2 new file mode 100644 index 00000000..64539b54 Binary files /dev/null and b/staticfiles/silk/fonts/glyphicons-halflings-regular.woff2 differ diff --git a/staticfiles/silk/js/components/cell.js b/staticfiles/silk/js/components/cell.js new file mode 100644 index 00000000..3a03a317 --- /dev/null +++ b/staticfiles/silk/js/components/cell.js @@ -0,0 +1,24 @@ +function configureSpanFontColors(selector, okValue, badValue) { + selector.each(function () { + var val = parseFloat($(this).text()); + if (val < okValue) { + $(this).addClass('very-good-font-color'); + } + else if (val < badValue) { + $(this).addClass('ok-font-color'); + } + else { + $(this).addClass('very-bad-font-color'); + } + }); +} + +function configureFontColors() { + configureSpanFontColors($('.time-taken-div .numeric'), 200, 500); + configureSpanFontColors($('.time-taken-queries-div .numeric'), 50, 200); + configureSpanFontColors($('.num-queries-div .numeric'), 10, 50); +} + +$(document).ready(function () { + configureFontColors(); +}); diff --git a/staticfiles/silk/js/components/filters.js b/staticfiles/silk/js/components/filters.js new file mode 100644 index 00000000..54f0dc67 --- /dev/null +++ b/staticfiles/silk/js/components/filters.js @@ -0,0 +1,50 @@ + +function configureResizingInputs() { + var $inputs = $('.resizing-input'); + + function resizeForText(text) { + var $this = $(this); + if (!text.trim()) { + text = $this.attr('placeholder').trim(); + } + var $span = $this.parent().find('span'); + $span.text(text); + var $inputSize = $span.width(); + $this.css("width", $inputSize); + } + + $inputs.find('input').keypress(function (e) { + if (e.which && e.charCode) { + var c = String.fromCharCode(e.keyCode | e.charCode); + var $this = $(this); + resizeForText.call($this, $this.val() + c); + } + }); + + $inputs.find('input').keyup(function (e) { // Backspace event only fires for keyup + if (e.keyCode === 8 || e.keyCode === 46) { + resizeForText.call($(this), $(this).val()); + } + }); + + $inputs.find('input').each(function () { + var $this = $(this); + resizeForText.call($this, $this.val()) + }); + + + $('.resizing-input .datetimepicker').datetimepicker({ + step: 10, + onChangeDateTime: function (dp, $input) { + resizeForText.call($input, $input.val()) + } + }); + +} + +/** + * Entry point for filter initialisation. + */ +function initFilters() { + configureResizingInputs(); +} diff --git a/staticfiles/silk/js/pages/base.js b/staticfiles/silk/js/pages/base.js new file mode 100644 index 00000000..c031a862 --- /dev/null +++ b/staticfiles/silk/js/pages/base.js @@ -0,0 +1,5 @@ +$(document).ready(function () { + configureSpanFontColors($('#num-joins-div').find('.numeric'), 3, 5); + configureSpanFontColors($('#time-taken-div').find('.numeric'), 200, 500); + configureSpanFontColors($('#num-queries-div').find('.numeric'), 10, 500); +}); diff --git a/staticfiles/silk/js/pages/clear_db.js b/staticfiles/silk/js/pages/clear_db.js new file mode 100644 index 00000000..73b84f60 --- /dev/null +++ b/staticfiles/silk/js/pages/clear_db.js @@ -0,0 +1,7 @@ +$(document).ready(function () { + initFilters(); + var $inputs = $('.resizing-input'); + $inputs.focusout(function () { + $('#filter-form').submit(); + }); +}); diff --git a/staticfiles/silk/js/pages/detail_base.js b/staticfiles/silk/js/pages/detail_base.js new file mode 100644 index 00000000..0cc359d1 --- /dev/null +++ b/staticfiles/silk/js/pages/detail_base.js @@ -0,0 +1 @@ +hljs.initHighlightingOnLoad(); diff --git a/staticfiles/silk/js/pages/profile_detail.js b/staticfiles/silk/js/pages/profile_detail.js new file mode 100644 index 00000000..4f1c5b9b --- /dev/null +++ b/staticfiles/silk/js/pages/profile_detail.js @@ -0,0 +1,15 @@ +function createViz() { + var profileDotURL = JSON.parse(document.getElementById("profileDotURL").textContent); + + $.get( + profileDotURL, + { cutoff: $('#percent').val() }, + function (response) { + var svg = '#graph-div'; + $(svg).html(Viz(response.dot)); + $(svg + ' svg').attr('width', 960).attr('height', 600); + svgPanZoom(svg + ' svg', { controlIconsEnabled: true }); + } + ); +} +createViz(); diff --git a/staticfiles/silk/js/pages/profiling.js b/staticfiles/silk/js/pages/profiling.js new file mode 100644 index 00000000..41652954 --- /dev/null +++ b/staticfiles/silk/js/pages/profiling.js @@ -0,0 +1,4 @@ +$(document).ready(function () { + initFilters(); + initFilterButton(); +}); diff --git a/staticfiles/silk/js/pages/raw.js b/staticfiles/silk/js/pages/raw.js new file mode 100644 index 00000000..0cc359d1 --- /dev/null +++ b/staticfiles/silk/js/pages/raw.js @@ -0,0 +1 @@ +hljs.initHighlightingOnLoad(); diff --git a/staticfiles/silk/js/pages/request.js b/staticfiles/silk/js/pages/request.js new file mode 100644 index 00000000..0cc359d1 --- /dev/null +++ b/staticfiles/silk/js/pages/request.js @@ -0,0 +1 @@ +hljs.initHighlightingOnLoad(); diff --git a/staticfiles/silk/js/pages/requests.js b/staticfiles/silk/js/pages/requests.js new file mode 100644 index 00000000..41652954 --- /dev/null +++ b/staticfiles/silk/js/pages/requests.js @@ -0,0 +1,4 @@ +$(document).ready(function () { + initFilters(); + initFilterButton(); +}); diff --git a/staticfiles/silk/js/pages/root_base.js b/staticfiles/silk/js/pages/root_base.js new file mode 100644 index 00000000..ad3e1d29 --- /dev/null +++ b/staticfiles/silk/js/pages/root_base.js @@ -0,0 +1,15 @@ +function initFilterButton() { + $('#filter-button').click(function () { + $(this).toggleClass('active'); + $('body').toggleClass('cbp-spmenu-push-toleft'); + $('#cbp-spmenu-s2').toggleClass('cbp-spmenu-open'); + initFilters(); + }); +} +function submitFilters() { + $('#filter-form2').submit(); +} +function submitEmptyFilters() { + $('#cbp-spmenu-s2 :input:not([type=hidden])').val(''); + submitFilters(); +} diff --git a/staticfiles/silk/js/pages/sql.js b/staticfiles/silk/js/pages/sql.js new file mode 100644 index 00000000..ba1882df --- /dev/null +++ b/staticfiles/silk/js/pages/sql.js @@ -0,0 +1,17 @@ +$(document).ready(function () { + document.querySelectorAll(".data-row").forEach((rowElement) => { + let sqlDetailUrl = rowElement.dataset.sqlDetailUrl; + rowElement.addEventListener("click", (e) => { + switch (e.button) { + case 0: + window.location = sqlDetailUrl; + break; + case 1: + window.open(sqlDetailUrl); + break; + default: + break; + } + }); + }); +}); diff --git a/staticfiles/silk/js/pages/sql_detail.js b/staticfiles/silk/js/pages/sql_detail.js new file mode 100644 index 00000000..6daf3e15 --- /dev/null +++ b/staticfiles/silk/js/pages/sql_detail.js @@ -0,0 +1,4 @@ +$(document).ready(function () { + configureSpanFontColors($('#num-joins-div').find('.numeric'), 3, 5); + configureSpanFontColors($('#time-taken-div').find('.numeric'), 200, 500); +}); diff --git a/staticfiles/silk/js/pages/summary.js b/staticfiles/silk/js/pages/summary.js new file mode 100644 index 00000000..73b84f60 --- /dev/null +++ b/staticfiles/silk/js/pages/summary.js @@ -0,0 +1,7 @@ +$(document).ready(function () { + initFilters(); + var $inputs = $('.resizing-input'); + $inputs.focusout(function () { + $('#filter-form').submit(); + }); +}); diff --git a/uv.lock b/uv.lock index 7f2ab811..00fa2675 100644 --- a/uv.lock +++ b/uv.lock @@ -450,6 +450,7 @@ dependencies = [ { name = "cattrs" }, { name = "certifi" }, { name = "cffi" }, + { name = "channels" }, { name = "chardet" }, { name = "charset-normalizer" }, { name = "click" }, @@ -459,6 +460,7 @@ dependencies = [ { name = "contourpy" }, { name = "crispy-bootstrap5" }, { name = "cryptography" }, + { name = "cssbeautifier" }, { name = "cssselect2" }, { name = "ctranslate2" }, { name = "cycler" }, @@ -473,6 +475,7 @@ dependencies = [ { name = "dj-rest-auth" }, { name = "django" }, { name = "django-allauth" }, + { name = "django-appconf" }, { name = "django-appointment" }, { name = "django-autoslug" }, { name = "django-background-tasks" }, @@ -487,9 +490,11 @@ dependencies = [ { name = "django-extensions" }, { name = "django-filter" }, { name = "django-formtools" }, + { name = "django-imagekit" }, { name = "django-import-export" }, { name = "django-js-asset" }, { name = "django-ledger" }, + { name = "django-manager-utils" }, { name = "django-model-utils" }, { name = "django-money" }, { name = "django-next-url-mixin" }, @@ -502,6 +507,7 @@ dependencies = [ { name = "django-plans" }, { name = "django-prometheus" }, { name = "django-q2" }, + { name = "django-query-builder" }, { name = "django-schema-graph" }, { name = "django-sekizai" }, { name = "django-sequences" }, @@ -518,9 +524,11 @@ dependencies = [ { name = "djangorestframework-simplejwt" }, { name = "djangoviz" }, { name = "djhtml" }, + { name = "djlint" }, { name = "docopt" }, { name = "docutils" }, { name = "easy-thumbnails" }, + { name = "editorconfig" }, { name = "emoji" }, { name = "et-xmlfile" }, { name = "eval-type-backport" }, @@ -530,6 +538,7 @@ dependencies = [ { name = "fastavro" }, { name = "filelock" }, { name = "fire" }, + { name = "fleming" }, { name = "fonttools" }, { name = "fpdf" }, { name = "fpdf2" }, @@ -567,6 +576,8 @@ dependencies = [ { name = "jiter" }, { name = "jmespath" }, { name = "joblib" }, + { name = "jsbeautifier" }, + { name = "json5" }, { name = "jsonpatch" }, { name = "jsonpointer" }, { name = "jwt" }, @@ -624,8 +635,10 @@ dependencies = [ { name = "packaging" }, { name = "pandas" }, { name = "pango" }, + { name = "pathspec" }, { name = "pdfkit" }, { name = "phonenumbers" }, + { name = "pilkit" }, { name = "pillow" }, { name = "platformdirs" }, { name = "prometheus-client" }, @@ -699,6 +712,7 @@ dependencies = [ { name = "rich" }, { name = "rsa" }, { name = "rubicon-objc" }, + { name = "ruff" }, { name = "s3transfer" }, { name = "sacremoses" }, { name = "safetensors" }, @@ -708,6 +722,7 @@ dependencies = [ { name = "selenium" }, { name = "sentence-transformers" }, { name = "sentencepiece" }, + { name = "setuptools" }, { name = "shapely" }, { name = "simsimd" }, { name = "six" }, @@ -804,6 +819,7 @@ requires-dist = [ { name = "cattrs", specifier = ">=25.1.1" }, { name = "certifi", specifier = ">=2025.6.15" }, { name = "cffi", specifier = ">=1.17.1" }, + { name = "channels", specifier = ">=4.2.2" }, { name = "chardet", specifier = ">=5.2.0" }, { name = "charset-normalizer", specifier = ">=3.4.2" }, { name = "click", specifier = ">=7.1.2" }, @@ -813,6 +829,7 @@ requires-dist = [ { name = "contourpy", specifier = ">=1.3.2" }, { name = "crispy-bootstrap5", specifier = ">=2025.6" }, { name = "cryptography", specifier = ">=45.0.4" }, + { name = "cssbeautifier", specifier = ">=1.15.4" }, { name = "cssselect2", specifier = ">=0.8.0" }, { name = "ctranslate2", specifier = ">=4.6.0" }, { name = "cycler", specifier = ">=0.12.1" }, @@ -827,6 +844,7 @@ requires-dist = [ { name = "dj-rest-auth", specifier = ">=7.0.1" }, { name = "django", specifier = ">=5.2.3" }, { name = "django-allauth", specifier = ">=65.9.0" }, + { name = "django-appconf", specifier = ">=1.1.0" }, { name = "django-appointment", specifier = ">=3.6.0" }, { name = "django-autoslug", specifier = ">=1.9.9" }, { name = "django-background-tasks", specifier = ">=1.2.8" }, @@ -841,9 +859,11 @@ requires-dist = [ { name = "django-extensions", specifier = ">=4.1" }, { name = "django-filter", specifier = ">=25.1" }, { name = "django-formtools", specifier = ">=2.5.1" }, + { name = "django-imagekit", specifier = ">=5.0.0" }, { name = "django-import-export", specifier = ">=4.3.8" }, { name = "django-js-asset", specifier = ">=3.1.2" }, { name = "django-ledger", specifier = "==0.7.8" }, + { name = "django-manager-utils", specifier = ">=3.1.5" }, { name = "django-model-utils", specifier = ">=5.0.0" }, { name = "django-money", specifier = ">=3.5.4" }, { name = "django-next-url-mixin", specifier = ">=0.4.0" }, @@ -856,6 +876,7 @@ requires-dist = [ { name = "django-plans", specifier = ">=2.0.0" }, { name = "django-prometheus", specifier = ">=2.4.1" }, { name = "django-q2", specifier = ">=1.8.0" }, + { name = "django-query-builder", specifier = ">=3.2.0" }, { name = "django-schema-graph", specifier = ">=3.1.0" }, { name = "django-sekizai", specifier = ">=4.1.0" }, { name = "django-sequences", specifier = ">=3.0" }, @@ -872,9 +893,11 @@ requires-dist = [ { name = "djangorestframework-simplejwt", specifier = ">=5.5.0" }, { name = "djangoviz", specifier = ">=0.1.1" }, { name = "djhtml", specifier = ">=3.0.8" }, + { name = "djlint", specifier = ">=0.3.4" }, { name = "docopt", specifier = ">=0.6.2" }, { name = "docutils", specifier = ">=0.21.2" }, { name = "easy-thumbnails", specifier = ">=2.10" }, + { name = "editorconfig", specifier = ">=0.17.1" }, { name = "emoji", specifier = ">=2.14.1" }, { name = "et-xmlfile", specifier = ">=2.0.0" }, { name = "eval-type-backport", specifier = ">=0.2.2" }, @@ -884,6 +907,7 @@ requires-dist = [ { name = "fastavro", specifier = ">=1.11.1" }, { name = "filelock", specifier = ">=3.18.0" }, { name = "fire", specifier = ">=0.7.0" }, + { name = "fleming", specifier = ">=0.7.0" }, { name = "fonttools", specifier = ">=4.58.4" }, { name = "fpdf", specifier = ">=1.7.2" }, { name = "fpdf2", specifier = ">=2.8.3" }, @@ -921,6 +945,8 @@ requires-dist = [ { name = "jiter", specifier = ">=0.10.0" }, { name = "jmespath", specifier = ">=1.0.1" }, { name = "joblib", specifier = ">=1.5.1" }, + { name = "jsbeautifier", specifier = ">=1.15.4" }, + { name = "json5", specifier = ">=0.12.0" }, { name = "jsonpatch", specifier = ">=1.33" }, { name = "jsonpointer", specifier = ">=3.0.0" }, { name = "jwt", specifier = ">=1.4.0" }, @@ -978,8 +1004,10 @@ requires-dist = [ { name = "packaging", specifier = ">=24.2" }, { name = "pandas", specifier = ">=2.3.0" }, { name = "pango", specifier = ">=0.0.1" }, + { name = "pathspec", specifier = ">=0.12.1" }, { name = "pdfkit", specifier = ">=1.0.0" }, { name = "phonenumbers", specifier = ">=9.0.8" }, + { name = "pilkit", specifier = ">=3.0" }, { name = "pillow", specifier = ">=11.2.1" }, { name = "platformdirs", specifier = ">=4.3.8" }, { name = "prometheus-client", specifier = ">=0.22.1" }, @@ -1053,6 +1081,7 @@ requires-dist = [ { name = "rich", specifier = ">=14.0.0" }, { name = "rsa", specifier = ">=4.9.1" }, { name = "rubicon-objc", specifier = ">=0.5.1" }, + { name = "ruff", specifier = ">=0.12.4" }, { name = "s3transfer", specifier = ">=0.13.0" }, { name = "sacremoses", specifier = ">=0.1.1" }, { name = "safetensors", specifier = ">=0.5.3" }, @@ -1062,6 +1091,7 @@ requires-dist = [ { name = "selenium", specifier = ">=4.32.0" }, { name = "sentence-transformers", specifier = ">=4.1.0" }, { name = "sentencepiece", specifier = ">=0.2.0" }, + { name = "setuptools", specifier = ">=80.9.0" }, { name = "shapely", specifier = ">=2.1.1" }, { name = "simsimd", specifier = ">=6.4.9" }, { name = "six", specifier = ">=1.17.0" }, @@ -1181,6 +1211,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009, upload-time = "2024-09-04T20:44:45.309Z" }, ] +[[package]] +name = "channels" +version = "4.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asgiref" }, + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/16/d6/049f93c3c96a88265a52f85da91d2635279261bbd4a924b45caa43b8822e/channels-4.2.2.tar.gz", hash = "sha256:8d7208e48ab8fdb972aaeae8311ce920637d97656ffc7ae5eca4f93f84bcd9a0", size = 26647, upload-time = "2025-03-30T14:59:20.35Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/bf/4799809715225d19928147d59fda0d3a4129da055b59a9b3e35aa6223f52/channels-4.2.2-py3-none-any.whl", hash = "sha256:ff36a6e1576cacf40bcdc615fa7aece7a709fc4fdd2dc87f2971f4061ffdaa81", size = 31048, upload-time = "2025-03-30T14:59:18.969Z" }, +] + [[package]] name = "chardet" version = "5.2.0" @@ -1361,6 +1404,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/4b/3256759723b7e66380397d958ca07c59cfc3fb5c794fb5516758afd05d41/cryptography-45.0.4-cp37-abi3-win_amd64.whl", hash = "sha256:627ba1bc94f6adf0b0a2e35d87020285ead22d9f648c7e75bb64f367375f3b22", size = 3395508, upload-time = "2025-06-10T00:03:24.586Z" }, ] +[[package]] +name = "cssbeautifier" +version = "1.15.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "editorconfig" }, + { name = "jsbeautifier" }, + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f7/01/fdf41c1e5f93d359681976ba10410a04b299d248e28ecce1d4e88588dde4/cssbeautifier-1.15.4.tar.gz", hash = "sha256:9bb08dc3f64c101a01677f128acf01905914cf406baf87434dcde05b74c0acf5", size = 25376, upload-time = "2025-02-27T17:53:51.341Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/51/ef6c5628e46092f0a54c7cee69acc827adc6b6aab57b55d344fefbdf28f1/cssbeautifier-1.15.4-py3-none-any.whl", hash = "sha256:78c84d5e5378df7d08622bbd0477a1abdbd209680e95480bf22f12d5701efc98", size = 123667, upload-time = "2025-02-27T17:53:43.594Z" }, +] + [[package]] name = "cssselect2" version = "0.8.0" @@ -1540,6 +1597,18 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/9d/a3/00aa9d5bb5df4f7464495675074dc11107c08b3eea3462fb3edc059d71e1/django_allauth-65.9.0.tar.gz", hash = "sha256:a06bca9974df44321e94c33bcf770bb6f924d1a44b57defbce4d7ec54a55483e", size = 1710514, upload-time = "2025-06-01T19:21:07.771Z" } +[[package]] +name = "django-appconf" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/a9/dcf95ff3fa0620b6818fc02276fbbb8926e7f286039b6d015e56e8b7af39/django-appconf-1.1.0.tar.gz", hash = "sha256:9fcead372f82a0f21ee189434e7ae9c007cbb29af1118c18251720f3d06243e4", size = 15986, upload-time = "2025-02-13T16:09:40.258Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/9e/f3a899991e4aaae4b69c1aa187ba4a32e34742475c91eb13010ee7fbe9db/django_appconf-1.1.0-py3-none-any.whl", hash = "sha256:7abd5a163ff57557f216e84d3ce9dac36c37ffce1ab9a044d3d53b7c943dd10f", size = 6389, upload-time = "2025-02-13T16:09:39.133Z" }, +] + [[package]] name = "django-appointment" version = "3.6.0" @@ -1715,6 +1784,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/12/63/91a107e3aaaf3987bad036494dfd8cc2675f4a66d22e65ffd6711f84ba70/django_formtools-2.5.1-py3-none-any.whl", hash = "sha256:bce9b64eda52cc1eef6961cc649cf75aacd1a707c2fff08d6c3efcbc8e7e761a", size = 170651, upload-time = "2023-12-19T10:30:02.816Z" }, ] +[[package]] +name = "django-imagekit" +version = "5.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django-appconf" }, + { name = "pilkit" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/cd/02611fc0cda31b17fec62c3235234ccf9920a4137de2b677e603749b968e/django-imagekit-5.0.0.tar.gz", hash = "sha256:aae9f74a8e9b6ceb5d15f7d8e266302901e76d9f532c78bd5135cb0fa206a6b0", size = 60041, upload-time = "2023-09-28T00:30:40.062Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/0e/a2a2044e7dab7c7aced92574b7428d65edc370024b94a72d5f00293076fe/django_imagekit-5.0.0-py3-none-any.whl", hash = "sha256:a8e77ed6549751026a51f961bb2cd5fda739be691496da8eecbe68ffb966c261", size = 39240, upload-time = "2023-09-28T01:00:43.931Z" }, +] + [[package]] name = "django-import-export" version = "4.3.8" @@ -1761,6 +1843,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6e/ee/bda575f6be38a5b295e5242215ef59765b6c8e0604a0f9dbafc0a256a916/django_ledger-0.7.8-py3-none-any.whl", hash = "sha256:3a335162cfefedd50f83966d8c373c90b450f6afce76d4567525450ad55cf8bf", size = 2668860, upload-time = "2025-06-25T15:01:45.842Z" }, ] +[[package]] +name = "django-manager-utils" +version = "3.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, + { name = "django-query-builder" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/32/f8/1bbdb3c5c87e541a6ab5aa69bf0bb9e5bfb293d8777a0ddc993990684471/django-manager-utils-3.1.5.tar.gz", hash = "sha256:eabcccb43dfb76ace4b3998ac7084d081dfbada1d3444f3630e4671ef0f73065", size = 21425, upload-time = "2024-08-07T02:36:51.024Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/ec/ec78f08c38ceb38e273fed5568a0ebdf5dbca4b3ead02ec4f0d4bca8bd37/django_manager_utils-3.1.5-py2.py3-none-any.whl", hash = "sha256:df914093da591d08f6e58be8497f45aaccc411a436da25b8481ec9958a94e9db", size = 21906, upload-time = "2024-08-07T02:36:49.661Z" }, +] + [[package]] name = "django-model-utils" version = "5.0.0" @@ -1913,6 +2008,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3f/85/aa9838ac8b65dff962366e0d79eed5fa0c2170218ea0122a110ca4638471/django_q2-1.8.0-py3-none-any.whl", hash = "sha256:78aaaf18dff1ad3e35bcf6556666f2c26494120f0b75961c13206e37d180dfaa", size = 89579, upload-time = "2025-04-25T14:42:36.029Z" }, ] +[[package]] +name = "django-query-builder" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, + { name = "fleming" }, + { name = "pytz" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/0d/42dfd9ac19ba98dfe2487b0bcec26f21412bc0fcb1f4efe0ecb7daf63ea7/django-query-builder-3.2.0.tar.gz", hash = "sha256:e22228cb66305a08b2763a46c028b14c6627e7b31d536c7dd8a1c37c1a1c8c70", size = 49965, upload-time = "2023-08-29T20:58:44.639Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/0a/40c91a58906c7fb87733e8f623052fcd2a8ae6d92cdb2501b1c34b4380a2/django_query_builder-3.2.0-py2.py3-none-any.whl", hash = "sha256:560ee939533b9e2cdd707a13a9cb79e34599d8455405934d27233532889c8abf", size = 57010, upload-time = "2023-08-29T20:58:42.88Z" }, +] + [[package]] name = "django-schema-graph" version = "3.1.0" @@ -2103,6 +2212,22 @@ version = "3.0.8" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/49/e8/c2d12facfc47fec732633ea3c2d820298e0e314331fc43bcf694099abcb5/djhtml-3.0.8.tar.gz", hash = "sha256:ec3b4cf25f0959474c7793da1becba654ca9587689ce143955bcbc2638eeabce", size = 27886, upload-time = "2025-05-22T07:32:23.81Z" } +[[package]] +name = "djlint" +version = "0.3.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "colorama" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6c/7b/bbc7107c1df7f7252ea8e7b047198e80021918bf56cdf0a699070fbb656e/djlint-0.3.4.tar.gz", hash = "sha256:f4b4d49609bd2177ad1e34ab19047fae5fc185abfd8881e18a2217c14441f330", size = 555127, upload-time = "2021-09-01T09:57:30.303Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/98/bf948c374f7d568dceee27673c82b0ef704125f631da6c335bc179a7fe30/djlint-0.3.4-py3-none-any.whl", hash = "sha256:0075fdbccd132c5828ae73650ae925fce8bd5a224b18568f28365a14476da8c1", size = 26703, upload-time = "2021-09-01T09:57:28.962Z" }, +] + [[package]] name = "docopt" version = "0.6.2" @@ -2131,6 +2256,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/22/ef/de1f3a7421d7a9ce2314ee7bc0d37a8264a50872e2cd5af1240798b0bbba/easy_thumbnails-2.10-py3-none-any.whl", hash = "sha256:9e446fb9da1ca0cbc99bdd1fe8fdd5c38784323d64393a37e16d13cd736980b2", size = 78939, upload-time = "2024-09-12T12:40:59.135Z" }, ] +[[package]] +name = "editorconfig" +version = "0.17.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/88/3a/a61d9a1f319a186b05d14df17daea42fcddea63c213bcd61a929fb3a6796/editorconfig-0.17.1.tar.gz", hash = "sha256:23c08b00e8e08cc3adcddb825251c497478df1dada6aefeb01e626ad37303745", size = 14695, upload-time = "2025-06-09T08:21:37.097Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/fd/a40c621ff207f3ce8e484aa0fc8ba4eb6e3ecf52e15b42ba764b457a9550/editorconfig-0.17.1-py3-none-any.whl", hash = "sha256:1eda9c2c0db8c16dbd50111b710572a5e6de934e39772de1959d41f64fc17c82", size = 16360, upload-time = "2025-06-09T08:21:35.654Z" }, +] + [[package]] name = "emoji" version = "2.14.1" @@ -2251,6 +2385,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cd/77/59df23681f4fd19b7cbbb5e92484d46ad587554f5d490f33ef907e456132/Flask-2.0.3-py3-none-any.whl", hash = "sha256:59da8a3170004800a2837844bfa84d49b022550616070f7cb1a659682b2e7c9f", size = 95575, upload-time = "2022-02-14T20:01:06.923Z" }, ] +[[package]] +name = "fleming" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, + { name = "pytz" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/ac/2e6429ac69973336be624be1f2451eb61c368526c40d1652319fd41d6049/fleming-0.7.0.tar.gz", hash = "sha256:dd93f95f17f220d8ec3cedc2090b3703d7499e6c723bc9f45eb5f7bda8d75a63", size = 11917, upload-time = "2022-04-07T20:23:21.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/e8/57033f12be2b5ec04f2d3ec168a7de132e586a6d0b28112988a15a03123a/fleming-0.7.0-py2.py3-none-any.whl", hash = "sha256:9bdd83f8497c19be4e316726b8419be45ab5000c6398876e6aea456676345613", size = 10570, upload-time = "2022-04-07T20:23:19.729Z" }, +] + [[package]] name = "fonttools" version = "4.58.4" @@ -2819,6 +2966,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7d/4f/1195bbac8e0c2acc5f740661631d8d750dc38d4a32b23ee5df3cde6f4e0d/joblib-1.5.1-py3-none-any.whl", hash = "sha256:4719a31f054c7d766948dcd83e9613686b27114f190f717cec7eaa2084f8a74a", size = 307746, upload-time = "2025-05-23T12:04:35.124Z" }, ] +[[package]] +name = "jsbeautifier" +version = "1.15.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "editorconfig" }, + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ea/98/d6cadf4d5a1c03b2136837a435682418c29fdeb66be137128544cecc5b7a/jsbeautifier-1.15.4.tar.gz", hash = "sha256:5bb18d9efb9331d825735fbc5360ee8f1aac5e52780042803943aa7f854f7592", size = 75257, upload-time = "2025-02-27T17:53:53.252Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/14/1c65fccf8413d5f5c6e8425f84675169654395098000d8bddc4e9d3390e1/jsbeautifier-1.15.4-py3-none-any.whl", hash = "sha256:72f65de312a3f10900d7685557f84cb61a9733c50dcc27271a39f5b0051bf528", size = 94707, upload-time = "2025-02-27T17:53:46.152Z" }, +] + +[[package]] +name = "json5" +version = "0.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/12/be/c6c745ec4c4539b25a278b70e29793f10382947df0d9efba2fa09120895d/json5-0.12.0.tar.gz", hash = "sha256:0b4b6ff56801a1c7dc817b0241bca4ce474a0e6a163bfef3fc594d3fd263ff3a", size = 51907, upload-time = "2025-04-03T16:33:13.201Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/9f/3500910d5a98549e3098807493851eeef2b89cdd3032227558a104dfe926/json5-0.12.0-py3-none-any.whl", hash = "sha256:6d37aa6c08b0609f16e1ec5ff94697e2cbbfbad5ac112afa05794da9ab7810db", size = 36079, upload-time = "2025-04-03T16:33:11.927Z" }, +] + [[package]] name = "jsonpatch" version = "1.33" @@ -3939,6 +4108,15 @@ version = "0.0.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/c7/0b/8c17d637b4cb687cadb2460d30e9ada0518690bb551d2c65aa4c587f93c1/pango-0.0.1.tar.gz", hash = "sha256:70843893a43933d33db2820832563c168ca3f3af0101248b9a58f079fb36fbe8", size = 1021, upload-time = "2023-09-18T14:55:57.306Z" } +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, +] + [[package]] name = "pdfkit" version = "1.0.0" @@ -3957,6 +4135,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/48/2e/23c1eca9f73e332b947c66c431690672783cf49b8418aa650ce06bdf6115/phonenumbers-9.0.8-py2.py3-none-any.whl", hash = "sha256:53d357111c0ead0d6408ae443613b18d3a053431ca1ddf7e881457c0969afcf9", size = 2583252, upload-time = "2025-06-25T05:41:34.815Z" }, ] +[[package]] +name = "pilkit" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pillow" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/a5/bbe12d2c9dc06e29224c45a2cd7aa0ce923648588b6a15aadfee150bbd0c/pilkit-3.0.tar.gz", hash = "sha256:f6719e8cc0482e5447f5cb94f18b949d8e604ea9673a9b019c74d41b779e4eab", size = 402342, upload-time = "2023-09-27T22:47:18.638Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/ec/877b84b82cbcba6203e39d068cecfdbcfb61de69260cb851ea11be9b67f3/pilkit-3.0-py3-none-any.whl", hash = "sha256:fe1707b0411a1d0cbf9ad3986779fa5a346cec4582a188740924aa39f504d117", size = 20073, upload-time = "2023-09-27T22:47:16.285Z" }, +] + [[package]] name = "pillow" version = "11.2.1" @@ -5060,6 +5250,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/58/0a/e451c3dbda38dd6abab1fd16c3b35623fc0635dffcbbf97f1acc55a58508/rubicon_objc-0.5.1-py3-none-any.whl", hash = "sha256:17092756241b8370231cfaad45ad6e8ce99534987f2acbc944d65df5bdf8f6cd", size = 63323, upload-time = "2025-06-03T06:33:48.863Z" }, ] +[[package]] +name = "ruff" +version = "0.12.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9b/ce/8d7dbedede481245b489b769d27e2934730791a9a82765cb94566c6e6abd/ruff-0.12.4.tar.gz", hash = "sha256:13efa16df6c6eeb7d0f091abae50f58e9522f3843edb40d56ad52a5a4a4b6873", size = 5131435, upload-time = "2025-07-17T17:27:19.138Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/9f/517bc5f61bad205b7f36684ffa5415c013862dee02f55f38a217bdbe7aa4/ruff-0.12.4-py3-none-linux_armv6l.whl", hash = "sha256:cb0d261dac457ab939aeb247e804125a5d521b21adf27e721895b0d3f83a0d0a", size = 10188824, upload-time = "2025-07-17T17:26:31.412Z" }, + { url = "https://files.pythonhosted.org/packages/28/83/691baae5a11fbbde91df01c565c650fd17b0eabed259e8b7563de17c6529/ruff-0.12.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:55c0f4ca9769408d9b9bac530c30d3e66490bd2beb2d3dae3e4128a1f05c7442", size = 10884521, upload-time = "2025-07-17T17:26:35.084Z" }, + { url = "https://files.pythonhosted.org/packages/d6/8d/756d780ff4076e6dd035d058fa220345f8c458391f7edfb1c10731eedc75/ruff-0.12.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a8224cc3722c9ad9044da7f89c4c1ec452aef2cfe3904365025dd2f51daeae0e", size = 10277653, upload-time = "2025-07-17T17:26:37.897Z" }, + { url = "https://files.pythonhosted.org/packages/8d/97/8eeee0f48ece153206dce730fc9e0e0ca54fd7f261bb3d99c0a4343a1892/ruff-0.12.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9949d01d64fa3672449a51ddb5d7548b33e130240ad418884ee6efa7a229586", size = 10485993, upload-time = "2025-07-17T17:26:40.68Z" }, + { url = "https://files.pythonhosted.org/packages/49/b8/22a43d23a1f68df9b88f952616c8508ea6ce4ed4f15353b8168c48b2d7e7/ruff-0.12.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:be0593c69df9ad1465e8a2d10e3defd111fdb62dcd5be23ae2c06da77e8fcffb", size = 10022824, upload-time = "2025-07-17T17:26:43.564Z" }, + { url = "https://files.pythonhosted.org/packages/cd/70/37c234c220366993e8cffcbd6cadbf332bfc848cbd6f45b02bade17e0149/ruff-0.12.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7dea966bcb55d4ecc4cc3270bccb6f87a337326c9dcd3c07d5b97000dbff41c", size = 11524414, upload-time = "2025-07-17T17:26:46.219Z" }, + { url = "https://files.pythonhosted.org/packages/14/77/c30f9964f481b5e0e29dd6a1fae1f769ac3fd468eb76fdd5661936edd262/ruff-0.12.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:afcfa3ab5ab5dd0e1c39bf286d829e042a15e966b3726eea79528e2e24d8371a", size = 12419216, upload-time = "2025-07-17T17:26:48.883Z" }, + { url = "https://files.pythonhosted.org/packages/6e/79/af7fe0a4202dce4ef62c5e33fecbed07f0178f5b4dd9c0d2fcff5ab4a47c/ruff-0.12.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c057ce464b1413c926cdb203a0f858cd52f3e73dcb3270a3318d1630f6395bb3", size = 11976756, upload-time = "2025-07-17T17:26:51.754Z" }, + { url = "https://files.pythonhosted.org/packages/09/d1/33fb1fc00e20a939c305dbe2f80df7c28ba9193f7a85470b982815a2dc6a/ruff-0.12.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e64b90d1122dc2713330350626b10d60818930819623abbb56535c6466cce045", size = 11020019, upload-time = "2025-07-17T17:26:54.265Z" }, + { url = "https://files.pythonhosted.org/packages/64/f4/e3cd7f7bda646526f09693e2e02bd83d85fff8a8222c52cf9681c0d30843/ruff-0.12.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2abc48f3d9667fdc74022380b5c745873499ff827393a636f7a59da1515e7c57", size = 11277890, upload-time = "2025-07-17T17:26:56.914Z" }, + { url = "https://files.pythonhosted.org/packages/5e/d0/69a85fb8b94501ff1a4f95b7591505e8983f38823da6941eb5b6badb1e3a/ruff-0.12.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2b2449dc0c138d877d629bea151bee8c0ae3b8e9c43f5fcaafcd0c0d0726b184", size = 10348539, upload-time = "2025-07-17T17:26:59.381Z" }, + { url = "https://files.pythonhosted.org/packages/16/a0/91372d1cb1678f7d42d4893b88c252b01ff1dffcad09ae0c51aa2542275f/ruff-0.12.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:56e45bb11f625db55f9b70477062e6a1a04d53628eda7784dce6e0f55fd549eb", size = 10009579, upload-time = "2025-07-17T17:27:02.462Z" }, + { url = "https://files.pythonhosted.org/packages/23/1b/c4a833e3114d2cc0f677e58f1df6c3b20f62328dbfa710b87a1636a5e8eb/ruff-0.12.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:478fccdb82ca148a98a9ff43658944f7ab5ec41c3c49d77cd99d44da019371a1", size = 10942982, upload-time = "2025-07-17T17:27:05.343Z" }, + { url = "https://files.pythonhosted.org/packages/ff/ce/ce85e445cf0a5dd8842f2f0c6f0018eedb164a92bdf3eda51984ffd4d989/ruff-0.12.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0fc426bec2e4e5f4c4f182b9d2ce6a75c85ba9bcdbe5c6f2a74fcb8df437df4b", size = 11343331, upload-time = "2025-07-17T17:27:08.652Z" }, + { url = "https://files.pythonhosted.org/packages/35/cf/441b7fc58368455233cfb5b77206c849b6dfb48b23de532adcc2e50ccc06/ruff-0.12.4-py3-none-win32.whl", hash = "sha256:4de27977827893cdfb1211d42d84bc180fceb7b72471104671c59be37041cf93", size = 10267904, upload-time = "2025-07-17T17:27:11.814Z" }, + { url = "https://files.pythonhosted.org/packages/ce/7e/20af4a0df5e1299e7368d5ea4350412226afb03d95507faae94c80f00afd/ruff-0.12.4-py3-none-win_amd64.whl", hash = "sha256:fe0b9e9eb23736b453143d72d2ceca5db323963330d5b7859d60d101147d461a", size = 11209038, upload-time = "2025-07-17T17:27:14.417Z" }, + { url = "https://files.pythonhosted.org/packages/11/02/8857d0dfb8f44ef299a5dfd898f673edefb71e3b533b3b9d2db4c832dd13/ruff-0.12.4-py3-none-win_arm64.whl", hash = "sha256:0618ec4442a83ab545e5b71202a5c0ed7791e8471435b94e655b570a5031a98e", size = 10469336, upload-time = "2025-07-17T17:27:16.913Z" }, +] + [[package]] name = "s3transfer" version = "0.13.0"