from django import forms
from django.core.urlresolvers import reverse
from django.contrib import admin
-from django.contrib.admin.views.main import ChangeList as DjangoChangeList
from django.conf import settings
from django.db.models import Q
from django.template.defaultfilters import date
from auf.django.metadata.admin import AUFMetadataAdminMixin, AUFMetadataInlineAdminMixin, AUF_METADATA_READONLY_FIELDS
from forms import ContratForm, AyantDroitForm, EmployeAdminForm, AjaxSelect
from dae.utils import get_employe_from_user
+from change_list import ChangeList
from groups import grp_drh
import models as rh
import filters
# FILTRAGE PAR DATE
################################################################################
class DateRangeMixin(object):
- """
- Mixin pour que le model admin utilise le changelist trafiqué permettant de filter par range
- non strict.
- Par défaut, le filtrage est configuré sur aujourd'hui, soit les actifs
- """
- date_borne_gauche = 'date_debut'
- date_borne_droite = 'date_fin'
+ prefixe_recherche_temporelle = ""
def get_changelist(self, request, **kwargs):
- if request.META.has_key('HTTP_REFERER'):
- referer = request.META['HTTP_REFERER']
- referer = "/".join(referer.split('/')[3:])
- referer = "/%s" % referer.split('?')[0]
- change_list_view = 'admin:%s_%s_changelist' % (self.model._meta.app_label, self.model.__name__.lower())
- if referer != reverse(change_list_view):
- params = request.GET.copy()
- today = datetime.date.today()
- params.update({'%s__gte' % self.date_borne_gauche : str(today), '%s__lte' % self.date_borne_droite : str(today) })
- request.GET = params
return ChangeList
-class ChangeList(DjangoChangeList):
-
- def __init__(self, *args, **kwargs):
- super(ChangeList, self).__init__(*args, **kwargs)
-
- def get_query_set(self):
- old = self.params.copy()
- date_debut = None
- date_fin = None
- for k, v in self.params.items():
- if 'date_debut' in k:
- prefix_debut = "".join(k.split('date_debut')[0:-1]) + 'date_debut'
- date_debut = v
- del self.params[k]
- elif 'date_fin' in k:
- prefix_fin = "".join(k.split('date_fin')[0:-1]) + 'date_fin'
- date_fin = v
- del self.params[k]
- qs = super(ChangeList, self).get_query_set()
-
- # hack pour spécifier un range
- if date_fin is None and date_debut is not None:
- date_fin = '2020-01-01'
- prefix_fin = prefix_debut.replace('debut', 'fin')
- if date_debut is None and date_fin is not None:
- date_debut = '1000-01-01'
- prefix_debut = prefix_fin.replace('fin', 'debut')
-
- if date_debut is not None and date_fin is not None:
- q_left = (Q(**{'%s__isnull' % prefix_debut : True}) | Q(**{'%s__lte' % prefix_debut : date_debut})) & (Q(**{'%s__gte' % prefix_fin : date_debut}) & Q(**{'%s__lte' % prefix_fin : date_fin}))
- q_right = (Q(**{'%s__isnull' % prefix_fin : True}) | Q(**{'%s__gte' % prefix_fin : date_fin})) & (Q(**{'%s__gte' % prefix_debut : date_debut}) & Q(**{'%s__lte' % prefix_debut : date_fin}))
- q_both = Q(**{'%s__isnull' % prefix_fin : True}) | Q(**{'%s__lte' % prefix_fin : date_fin}) & (Q(**{'%s__isnull' % prefix_debut : True}) | Q(**{'%s__gte' % prefix_debut : date_debut}))
- q_non_supprime = Q(**{'%s__exact' % prefix_debut.replace('date_debut', 'supprime') : False})
- q = (q_left | q_right | q_both) & q_non_supprime
- qs = qs.filter(q).distinct()
-
- self.params = old
- return qs
-
-################################################################################
# Override of the InlineModelAdmin to support the link in the tabular inline
class LinkedInline(admin.options.InlineModelAdmin):
'poste__type_poste__famille_emploi',
'poste__type_poste',
'rh_contrats__type_contrat',
- 'date_debut',
- 'date_fin',
)
inlines = (DossierPieceInline, ContratInline,
RemunerationInline,
'poste__type_poste__id__exact',
'poste__type_poste__famille_emploi__id__exact',
'rh_contrats__type_contrat__id__exact',
- 'date_debut__gte',
- 'date_debut__isnull',
- 'date_fin__lte',
- 'date_fin__isnull',
):
return True
--- /dev/null
+import time, datetime
+from django.core.exceptions import SuspiciousOperation
+from django.conf import settings
+from django.contrib.admin.options import IncorrectLookupParameters
+from django.contrib.admin.views.main import ChangeList as DjangoChangeList
+from django.contrib.admin.views.main import ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, PAGE_VAR, SEARCH_VAR, TO_FIELD_VAR, IS_POPUP_VAR, ERROR_FLAG, field_needs_distinct
+from django.utils.encoding import force_unicode, smart_str
+from django.utils.http import urlencode
+from django.db.models import Q
+
+
+KEY_ANNEE = 'annee'
+KEY_DATE_DEBUT = 'date_debut'
+KEY_DATE_FIN = 'date_fin'
+KEY_STATUT = 'statut'
+
+STATUT_ACTIF = 'Actif'
+STATUT_INACTIF = 'Inactif'
+STATUT_FUTUR = 'Futur'
+STATUT_INCONNU = 'Inconnu'
+
+class ChangeList(DjangoChangeList):
+
+ internal_fields = (KEY_ANNEE, KEY_DATE_DEBUT, KEY_DATE_FIN, KEY_STATUT, )
+ STATUT_CHOICES = ('', STATUT_ACTIF, STATUT_INACTIF, STATUT_FUTUR, STATUT_INCONNU )
+
+ def __init__(self, *args, **kwargs):
+ super(ChangeList, self).__init__(*args, **kwargs)
+
+ def get_prefix(self):
+ return getattr(self.model_admin, 'prefixe_recherche_temporelle', "")
+
+ def get_annees(self):
+ prefix = self.get_prefix()
+ date_debut = "%s%s" % (prefix, KEY_DATE_DEBUT)
+ date_fin = "%s%s" % (prefix, KEY_DATE_FIN)
+ annees = self.model.objects.all().values(date_debut, date_fin)
+ annees_debut = [d[date_debut].year for d in annees if d[date_debut] is not None]
+ annees_fin = [d[date_fin].year for d in annees if d[date_fin] is not None]
+ annees = set(annees_debut + annees_fin)
+ annees = list(annees)
+ annees.sort(reverse=True)
+ return annees
+
+
+ def get_q_inconnu(self, prefix):
+ date_debut_nulle = Q(**{"%s%s__isnull" % (prefix, KEY_DATE_DEBUT) : True})
+ date_fin_nulle = Q(**{"%s%s__isnull" % (prefix, KEY_DATE_FIN) : True})
+ return Q(date_debut_nulle & date_fin_nulle)
+
+ def get_q_range(self, prefix, borne_gauche=None, borne_droite=None):
+
+ date_debut_nulle = Q(**{"%s%s__isnull" % (prefix, KEY_DATE_DEBUT) : True})
+ date_fin_nulle = Q(**{"%s%s__isnull" % (prefix, KEY_DATE_FIN) : True})
+ date_debut_superieure_ou_egale_a_borne_gauche = Q(**{"%s%s__gte" % (prefix, KEY_DATE_DEBUT) : borne_gauche})
+ date_debut_strict_superieure_ou_egale_a_borne_gauche = Q(**{"%s%s__gt" % (prefix, KEY_DATE_DEBUT) : borne_gauche})
+ date_debut_inferieure_ou_egale_a_borne_gauche = Q(**{"%s%s__lte" % (prefix, KEY_DATE_DEBUT) : borne_gauche})
+ date_fin_superieure_ou_egale_a_borne_gauche = Q(**{"%s%s__gte" % (prefix, KEY_DATE_FIN) : borne_gauche})
+ date_fin_inferieure_ou_egale_a_borne_droite = Q(**{"%s%s__lte" % (prefix, KEY_DATE_FIN) : borne_droite})
+ date_fin_strict_inferieure_ou_egale_a_borne_droite = Q(**{"%s%s__lt" % (prefix, KEY_DATE_FIN) : borne_droite})
+ date_debut_inferieure_ou_egale_a_borne_droite = Q(**{"%s%s__lte" % (prefix, KEY_DATE_DEBUT) : borne_droite})
+ date_fin_superieure_ou_egale_a_borne_droite = Q(**{"%s%s__gte" % (prefix, KEY_DATE_FIN) : borne_droite})
+
+ if borne_droite is None:
+ q_range = date_debut_strict_superieure_ou_egale_a_borne_gauche
+
+ if borne_gauche is None:
+ q_range = date_fin_strict_inferieure_ou_egale_a_borne_droite
+
+ if borne_droite is not None and borne_gauche is not None:
+ q_range = (date_debut_superieure_ou_egale_a_borne_gauche & date_fin_inferieure_ou_egale_a_borne_droite) | \
+ ((date_debut_inferieure_ou_egale_a_borne_gauche | date_debut_nulle) & date_fin_superieure_ou_egale_a_borne_gauche & date_fin_inferieure_ou_egale_a_borne_droite) | \
+ ((date_fin_superieure_ou_egale_a_borne_droite | date_fin_nulle) & date_debut_inferieure_ou_egale_a_borne_droite) | \
+ (date_debut_inferieure_ou_egale_a_borne_gauche & date_fin_superieure_ou_egale_a_borne_droite)
+
+ if borne_droite is None and borne_gauche is None:
+ q_range = Q()
+
+ return q_range
+
+ def get_query_set(self):
+
+ lookup_recherche_temporelle = {}
+ use_distinct = False
+
+ qs = self.root_query_set
+ lookup_params = self.params.copy() # a dictionary of the query string
+ for i in (ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR, TO_FIELD_VAR):
+ if i in lookup_params:
+ del lookup_params[i]
+ for key, value in lookup_params.items():
+ if not isinstance(key, str):
+ # 'key' will be used as a keyword argument later, so Python
+ # requires it to be a string.
+ del lookup_params[key]
+ lookup_params[smart_str(key)] = value
+
+ # ignorer les param GET pour la recherche temporelle
+ if len([i for i in self.internal_fields if key.endswith(i)]) > 0:
+ del lookup_params[key]
+ lookup_recherche_temporelle[key] = value
+ continue
+
+ if not use_distinct:
+ # Check if it's a relationship that might return more than one
+ # instance
+ field_name = key.split('__', 1)[0]
+ try:
+ f = self.lookup_opts.get_field_by_name(field_name)[0]
+ except models.FieldDoesNotExist:
+ raise IncorrectLookupParameters
+ use_distinct = field_needs_distinct(f)
+
+ # if key ends with __in, split parameter into separate values
+ if key.endswith('__in'):
+ value = value.split(',')
+ lookup_params[key] = value
+
+ # if key ends with __isnull, special case '' and false
+ if key.endswith('__isnull'):
+ if value.lower() in ('', 'false'):
+ value = False
+ else:
+ value = True
+ lookup_params[key] = value
+
+ if not self.model_admin.lookup_allowed(key, value):
+ raise SuspiciousOperation(
+ "Filtering by %s not allowed" % key
+ )
+
+ # Apply lookup parameters from the query string.
+ try:
+ qs = qs.filter(**lookup_params)
+ # Naked except! Because we don't have any other way of validating "params".
+ # They might be invalid if the keyword arguments are incorrect, or if the
+ # values are not in the correct type, so we might get FieldError, ValueError,
+ # ValicationError, or ? from a custom field that raises yet something else
+ # when handed impossible data.
+ except:
+ raise IncorrectLookupParameters
+
+ q = Q()
+ prefix = self.get_prefix()
+ borne_gauche = None
+ borne_droite = None
+ for k, v in lookup_recherche_temporelle.items():
+
+ if k.endswith(KEY_ANNEE):
+ borne_gauche = "%s-01-01" % v
+ borne_droite = "%s-12-31" % v
+
+ if k.endswith(KEY_DATE_DEBUT):
+ date = time.strptime(v, settings.DATE_INPUT_FORMATS[0])
+ borne_gauche = time.strftime("%Y-%m-%d", date)
+
+ if k.endswith(KEY_DATE_FIN):
+ date = time.strptime(v, settings.DATE_INPUT_FORMATS[0])
+ borne_droite = time.strftime("%Y-%m-%d", date)
+
+ if k.endswith(KEY_STATUT):
+ aujourdhui = datetime.date.today()
+ if v == STATUT_ACTIF:
+ borne_gauche = aujourdhui
+ borne_droite = aujourdhui
+ elif v == STATUT_INACTIF:
+ borne_droite = aujourdhui
+ elif v == STATUT_FUTUR:
+ borne_gauche = aujourdhui
+ elif v == STATUT_INCONNU:
+ q = q & self.get_q_inconnu(prefix)
+
+ q_range = self.get_q_range(prefix, borne_gauche, borne_droite)
+ qs = qs.filter(q & q_range)
+
+
+ # Use select_related() if one of the list_display options is a field
+ # with a relationship and the provided queryset doesn't already have
+ # select_related defined.
+ if not qs.query.select_related:
+ if self.list_select_related:
+ qs = qs.select_related()
+ else:
+ for field_name in self.list_display:
+ try:
+ f = self.lookup_opts.get_field(field_name)
+ except models.FieldDoesNotExist:
+ pass
+ else:
+ if isinstance(f.rel, models.ManyToOneRel):
+ qs = qs.select_related()
+ break
+
+ # Set ordering.
+ if self.order_field:
+ qs = qs.order_by('%s%s' % ((self.order_type == 'desc' and '-' or ''), self.order_field))
+
+ # Apply keyword searches.
+ def construct_search(field_name):
+ if field_name.startswith('^'):
+ return "%s__istartswith" % field_name[1:]
+ elif field_name.startswith('='):
+ return "%s__iexact" % field_name[1:]
+ elif field_name.startswith('@'):
+ return "%s__search" % field_name[1:]
+ else:
+ return "%s__icontains" % field_name
+
+ if self.search_fields and self.query:
+ orm_lookups = [construct_search(str(search_field))
+ for search_field in self.search_fields]
+ for bit in self.query.split():
+ or_queries = [models.Q(**{orm_lookup: bit})
+ for orm_lookup in orm_lookups]
+ qs = qs.filter(reduce(operator.or_, or_queries))
+ if not use_distinct:
+ for search_spec in orm_lookups:
+ field_name = search_spec.split('__', 1)[0]
+ f = self.lookup_opts.get_field_by_name(field_name)[0]
+ if field_needs_distinct(f):
+ use_distinct = True
+ break
+
+ if use_distinct:
+ return qs.distinct()
+ else:
+ return qs
+
+ def get_query_string(self, new_params=None, remove=None):
+ if new_params is None: new_params = {}
+ if remove is None: remove = []
+ p = self.params.copy()
+ for r in remove:
+ for k in p.keys():
+ if k.startswith(r):
+ del p[k]
+ for k, v in new_params.items():
+ if v is None:
+ if k in p:
+ del p[k]
+ else:
+ p[k] = v
+ return '?%s' % urlencode(p)
from django.template import Library
from django.db import connection
+from django.contrib.admin import widgets as adminwidgets
from django import forms
+from .. import change_list as pcl
register = Library()
@register.inclusion_tag('admin/rh/annee_select.html')
def recherche_par_annees(cl):
- cursor = connection.cursor()
- cursor.execute("SELECT year(date_debut) FROM rh_dossier WHERE year(date_debut) IS NOT NULL GROUP BY year(date_debut)")
- set_annees = set(row[0] for row in cursor.fetchall())
- cursor.execute("SELECT year(date_fin) FROM rh_dossier WHERE year(date_fin) IS NOT NULL GROUP BY year(date_fin)")
- for row in cursor.fetchall():
- set_annees.add(row[0])
- list_annees = list(set_annees)
- list_annees.insert(0, '')
+ list_annees = ['', ] + cl.get_annees()
+ statut_choices = ((cl.get_query_string({pcl.KEY_STATUT : p }, (pcl.KEY_ANNEE, pcl.KEY_DATE_DEBUT, pcl.KEY_DATE_FIN)), p) for p in cl.STATUT_CHOICES)
+ annee_choices = ((cl.get_query_string({pcl.KEY_ANNEE : a}, (pcl.KEY_STATUT , pcl.KEY_DATE_DEBUT, pcl.KEY_DATE_FIN)), a) for a in list_annees)
+ on_change = """window.location=window.location.pathname+this.options[this.selectedIndex].value"""
class RechercheTemporelle(forms.Form):
- periode = forms.ChoiceField(
- choices=((cl.get_query_string({'periode': p }, ('annee', 'date_debut', 'date_fin')), p) for p in cl.PERIODE_CHOICE),
- widget=forms.Select(attrs = {
- 'onchange' : """window.location=window.location.pathname+this.options[this.selectedIndex].value""",
- }))
- annee = forms.ChoiceField(choices=((cl.get_query_string({'annee': a}, ('periode', 'date_debut', 'date_fin')), a) for a in list_annees),
- widget=forms.Select(attrs = {
- 'onchange' : """window.location=window.location.pathname+this.options[this.selectedIndex].value""",
- }))
- date_debut = forms.DateField()
- date_fin = forms.DateField()
- params = cl.params
- if 'periode' in params:
- params['periode'] = cl.get_query_string({'periode': params['periode']})
- if 'annee' in params:
- params['annee'] = cl.get_query_string({'annee': params['annee']})
+ statut = forms.ChoiceField(choices=statut_choices, widget=forms.Select(attrs = {'onchange' : on_change}))
+ annee = forms.ChoiceField(choices=annee_choices, widget=forms.Select(attrs = {'onchange' : on_change}))
+ date_debut = forms.DateField(widget=adminwidgets.AdminDateWidget)
+ date_fin = forms.DateField(widget=adminwidgets.AdminDateWidget)
+
+ params = cl.params.copy()
+ if pcl.KEY_STATUT in params:
+ params[pcl.KEY_STATUT] = cl.get_query_string({pcl.KEY_STATUT: params[pcl.KEY_STATUT]})
+ if pcl.KEY_ANNEE in params:
+ params[pcl.KEY_ANNEE] = cl.get_query_string({pcl.KEY_ANNEE: params[pcl.KEY_ANNEE]})
f = RechercheTemporelle(params)
return {
'form': f,
- 'plage_date_querystring': cl.get_query_string(remove=('annee', 'periode'))
+ 'plage_date_querystring': cl.get_query_string(remove=(pcl.KEY_ANNEE, pcl.KEY_STATUT))
}