Compare commits
2 Commits
fd3126d341
...
a45773997b
| Author | SHA1 | Date | |
|---|---|---|---|
| a45773997b | |||
| dbde93f444 |
@ -515,11 +515,6 @@ class AppointmentRequest(models.Model):
|
|||||||
|
|
||||||
self.jitsi_meeting_config = meeting_config
|
self.jitsi_meeting_config = meeting_config
|
||||||
self.jitsi_meeting_created = True
|
self.jitsi_meeting_created = True
|
||||||
|
|
||||||
print(f"DEBUG - Field lengths before save:")
|
|
||||||
print(f"jitsi_room_id: {len(self.jitsi_room_id) if self.jitsi_room_id else 0} chars")
|
|
||||||
print(f"jitsi_meet_url: {len(self.jitsi_meet_url) if self.jitsi_meet_url else 0} chars")
|
|
||||||
print(f"jitsi_meeting_password: {len(self.jitsi_meeting_password) if self.jitsi_meeting_password else 0} chars")
|
|
||||||
|
|
||||||
self.save()
|
self.save()
|
||||||
return self.jitsi_meet_url
|
return self.jitsi_meet_url
|
||||||
|
|||||||
@ -137,20 +137,17 @@ class AppointmentRequestSerializer(serializers.ModelSerializer):
|
|||||||
if not obj.has_jitsi_meeting or not obj.email:
|
if not obj.has_jitsi_meeting or not obj.email:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Try to get the participant user from the appointment's email
|
|
||||||
participant_user = None
|
participant_user = None
|
||||||
try:
|
try:
|
||||||
from users.models import CustomUser
|
from users.models import CustomUser
|
||||||
participant_user = CustomUser.objects.get(email=obj.email)
|
participant_user = CustomUser.objects.get(email=obj.email)
|
||||||
except CustomUser.DoesNotExist:
|
except CustomUser.DoesNotExist:
|
||||||
# Participant user doesn't exist (not registered)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Check if current user is authorized to view this URL
|
|
||||||
current_user = request.user
|
current_user = request.user
|
||||||
is_authorized = (
|
is_authorized = (
|
||||||
current_user.is_staff or # Staff can see participant URLs for all appointments
|
current_user.is_staff or
|
||||||
current_user.email == obj.email or # Participant can see their own URL
|
current_user.email == obj.email or
|
||||||
(hasattr(obj, 'invited_participants') and current_user.email in obj.invited_participants)
|
(hasattr(obj, 'invited_participants') and current_user.email in obj.invited_participants)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -225,21 +222,35 @@ class AppointmentRequestCreateSerializer(serializers.ModelSerializer):
|
|||||||
required=True,
|
required=True,
|
||||||
help_text="List of selected day-time combinations: [{'day': 0, 'time_slot': 'morning'}]"
|
help_text="List of selected day-time combinations: [{'day': 0, 'time_slot': 'morning'}]"
|
||||||
)
|
)
|
||||||
|
timezone = serializers.CharField(
|
||||||
|
required=False,
|
||||||
|
default='UTC',
|
||||||
|
help_text="User's timezone (e.g., 'America/New_York', 'Africa/Accra')"
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = AppointmentRequest
|
model = AppointmentRequest
|
||||||
fields = [
|
fields = [
|
||||||
'first_name', 'last_name', 'email', 'phone', 'reason',
|
'first_name', 'last_name', 'email', 'phone', 'reason',
|
||||||
'selected_slots'
|
'selected_slots', 'timezone'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def validate_timezone(self, value):
|
||||||
|
"""Validate that the timezone string is valid"""
|
||||||
|
try:
|
||||||
|
from zoneinfo import ZoneInfo
|
||||||
|
ZoneInfo(value)
|
||||||
|
return value
|
||||||
|
except Exception:
|
||||||
|
# If invalid, default to UTC but don't raise error
|
||||||
|
return 'UTC'
|
||||||
|
|
||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
selected_slots = data.get('selected_slots')
|
selected_slots = data.get('selected_slots')
|
||||||
|
|
||||||
if not selected_slots:
|
if not selected_slots:
|
||||||
raise serializers.ValidationError("At least one time slot must be selected.")
|
raise serializers.ValidationError("At least one time slot must be selected.")
|
||||||
|
|
||||||
# Your existing validation logic
|
|
||||||
availability = get_admin_availability()
|
availability = get_admin_availability()
|
||||||
if not availability:
|
if not availability:
|
||||||
raise serializers.ValidationError("No admin availability set.")
|
raise serializers.ValidationError("No admin availability set.")
|
||||||
@ -257,34 +268,27 @@ class AppointmentRequestCreateSerializer(serializers.ModelSerializer):
|
|||||||
f"Slot {i+1}: '{time_slot}' on {day_name} is not available."
|
f"Slot {i+1}: '{time_slot}' on {day_name} is not available."
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add dates to the slots and store enhanced version
|
|
||||||
enhanced_slots = self._add_dates_to_slots(selected_slots)
|
enhanced_slots = self._add_dates_to_slots(selected_slots)
|
||||||
data['selected_slots'] = enhanced_slots
|
data['selected_slots'] = enhanced_slots
|
||||||
|
|
||||||
# Calculate preferred_dates and preferred_time_slots for backward compatibility
|
|
||||||
data['preferred_dates'] = self._extract_unique_dates(enhanced_slots)
|
data['preferred_dates'] = self._extract_unique_dates(enhanced_slots)
|
||||||
data['preferred_time_slots'] = self._extract_unique_time_slots(enhanced_slots)
|
data['preferred_time_slots'] = self._extract_unique_time_slots(enhanced_slots)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def _add_dates_to_slots(self, selected_slots):
|
def _add_dates_to_slots(self, selected_slots):
|
||||||
"""Add actual dates to the slots based on day numbers"""
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
today = datetime.now().date()
|
today = datetime.now().date()
|
||||||
enhanced_slots = []
|
enhanced_slots = []
|
||||||
|
|
||||||
# We need to find the next occurrence of each day
|
|
||||||
# Keep track of which dates we've assigned to which day
|
|
||||||
day_date_map = {}
|
day_date_map = {}
|
||||||
|
|
||||||
for slot in selected_slots:
|
for slot in selected_slots:
|
||||||
day = slot.get('day')
|
day = slot.get('day')
|
||||||
time_slot = slot.get('time_slot')
|
time_slot = slot.get('time_slot')
|
||||||
|
|
||||||
# If we haven't assigned a date to this day yet, find the next occurrence
|
|
||||||
if day not in day_date_map:
|
if day not in day_date_map:
|
||||||
found_date = None
|
found_date = None
|
||||||
for days_ahead in range(1, 15): # Look ahead 2 weeks
|
for days_ahead in range(1, 15):
|
||||||
check_date = today + timedelta(days=days_ahead)
|
check_date = today + timedelta(days=days_ahead)
|
||||||
if check_date.weekday() == day:
|
if check_date.weekday() == day:
|
||||||
found_date = check_date
|
found_date = check_date
|
||||||
@ -293,7 +297,6 @@ class AppointmentRequestCreateSerializer(serializers.ModelSerializer):
|
|||||||
if found_date:
|
if found_date:
|
||||||
day_date_map[day] = found_date.strftime('%Y-%m-%d')
|
day_date_map[day] = found_date.strftime('%Y-%m-%d')
|
||||||
|
|
||||||
# Add the slot with date
|
|
||||||
if day in day_date_map:
|
if day in day_date_map:
|
||||||
enhanced_slots.append({
|
enhanced_slots.append({
|
||||||
'day': day,
|
'day': day,
|
||||||
@ -304,7 +307,6 @@ class AppointmentRequestCreateSerializer(serializers.ModelSerializer):
|
|||||||
return enhanced_slots
|
return enhanced_slots
|
||||||
|
|
||||||
def _extract_unique_dates(self, enhanced_slots):
|
def _extract_unique_dates(self, enhanced_slots):
|
||||||
"""Extract unique dates from enhanced slots"""
|
|
||||||
dates = []
|
dates = []
|
||||||
for slot in enhanced_slots:
|
for slot in enhanced_slots:
|
||||||
date_str = slot.get('date')
|
date_str = slot.get('date')
|
||||||
@ -313,7 +315,6 @@ class AppointmentRequestCreateSerializer(serializers.ModelSerializer):
|
|||||||
return dates
|
return dates
|
||||||
|
|
||||||
def _extract_unique_time_slots(self, enhanced_slots):
|
def _extract_unique_time_slots(self, enhanced_slots):
|
||||||
"""Extract unique time slots from enhanced slots"""
|
|
||||||
time_slots = []
|
time_slots = []
|
||||||
for slot in enhanced_slots:
|
for slot in enhanced_slots:
|
||||||
time_slot = slot.get('time_slot')
|
time_slot = slot.get('time_slot')
|
||||||
@ -322,9 +323,17 @@ class AppointmentRequestCreateSerializer(serializers.ModelSerializer):
|
|||||||
return time_slots
|
return time_slots
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
# Create the appointment with all data
|
# Extract timezone before creating
|
||||||
# The selected_slots will be saved to the database automatically
|
timezone = validated_data.pop('timezone', 'UTC')
|
||||||
return super().create(validated_data)
|
|
||||||
|
# Create appointment
|
||||||
|
appointment = super().create(validated_data)
|
||||||
|
|
||||||
|
# Set timezone on the created appointment
|
||||||
|
appointment.user_timezone = timezone
|
||||||
|
appointment.save(update_fields=['user_timezone'])
|
||||||
|
|
||||||
|
return appointment
|
||||||
|
|
||||||
class AppointmentScheduleSerializer(serializers.Serializer):
|
class AppointmentScheduleSerializer(serializers.Serializer):
|
||||||
scheduled_datetime = serializers.DateTimeField()
|
scheduled_datetime = serializers.DateTimeField()
|
||||||
@ -333,7 +342,6 @@ 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')
|
|
||||||
|
|
||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
scheduled_datetime = data.get('scheduled_datetime')
|
scheduled_datetime = data.get('scheduled_datetime')
|
||||||
@ -420,6 +428,7 @@ class AppointmentScheduleSerializer(serializers.Serializer):
|
|||||||
'scheduled_datetime': instance.scheduled_datetime,
|
'scheduled_datetime': instance.scheduled_datetime,
|
||||||
'scheduled_duration': instance.scheduled_duration,
|
'scheduled_duration': instance.scheduled_duration,
|
||||||
'jitsi_meeting_created': instance.jitsi_meeting_created,
|
'jitsi_meeting_created': instance.jitsi_meeting_created,
|
||||||
|
'user_timezone': instance.user_timezone,
|
||||||
}
|
}
|
||||||
|
|
||||||
if instance.has_jitsi_meeting:
|
if instance.has_jitsi_meeting:
|
||||||
@ -429,7 +438,6 @@ class AppointmentScheduleSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
return representation
|
return representation
|
||||||
|
|
||||||
|
|
||||||
class AppointmentDetailSerializer(serializers.ModelSerializer):
|
class AppointmentDetailSerializer(serializers.ModelSerializer):
|
||||||
meeting_info = serializers.SerializerMethodField()
|
meeting_info = serializers.SerializerMethodField()
|
||||||
meeting_analytics = serializers.SerializerMethodField()
|
meeting_analytics = serializers.SerializerMethodField()
|
||||||
|
|||||||
@ -76,6 +76,7 @@ class AppointmentRequestCreateView(generics.CreateAPIView):
|
|||||||
EmailService.send_admin_notification(appointment, availability_mismatch=True)
|
EmailService.send_admin_notification(appointment, availability_mismatch=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AppointmentRequestDetailView(generics.RetrieveAPIView):
|
class AppointmentRequestDetailView(generics.RetrieveAPIView):
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
queryset = AppointmentRequest.objects.all()
|
queryset = AppointmentRequest.objects.all()
|
||||||
@ -103,9 +104,6 @@ class ScheduleAppointmentView(generics.GenericAPIView):
|
|||||||
|
|
||||||
create_jitsi_meeting = serializer.validated_data.get('create_jitsi_meeting', True)
|
create_jitsi_meeting = serializer.validated_data.get('create_jitsi_meeting', True)
|
||||||
jitsi_custom_config = serializer.validated_data.get('jitsi_custom_config', {})
|
jitsi_custom_config = serializer.validated_data.get('jitsi_custom_config', {})
|
||||||
user_timezone = serializer.validated_data.get('timezone', 'UTC')
|
|
||||||
|
|
||||||
appointment.user_timezone = user_timezone
|
|
||||||
|
|
||||||
admin_user = request.user
|
admin_user = request.user
|
||||||
|
|
||||||
@ -153,7 +151,7 @@ class ScheduleAppointmentView(generics.GenericAPIView):
|
|||||||
})
|
})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)
|
return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
class RejectAppointmentView(generics.GenericAPIView):
|
class RejectAppointmentView(generics.GenericAPIView):
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
serializer_class = AppointmentRejectSerializer
|
serializer_class = AppointmentRejectSerializer
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user