feat: update default user timezone to 'America/New_York' and add time conflict checks for scheduling appointments #76

Merged
Saani merged 1 commits from feature/meetings into main 2025-12-07 13:36:15 +00:00
3 changed files with 47 additions and 10 deletions
Showing only changes of commit ff3d135a20 - Show all commits

View File

@ -0,0 +1,18 @@
# Generated by Django 5.2.8 on 2025-12-07 12:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('meetings', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='appointmentrequest',
name='user_timezone',
field=models.CharField(blank=True, default='America/New_York', max_length=63),
),
]

View File

@ -12,6 +12,7 @@ import time
from datetime import datetime, timedelta from datetime import datetime, timedelta
import jwt import jwt
import json import json
from django.db import IntegrityError
class EncryptionManager: class EncryptionManager:
def __init__(self): def __init__(self):
@ -205,7 +206,7 @@ class AppointmentRequest(models.Model):
) )
scheduled_datetime = models.DateTimeField(null=True, blank=True) scheduled_datetime = models.DateTimeField(null=True, blank=True)
user_timezone = models.CharField(max_length=63, default='UTC', blank=True) user_timezone = models.CharField(max_length=63, default='America/New_York', blank=True)
scheduled_duration = models.PositiveIntegerField( scheduled_duration = models.PositiveIntegerField(
default=30, default=30,
help_text="Duration in minutes" help_text="Duration in minutes"
@ -385,6 +386,17 @@ class AppointmentRequest(models.Model):
self.jitsi_room_id = f"appointment_{str(self.id).replace('-', '')}_{timestamp}" self.jitsi_room_id = f"appointment_{str(self.id).replace('-', '')}_{timestamp}"
return self.jitsi_room_id return self.jitsi_room_id
def _time_conflicts(self, start_dt, duration, exclude_self=False):
end_dt = start_dt + timedelta(minutes=duration)
qs = AppointmentRequest.objects.filter(
scheduled_datetime__lt=end_dt,
scheduled_datetime__gte=start_dt - timedelta(minutes=duration),
status='scheduled'
)
if exclude_self:
qs = qs.exclude(pk=self.pk)
return qs.exists()
def generate_jwt_token(self, user, user_type='participant'): def generate_jwt_token(self, user, user_type='participant'):
if not self.jitsi_room_id: if not self.jitsi_room_id:
@ -635,7 +647,11 @@ class AppointmentRequest(models.Model):
else: else:
return meeting_start - timedelta(minutes=5) <= now <= meeting_end return meeting_start - timedelta(minutes=5) <= now <= meeting_end
def schedule_appointment(self, datetime_obj, moderator_user, participant_user=None, duration=60, create_meeting=True, commit=True): def schedule_appointment(self, datetime_obj, duration=30, moderator_user=None, participant_user=None, create_meeting=True, commit=True):
if self._time_conflicts(datetime_obj, duration, exclude_self=False):
raise IntegrityError("Time slot already booked — cannot schedule appointment at that datetime.")
self.status = 'scheduled' self.status = 'scheduled'
self.scheduled_datetime = datetime_obj self.scheduled_datetime = datetime_obj
self.scheduled_duration = duration self.scheduled_duration = duration
@ -647,18 +663,22 @@ class AppointmentRequest(models.Model):
participant_user=participant_user, participant_user=participant_user,
with_moderation=True with_moderation=True
) )
if commit: if commit:
self.save() self.save()
def reschedule_appointment(self, new_datetime, new_duration, commit=True):
def reschedule_appointment(self, new_datetime, new_duration=30, commit=True):
if self._time_conflicts(new_datetime, new_duration, exclude_self=True):
raise IntegrityError("Time slot already booked — cannot reschedule to that datetime.")
self.status = 'scheduled' self.status = 'scheduled'
self.scheduled_datetime = new_datetime self.scheduled_datetime = new_datetime
self.scheduled_duration = new_duration self.scheduled_duration = new_duration
self.rejection_reason = ''
if commit: if commit:
self.save() self.save()
return self
def reject_appointment(self, reason='', commit=True): def reject_appointment(self, reason='', commit=True):
self.status = 'rejected' self.status = 'rejected'

View File

@ -224,7 +224,7 @@ class AppointmentRequestCreateSerializer(serializers.ModelSerializer):
) )
timezone = serializers.CharField( timezone = serializers.CharField(
required=False, required=False,
default='UTC', default='America/New_York',
help_text="User's timezone (e.g., 'America/New_York', 'Africa/Accra')" help_text="User's timezone (e.g., 'America/New_York', 'Africa/Accra')"
) )
@ -236,7 +236,6 @@ class AppointmentRequestCreateSerializer(serializers.ModelSerializer):
] ]
def validate_timezone(self, value): def validate_timezone(self, value):
"""Validate that the timezone string is valid"""
try: try:
from zoneinfo import ZoneInfo from zoneinfo import ZoneInfo
ZoneInfo(value) ZoneInfo(value)
@ -338,7 +337,7 @@ class AppointmentScheduleSerializer(serializers.Serializer):
time_slot = serializers.CharField(required=False, write_only=True) time_slot = serializers.CharField(required=False, write_only=True)
create_jitsi_meeting = serializers.BooleanField(default=True) create_jitsi_meeting = serializers.BooleanField(default=True)
jitsi_custom_config = serializers.JSONField(required=False, default=dict) jitsi_custom_config = serializers.JSONField(required=False, default=dict)
timezone = serializers.CharField(required=False, default='UTC') timezone = serializers.CharField(required=False, default='America/New_York')
def validate(self, data): def validate(self, data):
scheduled_datetime = data.get('scheduled_datetime') scheduled_datetime = data.get('scheduled_datetime')