isssue resolved 7

This commit is contained in:
Faheed 2025-12-04 12:08:35 +03:00
parent de9c3153d5
commit 934d03b5bb
14 changed files with 221 additions and 46 deletions

View File

@ -491,4 +491,43 @@ MESSAGE_TAGS = {
AUTH_USER_MODEL = "recruitment.CustomUser"
ZOOM_WEBHOOK_API_KEY = "2GNDC5Rvyw9AHoGikHXsQB"
ZOOM_WEBHOOK_API_KEY = "2GNDC5Rvyw9AHoGikHXsQB"
#logger:
LOGGING={
"version": 1,
"disable_existing_loggers": False,
"handlers": {
"file": {
"class": "logging.FileHandler",
"filename": os.path.join(BASE_DIR, "general.log"),
"level": "DEBUG",
"formatter": "verbose",
},
"console":{
"class": "logging.StreamHandler",
"level": "DEBUG",
"formatter": "simple"
}
},
"loggers": {
"": {
"handlers": ["file", "console"],
"level": "DEBUG",
"propagate": True,
},
},
"formatters": {
"verbose": {
"format": "[{asctime}] {levelname} [{name}:{lineno}] {message}",
"style": "{",
},
"simple": {
"format": "{levelname} {message}",
"style": "{",
},
}
}

View File

@ -18,7 +18,7 @@ from .validators import validate_hash_tags, validate_image_size
from django.contrib.auth.models import AbstractUser
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db.models import F, Value, IntegerField, CharField
from django.db.models import F, Value, IntegerField, CharField,Q
from django.db.models.functions import Coalesce, Cast
from django.db.models.fields.json import KeyTransform, KeyTextTransform
@ -65,11 +65,20 @@ class CustomUser(AbstractUser):
"unique": _("A user with this email already exists."),
},
)
class Meta:
verbose_name = _("User")
verbose_name_plural = _("Users")
@property
def get_unread_message_count(self):
message_list = (
Message.objects.filter(Q(sender=self) | Q(recipient=self), is_read=False)
)
return message_list.count() or 0
User = get_user_model()
@ -2108,6 +2117,7 @@ class HiringAgency(Base):
# 2. Call the original delete method for the Agency instance
super().delete(*args, **kwargs)
class AgencyJobAssignment(Base):
@ -2267,6 +2277,15 @@ class AgencyJobAssignment(Base):
# self.save(update_fields=['status'])
return True
return False
@property
def applications_submited_count(self):
"""Return the number of applications submitted by the agency for this job"""
return Application.objects.filter(
hiring_agency=self.agency,
job=self.job
).count()
def extend_deadline(self, new_deadline):
"""Extend the deadline for this assignment"""

View File

@ -1,3 +1,7 @@
#logger for recruitment views
import logging
logger = logging.getLogger(__name__)
import json
import io
import zipfile
@ -433,7 +437,7 @@ def create_job(request):
job = form.save(commit=False)
job.save()
job_apply_url_relative = reverse(
"application_detail", kwargs={"slug": job.slug}
"job_application_detail", kwargs={"slug": job.slug}
)
job_apply_url_absolute = request.build_absolute_uri(
job_apply_url_relative
@ -712,7 +716,11 @@ def request_cvs_download(request, slug):
"""
View to initiate the background task.
"""
job = get_object_or_404(JobPosting, slug=slug)
if job.status != 'CLOSED':
messages.info('request',_("You can request bulk CV dowload only if the job status is changed to CLOSED"))
return redirect('job_detail',kwargs={slug:job.slug})
job.zip_created = False
job.save(update_fields=["zip_created"])
# Use async_task to run the function in the background
@ -732,6 +740,10 @@ def download_ready_cvs(request, slug):
View to serve the file once it is ready.
"""
job = get_object_or_404(JobPosting, slug=slug)
if job.status != 'CLOSED':
messages.info('request',_("You can request bulk CV dowload only if the job status is changed to CLOSED"))
return redirect('job_detail',kwargs={slug:job.slug})
if not job.applications.exists():
messages.warning(request, _("No applications found for this job. ZIP file download unavailable."))
return redirect('job_detail', slug=slug)
@ -3331,6 +3343,7 @@ def agency_detail(request, slug):
hired_applications = applications.filter(stage="Hired").count()
rejected_applications = applications.filter(stage="Rejected").count()
job_assignments=AgencyJobAssignment.objects.filter(agency=agency)
total_job_assignments=job_assignments.count()
print(job_assignments)
context = {
"agency": agency,
@ -3342,7 +3355,8 @@ def agency_detail(request, slug):
"generated_password": agency.generated_password
if agency.generated_password
else None,
"job_assignments":job_assignments
"job_assignments":job_assignments,
"total_job_assignments":total_job_assignments,
}
return render(request, "recruitment/agency_detail.html", context)
@ -3747,7 +3761,8 @@ def agency_assignment_list(request):
if search_query:
assignments = assignments.filter(
Q(agency__name__icontains=search_query)
| Q(job__title__icontains=search_query)
| Q(job__title__icontains=search_query)|
Q(agency__contact_person__icontains=search_query)
)
if status_filter:

View File

@ -20,6 +20,7 @@
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css" integrity="sha512-Kc323vGBEqzTmouAECnVceyQqyqdsSiqLQISBL29aUW4U/M7pSPA/gEUZQqv1cwx4OnYxTxve5UMg5GT6L4JJg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="{% static 'css/main.css' %}">
<script src="{% static 'js/typo.js' %}"></script>
{% block customCSS %}{% endblock %}
@ -134,12 +135,23 @@
</form>
{% endif %}
</li>
<li class="nav-item me-2 d-none d-lg-block">
{% comment %} <li class="nav-item me-2 d-none d-lg-block">
<a class="nav-link text-white" href="{% url 'message_list' %}">
<i class="fas fa-envelope"></i> <span>{% trans "Messages" %}</span>
</a>
</li>
{% endcomment %}
<li class="nav-item mx-3 d-none d-lg-block mt-2">
<a href="{% url 'message_list' %}"
class=" btn btn-sm btn-outline-warning position-relative">
<i class="fas fa-envelope me-1"></i>
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
{{ request.user.get_unread_message_count }}
</span>
</a>
</li>
<li class="nav-item dropdown">
<button
class="nav-link p-0 border-0 bg-transparent dropdown-toggle"

View File

@ -146,12 +146,12 @@
<div class="container-fluid py-4">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item "><a href="{% url 'job_detail' submission.template.job.slug %}" class="text-secondary text-decoration-none">Job Detail</a></li>
<li class="breadcrumb-item"><a href="{% url 'form_builder' submission.template.pk%}" class="text-secondary">Form Template</a></li>
<li class="breadcrumb-item "><a href="{% url 'job_detail' submission.template.job.slug %}" class="text-secondary text-decoration-none">{% trans "Job Detail" %}</a></li>
<li class="breadcrumb-item"><a href="{% url 'form_builder' submission.template.pk%}" class="text-secondary text-decoration-none">{% trans "Form Template" %}</a></li>
<li class="breadcrumb-item active" aria-current="page" style="
color: #F43B5E; /* Rosy Accent Color */
font-weight: 600;
">Submission Details</li>
">{% trans "Submission Details" %}</li>
</ol>
</nav>
<div class="row">

View File

@ -182,6 +182,39 @@
{% endblock %}
{% block content %}
{% comment %} <div class="container py-4">
<!-- Search and Filter Section -->
<div class="card shadow-sm mb-4">
<div class="card-body">
<form method="get" action="" class="w-100">
<div class="row g-3 align-items-end">
<div class="col-12 col-md-5">
<label for="search" class="form-label small text-muted">{% trans "Search by name or Email" %}</label>
<div class="input-group">
{% include 'includes/search_form.html' %}
</div>
</div>
<div class="col-12 col-md-2">
<label for="date_from" class="form-label small text-muted">{% trans "From Date" %}</label>
<input type="date" class="form-control" id="date_from" name="date_from" value="{{ request.GET.date_from }}">
</div>
<div class="col-12 col-md-2">
<label for="date_to" class="form-label small text-muted">{% trans "To Date" %}</label>
<input type="date" class="form-control" id="date_to" name="date_to" value="{{ request.GET.date_to }}">
</div>
<div class="col-12 col-md-3 d-flex gap-2">
<button type="submit" class="btn btn-main-action flex-grow-1">
<i class="fas fa-search me-1"></i> {% trans "Filter" %}
</button>
<a href="?" class="btn btn-outline-secondary flex-grow-1">
<i class="fas fa-times me-1"></i> {% trans "Clear" %}
</a>
</div>
</div>
</form>
</div>
</div>
{% endcomment %}
<div class="container py-4">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
@ -254,7 +287,7 @@
<div class="card-view">
<div class="row g-4">
{% for submission in page_obj %}
<div class="col-12">
<div class="col-md-6 col-lg-6">
<div class="card h-100">
<div class="card-header">
<h3 class="h5 mb-2">{% trans "Submission" %} #{{ submission.id }}</h3>

View File

@ -32,11 +32,17 @@
{% trans "To" %}
</label>
<div class="border rounded p-3 bg-light" style="max-height: 200px; overflow-y: auto;">
{% for choice in form.to %}
{% for choice in form.to|slice:":1" %}
<div class="form-check mb-2">
{{ choice }}
</div>
{% endfor %}
{% if form.to|length > 0 %}
<div class="text-muted small mt-2">
<i class="fas fa-info-circle me-1"></i>
{% blocktrans count total=form.to|length %}{{ total }} recipient selected{% plural %}{{ total }} recipients selected{% endblocktrans %}
</div>
{% endif %}
</div>
{% if form.to.errors %}
<div class="text-danger small mt-1">

View File

@ -218,7 +218,9 @@
<div class="card-body">
<h5 class="text-muted mb-3">{% trans "Administrative & Location" %}
{% if job.status == 'DRAFT'%}
<a href="{% url 'job_update' job.slug %}" class="btn btn-main-action btn-sm"><li class="fa fa-edit"></li>{% trans "Edit Job" %}</a>
{% endif %}
<div class="float-end">
<div class="d-flex align-items-center">
<i class="fas fa-user-tie me-2 text-primary"></i> <strong>{% trans "Assigned to :" %} </strong> {{ job.assigned_to|default:"" }}
@ -331,14 +333,21 @@
<a href="{% url 'applications_screening_view' job.slug %}" class="btn btn-main-action">
<i class="fas fa-layer-group me-1"></i> {% trans "Manage Applications" %}
</a>
<a href="{% url 'request_cvs_download' job.slug %}" class="btn btn-main-action">
<i class="fa-solid fa-download me-1"></i> {% trans "Generate All CVs" %}
</a>
<a href="{% url 'download_ready_cvs' job.slug %}" class="btn btn-outline-primary">
<i class="fa-solid fa-eye me-1"></i> {% trans "View All CVs" %}
</a>
{% if not job.form_template.is_active %}
{% if not jobzip_created %}
<a href="{% url 'request_cvs_download' job.slug %}" class="btn btn-main-action">
<i class="fa-solid fa-download me-1"></i> {% trans "Generate All CVs" %}
</a>
{% endif %}
{% if job.zip_created %}
<a href="{% url 'download_ready_cvs' job.slug %}" class="btn btn-outline-primary">
<i class="fa-solid fa-eye me-1"></i> {% trans "View All CVs" %}
</a>
{% endif %}
{% else %}
<p>{% trans "Bulk CV dowload is inactive. To activate please change the status of the job to CLOSED." %}</p>
{% endif %}
</div>
</div>

View File

@ -257,13 +257,52 @@
<form method="post" action="{% url 'person_update' person.slug %}" enctype="multipart/form-data" id="person-form">
{% csrf_token %}
{{form|crispy}}
<div class="row">
<div class="col-md-6">
<label for="{{ form.first_name.id_for_label }}">{{ form.first_name.label }}</label>
{% include "bootstrap5/field.html" with field=form.first_name %}
</div>
<div class="col-md-6">
<label for="{{ form.middle_name.id_for_label }}">{{ form.middle_name.label }}</label>
{% include "bootstrap5/field.html" with field=form.middle_name %}
</div>
<div class="col-md-6">
<label for="{{ form.last_name.id_for_label }}">{{ form.last_name.label }}</label>
{% include "bootstrap5/field.html" with field=form.last_name %}
</div>
<div class="col-md-6">
<label for="{{ form.email.id_for_label }}">{{ form.email.label }}</label>
{% include "bootstrap5/field.html" with field=form.email %}
</div>
<div class="col-md-6">
<label for="{{ form.phone.id_for_label }}">{{ form.phone.label }}</label>
{% include "bootstrap5/field.html" with field=form.phone %}
</div>
<div class="col-md-6">
<label for="{{ form.date_of_birth.id_for_label }}">{{ form.date_of_birth.label }}</label>
{% include "bootstrap5/field.html" with field=form.date_of_birth %}
</div>
<div class="col-md-6">
<label for="{{ form.nationality.id_for_label }}">{{ form.nationality.label }}</label>
{% include "bootstrap5/field.html" with field=form.nationality %}
</div>
<div class="col-md-6">
<label for="{{ form.gender.id_for_label }}">{{ form.gender.label }}</label>
{% include "bootstrap5/field.html" with field=form.gender %}
</div>
<div class="col-md-12">
<label for="{{ form.address.id_for_label }}">{{ form.address.label }}</label>
{% include "bootstrap5/field.html" with field=form.address %}
</div>
</div>
</form>
<div class="d-flex gap-2">
<div class="d-flex gap-2">
<button form="person-form" type="submit" class="btn btn-main-action">
<i class="fas fa-save me-1"></i> {% trans "Update" %}
</button>
</div>
</div>
</div>
</div>

View File

@ -152,10 +152,15 @@
<a class="nav-link text-white" href="{% url 'user_detail' request.user.pk %}">
<i class="fas fa-user-circle me-1"></i> <span>{% trans "My Profile" %}</span></a></li>
{% endif %}
<li class="nav-item me-2">
<a class="nav-link text-white" href="{% url 'message_list' %}">
<i class="fas fa-envelope"></i> <span>{% trans "Messages" %}</span>
</a>
<li class="nav-item mx-2 mt-2">
<a href="{% url 'message_list' %}"
class=" btn btn-sm btn-outline-warning position-relative">
<i class="fas fa-envelope me-1"></i>
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
{{ request.user.get_unread_message_count }}
</span>
</a>
</li>
<li class="nav-item ms-3">

View File

@ -136,7 +136,7 @@
<div class="mb-3">
<label class="text-muted small">{% trans "Status" %}</label>
<div>
<span class="status-badge status-{{ assignment.status }}">
<span class="status-badge bg-primary-theme text-white">
{{ assignment.get_status_display }}
</span>
</div>
@ -261,7 +261,7 @@
</div>
</td>
<td>
<span class="badge bg-info">{{ application.get_stage_display }}</span>
<span class="badge bg-primary-theme">{{ application.get_stage_display }}</span>
</td>
<td>
<div class="small text-muted">
@ -271,7 +271,7 @@
</div>
</td>
<td class="px-4">
<span class="badge bg-soft-info text-info rounded-pill px-3">
<span class="badge bg-primary-theme px-3">
{{application.get_stage_display }}</span>
</td>
<td class="px-4">
@ -339,9 +339,9 @@
<div class="text-muted">/ {{ assignment.max_candidates }} {% trans "applications" %}</div>
</div>
<div class="progress mt-3" style="height: 8px;">
<div class="progress mt-3 " style="height: 8px;">
{% widthratio total_applications assignment.max_candidates 100 as progress %}
<div class="progress-bar" style="width: {{ progress }}%"></div>
<div class="progress-bar bg-primary-theme" style="width: {{ progress }}%"></div>
</div>
</div>
@ -354,7 +354,7 @@
</h5>
<div class="d-grid gap-2">
<a href="{}"
<a href="{% url "message_list" %}"
class="btn btn-outline-primary">
<i class="fas fa-envelope me-1"></i> {% trans "Send Message" %}
</a>

View File

@ -43,10 +43,10 @@
border-radius: 0.35rem;
font-weight: 700;
}
.status-ACTIVE { background-color: var(--kaauh-success); color: white; }
{% comment %} .status-ACTIVE { background-color: var(--kaauh-success); color: white; }
.status-EXPIRED { background-color: var(--kaauh-danger); color: white; }
.status-COMPLETED { background-color: var(--kaauh-info); color: white; }
.status-CANCELLED { background-color: var(--kaauh-warning); color: #856404; }
.status-CANCELLED { background-color: var(--kaauh-warning); color: #856404; } {% endcomment %}
</style>
{% endblock %}
@ -130,7 +130,7 @@
</td>
<td>
<div class="d-flex align-items-center">
<span class="badge bg-primary me-2">{{ assignment.submitted_count }}</span>
<span class="badge bg-primary-theme text-white me-2">{{ assignment.applications_submited_count}}</span>
<span class="text-muted">/ {{ assignment.max_candidates }}</span>
</div>
<div class="progress mt-1" style="height: 4px;">
@ -150,7 +150,7 @@
{% endif %}
</td>
<td>
<span class="status-badge status-{{ assignment.status }}">
<span class="status-badge bg-primary-theme text-white">
{{ assignment.get_status_display }}
</span>
</td>

View File

@ -531,6 +531,7 @@
aria-selected="true"
>
<i class="fas fa-users me-1"></i>
<span>({{total_applications}})</span>
{% trans "Recent Applications" %}
</button>
</li>
@ -546,6 +547,7 @@
aria-selected="false"
>
<i class="fas fa-briefcase me-1"></i>
<span>({{total_job_assignments}})</span>
{% trans "Assigned Jobs" %}
</button>
</li>

View File

@ -52,12 +52,8 @@
{% comment %} <a href="{% url 'agency_portal_submit_application' %}" class="btn btn-main-action me-2">
<i class="fas fa-user-plus me-1"></i> {% trans "Submit Application" %}
</a>
<a href="#" class="btn btn-outline-secondary position-relative">
<i class="fas fa-envelope me-1"></i> {% trans "Messages" %}
{% if total_unread_messages > 0 %}
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
{{ total_unread_messages }}
</span>
>
{% endif %}
</a> {% endcomment %}
</div>
@ -207,15 +203,15 @@
class="btn btn-sm btn-main-action">
<i class="fas fa-eye me-1"></i> {% trans "View Details" %}
</a>
{% if stats.unread_messages > 0 %}
<a href="{% url 'agency_portal_assignment_detail' stats.assignment.slug %}#messages"
{% comment %} {% if stats.unread_messages > 0 %}
<a href="{% url 'message_list' %}"
class="btn btn-sm btn-outline-warning position-relative">
<i class="fas fa-envelope me-1"></i>
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
{{ stats.unread_messages }}
</span>
</a>
{% endif %}
{% endif %} {% endcomment %}
</div>
</div>
</div>