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):
- PERIODE_CHOICE = ('', 'actuelle', 'passee', 'future')
-
- def __init__(self, *args, **kwargs):
- self.annees = {'actuelle': 'actuelle', 'passee': 'passee', 'future': 'future'}
- super(ChangeList, self).__init__(*args, **kwargs)
-
- def get_query_set(self):
- old = self.params.copy()
- periode = None
- annee = None
- date_debut = None
- date_fin = None
- today = datetime.date.today()
- for k, v in self.params.items():
- if 'periode' == k:
- periode = self.params[k]
- del self.params[k]
- if 'annee' == k:
- annee = self.params[k]
- del self.params[k]
- if 'date_debut' == k:
- date_debut = self.params[k]
- del self.params[k]
- if 'date_fin' == k:
- date_fin = self.params[k]
- del self.params[k]
-
- qs = super(ChangeList, self).get_query_set()
- if periode == 'actuelle':
- qs = qs.filter(date_fin__exact=today, date_debut__exact=today).distinct()
- elif periode == 'passee':
- qs = qs.filter(date_fin__lt=today)
- elif periode == 'future':
- qs = qs.filter(date_debut__gt=today)
- elif annee:
- date_debut = datetime.date(int(annee), 01, 01)
- date_fin = datetime.date(int(annee), 12, 31)
-
- if date_debut and date_fin:
- prefix_debut = 'date_debut'
- prefix_fin = 'date_fin'
- 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):
template = "admin/linked.html"
class EmployeAdmin(DateRangeMixin, AUFMetadataAdminMixin, ProtectRegionMixin, admin.ModelAdmin,):
++ prefixe_recherche_temporelle = "rh_dossiers__"
alphabet_filter = 'nom'
DEFAULT_ALPHABET = u'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
search_fields = ('id', 'nom', 'prenom', 'nom_affichage', )
form = EmployeAdminForm
list_display = ('_id', '_apercu', '_nom', '_dossiers_postes', '_date_modification', 'user_modification', )
list_display_links = ('_nom',)
-- list_filter = ('rh_dossiers__poste__implantation__region', 'rh_dossiers__poste__implantation', 'nb_postes', 'rh_dossiers__date_debut', 'rh_dossiers__date_fin')
-- date_borne_gauche = 'rh_dossiers__date_debut'
-- date_borne_droite = 'rh_dossiers__date_fin'
++ list_filter = ('rh_dossiers__poste__implantation__region', 'rh_dossiers__poste__implantation', 'nb_postes', )
inlines = (AyantDroitInline,
DossierROInline,
EmployePieceInline,
'_occupe_par',
'implantation',
'_service',
- 'date_debut',
- 'date_fin',
+ '_responsable',
- 'date_debut',
- 'date_fin',
'_date_modification',
'user_modification',
)
'service',
'type_poste',
'type_poste__famille_emploi',
-- 'date_debut',
-- 'date_fin',
'vacant',
)
list_display_links = ('_nom',)
--- /dev/null
+import time, datetime
- from django.core.exceptions import SuspiciousOperation
++from django.db.models import Q
+from django.conf import settings
++from django.contrib.admin.filterspecs import FilterSpec
+from django.contrib.admin.options import IncorrectLookupParameters
++from django.contrib.admin.util import quote, get_fields_from_path
+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.core.exceptions import SuspiciousOperation
++from django.core.paginator import InvalidPage
++from django.db import models
+from django.utils.encoding import force_unicode, smart_str
++from django.utils.translation import ugettext, ugettext_lazy
+from django.utils.http import urlencode
- from django.db.models import Q
-
++import operator
+
+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)