recherche temporelle employe et poste + fix imports
authorOlivier Larchevêque <olivier.larcheveque@auf.org>
Thu, 16 Feb 2012 19:03:58 +0000 (14:03 -0500)
committerOlivier Larchevêque <olivier.larcheveque@auf.org>
Thu, 16 Feb 2012 19:03:58 +0000 (14:03 -0500)
1  2 
project/rh/admin.py
project/rh/change_list.py
project/rh/templates/admin/rh/employe/change_list.html
project/rh/templates/admin/rh/poste/change_list.html

@@@ -19,15 -19,84 +19,11 @@@ from groups import grp_dr
  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"
@@@ -369,6 -429,6 +350,7 @@@ class DossierCommentaireAdmin(admin.Mod
  
  
  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,
@@@ -530,8 -590,9 +510,7 @@@ class PosteAdmin(DateRangeMixin, AUFMet
          '_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',)
index fe21c17,0000000..0a38121
mode 100644,000000..100644
--- /dev/null
@@@ -1,243 -1,0 +1,248 @@@
 +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)
@@@ -1,1 -1,1 +1,7 @@@
  {% extends "alphafilter/change_list.html" %}
++{% load change_list %}
++
++{% block search %}
++    {{ block.super }}
++    {% recherche_par_annees cl %}
++{% endblock %}
index 0000000,0000000..77f6272
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++{% extends "admin/change_list.html" %}
++{% load change_list %}
++
++{% block search %}
++    {{ block.super }}
++    {% recherche_par_annees cl %}
++{% endblock %}