diff --git a/meetings/models.py b/meetings/models.py index 0c4eef3..21dd421 100644 --- a/meetings/models.py +++ b/meetings/models.py @@ -515,11 +515,6 @@ class AppointmentRequest(models.Model): self.jitsi_meeting_config = meeting_config 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() return self.jitsi_meet_url diff --git a/meetings/serializers.py b/meetings/serializers.py index 1757563..3fad7fd 100644 --- a/meetings/serializers.py +++ b/meetings/serializers.py @@ -137,20 +137,17 @@ class AppointmentRequestSerializer(serializers.ModelSerializer): if not obj.has_jitsi_meeting or not obj.email: return None - # Try to get the participant user from the appointment's email participant_user = None try: from users.models import CustomUser participant_user = CustomUser.objects.get(email=obj.email) except CustomUser.DoesNotExist: - # Participant user doesn't exist (not registered) return None - # Check if current user is authorized to view this URL current_user = request.user is_authorized = ( - current_user.is_staff or # Staff can see participant URLs for all appointments - current_user.email == obj.email or # Participant can see their own URL + current_user.is_staff or + current_user.email == obj.email or (hasattr(obj, 'invited_participants') and current_user.email in obj.invited_participants) ) @@ -225,21 +222,35 @@ class AppointmentRequestCreateSerializer(serializers.ModelSerializer): required=True, 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: model = AppointmentRequest fields = [ '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): selected_slots = data.get('selected_slots') if not selected_slots: raise serializers.ValidationError("At least one time slot must be selected.") - # Your existing validation logic availability = get_admin_availability() if not availability: 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." ) - # Add dates to the slots and store enhanced version enhanced_slots = self._add_dates_to_slots(selected_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_time_slots'] = self._extract_unique_time_slots(enhanced_slots) return data def _add_dates_to_slots(self, selected_slots): - """Add actual dates to the slots based on day numbers""" from datetime import datetime today = datetime.now().date() 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 = {} for slot in selected_slots: day = slot.get('day') 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: 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) if check_date.weekday() == day: found_date = check_date @@ -293,7 +297,6 @@ class AppointmentRequestCreateSerializer(serializers.ModelSerializer): if found_date: day_date_map[day] = found_date.strftime('%Y-%m-%d') - # Add the slot with date if day in day_date_map: enhanced_slots.append({ 'day': day, @@ -304,7 +307,6 @@ class AppointmentRequestCreateSerializer(serializers.ModelSerializer): return enhanced_slots def _extract_unique_dates(self, enhanced_slots): - """Extract unique dates from enhanced slots""" dates = [] for slot in enhanced_slots: date_str = slot.get('date') @@ -313,7 +315,6 @@ class AppointmentRequestCreateSerializer(serializers.ModelSerializer): return dates def _extract_unique_time_slots(self, enhanced_slots): - """Extract unique time slots from enhanced slots""" time_slots = [] for slot in enhanced_slots: time_slot = slot.get('time_slot') @@ -322,9 +323,17 @@ class AppointmentRequestCreateSerializer(serializers.ModelSerializer): return time_slots def create(self, validated_data): - # Create the appointment with all data - # The selected_slots will be saved to the database automatically - return super().create(validated_data) + # Extract timezone before creating + timezone = validated_data.pop('timezone', 'UTC') + + # 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): scheduled_datetime = serializers.DateTimeField() @@ -333,7 +342,6 @@ class AppointmentScheduleSerializer(serializers.Serializer): time_slot = serializers.CharField(required=False, write_only=True) create_jitsi_meeting = serializers.BooleanField(default=True) jitsi_custom_config = serializers.JSONField(required=False, default=dict) - timezone = serializers.CharField(required=False, default='UTC') def validate(self, data): scheduled_datetime = data.get('scheduled_datetime') @@ -420,6 +428,7 @@ class AppointmentScheduleSerializer(serializers.Serializer): 'scheduled_datetime': instance.scheduled_datetime, 'scheduled_duration': instance.scheduled_duration, 'jitsi_meeting_created': instance.jitsi_meeting_created, + 'user_timezone': instance.user_timezone, } if instance.has_jitsi_meeting: @@ -429,7 +438,6 @@ class AppointmentScheduleSerializer(serializers.Serializer): return representation - class AppointmentDetailSerializer(serializers.ModelSerializer): meeting_info = serializers.SerializerMethodField() meeting_analytics = serializers.SerializerMethodField() diff --git a/meetings/views.py b/meetings/views.py index 9e20b25..5ac1068 100644 --- a/meetings/views.py +++ b/meetings/views.py @@ -76,6 +76,7 @@ class AppointmentRequestCreateView(generics.CreateAPIView): EmailService.send_admin_notification(appointment, availability_mismatch=True) + class AppointmentRequestDetailView(generics.RetrieveAPIView): permission_classes = [IsAuthenticated] queryset = AppointmentRequest.objects.all() @@ -103,9 +104,6 @@ class ScheduleAppointmentView(generics.GenericAPIView): create_jitsi_meeting = serializer.validated_data.get('create_jitsi_meeting', True) 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 @@ -153,7 +151,7 @@ class ScheduleAppointmentView(generics.GenericAPIView): }) except Exception as e: return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST) - + class RejectAppointmentView(generics.GenericAPIView): permission_classes = [IsAuthenticated] serializer_class = AppointmentRejectSerializer