Skip to content

Commit

Permalink
#67 Allow user to subscribe to a recruitment process
Browse files Browse the repository at this point in the history
  • Loading branch information
rnudb committed Jan 16, 2023
1 parent da9efa7 commit 1f271f5
Show file tree
Hide file tree
Showing 11 changed files with 134 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Add prequalification info on interview minute
- Update prequalification color in process details
- Remove seekube ics import
- Allow user to subscribe to a recruitment process

# V1.17.2 (2023-01-16)
- Fix a bug where global subsidiary filter would yield no result on active source page
Expand Down
22 changes: 22 additions & 0 deletions interview/migrations/0025_process_subscribers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 3.2.16 on 2022-12-15 16:48

from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("interview", "0024_merge_20221212_1657"),
]

operations = [
migrations.AddField(
model_name="process",
name="subscribers",
field=models.ManyToManyField(
blank=True, related_name="subscribed_processes", to=settings.AUTH_USER_MODEL, verbose_name="Subscribers"
),
),
]
6 changes: 6 additions & 0 deletions interview/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,10 @@ class Process(models.Model):
verbose_name=_("Process creator"),
)

subscribers = models.ManyToManyField(
PyouPyouUser, verbose_name=_("Subscribers"), blank=True, related_name="subscribed_processes"
)

def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
is_new = False if self.id else True
if is_new:
Expand Down Expand Up @@ -436,6 +440,7 @@ def trigger_notification(self, is_new):
recipient_list = []
if self.subsidiary.responsible:
recipient_list.append(self.subsidiary.responsible.user.email)
recipient_list = recipient_list + [user.email for user in self.subscribers.all()]
mail.send_mail(
subject=subject, message=body, from_email=settings.MAIL_FROM, recipient_list=set(recipient_list)
)
Expand Down Expand Up @@ -632,6 +637,7 @@ def trigger_notification(self):
if subject and body_template:
url = os.path.join(settings.SITE_HOST, self.process.get_absolute_url().lstrip("/"))
body = render_to_string(body_template, {"interview": self, "url": url})
recipient_list = recipient_list + [user.email for user in self.process.subscribers.all()]
mail.send_mail(
subject=subject, message=body, from_email=settings.MAIL_FROM, recipient_list=set(recipient_list)
)
Expand Down
1 change: 1 addition & 0 deletions interview/static/interview/js/htmx.min.js

Large diffs are not rendered by default.

12 changes: 11 additions & 1 deletion interview/templates/interview/process_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,21 @@

{% block more_css %}
<link rel="stylesheet" href="{% static 'django_tables2/themes/paleblue/css/screen.css' %}" />
<script src="{% static "interview/js/htmx.min.js" %}"></script>
{% endblock %}

{% block content %}
<div class="container">
<h3> {% trans "Candidate:" %} <a href="{% url 'candidate' process.pk %}">{{ process.candidate.display_name }} </a> [{{ process.subsidiary }}]</h3>
<div class="row">
<h3 class="col-md-8"> {% trans "Candidate:" %} <a href="{% url 'candidate' process.pk %}">{{ process.candidate.display_name }} </a> [{{ process.subsidiary }}]</h3>
<h3 class="col-md-4" hx-post="/switch_process_subscription/{{ process.id }}/" hx-target="this" hx-swap="innerHTML" hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}' id="h3-subscribe-button-id">
{% if user not in process.subscribers.all %}
{% include "interview/subscribe_button.html" %}
{% else %}
{% include "interview/unsubscribe_button.html" %}
{% endif %}
</h3>
</div>
{% if process.is_open %}
<h5> {% trans "This process is in progress" %} </h5>
{% else %}
Expand Down
7 changes: 7 additions & 0 deletions interview/templates/interview/subscribe_button.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{% load i18n %}

<button type="button" class="btn btn-info" id="subscribe-button-id"
style="display: block; margin-left: auto; margin-right: auto">
{% trans "Subscribe to this process' notifications" %}
</button>

6 changes: 6 additions & 0 deletions interview/templates/interview/unsubscribe_button.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{% load i18n %}

<button type="button" class="btn btn-danger" id="subscribe-button-id"
style="display: block; margin-left: auto; margin-right: auto">
{% trans "Unsubscribe to this process' notifications" %}
</button>
37 changes: 36 additions & 1 deletion interview/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,42 @@ def test_status_and_notification(self):
self.assertEqual(Process.objects.get(id=p.id).state, Process.HIRED)
self.assertEqual(list(p.responsible.all()), [])
self.assertCountEqual(mail.outbox[0].to, [subsidiaryResponsible.user.email])
mail.outbox = 0
mail.outbox = []

def test_subscription_notification(self):
subsidiary = SubsidiaryFactory()
p = ProcessFactory(subsidiary=subsidiary)
u1 = ConsultantFactory(company=p.subsidiary)
u2 = ConsultantFactory(company=p.subsidiary)

p.subscribers.add(u1.user)

# change processs status to trigger mail notification
p.state = Process.HIRED
p.save()

# assert only one email was sent
self.assertEqual(len(mail.outbox), 1)
# assert it was sent to subscribed user
self.assertEqual(mail.outbox[0].to, [u1.user.email])
# reset outbox
mail.outbox = []

# change subscribers
p.subscribers.remove(u1.user)
p.subscribers.add(u2.user)
p.save()


# change processs status to trigger mail notification
p.state = Process.NO_GO
p.save()

# assert only one email was sent
self.assertEqual(len(mail.outbox), 1)
# assert it was sent to subscribed user
self.assertEqual(mail.outbox[0].to, [u2.user.email])
mail.outbox = []


class AnonymizesCanditateTestCase(TestCase):
Expand Down
19 changes: 19 additions & 0 deletions interview/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,25 @@ def process(request, process_id, slug_info=None):
return render(request, "interview/process_detail.html", context)


@login_required
@require_http_methods(["POST"])
def switch_process_subscription_ajax(request, process_id):
p = None
try:
if process_id is None:
raise Process.DoesNotExist
p = Process.objects.get(id=process_id)
except Process.DoesNotExist:
return HttpResponseNotFound()

if request.user in p.subscribers.all():
p.subscribers.remove(request.user)
return render(request, "interview/subscribe_button.html", {})

p.subscribers.add(request.user)
return render(request, "interview/unsubscribe_button.html", {})


@login_required
@require_http_methods(["POST"])
@privilege_level_check(
Expand Down
22 changes: 20 additions & 2 deletions locale/fr/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,13 @@ msgstr "Informations complémentaires"
msgid "Process creator"
msgstr "Créateur du processus"

#: interview/models.py:362
#: interview/models.py:328
#, fuzzy
#| msgid "Subsidiaries"
msgid "Subscribers"
msgstr "Abonnés"

#: interview/models.py:375
#: interview/templates/interview/interview_minute.html:10
#: interview/templates/interview/interview_minute_form.html:45
msgid "for subsidiary"
Expand Down Expand Up @@ -518,7 +524,19 @@ msgstr "Ajout d'une offre"
msgid "Offers: "
msgstr "Offres : "

#: interview/templates/interview/process_detail.html:16
#: interview/templates/interview/process_detail.html:35
#: interview/templates/interview/process_detail.html:58
#: interview/templates/interview/unsubscribe_button.html:7
msgid "Unsubscribe to this process' notifications"
msgstr "Se désabonner des notifications de ce processus"

#: interview/templates/interview/process_detail.html:38
#: interview/templates/interview/process_detail.html:56
#: interview/templates/interview/subscribe_button.html:7
msgid "Subscribe to this process' notifications"
msgstr "S'abonner aux notifications de ce processus"

#: interview/templates/interview/process_detail.html:53
msgid "Candidate:"
msgstr "Candidat :"

Expand Down
5 changes: 5 additions & 0 deletions pyoupyou/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
name="import-new-process-from-cognito-form",
),
url(r"^process/(?P<process_id>\d+)(?P<slug_info>(\w-?)*)/$", views.process, name="process-details"),
url(
r"^switch_process_subscription/(?P<process_id>\d+)/$",
views.switch_process_subscription_ajax,
name="switch-process-subscription",
),
url(r"^process/(?P<process_id>\d+)/close/$", views.close_process, name="process-close"),
url(r"^process/(?P<process_id>\d+)/reopen/$", views.reopen_process, name="process-reopen"),
url(r"^process/(?P<process_id>\d+)/interview/$", views.interview, {"action": "edit"}, name="process-new-interview"),
Expand Down

0 comments on commit 1f271f5

Please sign in to comment.