Fils RSS pour toutes les briques
authorEric Mc Sween <eric.mcsween@gmail.com>
Tue, 15 Mar 2011 19:27:04 +0000 (15:27 -0400)
committerEric Mc Sween <eric.mcsween@gmail.com>
Tue, 15 Mar 2011 19:27:04 +0000 (15:27 -0400)
auf_savoirs_en_partage/chercheurs/models.py
auf_savoirs_en_partage/savoirs/models.py
auf_savoirs_en_partage/savoirs/rss.py
auf_savoirs_en_partage/scripts/sphinx.conf.py.in
auf_savoirs_en_partage/sitotheque/models.py
auf_savoirs_en_partage/urls.py
buildout.cfg

index 8c1380a..ddc4414 100644 (file)
@@ -70,6 +70,9 @@ class ChercheurQuerySet(SEPQuerySet):
     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')
 
@@ -113,6 +116,9 @@ class ChercheurSphinxQuerySet(SEPSphinxQuerySet):
     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')
 
@@ -152,6 +158,9 @@ class ChercheurManager(SEPManager):
     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)
 
@@ -266,7 +275,7 @@ class Chercheur(Personne):
     
     @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):
index 2e02294..fa71313 100644 (file)
@@ -42,7 +42,16 @@ class RandomQuerySetMixin(object):
         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."""
@@ -79,6 +88,16 @@ class SEPSphinxQuerySet(SphinxQuerySet, RandomQuerySetMixin):
            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)
@@ -163,12 +182,7 @@ class SourceActualite(models.Model):
 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)
@@ -180,13 +194,8 @@ class ActualiteSphinxQuerySet(SEPSphinxQuerySet):
                                    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])
@@ -227,6 +236,9 @@ class Actualite(models.Model):
     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)
 
@@ -258,12 +270,7 @@ class EvenementSphinxQuerySet(SEPSphinxQuerySet):
         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):
 
@@ -338,9 +345,12 @@ class Evenement(models.Model):
         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)
@@ -489,18 +499,26 @@ class ListSet(models.Model):
     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()
@@ -508,6 +526,9 @@ class RecordManager(SEPManager):
     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
@@ -558,6 +579,9 @@ class Record(models.Model):
     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']
@@ -757,7 +781,7 @@ class RessourceSearch(Search):
         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):
index cc7889a..269efc5 100644 (file)
 # -*- 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
index 612a815..5f72808 100644 (file)
@@ -92,6 +92,7 @@ emit_source('savoirsenpartage_ressources',
                       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, 
@@ -109,7 +110,8 @@ emit_source('savoirsenpartage_ressources',
                 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',
index 196b8f2..801de35 100644 (file)
@@ -25,11 +25,17 @@ class SiteQuerySet(SEPQuerySet):
     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)
 
@@ -44,6 +50,9 @@ class SiteManager(SEPManager):
     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?)
@@ -78,6 +87,9 @@ class Site(models.Model):
     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]:
index 6c79f7e..9e841bc 100644 (file)
@@ -3,19 +3,13 @@
 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( 
@@ -35,7 +29,7 @@ urlpatterns = 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'),
@@ -52,7 +46,7 @@ urlpatterns = sep_patterns + patterns(
 
     # 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'),
@@ -61,7 +55,7 @@ urlpatterns = sep_patterns + patterns(
 
     # 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
@@ -69,7 +63,7 @@ urlpatterns = sep_patterns + patterns(
 
     # 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'
@@ -130,7 +124,12 @@ urlpatterns = sep_patterns + patterns(
     (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'),
 
index 1c16947..bc94421 100644 (file)
@@ -34,6 +34,7 @@ eggs = auf_references_client==0.4.9
     PyYAML==3.09
     South==0.7.3
     django_exportateur==1.0
+    python-dateutil==1.5
     auf.django.admingroup
     MySQL-python
     simplejson