diff --git a/meetings/migrations/0001_initial.py b/meetings/migrations/0001_initial.py new file mode 100644 index 0000000..362f868 --- /dev/null +++ b/meetings/migrations/0001_initial.py @@ -0,0 +1,67 @@ +# Generated by Django 5.2.8 on 2025-12-04 11:36 + +import meetings.models +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='AdminWeeklyAvailability', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('availability_schedule', models.JSONField(default=dict, help_text='Dictionary with days as keys and lists of time slots as values')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ], + options={ + 'verbose_name': 'Admin Weekly Availability', + 'verbose_name_plural': 'Admin Weekly Availability', + }, + ), + migrations.CreateModel( + name='AppointmentRequest', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('first_name', meetings.models.EncryptedCharField(max_length=2000)), + ('last_name', meetings.models.EncryptedCharField(max_length=2000)), + ('email', meetings.models.EncryptedEmailField(max_length=2000)), + ('phone', meetings.models.EncryptedCharField(blank=True, max_length=2000)), + ('reason', meetings.models.EncryptedTextField(blank=True)), + ('preferred_dates', models.JSONField(help_text='List of preferred dates (YYYY-MM-DD format)')), + ('preferred_time_slots', models.JSONField(help_text='List of preferred time slots (morning/afternoon/evening)')), + ('status', models.CharField(choices=[('pending_review', 'Pending Review'), ('scheduled', 'Scheduled'), ('rejected', 'Rejected'), ('completed', 'Completed'), ('cancelled', 'Cancelled')], default='pending_review', max_length=20)), + ('scheduled_datetime', models.DateTimeField(blank=True, null=True)), + ('scheduled_duration', models.PositiveIntegerField(default=60, help_text='Duration in minutes')), + ('rejection_reason', meetings.models.EncryptedTextField(blank=True, max_length=1000, null=True)), + ('jitsi_meet_url', models.URLField(blank=True, help_text='Jitsi Meet URL for the video session', null=True)), + ('jitsi_room_id', models.CharField(blank=True, help_text='Jitsi room ID', max_length=100, null=True, unique=True)), + ('jitsi_meeting_created', models.BooleanField(default=False)), + ('jitsi_moderator_token', models.TextField(blank=True, null=True)), + ('jitsi_participant_token', models.TextField(blank=True, null=True)), + ('jitsi_meeting_password', models.CharField(blank=True, max_length=2000, null=True)), + ('jitsi_meeting_config', models.JSONField(default=dict, help_text='Jitsi meeting configuration and settings')), + ('jitsi_recording_url', models.URLField(blank=True, help_text='URL to meeting recording', null=True)), + ('jitsi_meeting_data', models.JSONField(default=dict, help_text='Additional meeting data (participants, duration, etc)')), + ('selected_slots', models.JSONField(default=list, help_text='Original selected slots with day and time slot pairs')), + ('meeting_started_at', models.DateTimeField(blank=True, null=True)), + ('meeting_ended_at', models.DateTimeField(blank=True, null=True)), + ('meeting_duration_actual', models.PositiveIntegerField(default=0, help_text='Actual meeting duration in minutes')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ], + options={ + 'verbose_name': 'Appointment Request', + 'verbose_name_plural': 'Appointment Requests', + 'ordering': ['-created_at'], + 'indexes': [models.Index(fields=['status', 'scheduled_datetime'], name='meetings_ap_status_4e4e26_idx'), models.Index(fields=['email', 'created_at'], name='meetings_ap_email_b8ed9d_idx'), models.Index(fields=['jitsi_meeting_created', 'scheduled_datetime'], name='meetings_ap_jitsi_m_f3c488_idx'), models.Index(fields=['meeting_started_at'], name='meetings_ap_meeting_157142_idx')], + }, + ), + ] diff --git a/meetings/migrations/0002_alter_appointmentrequest_email_and_more.py b/meetings/migrations/0002_alter_appointmentrequest_email_and_more.py new file mode 100644 index 0000000..7e0fa8a --- /dev/null +++ b/meetings/migrations/0002_alter_appointmentrequest_email_and_more.py @@ -0,0 +1,64 @@ +# Generated by Django 5.2.8 on 2025-12-04 12:00 + +import meetings.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('meetings', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='appointmentrequest', + name='email', + field=meetings.models.EncryptedEmailField(), + ), + migrations.AlterField( + model_name='appointmentrequest', + name='first_name', + field=meetings.models.EncryptedCharField(), + ), + migrations.AlterField( + model_name='appointmentrequest', + name='jitsi_meeting_password', + field=models.CharField(blank=True, null=True), + ), + migrations.AlterField( + model_name='appointmentrequest', + name='jitsi_room_id', + field=models.CharField(blank=True, help_text='Jitsi room ID', null=True, unique=True), + ), + migrations.AlterField( + model_name='appointmentrequest', + name='last_name', + field=meetings.models.EncryptedCharField(), + ), + migrations.AlterField( + model_name='appointmentrequest', + name='phone', + field=meetings.models.EncryptedCharField(blank=True, null=True), + ), + migrations.AlterField( + model_name='appointmentrequest', + name='reason', + field=meetings.models.EncryptedTextField(blank=True, null=True), + ), + migrations.AlterField( + model_name='appointmentrequest', + name='rejection_reason', + field=meetings.models.EncryptedTextField(blank=True, null=True), + ), + migrations.AlterField( + model_name='appointmentrequest', + name='scheduled_duration', + field=models.PositiveIntegerField(default=30, help_text='Duration in minutes'), + ), + migrations.AlterField( + model_name='appointmentrequest', + name='status', + field=models.CharField(choices=[('pending_review', 'Pending Review'), ('scheduled', 'Scheduled'), ('rejected', 'Rejected'), ('completed', 'Completed'), ('cancelled', 'Cancelled')], default='pending_review'), + ), + ] diff --git a/meetings/models.py b/meetings/models.py index 716eea7..bf777c1 100644 --- a/meetings/models.py +++ b/meetings/models.py @@ -60,7 +60,6 @@ class EncryptedCharField(models.CharField): class EncryptedEmailField(EncryptedCharField): def __init__(self, *args, **kwargs): - kwargs['max_length'] = 2000 super().__init__(*args, **kwargs) def from_db_value(self, value, expression, connection): @@ -86,7 +85,6 @@ class EncryptedTextField(models.TextField): class EncryptedURLField(EncryptedCharField): def __init__(self, *args, **kwargs): - kwargs['max_length'] = kwargs.get('max_length', 3000) super().__init__(*args, **kwargs) class AdminWeeklyAvailability(models.Model): @@ -187,11 +185,11 @@ class AppointmentRequest(models.Model): ] id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - first_name = EncryptedCharField(max_length=2000) - last_name = EncryptedCharField(max_length=2000) - email = EncryptedEmailField(max_length=2000) - phone = EncryptedCharField(max_length=2000, blank=True) - reason = EncryptedTextField(blank=True) + first_name = EncryptedCharField() + last_name = EncryptedCharField() + email = EncryptedEmailField() + phone = EncryptedCharField(null=True, blank=True) + reason = EncryptedTextField(blank=True, null=True) preferred_dates = models.JSONField( help_text="List of preferred dates (YYYY-MM-DD format)" @@ -201,17 +199,16 @@ class AppointmentRequest(models.Model): ) status = models.CharField( - max_length=20, choices=STATUS_CHOICES, default='pending_review' ) scheduled_datetime = models.DateTimeField(null=True, blank=True) scheduled_duration = models.PositiveIntegerField( - default=60, + default=30, help_text="Duration in minutes" ) - rejection_reason = EncryptedTextField(blank=True, null=True, max_length=1000) + rejection_reason = EncryptedTextField(blank=True, null=True) jitsi_meet_url = models.URLField( blank=True, @@ -219,7 +216,6 @@ class AppointmentRequest(models.Model): help_text="Jitsi Meet URL for the video session" ) jitsi_room_id = models.CharField( - max_length=100, unique=True, null=True, blank=True, @@ -228,7 +224,7 @@ class AppointmentRequest(models.Model): jitsi_meeting_created = models.BooleanField(default=False) jitsi_moderator_token = models.TextField(blank=True, null=True) jitsi_participant_token = models.TextField(blank=True, null=True) - jitsi_meeting_password = models.CharField(max_length=2000, blank=True, null=True) + jitsi_meeting_password = models.CharField( blank=True, null=True) jitsi_meeting_config = models.JSONField( default=dict, diff --git a/users/migrations/0001_initial.py b/users/migrations/0001_initial.py new file mode 100644 index 0000000..c3944a6 --- /dev/null +++ b/users/migrations/0001_initial.py @@ -0,0 +1,72 @@ +# Generated by Django 5.2.8 on 2025-12-04 11:36 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='ContactMessage', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('email', models.EmailField(max_length=254)), + ('phone', models.CharField(blank=True, max_length=20)), + ('message', models.TextField()), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('is_read', models.BooleanField(default=False)), + ('is_responded', models.BooleanField(default=False)), + ], + options={ + 'verbose_name': 'Contact Message', + 'verbose_name_plural': 'Contact Messages', + 'ordering': ['-created_at'], + }, + ), + migrations.CreateModel( + name='CustomUser', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('email', models.EmailField(max_length=254, unique=True)), + ('first_name', models.CharField(max_length=50)), + ('last_name', models.CharField(max_length=50)), + ('is_staff', models.BooleanField(default=False)), + ('is_superuser', models.BooleanField(default=False)), + ('is_active', models.BooleanField(default=True)), + ('isVerified', models.BooleanField(default=False)), + ('verify_otp', models.CharField(blank=True, max_length=6, null=True)), + ('verify_otp_expiry', models.DateTimeField(blank=True, null=True)), + ('forgot_password_otp', models.CharField(blank=True, max_length=6, null=True)), + ('forgot_password_otp_expiry', models.DateTimeField(blank=True, null=True)), + ('phone_number', models.CharField(blank=True, max_length=20)), + ('last_login', models.DateTimeField(auto_now=True)), + ('date_joined', models.DateTimeField(auto_now_add=True)), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='UserProfile', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('bio', models.TextField(blank=True, max_length=500)), + ('timezone', models.CharField(default='UTC', max_length=50)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL)), + ], + ), + ]