Skip to content

Commit

Permalink
job_seekers_views: add list for prescribers
Browse files Browse the repository at this point in the history
  • Loading branch information
xavfernandez committed Aug 29, 2024
1 parent 98c413d commit c18e9dc
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
<span class="h4 mb-0">Candidats</span>
</div>
<div class="px-3 px-lg-4">
<a href="{% url 'job_seekers_views:list' %}" class="btn btn-outline-primary btn-block btn-ico mb-3">
<i class="ri-user-line ri-lg font-weight-normal"></i>
<span>Liste de mes candidats</span>
</a>
<ul class="list-unstyled mb-lg-5">
<li class="d-flex justify-content-between align-items-center mb-3">
<a href="{% url 'approvals:prolongation_requests_list' %}?only_pending=on" class="btn-link btn-ico">
Expand Down
5 changes: 5 additions & 0 deletions itou/templates/job_seekers_views/includes/list_counter.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{% load str_filters %}

<p class="mb-0" id="job-seekers-list-count"{% if request.htmx %} hx-swap-oob="true"{% endif %}>
{% with paginator.count as counter %}{{ counter }} résultat{{ counter|pluralizefr }}{% endwith %}
</p>
52 changes: 52 additions & 0 deletions itou/templates/job_seekers_views/includes/list_results.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{% load static %}
{% load str_filters %}

<section aria-labelledby="job-seekers-list-count" id="job-seekers-section">
{% if not page_obj %}
<div class="text-center my-3 my-md-4">
<img class="img-fluid" src="{% static 'img/illustration-siae-card-no-result.svg' %}" alt="" loading="lazy">
<p class="mb-1 mt-3">
<strong>Aucun candidat pour le moment</strong>
</p>
{% if request.user.is_employer %}
<p>
<i>Vous trouverez ici les candidats orientés vers d’autres structures.</i>
</p>
{% endif %}
</div>
{% else %}
<div class="mt-3 mt-md-4">
<table class="table">
<caption class="visually-hidden">Liste des candidats</caption>
<thead>
<tr>
<th scope="col">Prénom NOM</th>
<th scope="col">Statut du PASS IAE</th>
<th scope="col">Nombre de candidatures</th>
<th scope="col">Dernière mise à jour de candidature</th>
</tr>
</thead>
<tbody>
{% for job_seeker in page_obj %}
<tr>
<td>
<a href="{% url "job_seekers_views:details" public_id=job_seeker.public_id %}?back_url={{ request.get_full_path|urlencode }}" class="btn-link">{{ job_seeker.get_full_name|mask_unless:job_seeker.user_can_view_personal_information }}</a>
</td>
<td>
{% include "apply/includes/eligibility_badge.html" with job_seeker=job_seeker is_subject_to_eligibility_rules=True eligibility_diagnosis=job_seeker.valid_eligibility_diagnosis only %}
</td>
<td>{{ job_seeker.job_applications_nb }}</td>
<td>{{ job_seeker.last_updated_at|date:"d/m/Y" }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>

{% include "includes/pagination.html" with page=page_obj boost=True boost_target="#job-seekers-section" boost_indicator="#job-seekers-section" %}
{% endif %}
</section>

{% if request.htmx %}
{% include "job_seekers_views/includes/list_counter.html" with paginator=paginator request=request only %}
{% endif %}
42 changes: 42 additions & 0 deletions itou/templates/job_seekers_views/list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{% extends "layout/base.html" %}
{% load django_bootstrap5 %}
{% load matomo %}
{% load static %}
{% load str_filters %}
{% load format_filters %}

{% block title %}Candidats {{ block.super }}{% endblock %}

{% block title_content %}
<div class="d-flex flex-column flex-md-row gap-3 justify-content-md-between">
<h1 class="m-0">Candidats</h1>
<div class="d-flex flex-column flex-md-row gap-3" role="group" aria-label="Actions sur les candidatures">
<a href="{% url 'search:employers_home' %}" class="btn btn-lg btn-primary btn-ico">
<i class="ri-draft-line font-weight-medium" aria-hidden="true"></i>
<span>Postuler pour un candidat</span>
</a>
</div>
</div>
{% endblock %}

{% block title_prevstep %}
{% include "layout/previous_step.html" with back_url=back_url only %}
{% endblock %}

{% block content %}
<section class="s-section">
<div class="s-section__container container">
<div class="s-section__row row">
<div class="col-12">
<div class="d-flex flex-column flex-md-row align-items-md-center justify-content-md-between mb-3 mb-md-4">
{% include "job_seekers_views/includes/list_counter.html" with paginator=paginator request=request only %}
<div class="flex-column flex-md-row mt-3 mt-md-0">
{#{% bootstrap_field filters_form.job_seeker wrapper_class="w-lg-400px" show_label=False %}#}
</div>
</div>
{% include "job_seekers_views/includes/list_results.html" with page_obj=page_obj request=request only %}
</div>
</div>
</div>
</section>
{% endblock %}
10 changes: 10 additions & 0 deletions itou/utils/templatetags/nav.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,15 @@ def __repr__(self):
matomo_event_option="mes-candidatures",
),
# Prescribers.
"prescriber-jobseekers": NavItem(
label="Candidats",
icon="ri-user-line",
target=reverse("job_seekers_views:list"),
active_view_names=["job_seekers_views:list", "job_seekers_views:details"],
matomo_event_category="offcanvasNav",
matomo_event_name="clic",
matomo_event_option="candidats",
),
"prescriber-job-apps": NavItem(
label="Candidatures",
icon="ri-draft-line",
Expand Down Expand Up @@ -187,6 +196,7 @@ def nav(request):
menu_items.append(NAV_ENTRIES["job-seeker-job-apps"])
elif request.user.is_prescriber:
menu_items.append(NAV_ENTRIES["prescriber-job-apps"])
menu_items.append(NAV_ENTRIES["prescriber-jobseekers"])
if request.current_organization:
menu_items.append(
NavGroup(
Expand Down
1 change: 1 addition & 0 deletions itou/www/job_seekers_views/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@

urlpatterns = [
path("details/<uuid:public_id>", views.JobSeekerDetailView.as_view(), name="details"),
path("list", views.JobSeekerListView.as_view(), name="list"),
]
74 changes: 73 additions & 1 deletion itou/www/job_seekers_views/views.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.views.generic import DetailView
from django.db.models import Count, DateTimeField, Exists, IntegerField, Max, OuterRef, Subquery
from django.db.models.functions import Coalesce
from django.views.generic import DetailView, ListView

from itou.companies.enums import CompanyKind
from itou.eligibility.models.geiq import GEIQEligibilityDiagnosis
from itou.eligibility.models.iae import EligibilityDiagnosis
from itou.job_applications.models import JobApplication
from itou.users.enums import UserKind
from itou.users.models import User
from itou.utils.pagination import ItouPaginator
from itou.utils.urls import get_safe_url


Expand Down Expand Up @@ -63,3 +68,70 @@ def get_context_data(self, **kwargs):
"can_view_personal_information": self.request.user.can_view_personal_information(self.object),
"can_edit_personal_information": self.request.user.can_edit_personal_information(self.object),
}


class JobSeekerListView(LoginRequiredMixin, UserPassesTestMixin, ListView):
model = User
queryset = (
User.objects.filter(kind=UserKind.JOB_SEEKER).order_by("first_name", "last_name").prefetch_related("approvals")
)
paginate_by = 10
paginator_class = ItouPaginator

def test_func(self):
return self.request.user.is_authenticated and (
self.request.user.is_prescriber or self.request.user.is_employer
)

def get_template_names(self):
return ["job_seekers_views/includes/list_results.html" if self.request.htmx else "job_seekers_views/list.html"]

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
page_obj = context["page_obj"]
if page_obj is not None:
for job_seeker in page_obj:
job_seeker.user_can_view_personal_information = self.request.user.can_view_personal_information(
job_seeker
)
return context

def get_queryset(self):
queryset = super().get_queryset()
user_applications = JobApplication.objects.prescriptions_of(
self.request.user, self.request.current_organization
).filter(job_seeker=OuterRef("pk"))
subquery_count = Subquery(
user_applications.values("job_seeker").annotate(count=Count("pk")).values("count"),
output_field=IntegerField(),
)
subquery_last_update = Subquery(
user_applications.values("job_seeker").annotate(last_update=Max("updated_at")).values("last_update"),
output_field=DateTimeField(),
)
subquery_diagnosis = Subquery(
(
EligibilityDiagnosis.objects.valid()
.for_job_seeker_and_siae(job_seeker=OuterRef("pk"), siae=None)
.values("id")[:1]
),
output_field=IntegerField(),
)
query = queryset.filter(Exists(user_applications)).annotate(
job_applications_nb=Coalesce(subquery_count, 0),
last_updated_at=subquery_last_update,
valid_eligibility_diagnosis=subquery_diagnosis,
)

import sqlparse

print(
sqlparse.format(
str(query.query),
keyword_case="upper",
reindent=True,
reindent_align=True,
use_space_around_operators=True,
)
)
return query

0 comments on commit c18e9dc

Please sign in to comment.