def filter_expert(self):
return self.exclude(expertises=None)
+ def filter_date_modification(self, min=None, max=None):
+ return self._filter_date('date_modification', min=min, max=max)
+
def order_by_nom(self, direction=''):
return self.order_by(direction + 'nom', direction + 'prenom', '-date_modification')
def filter_expert(self):
return self.filter(expert=True)
+ def filter_date_modification(self, min=None, max=None):
+ return self._filter_date(self, 'date_modification', min=min, max=max)
+
def order_by_nom(self, direction=''):
return self.order_by(direction + 'nom_complet', '-date_modification')
def filter_expert(self):
return self.get_query_set().filter_expert()
+ def filter_date_modification(self, min=None, max=None):
+ return self.get_query_set().filter_date_modification(min=min, max=max)
+
def order_by_nom(self, direction=''):
return self.get_query_set().order_by_nom(self, direction=direction)
@property
def etablissement_display(self):
- return self.nom_etablissement + ', ' + self.pays
+ return (self.nom_etablissement or '') + (', ' + self.pays.nom if self.pays else '')
@property
def pays(self):
return [self[p] for p in positions]
class SEPQuerySet(models.query.QuerySet, RandomQuerySetMixin):
- pass
+
+ def _filter_date(self, field, min=None, max=None):
+ """Limite les résultats à ceux dont le champ ``field`` tombe entre
+ les dates ``min`` et ``max``."""
+ qs = self
+ if min:
+ qs = qs.filter(**{field + '__gte': min})
+ if max:
+ qs = qs.filter(**{field + '__lte': max})
+ return qs
class SEPSphinxQuerySet(SphinxQuerySet, RandomQuerySetMixin):
"""Fonctionnalités communes aux query sets de Sphinx."""
tous les champs."""
return self.search('"%s"' % region.nom)
+ def _filter_date(self, field, min=None, max=None):
+ """Limite les résultats à ceux dont le champ ``field`` tombe entre
+ les dates ``min`` et ``max``."""
+ qs = self
+ if min:
+ qs = qs.filter(**{field + '__gte': min.toordinal()+365})
+ if max:
+ qs = qs.filter(**{field + '__lte': max.toordinal()+365})
+ return qs
+
def _get_sphinx_results(self):
try:
return SphinxQuerySet._get_sphinx_results(self)
class ActualiteQuerySet(SEPQuerySet):
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
+ return self._filter_date('date', min=min, max=max)
def filter_type(self, type):
return self.filter(source__type=type)
weights=dict(titre=3))
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
-
+ return self._filter_date('date', min=min, max=max)
+
TYPE_CODES = {'actu': 1, 'appels': 2}
def filter_type(self, type):
return self.filter(type=self.TYPE_CODES[type])
def __unicode__ (self):
return "%s" % (self.titre)
+ def get_absolute_url(self):
+ return reverse('actualite', kwargs={'id': self.id})
+
def assigner_disciplines(self, disciplines):
self.disciplines.add(*disciplines)
return self.add_to_query('@type "%s"' % type)
def filter_debut(self, min=None, max=None):
- qs = self
- if min:
- qs = qs.filter(debut__gte=min.toordinal()+365)
- if max:
- qs = qs.filter(debut__lte=max.toordinal()+365)
- return qs
+ return self._filter_date('debut', min=min, max=max)
class EvenementManager(SEPManager):
verbose_name = u'évènement'
verbose_name_plural = u'évènements'
- def __unicode__(self,):
+ def __unicode__(self):
return "[%s] %s" % (self.uid, self.titre)
+ def get_absolute_url(self):
+ return reverse('evenement', kwargs={'id': self.id})
+
def duration_display(self):
delta = self.fin - self.debut
minutes, seconds = divmod(delta.seconds, 60)
def __unicode__(self,):
return self.name
+class RecordQuerySet(SEPQuerySet):
+
+ def filter_modified(self, min=None, max=None):
+ return self._filter_date(self, 'modified', min=min, max=max)
+
class RecordSphinxQuerySet(SEPSphinxQuerySet):
def __init__(self, model=None):
SEPSphinxQuerySet.__init__(self, model=model, index='savoirsenpartage_ressources',
weights=dict(title=3))
+ def filter_modified(self, min=None, max=None):
+ return self._filter_date(self, 'modified', min=min, max=max)
+
class RecordManager(SEPManager):
def get_query_set(self):
"""Ne garder que les ressources validées et qui sont soit dans aucun
listset ou au moins dans un listset validé."""
- qs = SEPQuerySet(self.model)
+ qs = RecordQuerySet(self.model)
qs = qs.filter(validated=True)
qs = qs.filter(Q(listsets__isnull=True) | Q(listsets__validated=True))
return qs.distinct()
def get_sphinx_query_set(self):
return RecordSphinxQuerySet(self.model)
+ def filter_modified(self, min=None, max=None):
+ return self.get_query_set().filter_modified(min=min, max=max)
+
class Record(models.Model):
#fonctionnement interne
def __unicode__(self):
return "[%s] %s" % (self.server, self.title)
+ def get_absolute_url(self):
+ return reverse('ressource', kwargs={'id': self.id})
+
def getServeurURL(self):
"""Retourne l'URL du serveur de provenance"""
return RESOURCES[self.server]['url']
if not self.q:
"""Montrer les résultats les plus récents si on n'a pas fait
une recherche par mots-clés."""
- results = results.order_by('-id')
+ results = results.order_by('-modified')
return results.all()
def url(self):
# -*- encoding: utf-8 -*-
-from datetime import datetime
+from datetime import datetime, date, timedelta
+from dateutil.parser import parse as parse_date
+from dateutil.tz import tzlocal, tzutc
+
from django.core.urlresolvers import reverse
-from django.contrib.syndication.feeds import Feed
-from savoirs.models import Actualite, Evenement
-from datetime import datetime, time
+from django.contrib.syndication.views import Feed
+
+from chercheurs.forms import ChercheurSearchForm
+from savoirs.forms import RessourceSearchForm, ActualiteSearchForm, EvenementSearchForm
+from sitotheque.forms import SiteSearchForm
+
+class FilChercheurs(Feed):
+ title = "Savoirs en partage - chercheurs"
+ link = "/chercheurs/"
+ description = "Fiches de chercheurs mises à jour récemment sur Savoirs en partage"
+
+ def get_object(self, request):
+ search_form = ChercheurSearchForm(request.GET)
+ return search_form.save(commit=False)
+
+ def items(self, search):
+ min_date = date.today() - timedelta(days=30)
+ return search.run().order_by('-date_modification').filter_date_modification(min=min_date)
+
+ def item_title(self, chercheur):
+ return unicode(chercheur)
+
+ def item_description(self, chercheur):
+ return chercheur.etablissement_display
+
+ def item_link(self, chercheur):
+ return reverse('chercheur', kwargs=dict(id=chercheur.id))
+
+ def item_pubdate(self, chercheur):
+ d = chercheur.date_modification
+ return datetime(d.year, d.month, d.day, tzinfo=tzlocal())
+
+class FilRessources(Feed):
+ title = "Savoirs en partage - ressources"
+ link = "/ressources/"
+ description = "Ressources nouvellement disponibles sur Savoirs en partage"
+
+ def get_object(self, request):
+ search_form = RessourceSearchForm(request.GET)
+ return search_form.save(commit=False)
+
+ def items(self, search):
+ min_date = date.today() - timedelta(days=30)
+ return search.run().order_by('-modified').filter_modified(min=min_date)
+
+ def item_title(self, ressource):
+ return ressource.title
+
+ def item_description(self, ressource):
+ return ressource.description
+
+ def item_author_name(self, ressource):
+ return ressource.creator
+
+ def item_pubdate(self, ressource):
+ try:
+ modified = parse_date(ressource.modified)
+ except ValueError:
+ modified = datetime.now()
+ if modified.tzinfo is None:
+ modified.tzinfo = tzutc()
+ return modified
+
+class FilActualitesBase(Feed):
+
+ def get_object(self, request):
+ search_form = ActualiteSearchForm(request.GET)
+ return search_form.save(commit=False)
+
+ def items(self, search):
+ min_date = date.today() - timedelta(days=30)
+ return search.run().filter_date(min=min_date).order_by('-date')
+
+ def item_title(self, actualite):
+ return actualite.titre
+
+ def item_description(self, actualite):
+ return actualite.texte
+
+ def item_author_name(self, actualite):
+ return actualite.source.nom
+
+ def item_pubdate(self, actualite):
+ d = actualite.date
+ return datetime(d.year, d.month, d.day, tzinfo=tzutc())
+
+class FilActualites(FilActualitesBase):
+ title = "Savoirs en partage - actualités"
+ link = "/actualites/"
+ description = "Actualités récentes sur Savoirs en partage"
+
+ def items(self, search):
+ return FilActualitesBase.items(self, search).filter_type('actu')
+
+class FilAppels(FilActualitesBase):
+ title = "Savoirs en partage - appels d'offres"
+ link = "/appels/"
+ description = "Appels d'offres récents sur Savoirs en partage"
-class FilActualite(Feed):
- title = "Dernières actualités du portail des ressources scientifiques et pédagogiques de l'AUF"
- link = '/'
- description = "Agrégateur de ressources scientifiques et pédagogiques de l'AUF"
- limitation = 10
- type = 'actu'
+ def items(self, search):
+ return FilActualitesBase.items(self, search).filter_type('appels')
- title_template = "savoirs/rss_actualite_titre.html"
- description_template = "savoirs/rss_actualite_description.html"
+class FilEvenements(Feed):
+ title = "Savoirs en partage - agenda"
+ link = "/agenda/"
+ description = "Agenda Savoirs en partage"
+ description_template = 'savoirs/rss_evenement_description.html'
- def items(self):
- return Actualite.objects.filter(visible=True).filter_type(self.type).order_by('-date')[:self.limitation]
+ def get_object(self, request):
+ search_form = EvenementSearchForm(request.GET)
+ return search_form.save(commit=False)
- def item_link(self, item):
- return item.url
+ def items(self, search):
+ min_date = date.today()
+ max_date = date.today() + timedelta(days=30)
+ return search.run().filter_debut(min=min_date, max=max_date).order_by('-debut')
- def item_pubdate(self,item):
- return datetime.combine(item.date, time())
+ def item_title(self, evenement):
+ return evenement.titre
- def item_author_name(self,item):
- if item.source:
- return item.source.nom
+ def item_author_name(self, evenement):
+ return ' '.join([evenement.prenom, evenement.nom])
-class FilAppels(FilActualite):
- type = 'appels'
+ def item_author_email(self, evenement):
+ return evenement.courriel
-class FilEvenement(Feed):
- title = "Calendrier des ressources scientifiques et pédagogiques de l'AUF"
- link = '/'
- description = "Evènements connexes aux ressources scientifiques et pédagogiques de l'AUF"
+class FilSites(Feed):
+ title = "Savoirs en partage - sites"
+ link = "/sites/"
+ description = "Sites récemment ajoutés à Savoirs en partage"
- title_template = "savoirs/rss_evenement_titre.html"
- description_template = "savoirs/rss_evenement_description.html"
+ def get_object(self, request):
+ search_form = SiteSearchForm(request.GET)
+ return search_form.save(commit=False)
- def items(self):
- return Evenement.objects.filter(approuve=True, debut__gte=datetime.now())
+ def items(self, search):
+ min_date = date.today() - timedelta(days=365)
+ return search.run().filter_date_maj(min=min_date)
- def item_link(self, item):
- return reverse('savoirs.views.evenement', args=[item.id])
+ def item_title(self, site):
+ return site.titre
- def item_pubdate(self,item):
- return item.debut
+ def item_description(self, site):
+ return site.description
- def item_author_name(self,item):
- return ""
+ def item_author_name(self, site):
+ return site.auteur
r.contributor AS contributor,
r.subject AS subject,
r.publisher AS publisher,
+ TO_DAYS(r.modified) AS modified,
GROUP_CONCAT(DISTINCT d.nom_discipline) AS disciplines,
GROUP_CONCAT(DISTINCT d.id_discipline) AS discipline_ids,
GROUP_CONCAT(DISTINCT p.nom) AS pays,
WHERE r.validated AND (l.spec IS NULL OR l.validated)
GROUP BY r.id''',
sql_query_info='SELECT * from savoirs_record WHERE id=$id',
- sql_attr_multi=['discipline_ids', 'region_ids']
+ sql_attr_multi=['discipline_ids', 'region_ids'],
+ sql_attr_uint=['modified']
)
emit_source('savoirsenpartage_actualites',
def filter_pays(self, pays):
return self.filter(pays=pays)
+ def filter_date_maj(self, min=None, max=None):
+ return self._filter_date('date_maj', min=min, max=max)
+
class SiteSphinxQuerySet(SEPSphinxQuerySet):
def __init__(self, model=None):
SEPSphinxQuerySet.__init__(self, model=model, index='savoirsenpartage_sites', weights=dict(titre=3))
+ def filter_date_maj(self, min=None, max=None):
+ return self._filter_date('date_maj', min=min, max=max)
+
def filter_pays(self, pays):
return self.filter(pays_ids=pays.id)
def filter_pays(self, pays):
return self.get_query_set().filter_pays(pays)
+ def filter_date_maj(self, min=None, max=None):
+ return self.get_query_set().filter_date_maj(self, min=min, max=max)
+
class Site(models.Model):
"""Fiche d'info d'un site web"""
url = models.URLField(verify_exists=False) # dc:identifier (dc:source?)
def __unicode__(self):
return "%s" % (self.titre)
+ def get_absolute_url(self):
+ return reverse('site', kwargs={'id': self.id})
+
def type_display(self):
for t in TYPE_SITE_CHOICES:
if self.type == t[0]:
from django.conf.urls.defaults import patterns, include, handler500, handler404, url
from django.conf import settings
from django.contrib import admin
-from savoirs.rss import FilActualite, FilEvenement, FilAppels
+from savoirs.rss import FilChercheurs, FilRessources, FilActualites, FilAppels, FilEvenements, FilSites
admin.autodiscover()
handler500 = "views.page_500"
handler404 = "views.page_404"
-site_feeds = {'actualites': FilActualite,
- 'agenda': FilEvenement,
- 'appels': FilAppels
- }
-
-
# Les URLs suivantes peuvent être préfixées de la discipline et/ou la
# région. Nous les regroupons donc dans un module qu'on incluera plus bas.
sep_patterns = patterns(
# agenda
(r'^agenda/$', 'savoirs.views.evenement_index', {}, 'agenda'),
- (r'^agenda/evenements/(?P<id>\d+)/$', 'savoirs.views.evenement'),
+ (r'^agenda/evenements/(?P<id>\d+)/$', 'savoirs.views.evenement', {}, 'evenement'),
(r'^agenda/evenements/moderer/$', 'savoirs.views.evenement_moderation'),
(r'^agenda/evenements/moderer/(.+)/accepter/$', 'savoirs.views.evenement_accepter'),
(r'^agenda/evenements/moderer/(.+)/refuser/$', 'savoirs.views.evenement_refuser'),
# ressources
(r'^ressources/$', 'savoirs.views.ressource_index', {}, 'ressources'),
- (r'^ressources/(?P<id>\d+)/$', 'savoirs.views.ressource_retrieve'),
+ (r'^ressources/(?P<id>\d+)/$', 'savoirs.views.ressource_retrieve', {}, 'ressource'),
# actualités
(r'^actualites/$', 'savoirs.views.actualite_index', {}, 'actualites'),
# sites
(r'^sites/$', 'sitotheque.views.index', {}, 'sites'),
- (r'^sites/(?P<id>\d+)/$', 'sitotheque.views.retrieve'),
+ (r'^sites/(?P<id>\d+)/$', 'sitotheque.views.retrieve', {}, 'site'),
(r'^sites/google.xml$', 'sitotheque.views.config_google'),
# sites AUF
# chercheurs
(r'^chercheurs/$', 'chercheurs.views.index', {}, 'chercheurs'),
- (r'^chercheurs/(?P<id>\d+)/$', 'chercheurs.views.retrieve'),
+ (r'^chercheurs/(?P<id>\d+)/$', 'chercheurs.views.retrieve', {}, 'chercheur'),
(r'^chercheurs/inscription/$', 'chercheurs.views.inscription', {}, 'inscription'),
(r'^chercheurs/inscription_faite/$', 'django.views.generic.simple.direct_to_template', dict(
template='chercheurs/inscription_faite.html'
(r'^stats/$', 'savoirs.admin_views.stats', {}, 'stats'),
# rss
- (r'^rss/(.*)/$', 'django.contrib.syndication.views.feed', {'feed_dict':site_feeds}),
+ (r'^rss/chercheurs/$', FilChercheurs()),
+ (r'^rss/ressources/$', FilRessources()),
+ (r'^rss/actualites/$', FilActualites()),
+ (r'^rss/appels/$', FilAppels()),
+ (r'^rss/agenda/$', FilEvenements()),
+ (r'^rss/sites/$', FilSites()),
(r'^json/get/$', 'savoirs.views.json_get'),
(r'^json/set/$', 'savoirs.views.json_set'),
PyYAML==3.09
South==0.7.3
django_exportateur==1.0
+ python-dateutil==1.5
auf.django.admingroup
MySQL-python
simplejson