def get_new_password_code(self):
return hashlib.md5(smart_str(self.courriel + self.encrypted_password)).hexdigest()[0:6]
- class ChercheurManager(models.Manager):
+ class ChercheurQuerySet(SEPQuerySet):
- def get_query_set(self):
- return ChercheurQuerySet(self.model).filter(personne__actif=True)
+ def filter_groupe(self, groupe):
+ return self.filter(groupes=groupe)
+
+ def filter_pays(self, pays):
+ return self.filter(Q(etablissement__pays=pays) | Q(etablissement_autre_pays=pays))
+
+ def filter_region(self, region):
+ return self.filter(Q(etablissement__pays__region=region) | Q(etablissement_autre_pays__region=region))
- def search(self, text):
- return self.get_query_set().search(text)
+ def filter_nord_sud(self, nord_sud):
+ return self.filter(Q(etablissement__pays__nord_sud=nord_sud) | Q(etablissement_autre_pays__nord_sud=nord_sud))
- def search_nom(self, nom):
- return self.get_query_set().search_nom(nom)
+ def filter_statut(self, statut):
+ return self.filter(statut=statut)
+
+ def filter_expert(self):
+ return self.exclude(expertises=None)
+
+ class ChercheurSphinxQuerySet(SEPSphinxQuerySet):
+
+ def __init__(self, model=None):
+ return SEPSphinxQuerySet.__init__(self, model=model, index='savoirsenpartage_chercheurs',
+ weights=dict(nom=2, prenom=2))
def filter_region(self, region):
- return self.get_query_set().filter_region(region)
+ return self.filter(region_id=region.id)
+
+ def filter_groupe(self, groupe):
+ return self.filter(groupe_ids=groupe.id)
+
+ def filter_pays(self, pays):
+ return self.filter(pays_id=pays.id)
- def filter_discipline(self, discipline):
- return self.get_query_set().filter_discipline(discipline)
-
- class ChercheurQuerySet(models.query.QuerySet, RandomQuerySetMixin):
-
- def search(self, text):
- q = None
- for word in text.split():
- matching_pays = list(Pays.objects.filter(Q(nom__icontains=word) | Q(region__nom__icontains=word)).values_list('pk', flat=True))
- matching_etablissements = list(Etablissement.objects.filter(Q(nom__icontains=word) | Q(pays__in=matching_pays)).values_list('pk', flat=True))
- matching_publications = list(Publication.objects.filter(titre__icontains=word).values_list('pk', flat=True))
- matching_groupes = list(Groupe.objects.filter(nom__icontains=word).values_list('pk', flat=True))
- matching_disciplines = list(Discipline.objects.filter(nom__icontains=word).values_list('pk', flat=True))
- part = (Q(personne__nom__icontains=word) |
- Q(personne__prenom__icontains=word) |
- Q(theme_recherche__icontains=word) |
- Q(etablissement__in=matching_etablissements) |
- Q(etablissement_autre_nom__icontains=word) |
- Q(etablissement_autre_pays__in=matching_pays) |
- Q(discipline__in=matching_disciplines) |
- Q(groupe_recherche__icontains=word) |
- Q(publications__in=matching_publications) |
- Q(these__titre__icontains=word) |
- Q(groupes__in=matching_groupes) |
- Q(expertises__nom__icontains=word) |
- Q(mots_cles__icontains=word) |
- Q(membre_association_francophone_details__icontains=word) |
- Q(membre_reseau_institutionnel_details__icontains=word)
- )
- if q is None:
- q = part
- else:
- q = q & part
- return self.filter(q).distinct() if q is not None else self
-
- def search_nom(self, nom):
- q = None
- for word in nom.split():
- part = Q(personne__nom__icontains=word) | Q(personne__prenom__icontains=word)
- if q is None:
- q = part
- else:
- q = q & part
- return self.filter(q) if q is not None else self
-
- def filter_discipline(self, discipline):
- """Ne conserve que les chercheurs dans la discipline donnée.
-
- Si ``disicipline`` est None, ce filtre n'a aucun effet."""
- if discipline is None:
- return self
- if not isinstance(discipline, Discipline):
- discipline = Discipline.objects.get(pk=discipline)
- return self.filter(Q(discipline=discipline) |
- Q(theme_recherche__icontains=discipline.nom) |
- Q(groupe_recherche__icontains=discipline.nom) |
- Q(publications__titre__icontains=discipline.nom) |
- Q(these__titre__icontains=discipline.nom) |
- Q(groupes__nom__icontains=discipline.nom) |
- Q(expertises__nom__icontains=discipline.nom) |
- Q(mots_cles__icontains=discipline.nom) |
- Q(membre_instance_auf_details__icontains=discipline.nom) |
- Q(membre_association_francophone_details__icontains=discipline.nom) |
- Q(expert_oif_details__icontains=discipline.nom) |
- Q(membre_reseau_institutionnel_details__icontains=discipline.nom)).distinct()
+ NORD_SUD_CODES = {'Nord': 1, 'Sud': 2}
+ def filter_nord_sud(self, nord_sud):
+ return self.filter(nord_sud=self.NORD_SUD_CODES[nord_sud])
+
+ STATUT_CODES = {'enseignant': 1, 'etudiant': 2, 'independant': 3}
+ def filter_statut(self, statut):
+ return self.filter(statut=self.STATUT_CODES[statut])
+
- def filter_expert(self):
- return self.filter(expert=1)
-
+ class ChercheurManager(SEPManager):
+
+ def get_query_set(self):
+ return ChercheurQuerySet(self.model)
+
+ def get_sphinx_query_set(self):
+ return ChercheurSphinxQuerySet(self.model).order_by('-date_modification')
def filter_region(self, region):
- """Ne conserve que les évènements dans la région donnée.
-
- Si ``region`` est None, ce filtre n'a aucun effet."""
- if region is None:
- return self
- return self.filter(Q(etablissement__pays__region=region) |
- Q(etablissement_autre_pays__region=region))
+ """Le filtrage de chercheurs par région n'est pas une recherche texte."""
+ return self.get_query_set().filter_region(region)
+
+ def filter_groupe(self, groupe):
+ return self.get_query_set().filter_groupe(groupe)
+
+ def filter_pays(self, pays):
+ return self.get_query_set().filter_pays(pays)
+
+ def filter_nord_sud(self, nord_sud):
+ return self.get_query_set().filter_nord_sud(nord_sud)
+
+ def filter_statut(self, statut):
+ return self.get_query_set().filter_statut(statut)
+
+ def filter_expert(self):
+ return self.get_query_set().filter_expert()
STATUT_CHOICES = (('enseignant', 'Enseignant-chercheur dans un établissement'), ('etudiant', 'Étudiant-chercheur doctorant'), ('independant', 'Chercheur indépendant docteur'))
class Chercheur(models.Model):
#contenu img.top, .resultats img.top { height:10px; position:relative; top:-10px; left:0;}
#contenu img.bottom, .resultats img.bottom{ height:10px; position:relative; bottom:-10px; left:0;}
-.contenu-wrapper { padding:0 25px; }
-
-ul a { text-decoration:none; }
-ul a:hover { text-decoration:underline; }
-
-ul.liste-de-l-accueil { padding:0 2.5em 0 0;}
-ul.liste-de-l-accueil li { margin:1em 0; padding-top:1.1em; border-top:2px solid #d5d5d5;}
-ul.liste-de-l-accueil img { float:left; margin:0 1em 5px 0; max-width:75px;}
-ul.liste-de-l-accueil span { display:block;}
-ul.liste-de-l-accueil .la-date { font-size:1.1em; }
-ul.liste-de-l-accueil a.la-date { color:black; }
-ul.liste-de-l-accueil .le-titre {display:block; font-size:1.2em; font-weight:bold; margin:.25em 0;}
-ul.liste-de-l-accueil .le-resume {font-size:1.1em;}
-ul.liste-de-l-accueil .le-resume a { display:inline;}
-
-.resultats { width:744px; display:inline; margin:0; padding:0; float:left; margin-top:100px; margin-bottom:50px; margin-left:0; background:url(../img/contenu-bkg-middle.png) repeat-y 0 0;}
-.resultats a:hover{text-decoration:none;}
-.resultatRecherche { padding:0 25px 1.5em;}
-.resultats .typeDocument { text-transform:uppercase;}
-.resultats .le-titre { font-size:1.2em; font-weight:bold; text-decoration:underline;}
-.resultats .resultatResume { font-size:1.1em; color:#000;}
-.resultats a.ligne-url { font-size:1.1em; text-decoration:none; }
-.resultats a:hover.ligne-url { text-decoration:underline;}
-
-.resultatPages { padding:0;margin-top:1.5em; text-align:center; font-size:1.2em;}
-.resultatPages span { padding:0; margin:0 auto;}
-.resultatPages span a { text-decoration:underline; color:#3a3125; }
-.resultatPages span span { text-decoration:none; font-weight:bold; color:#3a3125;}
-.resultatPages span a span { text-decoration:none; font-weight:normal; color:#3a3125;}
-.resultatPages span a:hover { text-decoration:none;}
-.resultatPages span a:hover span { text-decoration:none;}
-.resultatPages span a span.lien-texte { color:#97012c; text-decoration:underline; font-weight:bold;}
-.resultatPages span a span.lien-texte span { color:#97012c; text-decoration:none; font-weight:normal;}
-.resultatPages span a:hover span.lien-texte { text-decoration:none;}
-
-/* Divers */
-
-.clear { clear:both!important; display:block!important; width:0!important; height:0!important; margin:0!important; padding:0!important; visibility:hidden!important;}
-.clear-droite { clear:right!important; display:block!important; width:0!important; height:0!important; margin:0!important; padding:0!important; visibility:hidden!important;}
-
-/* Clearfix */
-
-.clearfix {display:inline-block; }
-.clearfix:after, .container:after { content:"."; display:block; height:0; clear:both; visibility:hidden;}
-* html .clearfix { height:1%; }
-.clearfix { display:block; }
-
-#edit-form td { vertical-align:top; }
-#edit-form td:first-child { width:150px; text-align:left; }
-#edit-form table { width:100%; }
-#edit-form input, #edit-form textarea { width:80%; }
-#edit-form textarea { height:100px; }
-#edit-form p { margin-bottom:2px; }
-#edit-form tr { border-top:1px black solid; }
-#edit-form tr:first-child { border-top:none; }
-
-.odd { background:#ddd; }
-
-#repertoire { border:1px solid #bbb; padding:20px; margin:10px; width:95% }
+/* Régions/Disciplines */
+
+#regions_disciplines { position: absolute; top: 30px; right: 0; width: 190px;
+ background: url(../img/col-droite-background-bottom.png) no-repeat bottom left;
+ font-size: 90%; padding-bottom: 8px; }
+#regions_disciplines div { background: url(../img/col-droite-background.png) repeat-x top left;
+ padding: 10px; }
+#regions_disciplines h1 { font-weight: normal; font-size: 180%; letter-spacing: -1px; }
+#regions_disciplines ul { margin: 0; padding: 0; list-style: none; }
+#regions_disciplines li { margin: 0; padding: 0; }
+#regions_disciplines a { display: block; margin-left: 5px; padding: 1px 5px; text-decoration: none; color: black; }
+#regions_disciplines li.active a,
+#regions_disciplines a:hover { background: #d5d5d5; margin-left: 0; border-left: 5px solid #666; }
+
+/* Liens */
+
+a { text-decoration: none; color: #97012c; }
+a:hover { text-decoration: underline; }
+a img { border: none; }
+
+/* Titres */
+
+h1 { font-weight: normal; font-size: 180%; letter-spacing: -1px; }
+h2 { font-weight: normal; font-size: 150%; letter-spacing: -1px; border-bottom: 2px solid #3a3125; clear: right; }
+h3 { font-size: 120%; letter-spacing: -1px; }
+h1 a, h2 a, h3 a { color: #3a3125; }
+.sous-titre { margin: -1em 0 1.5em 0; }
+
+/* Formulaires */
+
+form { margin: 1em 0; }
+form td, form th { vertical-align: top }
+form th { width: 20em; text-align: left; font-weight: normal; }
+input[type=text] { width: 20em }
+input.date, form input.time { width: 8em; }
+select { width: 80%; overflow: hidden }
+fieldset { padding: 10px; margin: 0; position: relative;
+ border-top: 1px solid black; border-bottom: none;
+ border-left: none; border-right: none; }
+fieldset fieldset { border: 1px solid #ccc; background: #fafafa; margin: 10px; padding: 15px; }
+legend { font-weight: bold; color: #000000; margin: 0; padding:0 0.5em; }
+legend em { font-weight: normal; font-style: normal; }
+form .help { font-size: 90%; margin: 0; }
+form ul.errorlist { color:red; margin:0; padding: 0; }
+form ul.errorlist li { list-style:none; }
+.horizontal-radio-buttons ul { margin-left:0 }
+.horizontal-radio-buttons li { display:inline }
-.errorlist { color:red; margin:0 }
-.errorlist li { list-style:none; }
+.delete-row { font-size: 90%; position:absolute; top:5px; right:5px }
+.add-row { font-size: 90%; float:right; margin-right:16px }
+.edit-publication { font-size: 90%; position: absolute; bottom: 5px; right: 5px; cursor: pointer; }
-.publications_autre {border:1px solid #CCC; background:#FAFAFA; margin:10px; padding:10px; display:none;}
+/* Tables */
-.infotip
-{
-width:300px;
-margin-top:5px;
-border:1px solid #CCC;
-background:#EEE;
-float:right;
-}
-.publication
-{
+table { width: 100%; }
+td, th { vertical-align: middle; padding: 5px }
+td ul, td ol { padding: 0; margin: 0; list-style: none; }
+tr.odd { background:#ddd; }
-}
-.message
-{
-font-weight:bold;
-font-size:12px;
-color:red;
-}
+/* Styles inline */
- .label { color: grey; font-weight: bold; width: 150px; }
++.label { font-weight: bold; width: 150px; }
-.ressource-retrieve * {margin-top:1em;}
-.ressource-retrieve .fiche {margin-top:2em;}
+/* Page d'accueil */
-.fiche, .original, .provenance {font-size:80%; margin:2px 0px;}
-.fiche {margin-top:6px;}
-.fiche a, .original a, .provenance a{text-decoration:none;}
-.back {position:absolute; top:20px; right:20px;}
+.demi-gauche { width: 345px; background-color: #f5f5f5; float: left; }
+.demi-droite { width: 345px; float: right; }
-.horizontal-radio-buttons ul { margin-left:0 }
-.horizontal-radio-buttons li { display:inline }
+.box { position: relative; padding: 10px; font-size: 90%; }
+.box h1 { margin: 0 0 3px 0; }
-.delete-row { position:absolute; top:5px; right:5px }
-.add-row { float:right; margin-right:16px }
+ul.actions-accueil { margin: 0; padding: 0 0 1em 0; list-style: none; font-size: 90%; }
+ul.actions-accueil li { display: inline; padding-right: 10px; }
-.cadre { width: 60%; margin: 100px auto; padding: 20px; background: #f9f9f9; border: 1px solid #aaa }
+ul.liste-de-l-accueil { padding: 0; margin: 1em 0; list-style: none; }
+ul.liste-de-l-accueil li { padding: 1em 0; border-top: 2px solid #d5d5d5;}
+ul.liste-de-l-accueil img { float:left; margin:0 1em 5px 0; max-width:75px;}
+ul.liste-de-l-accueil .titre { font-weight:bold; margin: 0.25em 0; }
-/* Boîte principale */
+#rss-agenda, #rss-actualites { position: absolute; top: 15px; right: 20px; }
-#boite-principale { position: absolute; top: 280px; width: 744px;
- background: url(../img/contenu-bkg-middle.png) repeat-y 0 0; }
-#boite-principale .top-border { position: absolute; top: -10px; }
-#boite-principale .bottom-border { position: absolute; bottom: -10px; }
-
-#flash-message { margin: 10px 20px; background: #f3e3e7; color: #b03d5e; padding: 1em; font-size: 120%;
- border: 1px solid #b03d5e; -moz-border-radius: 5px; -webkit-border-radius: 5px; }
-#contenu { position: relative; padding: 20px; }
+/* Actions */
-/* Contenu */
+ul.actions { font-size: 90%; float: right; text-align: right; list-style: none;
+ background: #f4f4f4; border: 1px solid #e4e4e4;
+ padding: 10px 10px 10px 10px;
+ -moz-border-radius: 5px; -webkit-border-radius: 5px; }
-#contenu h4 { margin-bottom: 4px; font-size: 220%; font-weight: normal; letter-spacing: -1px; }
-#contenu h4 a { text-decoration:none; color:black; }
-#contenu h4 a:hover { text-decoration:underline; }
-#contenu h5 { margin-bottom: 4px; font-size: 180%; font-weight: normal; letter-spacing: -1px; }
-#contenu h5 a { text-decoration:none; color:black; }
-#contenu h5 a:hover { text-decoration:underline; }
+/* Pages de recherche */
-#fiche_chercheur { font-size: 120%; }
-#fiche_chercheur h5 { font-size: 150%; border-color: #000000; border-width: 1px 0 0 0;
- border-style: solid none none none; margin-top: 20px; padding: 10px; }
-#fiche_chercheur .label { color: grey; font-weight: bold; width: 150px; }
-#fiche_chercheur .souligne { border-bottom: 1px solid #DDD; }
+.pagination { text-align: center; margin: 1em 0; }
+.resultatRecherche { padding: 0 25px 1.5em; }
+.details { font-size: 90%; margin-top: 6px; }
-/* Actions */
+/* Clearfix */
-.actions { float: right; text-align: right }
-.box .actions { float: none; text-align: left; margin-left: 0; list-style-type: none }
-.box .actions li { display: inline; padding-right: 10px; font-size: 110%; }
+.clearfix {display:inline-block; }
+.clearfix:after, .container:after { content:"."; display:block; height:0; clear:both; visibility:hidden;}
+* html .clearfix { height:1%; }
+.clearfix { display:block; }
-/* Page d'accueil */
+.cadre { width: 60%; margin: 100px auto; padding: 20px; background: #f9f9f9; border: 1px solid #aaa }
+.souligne { border-bottom: 1px solid #DDD; }
-.box { position: relative; padding: 10px 20px; }
-.demi-gauche { width: 345px; background-color: #f5f5f5; float: left; }
-.demi-droite { width: 345px; float: right; }
-#rss-agenda, #rss-actualites { position: absolute; top: 15px; right: 20px; }
# -*- encoding: utf-8 -*-
+ import operator
import re
+
from django.core.urlresolvers import reverse as url
from django.db import models
+ from django.db.models import Q
+ from django.db.models.query import QuerySet
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
import urllib, httplib, time, simplejson, pprint, math, re
from django.core.urlresolvers import reverse
from django.conf import settings
+ from django.utils.safestring import mark_safe
++from django.utils.text import truncate_words
from auf_savoirs_en_partage.backend_config import RESOURCES
from sep import SEP
from utils import smart_str
parts.append(part)
return re.compile('|'.join(parts), re.I)
+
+ EXACT_PHRASE_RE = re.compile(r'"([^"]*?)"')
+ def excerpt_function(manager, words):
+ """Construit une fonction qui extrait la partie pertinente d'un texte
+ suite à une recherche textuelle."""
++ if not words:
++ return lambda x: truncate_words(x, 50)
+ qs = manager.get_sphinx_query_set()
+ client = qs._get_sphinx_client()
+ index = qs._index
+ phrases = EXACT_PHRASE_RE.findall(words)
+ keywords = EXACT_PHRASE_RE.sub('', words).strip()
+
+ def excerpt(text):
+ # On essaie de gérer à peu près correctement les phrases exactes. La
+ # vraie solution serait d'utiliser Sphinx 1.10 beta1 et son option
+ # "query_mode", mais c'est plus de trouble. Peut-être plus tard?
+ excerpt = text
+ for phrase in phrases:
+ excerpt = client.BuildExcerpts([excerpt], index, phrase,
+ opts=dict(exact_phrase=True, limit=500, single_passage=True))[0]
+ if keywords:
+ excerpt = client.BuildExcerpts([excerpt], index, keywords,
+ opts=dict(limit=500, single_passage=True))[0]
+ return mark_safe(excerpt)
+
+ return excerpt
+
import os
import pytz
import random
-import simplejson
-import time
import uuid
import vobject
+ from backend_config import RESOURCES
from babel.dates import get_timezone_name
+ from caldav.lib import error
-from datamaster_modeles.models import Thematique, Pays, Region
++from babel.dates import get_timezone_name
++from datamaster_modeles.models import Region, Pays, Thematique
from django.contrib.auth.models import User
from django.db import models
from django.db.models import Q, Max
from django.db.models.signals import pre_delete
- from auf_savoirs_en_partage.backend_config import RESOURCES
+ from django.utils.encoding import smart_unicode
+ from djangosphinx.models import SphinxQuerySet
from savoirs.globals import META
-from savoirs.lib.calendrier import combine
from settings import CALENDRIER_URL, SITE_ROOT_URL
- from datamaster_modeles.models import Thematique, Pays, Region
- from lib.calendrier import combine
- from caldav.lib import error
+
+ # Fonctionnalités communes à tous les query sets
class RandomQuerySetMixin(object):
"""Mixin pour les modèles.
texte=entry.summary_detail.value,
url=entry.link, date=date)
- class ActualiteManager(models.Manager):
-
- def get_query_set(self):
- return ActualiteQuerySet(self.model).filter(visible=True)
+ class ActualiteQuerySet(SEPQuerySet):
- def search(self, text):
- return self.get_query_set().search(text)
+ def filter_date(self, min=None, max=None):
+ qs = self
+ if min:
+ qs = qs.filter(date__gte=min)
+ if max:
+ qs = qs.filter(date__lte=max)
+ return qs
- def filter_region(self, region):
- return self.get_query_set().filter_region(region)
+ class ActualiteSphinxQuerySet(SEPSphinxQuerySet):
- def filter_discipline(self, discipline):
- return self.get_query_set().filter_discipline(discipline)
+ def __init__(self, model=None):
+ SEPSphinxQuerySet.__init__(self, model=model, index='savoirsenpartage_actualites',
+ weights=dict(titre=3))
- class ActualiteQuerySet(models.query.QuerySet, RandomQuerySetMixin):
- def filter_date(self, min=None, max=None):
- qs = self
- if min:
- qs = qs.filter(date__gte=min.toordinal()+365)
- if max:
- qs = qs.filter(date__lte=max.toordinal()+365)
- return qs
-
+ class ActualiteManager(SEPManager):
+
+ def get_query_set(self):
+ return ActualiteQuerySet(self.model).filter(visible=True)
- def search(self, text):
- q = None
- for word in text.split():
- part = (Q(titre__icontains=word) | Q(texte__icontains=word) |
- Q(regions__nom__icontains=word) | Q(disciplines__nom__icontains=word))
- if q is None:
- q = part
- else:
- q = q & part
- return self.filter(q).distinct() if q is not None else self
+ def get_sphinx_query_set(self):
+ return ActualiteSphinxQuerySet(self.model).order_by('-date')
- def filter_discipline(self, discipline):
- """Ne conserve que les actualités dans la discipline donnée.
-
- Si ``disicipline`` est None, ce filtre n'a aucun effet."""
- if discipline is None:
- return self
- if not isinstance(discipline, Discipline):
- discipline = Discipline.objects.get(pk=discipline)
- return self.filter(Q(disciplines=discipline) |
- Q(titre__icontains=discipline.nom) |
- Q(texte__icontains=discipline.nom)).distinct()
-
- def filter_region(self, region):
- """Ne conserve que les actualités dans la région donnée.
-
- Si ``region`` est None, ce filtre n'a aucun effet."""
- if region is None:
- return self
- if not isinstance(region, Region):
- region = Region.objects.get(pk=region)
- return self.filter(Q(regions=region) |
- Q(titre__icontains=region.nom) |
- Q(texte__icontains=region.nom)).distinct()
+ def filter_date(self, min=None, max=None):
+ return self.get_query_set().filter_date(min=min, max=max)
class Actualite(models.Model):
id = models.AutoField(primary_key=True, db_column='id_actualite')
def assigner_regions(self, regions):
self.regions.add(*regions)
- class EvenementManager(models.Manager):
-
- def get_query_set(self):
- return EvenementQuerySet(self.model).filter(approuve=True)
+ # Agenda
- def search(self, text):
- return self.get_query_set().search(text)
+ class EvenementQuerySet(SEPQuerySet):
- def filter_region(self, region):
- return self.get_query_set().filter_region(region)
+ def filter_type(self, type):
+ return self.filter(type=type)
- def filter_discipline(self, discipline):
- return self.get_query_set().filter_discipline(discipline)
+ def filter_debut(self, min=None, max=None):
+ qs = self
+ if min:
+ qs = qs.filter(debut__gte=min)
+ if max:
+ qs = qs.filter(debut__lt=max+datetime.timedelta(days=1))
+ return qs
- class EvenementQuerySet(models.query.QuerySet, RandomQuerySetMixin):
+ class EvenementSphinxQuerySet(SEPSphinxQuerySet):
- def search(self, text):
- q = None
- for word in text.split():
- part = (Q(titre__icontains=word) |
- Q(mots_cles__icontains=word) |
- Q(discipline__nom__icontains=word) |
- Q(discipline_secondaire__nom__icontains=word) |
- Q(type__icontains=word) |
- Q(lieu__icontains=word) |
- Q(description__icontains=word) |
- Q(contact__icontains=word) |
- Q(regions__nom__icontains=word))
- if q is None:
- q = part
- else:
- q = q & part
- return self.filter(q).distinct() if q is not None else self
+ def __init__(self, model=None):
+ SEPSphinxQuerySet.__init__(self, model=model, index='savoirsenpartage_evenements',
+ weights=dict(titre=3))
- def search_titre(self, text):
+ def filter_type(self, type):
+ return self.add_to_query('@type "%s"' % type)
+
+ def filter_debut(self, min=None, max=None):
qs = self
- for word in text.split():
- qs = qs.filter(titre__icontains=word)
+ if min:
+ qs = qs.filter(debut__gte=min.toordinal()+365)
+ if max:
+ qs = qs.filter(debut__lte=max.toordinal()+365)
return qs
- def filter_discipline(self, discipline):
- """Ne conserve que les évènements dans la discipline donnée.
-
- Si ``disicipline`` est None, ce filtre n'a aucun effet."""
- if discipline is None:
- return self
- if not isinstance(discipline, Discipline):
- discipline = Discipline.objects.get(pk=discipline)
- return self.filter(Q(discipline=discipline) |
- Q(discipline_secondaire=discipline) |
- Q(titre__icontains=discipline.nom) |
- Q(mots_cles__icontains=discipline.nom) |
- Q(description__icontains=discipline.nom))
+ class EvenementManager(SEPManager):
- def filter_region(self, region):
- """Ne conserve que les évènements dans la région donnée.
-
- Si ``region`` est None, ce filtre n'a aucun effet."""
- if region is None:
- return self
- if not isinstance(region, Region):
- region = Region.objects.get(pk=region)
- return self.filter(Q(regions=region) |
- Q(titre__icontains=region.nom) |
- Q(mots_cles__icontains=region.nom) |
- Q(description__icontains=region.nom) |
- Q(pays__region=region) |
- Q(lieu__icontains=region.nom)).distinct()
+ def get_query_set(self):
+ return EvenementQuerySet(self.model).filter(approuve=True)
+
+ def get_sphinx_query_set(self):
+ return EvenementSphinxQuerySet(self.model).order_by('-debut')
+
+ def filter_type(self, type):
+ return self.get_query_set().filter_type(type)
+
+ def filter_debut(self, min=None, max=None):
+ return self.get_query_set().filter_debut(min=min, max=max)
-def build_time_zone_choices():
+def build_time_zone_choices(pays=None):
fr_names = set()
- tzones = []
+ timezones = pytz.country_timezones[pays] if pays else pytz.common_timezones
+ result = []
now = datetime.datetime.now()
- for tzname in pytz.common_timezones:
+ for tzname in timezones:
tz = pytz.timezone(tzname)
fr_name = get_timezone_name(tz, locale='fr_FR')
if fr_name in fr_names:
+++ /dev/null
--#coding: utf-8
--
--from django import template
--from django.template.defaultfilters import stringfilter
--from django.utils.html import conditional_escape
--from django.utils.safestring import mark_safe
--
--register = template.Library()
--
--EXCERPT_LENGTH = 200
--
--@register.filter
--@stringfilter
--def highlight(text, regexp=None, autoescape=None):
-- """Met en évidence les parties du texte qui correspondent à l'expression
-- régulière passée en argument."""
-- if autoescape:
-- text = conditional_escape(text)
-- if regexp:
-- text = regexp.sub(r'<b>\g<0></b>', text)
-- return mark_safe(text)
--
--@register.filter
--@stringfilter
--def excerpt(text, regexp=None):
-- """Tronque le texte autour de la première correspondance de l'expression
-- régulière."""
-- if len(text) <= EXCERPT_LENGTH:
-- return text
-- m = regexp is not None and regexp.search(text)
-- if m:
-- pos = m.start()
-- end_of_sentence = max(text.rfind('.', 0, pos), text.rfind('?', 0, pos), text.rfind('!', 0, pos))
-- start = end_of_sentence + 1 if end_of_sentence != -1 else 0
-- end = pos + EXCERPT_LENGTH
-- else:
-- start = 0
-- end = start + EXCERPT_LENGTH
-- if end < len(text) - 1:
-- try:
-- end = text.rindex(' ', start, end)
-- except ValueError:
-- pass
-- excerpt = text[start:end].strip()
-- if start > 0:
-- excerpt = '(...) ' + excerpt
-- if end < len(text) - 1:
-- excerpt += ' (...)'
-- return excerpt
--excerpt.is_safe = True
from django.utils.safestring import mark_safe
from django import forms
from django.conf import settings
- from lib.recherche import google_search, build_search_regexp
-from lib.recherche import google_search, build_search_regexp, excerpt_function
++from lib.recherche import google_search, excerpt_function
from lib import sep
from lib.calendrier import evenements, evenement_info, combine
from savoirs.globals import configuration
search_form = RecordSearchForm(request.GET)
ressources = search_form.get_query_set()
nb_resultats = ressources.count()
- search_regexp = search_form.get_search_regexp()
- if search_form.is_valid():
- excerpt = excerpt_function(Record.objects, search_form.cleaned_data['q'])
- else:
- excerpt = lambda x: x
++ excerpt = excerpt_function(Record.objects, search_form.cleaned_data['q'])
return render_to_response(
"savoirs/ressource_index.html",
- {'search_form': search_form, 'ressources': ressources,
- 'nb_resultats': nb_resultats, 'search_regexp': search_regexp},
- context_instance = RequestContext(request)
+ dict(search_form=search_form, ressources=ressources,
+ nb_resultats=nb_resultats, excerpt=excerpt),
+ context_instance=RequestContext(request)
)
def ressource_retrieve(request, id):
def actualite_index(request):
search_form = ActualiteSearchForm(request.GET)
actualites = search_form.get_query_set()
- search_regexp = search_form.get_search_regexp()
- if search_form.is_valid():
- excerpt = excerpt_function(Actualite.objects, search_form.cleaned_data['q'])
- else:
- excerpt = lambda x: x
++ excerpt = excerpt_function(Actualite.objects, search_form.cleaned_data['q'])
return render_to_response(
"savoirs/actualite_index.html",
dict(actualites=actualites, search_form=search_form,
- search_regexp=search_regexp, nb_resultats=actualites.count()),
+ excerpt=excerpt, nb_resultats=actualites.count()),
context_instance = RequestContext(request))
+def actualite(request, id):
+ actualite = get_object_or_404(Actualite, pk=id)
+ return render_to_response("savoirs/actualite.html",
+ dict(actualite=actualite),
+ context_instance=RequestContext(request))
+
# agenda
def evenement_index(request):
search_form = EvenementSearchForm(request.GET)
from django.shortcuts import render_to_response
from django.template import Context, RequestContext
from django.db.models import Q
-from forms import SiteSearchForm
-from models import Site
+
- from models import Site
- from forms import SiteSearchForm
+ from savoirs.lib.recherche import excerpt_function
++from sitotheque.models import Site
++from sitotheque.forms import SiteSearchForm
def index(request):
search_form = SiteSearchForm(request.GET)
sites = search_form.get_query_set()
- search_regexp = search_form.get_search_regexp()
- nb_sites = sites.count()
+ excerpt = excerpt_function(Site.objects, search_form.cleaned_data['q'])
+ nb_sites = sites.count()
return render_to_response("sites/index.html",
- dict(sites=sites, search_form=search_form,
+ dict(sites=sites, search_form=search_form,
- search_regexp=search_regexp, nb_sites=nb_sites),
+ excerpt=excerpt, nb_sites=nb_sites),
context_instance = RequestContext(request))
def retrieve(request, id):
DROP COLUMN publication3,
DROP COLUMN publication4;
+ ANALYZE TABLE chercheurs_chercheur;
-
-- On ne peut pas à la fois forcer une clé unique sur le courriel et conserver
-- les comptes inactifs dans la table.
--- /dev/null
+ au
+ aux
+ avec
+ ce
+ ces
+ dans
+ de
+ des
+ du
+ elle
+ en
+ et
+ eux
+ il
+ je
+ la
+ le
+ leur
+ lui
+ ma
+ mais
+ me
-même
++même
+ mes
+ moi
+ mon
+ ne
+ nos
+ notre
+ nous
+ on
+ ou
+ par
+ pas
+ pour
+ qu
+ que
+ qui
+ sa
+ se
+ ses
+ son
+ sur
+ ta
+ te
+ tes
+ toi
+ ton
+ tu
+ un
+ une
+ vos
+ votre
+ vous
+ c
+ d
+ j
+ l
-à
++à
+ m
+ n
+ s
+ t
+ y
-été
-étée
-étées
-étés
-étant
++été
++étée
++étées
++étés
++étant
+ suis
+ es
+ est
+ sommes
-êtes
++êtes
+ sont
+ serai
+ seras
+ sera
+ serons
+ serez
+ seront
+ serais
+ serait
+ serions
+ seriez
+ seraient
-étais
-était
-étions
-étiez
-étaient
++étais
++était
++étions
++étiez
++étaient
+ fus
+ fut
-fûmes
-fûtes
++fûmes
++fûtes
+ furent
+ sois
+ soit
+ soyons
+ soyez
+ soient
+ fusse
+ fusses
-fût
++fût
+ fussions
+ fussiez
+ fussent
+ ayant
+ eu
+ eue
+ eues
+ eus
+ ai
+ as
+ avons
+ avez
+ ont
+ aurai
+ auras
+ aura
+ aurons
+ aurez
+ auront
+ aurais
+ aurait
+ aurions
+ auriez
+ auraient
+ avais
+ avait
+ avions
+ aviez
+ avaient
+ eut
-eûmes
-eûtes
++eûmes
++eûtes
+ eurent
+ aie
+ aies
+ ait
+ ayons
+ ayez
+ aient
+ eusse
+ eusses
-eût
++eût
+ eussions
+ eussiez
+ eussent
+ ceci
-celà
++celà
+ cet
+ cette
+ ici
+ ils
+ les
+ leurs
+ quel
+ quels
+ quelle
+ quelles
+ sans
+ soi
- {% load search %}
+ {% load sep %}
<div class="resultatRecherche">
- <div class="la-date">{{ actualite.date|date:"d F Y" }}</div>
- <a class="le-titre" href="{{ actualite.url }}">{{ actualite.titre|apply:excerpt }}</a>
- <div class="resultatResume">{{ actualite.texte|apply:excerpt }}</div>
+ <div>{{ actualite.date|date:"d F Y" }}</div>
+ <div class="titre">
- <a href="{% url savoirs.views.actualite actualite.id %}">{{ actualite.titre|highlight:search_regexp }}</a>
++ <a href="{% url savoirs.views.actualite actualite.id %}">{{ actualite.titre|apply:excerpt }}</a>
+ </div>
- <div>{{ actualite.texte|highlight:search_regexp }}</div>
++ <div>{{ actualite.texte|apply:excerpt }}</div>
{% if actualite.source %}
- <div><span class="lbl">Source:</span> {{ actualite.source.nom }}</div>
+ <div><span class="label">Source:</span> {{ actualite.source.nom }}</div>
{% endif %}
</div>
- {% load search %}
+ {% load sep %}
<div class="resultatRecherche">
- <div class="la-date">{{ evenement.debut|date:"d/m/Y H\hi" }}</div>
- <div><a href="{% url savoirs.views.evenement evenement.pk %}" class="le-titre">{{ evenement.titre|apply:excerpt }}</a></div>
- <div class="le-resume">{{ evenement.description|apply:excerpt }}</div>
+ <div>{{ evenement.debut|date:"d/m/Y H\hi" }}</div>
- <div class="titre"><a href="{% url savoirs.views.evenement evenement.pk %}">{{ evenement.titre|highlight:search_regexp }}</a></div>
- <div>{{ evenement.description|excerpt:search_regexp|highlight:search_regexp }}</div>
++ <div class="titre"><a href="{% url savoirs.views.evenement evenement.pk %}">{{ evenement.titre|apply:excerpt }}</a></div>
++ <div>{{ evenement.description|apply:excerpt }}</div>
</div>
{% load sep %}
{% block contenu %}
-<script>
- $(document).ready(function(){
- makePageLinks ({{ page|default:0 }}, {{ data.last_page|default:0 }},
- '{{ data.more_link }}');
- });
-
- {% if user.is_authenticated %}
- function showEditModal (uri) {
- $('#jsonsource').load ('{% url savoirs.views.json_get %}?uri='+uri,
- function() {
- __jf = new JSONForm ('schema', 'mainform', 'jsonsource');
- __jf.setup ();
- $('#edit-form').dialog({height: 400, width: 650, modal: true});
- });
- }
- {% endif %}
-</script>
<div class="texte">
- <h4>Résultats correspondant à « {{ q }} »</h4>
+ <h1>Résultats correspondant à « {{ q }} »</h1>
{% if ressources %}
- <h2>
- Ressources ({{ ressources|length }} sur {{ total_ressources }})
- <h5>Ressources ({{ ressources|length }} sur {{ total_ressources }})</h5>
++ <h2>Ressources ({{ ressources|length }} sur {{ total_ressources }})</h2>
++ <div class="sous-titre">
+ <a class="action" href="{% url savoirs.views.ressource_index %}{{ briques_query_string }}"
- >(Voir toutes les ressources correspondant à cette recherche)</a>
- </h2>
++ >Voir toutes les ressources correspondant à cette recherche</a>
++ </div>
{% for ressource in ressources %}
{% include "savoirs/ressource_resultat.html" %}
{% endfor %}
{% endif %}
{% if actualites %}
- <h2>
- Actualités ({{ actualites|length }} sur {{ total_actualites }})
- <h5>Actualités ({{ actualites|length }} sur {{ total_actualites }})</h5>
++ <h2>Actualités ({{ actualites|length }} sur {{ total_actualites }})</h2>
++ <div class="sous-titre">
+ <a class="action" href="{% url savoirs.views.actualite_index %}{{ briques_query_string }}"
- >(Voir toutes les actualités correspondant à cette recherche)</a>
- </h2>
++ >Voir toutes les actualités correspondant à cette recherche</a>
++ </div>
{% for actualite in actualites %}
{% include "savoirs/actualite_resultat.html" %}
{% endfor %}
{% endif %}
{% if evenements %}
- <h2>
- Évènements ({{ evenements|length }} sur {{ total_evenements }})
- <h5>Évènements ({{ evenements|length }} sur {{ total_evenements }})</h5>
++ <h2>Évènements ({{ evenements|length }} sur {{ total_evenements }})</h2>
++ <div class="sous-titre">
+ <a class="action" href="{% url savoirs.views.evenement_index %}{{ briques_query_string }}"
- >(Voir tous les évènements correspondant à cette recherche)</a>
- </h2>
++ >Voir tous les évènements correspondant à cette recherche</a>
++ </div>
{% for evenement in evenements %}
{% include "savoirs/evenement_resultat.html" %}
{% endfor %}
{% endif %}
{% if chercheurs %}
- <h2>
- Chercheurs ({{ chercheurs|length }} sur {{ total_chercheurs }})
- <h5>Chercheurs ({{ chercheurs|length }} sur {{ total_chercheurs }})</h5>
++ <h2>Chercheurs ({{ chercheurs|length }} sur {{ total_chercheurs }})</h2>
++ <div class="sous-titre">
+ <a class="action" href="{% url chercheurs.views.index %}{{ briques_query_string }}"
- >(Voir tous les chercheurs correspondant à cette recherche)</a>
- </h2>
++ >Voir tous les chercheurs correspondant à cette recherche</a>
++ </div>
<ul>
{% for chercheur in chercheurs %}
<li><a href="{% url chercheurs.views.retrieve chercheur.id %}">{{ chercheur }}</a></li>
{% endif %}
{% if sites %}
- <h2>
- Sites de la sitothèque ({{ sites|length }} sur {{ total_sites }})
- <h5>Sites de la sitothèque ({{ sites|length }} sur {{ total_sites }})</h5>
++ <h2>Sites de la sitothèque ({{ sites|length }} sur {{ total_sites }})</h2>
++ <div class="sous-titre">
+ <a class="action" href="{% url sitotheque.views.index %}{{ briques_query_string }}"
- >(Voir tous les sites correspondant à cette recherche)</a>
- </h2>
++ >Voir tous les sites correspondant à cette recherche</a>
++ </div>
{% for site in sites %}
{% include "sites/resultat.html" %}
{% endfor %}
{% endif %}
{% if sites_auf %}
- <h2>
- Sites AUF
- <h5>Sites AUF</h5>
++ <h2>Sites AUF</h2>
++ <div class="sous-titre">
+ <a class="action" href="{% url savoirs.views.sites_auf %}?q={{ q|urlencode }}"
- >(Voir tous les sites AUF correspondant à cette recherche)</a>
- </h2>
++ >Voir tous les sites AUF correspondant à cette recherche</a>
++ </div>
{% for r in sites_auf %}
{% include "savoirs/sites_auf_resultat.html" %}
{% endfor %}
{% endif %}
{% if not ressources and not actualites and not evenements and not chercheurs and not sites %}
- <h5>Aucun résultat</h5>
- <p class="pad"><a href="{% url savoirs.views.index %}">Retour à la page d'accueil</a></p>
+ <h2>Aucun résultat</h2>
- <p class="pad"><a href="{% url savoirs.views.index %}">Retour à la page d'accueil</a></p>
++ <p><a href="{% url savoirs.views.index %}">Retour à la page d'accueil</a></p>
{% endif %}
</div>
- {% load search %}
+ {% load sep %}
<div class="resultatRecherche">
- <a class="le-titre" href="{% url savoirs.views.ressource_retrieve ressource.id %}">{{ ressource.title|apply:excerpt }}</a>
+ <div class="titre">
- <a href="{% url savoirs.views.ressource_retrieve ressource.id %}">{{ ressource.title|highlight:search_regexp }}</a>
++ <a href="{% url savoirs.views.ressource_retrieve ressource.id %}">{{ ressource.title|apply:excerpt }}</a>
+ </div>
{% if ressource.creator %}
- <div><span class="label">Auteur:</span> {{ ressource.creator|highlight:search_regexp }}</div>
- <div><span class="lbl">Auteur:</span> {{ ressource.creator|apply:excerpt }}</div>
++ <div><span class="label">Auteur:</span> {{ ressource.creator|apply:excerpt }}</div>
{% endif %}
{% if ressource.description %}
- <div><span class="label">Résumé:</span> {{ ressource.description|excerpt:search_regexp|highlight:search_regexp }}</div>
- <div class="resultatResume"><span class="lbl">Description:</span> {{ ressource.description|apply:excerpt }}</div>
++ <div><span class="label">Résumé:</span> {{ ressource.description|apply:excerpt }}</div>
{% endif %}
- <div class="fiche"><span>Fiche: </span><a href="{% url savoirs.views.ressource_retrieve ressource.id %}">{% url savoirs.views.ressource_retrieve ressource.id %}</a></div>
- <div class="original"><span>Contenu original: </span><a target="_blank" href="{{ ressource.uri }}">{{ ressource.uri }}</a></div>
- <div class="provenance"><span>Provenance: </span><a target="_blank" href="{{ ressource.getServeurURL }}">{{ ressource.getServeurURL }}</a></div>
+ <div class="details">
+ <div><span>Fiche: </span><a href="{% url savoirs.views.ressource_retrieve ressource.id %}">{% url savoirs.views.ressource_retrieve ressource.id %}</a></div>
+ <div><span>Contenu original: </span><a target="_blank" href="{{ ressource.uri }}">{{ ressource.uri }}</a></div>
+ <div><span>Provenance: </span><a target="_blank" href="{{ ressource.getServeurURL }}">{{ ressource.getServeurURL }}</a></div>
+ </div>
</div>
- {% load search %}
+ {% load sep %}
<div class="resultatRecherche">
- <div class="titre"><a href="{% url sitotheque.views.retrieve site.id %}">{{ site|highlight:search_regexp }}</a></div>
- <div>{{ site.description|excerpt:search_regexp|highlight:search_regexp }}</div>
- <div><a class="le-titre" href="{% url sitotheque.views.retrieve site.id %}">{{ site.titre|apply:excerpt }}</a></div>
- <div class="resultatResume">{{ site.description|apply:excerpt }}</div>
- <div><span class="lbl">URL:</span> <a href="{{ site.url }}">{{ site.url }}</a></div>
++ <div class="titre"><a href="{% url sitotheque.views.retrieve site.id %}">{{ site.titre|apply:excerpt }}</a></div>
++ <div>{{ site.description|apply:excerpt }}</div>
+ <div><span class="label">URL:</span> <a href="{{ site.url }}">{{ site.url }}</a></div>
</div>
[buildout]
newest = false
- parts = django articles harvest
-parts = django articles harvest sphinx_conf
++parts = django articles harvest sphinx_config
find-links = http://pypi.auf.org/caldav/
http://pypi.auf.org/auf_references_client/
http://pypi.auf.org/auf_references_modeles/
recipe = buildout_script
template_dir = ${buildout:directory}/auf_savoirs_en_partage/scripts/
template = import_chercheurs.in
+
-[sphinx_conf]
++[sphinx_config]
+ recipe = buildout_script
+ template_dir = ${buildout:directory}/auf_savoirs_en_partage/scripts/
+ template = sphinx.conf.py.in