From f3f60d4fc5ddd4f51ae9ccbc024edc39387252ce Mon Sep 17 00:00:00 2001 From: Faheed Date: Sun, 23 Nov 2025 13:14:36 +0300 Subject: [PATCH] updated the messages creation --- ...ter_interviewschedule_template_location.py | 19 +++ ...ter_interviewschedule_template_location.py | 19 +++ recruitment/models.py | 3 + recruitment/tasks.py | 6 +- recruitment/views.py | 124 +++++++++--------- templates/recruitment/candidate_profile.html | 2 +- 6 files changed, 109 insertions(+), 64 deletions(-) create mode 100644 recruitment/migrations/0004_alter_interviewschedule_template_location.py create mode 100644 recruitment/migrations/0005_alter_interviewschedule_template_location.py diff --git a/recruitment/migrations/0004_alter_interviewschedule_template_location.py b/recruitment/migrations/0004_alter_interviewschedule_template_location.py new file mode 100644 index 0000000..13914f7 --- /dev/null +++ b/recruitment/migrations/0004_alter_interviewschedule_template_location.py @@ -0,0 +1,19 @@ +# Generated by Django 5.2.7 on 2025-11-23 09:22 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('recruitment', '0003_jobposting_cv_zip_file_jobposting_zip_created'), + ] + + operations = [ + migrations.AlterField( + model_name='interviewschedule', + name='template_location', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='schedule_templates', to='recruitment.interviewlocation', verbose_name='Location Template (Zoom/Onsite)'), + ), + ] diff --git a/recruitment/migrations/0005_alter_interviewschedule_template_location.py b/recruitment/migrations/0005_alter_interviewschedule_template_location.py new file mode 100644 index 0000000..b7edfe0 --- /dev/null +++ b/recruitment/migrations/0005_alter_interviewschedule_template_location.py @@ -0,0 +1,19 @@ +# Generated by Django 5.2.7 on 2025-11-23 09:41 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('recruitment', '0004_alter_interviewschedule_template_location'), + ] + + operations = [ + migrations.AlterField( + model_name='interviewschedule', + name='template_location', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='schedule_templates', to='recruitment.interviewlocation', verbose_name='Location Template (Zoom/Onsite)'), + ), + ] diff --git a/recruitment/models.py b/recruitment/models.py index 2cef9e2..1090a5c 100644 --- a/recruitment/models.py +++ b/recruitment/models.py @@ -1162,6 +1162,9 @@ class OnsiteLocationDetails(InterviewLocation): verbose_name_plural = _("Onsite Location Details") + + + # --- 2. Scheduling Models --- class InterviewSchedule(Base): diff --git a/recruitment/tasks.py b/recruitment/tasks.py index 511aa8f..3f84931 100644 --- a/recruitment/tasks.py +++ b/recruitment/tasks.py @@ -760,6 +760,7 @@ from django.utils.html import strip_tags def _task_send_individual_email(subject, body_message, recipient, attachments,sender,job): """Internal helper to create and send a single email.""" + from_email = getattr(settings, 'DEFAULT_FROM_EMAIL', 'noreply@kaauh.edu.sa') is_html = '<' in body_message and '>' in body_message @@ -780,7 +781,8 @@ def _task_send_individual_email(subject, body_message, recipient, attachments,se try: result=email_obj.send(fail_silently=False) - if result==1: + if result==1 and sender and job: # job is none when email sent after message creation + try: user=get_object_or_404(User,email=recipient) new_message = Message.objects.create( @@ -798,7 +800,7 @@ def _task_send_individual_email(subject, body_message, recipient, attachments,se else: - logger.error("fialed to send email") + logger.error("failed to send email") except Exception as e: diff --git a/recruitment/views.py b/recruitment/views.py index 65b5ceb..1692ce7 100644 --- a/recruitment/views.py +++ b/recruitment/views.py @@ -4674,55 +4674,40 @@ def message_create(request): message.sender = request.user message.save() # Send email if message_type is 'email' and recipient has email - if message.message_type == 'email' and message.recipient and message.recipient.email: + + if message.recipient and message.recipient.email: + try: - from .email_service import send_bulk_email + - email_result = send_bulk_email( + email_result = async_task('recruitment.tasks._task_send_individual_email', subject=message.subject, - message=message.content, - recipient_list=[message.recipient.email], - request=request, + body_message=message.content, + recipient=message.recipient.email, attachments=None, - async_task_=True, - from_interview=False + sender=False, + job=False ) - - if email_result["success"]: - message.is_email_sent = True - message.email_address = message.recipient.email - message.save(update_fields=['is_email_sent', 'email_address']) + if email_result: messages.success(request, "Message sent successfully via email!") else: - messages.warning(request, f"Message saved but email failed: {email_result.get('message', 'Unknown error')}") + + messages.warning(request, f"email failed: {email_result.get('message', 'Unknown error')}") except Exception as e: + messages.warning(request, f"Message saved but email sending failed: {str(e)}") else: + messages.success(request, "Message sent successfully!") - ["recipient", "job", "subject", "content", "message_type"] - recipient_email = form.cleaned_data['recipient'].email # Assuming recipient is a User or Model with an 'email' field - subject = form.cleaned_data['subject'] - custom_message = form.cleaned_data['content'] - job_id = form.cleaned_data['job'].id if 'job' in form.cleaned_data and form.cleaned_data['job'] else None - sender_user_id = request.user.id - - task_id = async_task( - 'recruitment.tasks.send_bulk_email_task', - subject, - custom_message, # Pass the custom message - [recipient_email], # Pass the specific recipient as a list of one - - sender_user_id=sender_user_id, - job_id=job_id, - hook='recruitment.tasks.email_success_hook') - - logger.info(f"{task_id} queued.") + return redirect("message_list") else: + messages.error(request, "Please correct the errors below.") else: + form = MessageForm(request.user) context = { @@ -4759,27 +4744,21 @@ def message_reply(request, message_id): message.save() # Send email if message_type is 'email' and recipient has email - if message.message_type == 'email' and message.recipient and message.recipient.email: + if message.recipient and message.recipient.email: try: - from .email_service import send_bulk_email - - email_result = send_bulk_email( + email_result = async_task('recruitment.tasks._task_send_individual_email', subject=message.subject, - message=message.content, - recipient_list=[message.recipient.email], - request=request, + body_message=message.content, + recipient=message.recipient.email, attachments=None, - async_task_=True, - from_interview=False + sender=False, + job=False ) - - if email_result["success"]: - message.is_email_sent = True - message.email_address = message.recipient.email - message.save(update_fields=['is_email_sent', 'email_address']) - messages.success(request, "Reply sent successfully via email!") + if email_result: + messages.success(request, "Message sent successfully via email!") else: - messages.warning(request, f"Reply saved but email failed: {email_result.get('message', 'Unknown error')}") + + messages.warning(request, f"email failed: {email_result.get('message', 'Unknown error')}") except Exception as e: messages.warning(request, f"Reply saved but email sending failed: {str(e)}") @@ -5763,15 +5742,15 @@ def send_interview_email(request, slug): return redirect("meeting_details", slug=meeting.slug) -# def schedule_interview_location_form(request,slug): -# schedule=get_object_or_404(InterviewSchedule,slug=slug) -# if request.method=='POST': -# form=InterviewScheduleLocationForm(request.POST,instance=schedule) -# form.save() -# return redirect('list_meetings') -# else: -# form=InterviewScheduleLocationForm(instance=schedule) -# return render(request,'interviews/schedule_interview_location_form.html',{'form':form,'schedule':schedule}) +def schedule_interview_location_form(request,slug): + schedule=get_object_or_404(InterviewSchedule,slug=slug) + if request.method=='POST': + form=InterviewScheduleLocationForm(request.POST,instance=schedule) + form.save() + return redirect('list_meetings') + else: + form=InterviewScheduleLocationForm(instance=schedule) + return render(request,'interviews/schedule_interview_location_form.html',{'form':form,'schedule':schedule}) class MeetingListView(ListView): @@ -5782,6 +5761,7 @@ class MeetingListView(ListView): template_name = "meetings/list_meetings.html" context_object_name = "meetings" paginate_by = 100 + def get_queryset(self): # Start with a base queryset, ensuring an InterviewLocation link exists. @@ -5794,8 +5774,9 @@ class MeetingListView(ListView): 'interview_location__zoommeetingdetails', 'interview_location__onsitelocationdetails', ) - # Note: Printing the queryset here can consume memory for large sets. + # Note: Printing the queryset here can consume memory for large sets. + # Get filters from GET request search_query = self.request.GET.get("q") status_filter = self.request.GET.get("status") @@ -5807,12 +5788,11 @@ class MeetingListView(ListView): if type_filter: # Use .title() to handle case variations from URL (e.g., 'remote' -> 'Remote') normalized_type = type_filter.title() - print(normalized_type) + # Assuming InterviewLocation.LocationType is accessible/defined if normalized_type in ['Remote', 'Onsite']: queryset = queryset.filter(interview_location__location_type=normalized_type) - print(queryset) - + # 3. Search by Topic (stored on InterviewLocation) if search_query: queryset = queryset.filter(interview_location__topic__icontains=search_query) @@ -5886,6 +5866,28 @@ class MeetingListView(ListView): return context + +# class MeetingListView(ListView): +# """ +# A unified view to list both Remote and Onsite Scheduled Interviews. +# """ +# model = InterviewLocation +# template_name = "meetings/list_meetings.html" +# context_object_name = "meetings" + + + +# def get_queryset(self): +# # Start with a base queryset, ensuring an InterviewLocation link exists. +# queryset = super().get_queryset().prefetch_related( +# 'zoommeetingdetails', +# 'onsitelocationdetails', +# ) +# print(queryset) + +# return queryset + + def reschedule_onsite_meeting(request, slug, candidate_id, meeting_id): """Handles the rescheduling of an Onsite Interview (updates OnsiteLocationDetails).""" job = get_object_or_404(JobPosting, slug=slug) diff --git a/templates/recruitment/candidate_profile.html b/templates/recruitment/candidate_profile.html index 9d4c3b2..67d1344 100644 --- a/templates/recruitment/candidate_profile.html +++ b/templates/recruitment/candidate_profile.html @@ -499,7 +499,7 @@

- {% trans "Applied" %}: {{ application.applied_date|date:"d M Y" }} + {% trans "Applied" %}: {{ application.created_at|date:"d M Y" }}