Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

275 simplify user model 2 #334

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# v1.23.0 (XXXX-XX-XX)
- Simplify user model by moving Consultant information over to user instead

# v1.22.0 (2024-07-31)
- Add new reports in process analysis: count process per interview numbers, candidates source per subsidiary
- Add subsidiary model field: show the subsidiary by default in the report analysis
Expand Down
6 changes: 3 additions & 3 deletions interview/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from django.contrib.auth.views import redirect_to_login

from ref.models import Consultant
from ref.models import PyouPyouUser


# Strongly inspired by django's user_passes_test decorator
Expand All @@ -20,8 +20,8 @@ def decorator(view_func):
def wrapper(request, *args, **kwargs):
authorised = authorised_level
if authorised is None:
authorised = [Consultant.PrivilegeLevel.ALL]
if request.user.is_authenticated and request.user.consultant.privilege in authorised:
authorised = [PyouPyouUser.PrivilegeLevel.ALL]
if request.user.is_authenticated and request.user.privilege in authorised:
return view_func(request, *args, **kwargs)
return redirect_to_login(next=request.path)

Expand Down
6 changes: 3 additions & 3 deletions interview/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from interview.models import Interview, Sources, SourcesCategory, ContractType, InterviewKind, Process
from ref.factory import SubsidiaryFactory
from ref.models import Subsidiary, Consultant
from ref.models import Subsidiary, PyouPyouUser


test_tz = pytz.timezone("Europe/Paris")
Expand Down Expand Up @@ -49,12 +49,12 @@ def negative_end_process(process, itw, next_planned_date):

def get_available_consultants_for_itw(subsidiary, all_itw_given_process):
# retrieve available consultants that have not yet been involved in the process
possible_interviewer = Consultant.objects.filter(company=subsidiary).exclude(
possible_interviewer = PyouPyouUser.objects.filter(company=subsidiary).exclude(
id__in=list(all_itw_given_process.values_list("interviewers", flat=True))
)
# if all consultants were already involved in the process, choose one at random
if not possible_interviewer:
possible_interviewer = Consultant.objects.filter(company=subsidiary)
possible_interviewer = PyouPyouUser.objects.filter(company=subsidiary)

return possible_interviewer

Expand Down
8 changes: 4 additions & 4 deletions interview/feeds.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from django.utils import timezone

from interview.models import Interview
from ref.models import Subsidiary, PyouPyouUser, Consultant
from ref.models import Subsidiary, PyouPyouUser


class AbstractPyoupyouInterviewFeed(ICalFeed):
Expand All @@ -17,12 +17,12 @@ class AbstractPyoupyouInterviewFeed(ICalFeed):
def __call__(self, request, *args, **kwargs):
user = PyouPyouUser.objects.filter(token=kwargs["token"]).first()
del kwargs["token"]
if not user or not user.is_active or user.consultant.privilege != Consultant.PrivilegeLevel.ALL:
if not user or not user.is_active or user.privilege != PyouPyouUser.PrivilegeLevel.ALL:
return HttpResponse("Unauthenticated user", status=401)
return super().__call__(request, *args, **kwargs)

def item_title(self, item):
itws = ", ".join([i.user.trigramme for i in item.interviewers.all()])
itws = ", ".join([i.trigramme for i in item.interviewers.all()])
return escape(force_str("#{} {} [{}]".format(item.rank, item.process.candidate.name, itws)))

def item_description(self, item):
Expand Down Expand Up @@ -102,4 +102,4 @@ def get_object(self, request, user_id=None):

def items(self, user):
last_month = timezone.now() - timedelta(days=30)
return Interview.objects.filter(interviewers__user=user, planned_date__gte=last_month).order_by("-planned_date")
return Interview.objects.filter(interviewers=user, planned_date__gte=last_month).order_by("-planned_date")
5 changes: 3 additions & 2 deletions interview/filters.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import django_filters

from interview.models import Process, Interview
from ref.models import Subsidiary, Consultant

from ref.models import Subsidiary, PyouPyouUser
from django.utils.translation import gettext_lazy as _


Expand All @@ -22,7 +23,7 @@ class InterviewSummaryFilter(django_filters.FilterSet):
class InterviewListFilter(django_filters.FilterSet):
last_state_change = django_filters.DateFromToRangeFilter(field_name="planned_date")
interviewer = django_filters.ModelChoiceFilter(
queryset=Consultant.objects.filter(user__is_active=True).select_related("user"), field_name="interviewers"
queryset=PyouPyouUser.objects.filter(is_active=True), field_name="interviewers"
)
state = django_filters.ChoiceFilter(choices=Interview.ITW_STATE, field_name="state", empty_label=_("All states"))

Expand Down
15 changes: 8 additions & 7 deletions interview/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,20 @@
from crispy_forms.layout import Layout, Div, Submit, Column, Field
from django_select2.forms import ModelSelect2MultipleWidget, ModelSelect2Widget

from interview.models import Consultant, Interview, Candidate, Process, Sources, Offer
from interview.models import Interview, Candidate, Process, Sources, Offer
from ref.models import PyouPyouUser


class MultipleConsultantWidget(ModelSelect2MultipleWidget):
model = Consultant
queryset = Consultant.objects.filter(user__is_active=True)
search_fields = ["user__trigramme__icontains", "user__full_name__icontains"]
model = PyouPyouUser
queryset = PyouPyouUser.objects.filter(is_active=True)
search_fields = ["trigramme__icontains", "full_name__icontains"]


class SingleConsultantWidget(ModelSelect2Widget):
model = Consultant
queryset = Consultant.objects.filter(user__is_active=True)
search_fields = ["user__trigramme__icontains", "user__full_name__icontains"]
model = PyouPyouUser
queryset = PyouPyouUser.objects.filter(is_active=True)
search_fields = ["trigramme__icontains", "full_name__icontains"]


class SourcesWidget(ModelSelect2Widget):
Expand Down
7 changes: 3 additions & 4 deletions interview/management/commands/create_dev_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@
InterviewFactory,
)
from interview.models import ContractType, SourcesCategory, InterviewKind, Interview, Process, Sources
from ref.factory import SubsidiaryFactory, PyouPyouUserFactory, ConsultantFactory
from ref.models import Consultant
from ref.factory import SubsidiaryFactory, PyouPyouUserFactory
from interview.factory import (
date_minus_time_ago,
date_random_plus_minus_time,
Expand Down Expand Up @@ -89,11 +88,11 @@ def handle(self, *args, **options):
# create consultants for this subsidiary
subsidiary_consultants = []
for k in range(5):
subsidiary_consultants.append(ConsultantFactory(company=subsidiary))
subsidiary_consultants.append(PyouPyouUserFactory(company=subsidiary))

# we need at least one consultant which is both a superuser and staff to access the admin board
# note: superusers cannot be created with manage.py because they also need a consultant
admin = subsidiary_consultants[0].user
admin = subsidiary_consultants[0]
admin.is_superuser = True
admin.is_staff = True
admin.save()
Expand Down
38 changes: 38 additions & 0 deletions interview/migrations/0025_auto_20230116_1355.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Generated by Django 3.2.16 on 2023-01-16 12:55

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


class Migration(migrations.Migration):

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

operations = [
migrations.AlterField(
model_name="interview",
name="interviewers",
field=models.ManyToManyField(to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name="process",
name="creator",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="process_creator",
to=settings.AUTH_USER_MODEL,
verbose_name="Process creator",
),
),
migrations.AlterField(
model_name="process",
name="responsible",
field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL),
),
]
13 changes: 13 additions & 0 deletions interview/migrations/0028_merge_20241021_1741.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Generated by Django 4.2.4 on 2024-10-21 15:41

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("interview", "0025_auto_20230116_1355"),
("interview", "0027_alter_offer_options"),
]

operations = []
44 changes: 22 additions & 22 deletions interview/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from django.utils.translation import gettext_lazy as _

from pyoupyou.settings import MINUTE_FORMAT, STALE_DAYS
from ref.models import Consultant, Subsidiary, PyouPyouUser
from ref.models import Subsidiary, PyouPyouUser

CharField.register_lookup(Lower)

Expand Down Expand Up @@ -219,22 +219,18 @@ def for_user(self, user):
q = (
super()
.get_queryset()
.filter(
Q(start_date__gte=user.date_joined)
| Q(responsible__in=[user.consultant])
| Q(interview__interviewers__user=user)
)
.filter(Q(start_date__gte=user.date_joined) | Q(responsible__in=[user]) | Q(interview__interviewers=user))
.distinct()
)
if user.consultant.is_external:
q = q.filter(sources=user.consultant.limited_to_source)
if user.is_external:
q = q.filter(sources=user.limited_to_source)
return q

def for_table(self, user):
qs = (
self.for_user(user)
.select_related("subsidiary", "candidate", "contract_type")
.prefetch_related("responsible__user")
.prefetch_related("responsible")
.annotate(current_rank=Count("interview", distinct=True))
)
return qs
Expand Down Expand Up @@ -311,7 +307,7 @@ class Process(models.Model):
contract_duration = models.PositiveIntegerField(verbose_name=_("Contract duration in month"), null=True, blank=True)
contract_start_date = models.DateField(null=True, blank=True)
sources = models.ForeignKey(Sources, null=True, blank=True, on_delete=models.SET_NULL)
responsible = models.ManyToManyField(Consultant, blank=True)
responsible = models.ManyToManyField(PyouPyouUser, blank=True)
state = models.CharField(
max_length=3, choices=PROCESS_STATE, verbose_name=_("Closed reason"), default=WAITING_INTERVIEWER_TO_BE_DESIGNED
)
Expand All @@ -323,7 +319,7 @@ class Process(models.Model):
other_informations = models.TextField(verbose_name=_("Other informations"), blank=True)

creator = models.ForeignKey(
Consultant,
PyouPyouUser,
null=True,
blank=True,
on_delete=models.SET_NULL,
Expand Down Expand Up @@ -456,20 +452,22 @@ def trigger_notification(self, is_new):

# add subsidiary responsible to recipient list
if self.subsidiary.responsible:
recipient_list.append(self.subsidiary.responsible.user.email)
recipient_list.append(self.subsidiary.responsible.email)

# add users subscribed to offer's notification

if self.offer:
recipient_list = recipient_list + [user.email for user in self.offer.subscribers.all()]

recipient_list = recipient_list + [user.email for user in self.subscribers.all()]

# not sure about line 465 about the merge conflict...
mail.send_mail(
subject=subject, message=body, from_email=settings.MAIL_FROM, recipient_list=set(recipient_list)
)

def get_all_interviewers_for_process(self):
return PyouPyouUser.objects.filter(consultant__interview__process=self)
return PyouPyouUser.objects.filter(interview__process=self)


class InterviewKind(models.Model):
Expand All @@ -484,11 +482,11 @@ def for_user(self, user):
q = (
super(InterviewManager, self)
.get_queryset()
.filter(Q(process__start_date__gte=user.date_joined) | Q(interviewers__in=[user.consultant]))
.filter(Q(process__start_date__gte=user.date_joined) | Q(interviewers__in=[user]))
.distinct()
)
if user.consultant.is_external:
q = q.filter(process__sources=user.consultant.limited_to_source)
if user.is_external:
q = q.filter(process__sources=user.limited_to_source)
return q

def for_table(self, user):
Expand All @@ -502,7 +500,7 @@ def for_table(self, user):
"kind_of_interview",
"process__offer__subsidiary",
)
.prefetch_related("interviewers__user")
.prefetch_related("interviewers")
)
return qs

Expand Down Expand Up @@ -542,7 +540,7 @@ class Interview(models.Model):
state = models.CharField(max_length=3, choices=ITW_STATE, verbose_name=_("next state"))
rank = models.IntegerField(verbose_name=_("Rank"), blank=True, null=True)
planned_date = models.DateTimeField(verbose_name=_("Planned date"), blank=True, null=True)
interviewers = models.ManyToManyField(Consultant)
interviewers = models.ManyToManyField(PyouPyouUser)

minute = models.TextField(verbose_name=_("Minute"), blank=True)
minute_format = models.CharField(max_length=3, choices=MINUTE_FORMAT, default=MINUTE_FORMAT[0][0])
Expand All @@ -554,7 +552,7 @@ class Interview(models.Model):
)

def __str__(self):
interviewers = ", ".join(i.user.trigramme for i in self.interviewers.all())
interviewers = ", ".join(i.trigramme for i in self.interviewers.all())
return "#{rank} - {process} - {itws}".format(rank=self.rank, process=self.process, itws=interviewers)

def save(self, force_insert=False, force_update=False, using=None, update_fields=None, trigger_notification=True):
Expand Down Expand Up @@ -639,19 +637,21 @@ def needs_attention(self):
@property
def interviewers_str(self):
if self.id:
return ", ".join(i.user.get_full_name() for i in self.interviewers.all())
return ", ".join(i.get_full_name() for i in self.interviewers.all())
return ""

@property
def interviewers_trigram_slug(self):
if self.id:
return "-".join(i.user.trigramme for i in self.interviewers.all())
return "-".join(i.trigramme for i in self.interviewers.all())
return ""

def trigger_notification(self):
recipient_list = self.process.subsidiary.notification_emails
if self.process.subsidiary.responsible:
recipient_list.append(self.process.subsidiary.responsible.email)
if self.id:
recipient_list = recipient_list + [i.user.email for i in self.interviewers.all()]
recipient_list = recipient_list + [i.email for i in self.interviewers.all()]

subject = None
body_template = None
Expand Down
8 changes: 4 additions & 4 deletions interview/templates/interview/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
<div class="container">

<div class="navbar-header">
<a class="navbar-brand" href="{% url 'dashboard' %}">[{{ user }}]</a>
<a class="navbar-brand" href="{% url 'dashboard' %}">[{{ user.trigramme }}]</a>
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false">
<i class="fa fa-bars fa-2x"></i>
</button>
Expand All @@ -44,10 +44,10 @@
<ul class="nav navbar-nav">
<li class="active" ><a href="{% url 'process-list' %}">{% trans "Ongoing processes" %}</a></li>
<li class="active" ><a href="{% url 'process-closed-list' %}">{% trans "Closed processes" %}</a></li>
{% if user.consultant.privilege != user.consultant.PrivilegeLevel.EXTERNAL_READONLY %}
{% if user.privilege != user.PrivilegeLevel.EXTERNAL_READONLY %}
<li><a href="{% url 'candidate-new' %}">{% trans "New candidate" %}</a></li>
{% endif %}
{% if user.consultant.privilege == user.consultant.PrivilegeLevel.ALL %}
{% if user.privilege == user.PrivilegeLevel.ALL %}
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{% trans "More" %} <span class="caret"></span></a>
<ul class="dropdown-menu">
Expand All @@ -69,7 +69,7 @@
<li><form class="navbar-form" role="search" action="{% url 'search' %}" method="GET">
<input type="text" class="form-control" placeholder="{% trans "Search" %}" name="q" value="{{ search_query }}">
</form></li>
{% if user.consultant.privilege == user.consultant.PrivilegeLevel.ALL %}
{% if user.privilege == user.PrivilegeLevel.ALL %}
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"> <i class="fa fa-calendar" aria-hidden="true"> </i> <span class="caret"></span></a>
<ul class="dropdown-menu">
Expand Down
2 changes: 1 addition & 1 deletion interview/templates/interview/dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
</div>
<div class="panel panel-primary">
<div class="panel-heading">
<div class="panel-title">{% blocktrans with subsidiary=user.consultant.company %} {{subsidiary}} processes{% endblocktrans %}</div>
<div class="panel-title">{% blocktrans with subsidiary=user.company %} {{subsidiary}} processes{% endblocktrans %}</div>
</div>
{% render_table subsidiary_processes_table %}
</div>
Expand Down
2 changes: 1 addition & 1 deletion interview/templates/interview/interview_minute.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ <h3>{% trans "Minute" %}
{% elif interview.state == "NO" %}
<i class="fa fa-thumbs-down text-danger" aria-hidden="true"></i>
{% endif %}
{% if user.consultant in interview.interviewers.all %}
{% if user in interview.interviewers.all %}
<a class="btn btn-info btn-xs row-action" href="{% url 'interview-minute-edit' interview_id=interview.pk %}"><i class="fa fa-file-text-o" aria-hidden="true"></i> {% trans "Change minute" %}</a>
{% endif %}
</h3>
Expand Down
Loading
Loading