Compare commits

..

No commits in common. "034a1ccccce007a5bb20b5d3aca8c105c222e95f" and "518e13f84c36e1c2fa7914494c565e32e9c25d3c" have entirely different histories.

2 changed files with 100 additions and 97 deletions

View File

@ -649,107 +649,106 @@ class AppointmentRequest(models.Model):
if commit: if commit:
self.save() self.save()
def reject_appointment(self, reason='', commit=True):
def reject_appointment(self, reason='', commit=True): self.status = 'rejected'
self.status = 'rejected' self.rejection_reason = reason
self.rejection_reason = reason self.scheduled_datetime = None
self.scheduled_datetime = None self.jitsi_meet_url = None
self.jitsi_meet_url = None self.jitsi_room_id = None
self.jitsi_room_id = None
if commit:
self.save()
def cancel_appointment(self, reason='', commit=True):
self.status = 'cancelled'
self.rejection_reason = reason
if commit:
self.save()
def complete_appointment(self, commit=True):
self.status = 'completed'
if commit:
self.save()
def start_meeting(self, commit=True):
if self.status == 'scheduled':
self.meeting_started_at = timezone.now()
if commit: if commit:
self.save(update_fields=['meeting_started_at']) self.save()
def end_meeting(self, commit=True): def cancel_appointment(self, reason='', commit=True):
if self.meeting_started_at and not self.meeting_ended_at: self.status = 'cancelled'
self.meeting_ended_at = timezone.now() self.rejection_reason = reason
if self.meeting_started_at:
duration = self.meeting_ended_at - self.meeting_started_at
self.meeting_duration_actual = int(duration.total_seconds() / 60)
if commit: if commit:
self.save(update_fields=[ self.save()
'meeting_ended_at',
'meeting_duration_actual'
])
def can_join_meeting(self, *args, **kwargs): def complete_appointment(self, commit=True):
if args: self.status = 'completed'
user_type = args[0] if commit:
elif 'user_type' in kwargs: self.save()
user_type = kwargs['user_type']
else:
user_type = 'participant'
if not self.scheduled_datetime or not self.has_jitsi_meeting:
return False
if self.status not in ['scheduled', 'in_progress']:
return False
now = timezone.now()
meeting_start = self.scheduled_datetime
meeting_end = meeting_start + timedelta(minutes=self.scheduled_duration + 30)
if user_type == 'moderator':
return meeting_start - timedelta(minutes=15) <= now <= meeting_end + timedelta(minutes=15)
else:
return meeting_start - timedelta(minutes=5) <= now <= meeting_end
def get_meeting_join_info(self, user_type='participant'):
if not self.has_jitsi_meeting:
return None
join_url = self.get_moderator_join_url() if user_type == 'moderator' else self.get_participant_join_url()
return {
'meeting_url': join_url,
'room_id': self.jitsi_room_id,
'scheduled_time': self.formatted_scheduled_datetime,
'duration': self.meeting_duration_display,
'password': self.jitsi_meeting_password if user_type == 'participant' else None,
'can_join_now': self.can_join_meeting(user_type),
'join_window_start': (self.scheduled_datetime - timedelta(minutes=15)).strftime("%I:%M %p") if user_type == 'moderator' else (self.scheduled_datetime - timedelta(minutes=5)).strftime("%I:%M %p"),
'join_window_end': (self.scheduled_datetime + timedelta(minutes=self.scheduled_duration + 30)).strftime("%I:%M %p"),
'status': self.get_status_display(),
}
def update_meeting_data(self, data): def start_meeting(self, commit=True):
if not isinstance(data, dict): if self.status == 'scheduled':
return self.meeting_started_at = timezone.now()
if commit:
current_data = self.jitsi_meeting_data or {} self.save(update_fields=['meeting_started_at'])
current_data.update(data)
self.jitsi_meeting_data = current_data
self.save(update_fields=['jitsi_meeting_data'])
def get_meeting_analytics(self): def end_meeting(self, commit=True):
return { if self.meeting_started_at and not self.meeting_ended_at:
'scheduled_duration': self.scheduled_duration, self.meeting_ended_at = timezone.now()
'actual_duration': self.meeting_duration_actual,
'started_at': self.meeting_started_at.isoformat() if self.meeting_started_at else None, if self.meeting_started_at:
'ended_at': self.meeting_ended_at.isoformat() if self.meeting_ended_at else None, duration = self.meeting_ended_at - self.meeting_started_at
'status': self.status, self.meeting_duration_actual = int(duration.total_seconds() / 60)
'punctuality': self._calculate_punctuality(),
'efficiency': self._calculate_efficiency(), if commit:
} self.save(update_fields=[
'meeting_ended_at',
'meeting_duration_actual'
])
def can_join_meeting(self, *args, **kwargs):
if args:
user_type = args[0]
elif 'user_type' in kwargs:
user_type = kwargs['user_type']
else:
user_type = 'participant'
if not self.scheduled_datetime or not self.has_jitsi_meeting:
return False
if self.status not in ['scheduled', 'in_progress']:
return False
now = timezone.now()
meeting_start = self.scheduled_datetime
meeting_end = meeting_start + timedelta(minutes=self.scheduled_duration + 30)
if user_type == 'moderator':
return meeting_start - timedelta(minutes=15) <= now <= meeting_end + timedelta(minutes=15)
else:
return meeting_start - timedelta(minutes=5) <= now <= meeting_end
def get_meeting_join_info(self, user_type='participant'):
if not self.has_jitsi_meeting:
return None
join_url = self.get_moderator_join_url() if user_type == 'moderator' else self.get_participant_join_url()
return {
'meeting_url': join_url,
'room_id': self.jitsi_room_id,
'scheduled_time': self.formatted_scheduled_datetime,
'duration': self.meeting_duration_display,
'password': self.jitsi_meeting_password if user_type == 'participant' else None,
'can_join_now': self.can_join_meeting(user_type),
'join_window_start': (self.scheduled_datetime - timedelta(minutes=15)).strftime("%I:%M %p") if user_type == 'moderator' else (self.scheduled_datetime - timedelta(minutes=5)).strftime("%I:%M %p"),
'join_window_end': (self.scheduled_datetime + timedelta(minutes=self.scheduled_duration + 30)).strftime("%I:%M %p"),
'status': self.get_status_display(),
}
def update_meeting_data(self, data):
if not isinstance(data, dict):
return
current_data = self.jitsi_meeting_data or {}
current_data.update(data)
self.jitsi_meeting_data = current_data
self.save(update_fields=['jitsi_meeting_data'])
def get_meeting_analytics(self):
return {
'scheduled_duration': self.scheduled_duration,
'actual_duration': self.meeting_duration_actual,
'started_at': self.meeting_started_at.isoformat() if self.meeting_started_at else None,
'ended_at': self.meeting_ended_at.isoformat() if self.meeting_ended_at else None,
'status': self.status,
'punctuality': self._calculate_punctuality(),
'efficiency': self._calculate_efficiency(),
}
def _calculate_punctuality(self): def _calculate_punctuality(self):
if not self.meeting_started_at or not self.scheduled_datetime: if not self.meeting_started_at or not self.scheduled_datetime:

View File

@ -79,6 +79,8 @@ class AppointmentRequestSerializer(serializers.ModelSerializer):
moderator_join_url = serializers.SerializerMethodField() moderator_join_url = serializers.SerializerMethodField()
participant_join_url = serializers.SerializerMethodField() participant_join_url = serializers.SerializerMethodField()
meeting_analytics = serializers.SerializerMethodField() meeting_analytics = serializers.SerializerMethodField()
# Add selected_slots field
selected_slots = serializers.JSONField() selected_slots = serializers.JSONField()
class Meta: class Meta:
@ -99,9 +101,9 @@ class AppointmentRequestSerializer(serializers.ModelSerializer):
'id', 'status', 'scheduled_datetime', 'scheduled_duration', 'id', 'status', 'scheduled_datetime', 'scheduled_duration',
'rejection_reason', 'jitsi_meet_url', 'jitsi_room_id', 'rejection_reason', 'jitsi_meet_url', 'jitsi_room_id',
'created_at', 'updated_at', 'preferred_dates', 'preferred_time_slots', 'created_at', 'updated_at', 'preferred_dates', 'preferred_time_slots',
'selected_slots' 'selected_slots' # Make selected_slots read-only in this serializer
] ]
def get_can_join_meeting(self, obj): def get_can_join_meeting(self, obj):
return obj.can_join_meeting('participant') return obj.can_join_meeting('participant')
@ -232,6 +234,8 @@ class AppointmentRequestCreateSerializer(serializers.ModelSerializer):
'first_name', 'last_name', 'email', 'phone', 'reason', 'first_name', 'last_name', 'email', 'phone', 'reason',
'selected_slots' 'selected_slots'
] ]
# Remove preferred_dates and preferred_time_slots from fields list
# They will be calculated automatically
def validate(self, data): def validate(self, data):
selected_slots = data.get('selected_slots') selected_slots = data.get('selected_slots')