Alertes courriel
authorEric Mc Sween <eric.mcsween@gmail.com>
Wed, 23 Mar 2011 19:27:59 +0000 (15:27 -0400)
committerEric Mc Sween <eric.mcsween@gmail.com>
Wed, 23 Mar 2011 19:27:59 +0000 (15:27 -0400)
Demande #1315

19 files changed:
auf_savoirs_en_partage/chercheurs/forms.py
auf_savoirs_en_partage/chercheurs/models.py
auf_savoirs_en_partage/development.py
auf_savoirs_en_partage/media/css/global.css
auf_savoirs_en_partage/media/img/icon-no.gif [new file with mode: 0644]
auf_savoirs_en_partage/media/img/icon-yes.gif [new file with mode: 0644]
auf_savoirs_en_partage/savoirs/forms.py
auf_savoirs_en_partage/savoirs/migrations/0011_auto__add_field_search_alerte_courriel.py [new file with mode: 0644]
auf_savoirs_en_partage/savoirs/migrations/0012_auto__add_field_search_derniere_alerte.py [new file with mode: 0644]
auf_savoirs_en_partage/savoirs/models.py
auf_savoirs_en_partage/savoirs/rss.py
auf_savoirs_en_partage/savoirs/views.py
auf_savoirs_en_partage/scripts/alertes_courriel.in [new file with mode: 0644]
auf_savoirs_en_partage/sitotheque/forms.py
auf_savoirs_en_partage/sitotheque/models.py
auf_savoirs_en_partage/templates/savoirs/recherches.html
auf_savoirs_en_partage/templates/savoirs/rss_evenement_description.html
auf_savoirs_en_partage/urls.py
buildout.cfg

index a87c77e..2fb68dc 100644 (file)
@@ -336,7 +336,7 @@ class ChercheurSearchEditForm(ChercheurSearchForm):
     """Formulaire d'édition d'une recherche sauvegardée."""
 
     class Meta(ChercheurSearchForm.Meta):
-        fields = ['nom'] + ChercheurSearchForm.Meta.fields
+        fields = ['nom', 'alerte_courriel'] + ChercheurSearchForm.Meta.fields
 
 class SendPasswordForm(forms.Form):
     email = forms.EmailField(required=True, label="Adresse électronique")
index 962a894..a4481bd 100644 (file)
@@ -299,6 +299,9 @@ class Chercheur(Personne):
     def activation_token(self):
         return sha_constructor(settings.SECRET_KEY + unicode(self.id)).hexdigest()[::2]
 
+    def get_absolute_url(self):
+        return url('chercheur', kwargs={'id': self.id})
+
 class Publication(models.Model):
     chercheur = models.ForeignKey(Chercheur, related_name='publications')
     auteurs = models.CharField(max_length=255, blank=True, verbose_name='auteur(s)')
@@ -405,7 +408,7 @@ class ChercheurSearch(Search):
         verbose_name = 'recherche de chercheurs'
         verbose_name_plural = 'recherches de chercheurs'
 
-    def run(self):
+    def run(self, min_date=None, max_date=None):
         results = Chercheur.objects
         if self.q:
             results = results.search(self.q)
@@ -438,6 +441,10 @@ class ChercheurSearch(Search):
             results = results.filter(membre_association_francophone=True)
         elif self.activites_francophonie == 'reseau_institutionnel':
             results = results.filter(membre_reseau_institutionnel=True)
+        if min_date:
+            results = results.filter_date_modification(min=min_date)
+        if max_date:
+            results = results.filter_date_modification(max=max_date)
         return results.all()
 
     def url(self):
@@ -447,3 +454,13 @@ class ChercheurSearch(Search):
     def rss_url(self):
         qs = self.query_string()
         return url('rss_chercheurs') + ('?' + qs if qs else '')
+
+    def get_email_alert_content(self, results):
+        content = ''
+        for chercheur in results:
+            content += u'-   [%s %s](%s%s)  \n' % (chercheur.nom.upper(),
+                                                   chercheur.prenom,
+                                                   settings.SITE_ROOT_URL,
+                                                   chercheur.get_absolute_url())
+            content += u'    %s\n\n' % chercheur.etablissement_display
+        return content
index 11e193b..884575a 100644 (file)
@@ -5,6 +5,7 @@ TEMPLATE_DEBUG = DEBUG
 INTERNAL_IPS = ('127.0.0.1',)
 EMAIL_HOST = 'localhost'
 EMAIL_PORT = 1025
+EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
 
 # Debug toolbar
 
index 23c9229..beb103e 100644 (file)
@@ -133,6 +133,8 @@ dd { padding: 0; margin: 0 0 1em 0; }
 ul.items-and-actions li { padding: 3px; border-bottom: 1px solid #ddd; }
 div.item-actions { float: right; }
 div.item-actions a { padding: 5px; }
+div.item-actions a.alerte { padding-right: 15px; }
+div.item-actions a.alerte.active { background: url(../img/icon-yes.gif) right center no-repeat; }
 
 /* Styles inline */
 
@@ -178,6 +180,6 @@ ul.actions { font-size: 90%; float: right; text-align: right; list-style: none;
 
 /* Cadre */
 
-.cadre { width: 60%; margin: 100px auto; padding: 20px; background: #f9f9f9; border: 1px solid #aaa }
+.cadre { width: 70%; margin: 50px auto; padding: 20px; background: #f9f9f9; border: 1px solid #aaa }
 .cadre h1 { margin: -20px -20px 0 -20px; padding: 5px; background: #aaa; color: white; font-size: 120%; font-weight: bold; text-align: center; }
 
diff --git a/auf_savoirs_en_partage/media/img/icon-no.gif b/auf_savoirs_en_partage/media/img/icon-no.gif
new file mode 100644 (file)
index 0000000..1b4ee58
Binary files /dev/null and b/auf_savoirs_en_partage/media/img/icon-no.gif differ
diff --git a/auf_savoirs_en_partage/media/img/icon-yes.gif b/auf_savoirs_en_partage/media/img/icon-yes.gif
new file mode 100644 (file)
index 0000000..7399282
Binary files /dev/null and b/auf_savoirs_en_partage/media/img/icon-yes.gif differ
index 8a8ba14..9549dc6 100644 (file)
@@ -63,7 +63,7 @@ class RessourceSearchEditForm(RessourceSearchForm):
     """Formulaire d'édition de recherche sauvegardée."""
 
     class Meta(RessourceSearchForm.Meta):
-        fields = ['nom'] + RessourceSearchForm.Meta.fields
+        fields = ['nom', 'alerte_courriel'] + RessourceSearchForm.Meta.fields
 
 class ActualiteSearchForm(forms.ModelForm):
     """Formulaire de recherche pour les actualités."""
@@ -77,7 +77,7 @@ class ActualiteSearchForm(forms.ModelForm):
 class ActualiteSearchEditForm(ActualiteSearchForm):
 
     class Meta(ActualiteSearchForm.Meta):
-        fields = ['nom'] + ActualiteSearchForm.Meta.fields
+        fields = ['nom', 'alerte_courriel'] + ActualiteSearchForm.Meta.fields
 
 class AppelSearchForm(forms.ModelForm):
     """Formulaire de recherche pour les actualités."""
@@ -91,7 +91,7 @@ class AppelSearchForm(forms.ModelForm):
 class AppelSearchEditForm(AppelSearchForm):
 
     class Meta(AppelSearchForm.Meta):
-        fields = ['nom'] + AppelSearchForm.Meta.fields
+        fields = ['nom', 'alerte_courriel'] + AppelSearchForm.Meta.fields
 
 class EvenementSearchForm(forms.ModelForm):
     """Formulaire de recherche pour les évènements."""
@@ -105,7 +105,7 @@ class EvenementSearchForm(forms.ModelForm):
 class EvenementSearchEditForm(EvenementSearchForm):
 
     class Meta(EvenementSearchForm.Meta):
-        fields = ['nom'] + EvenementSearchForm.Meta.fields
+        fields = ['nom', 'alerte_courriel'] + EvenementSearchForm.Meta.fields
 
 class SearchEditForm(forms.ModelForm):
 
diff --git a/auf_savoirs_en_partage/savoirs/migrations/0011_auto__add_field_search_alerte_courriel.py b/auf_savoirs_en_partage/savoirs/migrations/0011_auto__add_field_search_alerte_courriel.py
new file mode 100644 (file)
index 0000000..ebaf2ac
--- /dev/null
@@ -0,0 +1,307 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        
+        # Adding field 'Search.alerte_courriel'
+        db.add_column('savoirs_search', 'alerte_courriel', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
+
+
+    def backwards(self, orm):
+        
+        # Deleting field 'Search.alerte_courriel'
+        db.delete_column('savoirs_search', 'alerte_courriel')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'datamaster_modeles.bureau': {
+            'Meta': {'object_name': 'Bureau', 'db_table': "u'ref_bureau'"},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}),
+            'implantation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['datamaster_modeles.Implantation']", 'db_column': "'implantation'"}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'nom_court': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'nom_long': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'region': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['datamaster_modeles.Region']", 'db_column': "'region'"})
+        },
+        'datamaster_modeles.implantation': {
+            'Meta': {'ordering': "('nom',)", 'object_name': 'Implantation', 'db_table': "u'ref_implantation'"},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'adresse_physique_bureau': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_physique_code_postal': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'adresse_physique_code_postal_avant_ville': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            'adresse_physique_no': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'adresse_physique_pays': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'impl_adresse_physique'", 'db_column': "'adresse_physique_pays'", 'to': "orm['datamaster_modeles.Pays']"}),
+            'adresse_physique_precision': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_physique_precision_avant': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_physique_region': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_physique_rue': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_physique_ville': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'adresse_postale_boite_postale': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_postale_bureau': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_postale_code_postal': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}),
+            'adresse_postale_code_postal_avant_ville': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            'adresse_postale_no': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'adresse_postale_pays': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'impl_adresse_postale'", 'db_column': "'adresse_postale_pays'", 'to': "orm['datamaster_modeles.Pays']"}),
+            'adresse_postale_precision': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_postale_precision_avant': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_postale_region': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_postale_rue': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_postale_ville': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'bureau_rattachement': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['datamaster_modeles.Implantation']", 'db_column': "'bureau_rattachement'"}),
+            'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'code_meteo': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'commentaire': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'courriel': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'courriel_interne': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'date_extension': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'date_fermeture': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'date_inauguration': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'date_ouverture': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'fax': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'fax_interne': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'fuseau_horaire': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'hebergement_convention': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            'hebergement_convention_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'hebergement_etablissement': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}),
+            'modif_date': ('django.db.models.fields.DateField', [], {}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'nom_court': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'nom_long': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'region': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['datamaster_modeles.Region']", 'db_column': "'region'"}),
+            'remarque': ('django.db.models.fields.TextField', [], {}),
+            'responsable_implantation': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'statut': ('django.db.models.fields.IntegerField', [], {}),
+            'telephone': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'telephone_interne': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'blank': 'True'})
+        },
+        'datamaster_modeles.pays': {
+            'Meta': {'ordering': "('nom',)", 'object_name': 'Pays', 'db_table': "u'ref_pays'"},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'code': ('django.db.models.fields.CharField', [], {'max_length': '2', 'primary_key': 'True'}),
+            'code_bureau': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['datamaster_modeles.Bureau']", 'to_field': "'code'", 'db_column': "'code_bureau'"}),
+            'code_iso3': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '3', 'blank': 'True'}),
+            'developpement': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.IntegerField', [], {}),
+            'monnaie': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'nord_sud': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'region': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['datamaster_modeles.Region']", 'db_column': "'region'"})
+        },
+        'datamaster_modeles.region': {
+            'Meta': {'object_name': 'Region', 'db_table': "u'ref_region'"},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}),
+            'implantation_bureau': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'gere_region'", 'db_column': "'implantation_bureau'", 'to': "orm['datamaster_modeles.Implantation']"}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'datamaster_modeles.thematique': {
+            'Meta': {'object_name': 'Thematique', 'db_table': "u'ref_thematique'"},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'savoirs.actualite': {
+            'Meta': {'ordering': "['-date']", 'object_name': 'Actualite', 'db_table': "u'actualite'"},
+            'ancienid': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'ancienId_actualite'", 'blank': 'True'}),
+            'date': ('django.db.models.fields.DateField', [], {'db_column': "'date_actualite'"}),
+            'disciplines': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'actualites'", 'blank': 'True', 'to': "orm['savoirs.Discipline']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True', 'db_column': "'id_actualite'"}),
+            'regions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'actualites'", 'blank': 'True', 'to': "orm['datamaster_modeles.Region']"}),
+            'source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actualites'", 'to': "orm['savoirs.SourceActualite']"}),
+            'texte': ('django.db.models.fields.TextField', [], {'db_column': "'texte_actualite'"}),
+            'titre': ('django.db.models.fields.CharField', [], {'max_length': '765', 'db_column': "'titre_actualite'"}),
+            'url': ('django.db.models.fields.CharField', [], {'max_length': '765', 'db_column': "'url_actualite'"}),
+            'visible': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_column': "'visible_actualite'"})
+        },
+        'savoirs.actualitesearch': {
+            'Meta': {'object_name': 'ActualiteSearch'},
+            'date_max': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'date_min': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'search_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['savoirs.Search']", 'unique': 'True', 'primary_key': True})
+        },
+        'savoirs.appelsearch': {
+            'Meta': {'object_name': 'AppelSearch'},
+            'date_max': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'date_min': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'search_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['savoirs.Search']", 'unique': 'True', 'primary_key': 'True'})
+        },
+        'savoirs.discipline': {
+            'Meta': {'ordering': "['nom']", 'object_name': 'Discipline', 'db_table': "u'discipline'"},
+            'id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True', 'db_column': "'id_discipline'"}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '765', 'db_column': "'nom_discipline'"})
+        },
+        'savoirs.evenement': {
+            'Meta': {'ordering': "['-debut']", 'object_name': 'Evenement'},
+            'adresse': ('django.db.models.fields.TextField', [], {}),
+            'approuve': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'contact': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+            'courriel': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+            'debut': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'description': ('django.db.models.fields.TextField', [], {}),
+            'discipline': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'discipline'", 'null': 'True', 'to': "orm['savoirs.Discipline']"}),
+            'discipline_secondaire': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'discipline_secondaire'", 'null': 'True', 'to': "orm['savoirs.Discipline']"}),
+            'fin': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'fuseau': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'mots_cles': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'pays': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'evenements'", 'null': 'True', 'to': "orm['datamaster_modeles.Pays']"}),
+            'piece_jointe': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}),
+            'prenom': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'regions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'evenements'", 'blank': 'True', 'to': "orm['datamaster_modeles.Region']"}),
+            'titre': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'uid': ('django.db.models.fields.CharField', [], {'default': "'39604cea-5007-11e0-8d8b-f0def13a5ffb'", 'max_length': '255'}),
+            'url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'ville': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'savoirs.evenementsearch': {
+            'Meta': {'object_name': 'EvenementSearch', '_ormbases': ['savoirs.Search']},
+            'date_max': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'date_min': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'search_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['savoirs.Search']", 'unique': 'True', 'primary_key': 'True'}),
+            'titre': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'})
+        },
+        'savoirs.harvestlog': {
+            'Meta': {'object_name': 'HarvestLog'},
+            'added': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'context': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'processed': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['savoirs.Record']", 'null': 'True', 'blank': 'True'}),
+            'updated': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'savoirs.listset': {
+            'Meta': {'object_name': 'ListSet'},
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'server': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'spec': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'validated': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+        },
+        'savoirs.pagestatique': {
+            'Meta': {'object_name': 'PageStatique'},
+            'contenu': ('django.db.models.fields.TextField', [], {}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
+            'titre': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'savoirs.profile': {
+            'Meta': {'object_name': 'Profile'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'serveurs': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['savoirs.Serveur']", 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
+        },
+        'savoirs.record': {
+            'Meta': {'object_name': 'Record'},
+            'abstract': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'alt_title': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'contributor': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'creation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'creator': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'disciplines': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['savoirs.Discipline']", 'symmetrical': 'False', 'blank': 'True'}),
+            'format': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+            'isbn': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'issued': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'language': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'last_checksum': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'last_update': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'listsets': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['savoirs.ListSet']", 'null': 'True', 'blank': 'True'}),
+            'modified': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'orig_lang': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'pays': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['datamaster_modeles.Pays']", 'symmetrical': 'False', 'blank': 'True'}),
+            'publisher': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'regions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['datamaster_modeles.Region']", 'symmetrical': 'False', 'blank': 'True'}),
+            'server': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'source': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'subject': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'thematiques': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['datamaster_modeles.Thematique']", 'symmetrical': 'False', 'blank': 'True'}),
+            'title': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'type': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'uri': ('django.db.models.fields.CharField', [], {'max_length': '255', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+            'validated': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+        },
+        'savoirs.ressourcesearch': {
+            'Meta': {'object_name': 'RessourceSearch', '_ormbases': ['savoirs.Search']},
+            'auteur': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'publisher': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'search_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['savoirs.Search']", 'unique': 'True', 'primary_key': 'True'}),
+            'sujet': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'titre': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'})
+        },
+        'savoirs.search': {
+            'Meta': {'object_name': 'Search'},
+            'alerte_courriel': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'discipline': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['savoirs.Discipline']", 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'q': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'region': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['datamaster_modeles.Region']", 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'savoirs.serveur': {
+            'Meta': {'object_name': 'Serveur'},
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'})
+        },
+        'savoirs.sourceactualite': {
+            'Meta': {'object_name': 'SourceActualite'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'type': ('django.db.models.fields.CharField', [], {'default': "'actu'", 'max_length': '10'}),
+            'url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
+        }
+    }
+
+    complete_apps = ['savoirs']
diff --git a/auf_savoirs_en_partage/savoirs/migrations/0012_auto__add_field_search_derniere_alerte.py b/auf_savoirs_en_partage/savoirs/migrations/0012_auto__add_field_search_derniere_alerte.py
new file mode 100644 (file)
index 0000000..1e91d89
--- /dev/null
@@ -0,0 +1,308 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        
+        # Adding field 'Search.derniere_alerte'
+        db.add_column('savoirs_search', 'derniere_alerte', self.gf('django.db.models.fields.DateField')(null=True), keep_default=False)
+
+
+    def backwards(self, orm):
+        
+        # Deleting field 'Search.derniere_alerte'
+        db.delete_column('savoirs_search', 'derniere_alerte')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'datamaster_modeles.bureau': {
+            'Meta': {'object_name': 'Bureau', 'db_table': "u'ref_bureau'"},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}),
+            'implantation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['datamaster_modeles.Implantation']", 'db_column': "'implantation'"}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'nom_court': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'nom_long': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'region': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['datamaster_modeles.Region']", 'db_column': "'region'"})
+        },
+        'datamaster_modeles.implantation': {
+            'Meta': {'ordering': "('nom',)", 'object_name': 'Implantation', 'db_table': "u'ref_implantation'"},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'adresse_physique_bureau': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_physique_code_postal': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'adresse_physique_code_postal_avant_ville': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            'adresse_physique_no': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'adresse_physique_pays': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'impl_adresse_physique'", 'db_column': "'adresse_physique_pays'", 'to': "orm['datamaster_modeles.Pays']"}),
+            'adresse_physique_precision': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_physique_precision_avant': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_physique_region': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_physique_rue': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_physique_ville': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'adresse_postale_boite_postale': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_postale_bureau': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_postale_code_postal': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}),
+            'adresse_postale_code_postal_avant_ville': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            'adresse_postale_no': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'adresse_postale_pays': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'impl_adresse_postale'", 'db_column': "'adresse_postale_pays'", 'to': "orm['datamaster_modeles.Pays']"}),
+            'adresse_postale_precision': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_postale_precision_avant': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_postale_region': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_postale_rue': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_postale_ville': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'bureau_rattachement': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['datamaster_modeles.Implantation']", 'db_column': "'bureau_rattachement'"}),
+            'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'code_meteo': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'commentaire': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'courriel': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'courriel_interne': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'date_extension': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'date_fermeture': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'date_inauguration': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'date_ouverture': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'fax': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'fax_interne': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'fuseau_horaire': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'hebergement_convention': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            'hebergement_convention_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'hebergement_etablissement': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}),
+            'modif_date': ('django.db.models.fields.DateField', [], {}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'nom_court': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'nom_long': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'region': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['datamaster_modeles.Region']", 'db_column': "'region'"}),
+            'remarque': ('django.db.models.fields.TextField', [], {}),
+            'responsable_implantation': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'statut': ('django.db.models.fields.IntegerField', [], {}),
+            'telephone': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'telephone_interne': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'blank': 'True'})
+        },
+        'datamaster_modeles.pays': {
+            'Meta': {'ordering': "('nom',)", 'object_name': 'Pays', 'db_table': "u'ref_pays'"},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'code': ('django.db.models.fields.CharField', [], {'max_length': '2', 'primary_key': 'True'}),
+            'code_bureau': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['datamaster_modeles.Bureau']", 'to_field': "'code'", 'db_column': "'code_bureau'"}),
+            'code_iso3': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '3', 'blank': 'True'}),
+            'developpement': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.IntegerField', [], {}),
+            'monnaie': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'nord_sud': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'region': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['datamaster_modeles.Region']", 'db_column': "'region'"})
+        },
+        'datamaster_modeles.region': {
+            'Meta': {'object_name': 'Region', 'db_table': "u'ref_region'"},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}),
+            'implantation_bureau': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'gere_region'", 'db_column': "'implantation_bureau'", 'to': "orm['datamaster_modeles.Implantation']"}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'datamaster_modeles.thematique': {
+            'Meta': {'object_name': 'Thematique', 'db_table': "u'ref_thematique'"},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'savoirs.actualite': {
+            'Meta': {'ordering': "['-date']", 'object_name': 'Actualite', 'db_table': "u'actualite'"},
+            'ancienid': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'ancienId_actualite'", 'blank': 'True'}),
+            'date': ('django.db.models.fields.DateField', [], {'db_column': "'date_actualite'"}),
+            'disciplines': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'actualites'", 'blank': 'True', 'to': "orm['savoirs.Discipline']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True', 'db_column': "'id_actualite'"}),
+            'regions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'actualites'", 'blank': 'True', 'to': "orm['datamaster_modeles.Region']"}),
+            'source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actualites'", 'to': "orm['savoirs.SourceActualite']"}),
+            'texte': ('django.db.models.fields.TextField', [], {'db_column': "'texte_actualite'"}),
+            'titre': ('django.db.models.fields.CharField', [], {'max_length': '765', 'db_column': "'titre_actualite'"}),
+            'url': ('django.db.models.fields.CharField', [], {'max_length': '765', 'db_column': "'url_actualite'"}),
+            'visible': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_column': "'visible_actualite'"})
+        },
+        'savoirs.actualitesearch': {
+            'Meta': {'object_name': 'ActualiteSearch'},
+            'date_max': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'date_min': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'search_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['savoirs.Search']", 'unique': 'True', 'primary_key': True})
+        },
+        'savoirs.appelsearch': {
+            'Meta': {'object_name': 'AppelSearch'},
+            'date_max': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'date_min': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'search_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['savoirs.Search']", 'unique': 'True', 'primary_key': 'True'})
+        },
+        'savoirs.discipline': {
+            'Meta': {'ordering': "['nom']", 'object_name': 'Discipline', 'db_table': "u'discipline'"},
+            'id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True', 'db_column': "'id_discipline'"}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '765', 'db_column': "'nom_discipline'"})
+        },
+        'savoirs.evenement': {
+            'Meta': {'ordering': "['-debut']", 'object_name': 'Evenement'},
+            'adresse': ('django.db.models.fields.TextField', [], {}),
+            'approuve': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'contact': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+            'courriel': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+            'debut': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'description': ('django.db.models.fields.TextField', [], {}),
+            'discipline': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'discipline'", 'null': 'True', 'to': "orm['savoirs.Discipline']"}),
+            'discipline_secondaire': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'discipline_secondaire'", 'null': 'True', 'to': "orm['savoirs.Discipline']"}),
+            'fin': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'fuseau': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'mots_cles': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'pays': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'evenements'", 'null': 'True', 'to': "orm['datamaster_modeles.Pays']"}),
+            'piece_jointe': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}),
+            'prenom': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'regions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'evenements'", 'blank': 'True', 'to': "orm['datamaster_modeles.Region']"}),
+            'titre': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'uid': ('django.db.models.fields.CharField', [], {'default': "'d87c7bde-54ba-11e0-acbf-0024d77f8910'", 'max_length': '255'}),
+            'url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'ville': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'savoirs.evenementsearch': {
+            'Meta': {'object_name': 'EvenementSearch', '_ormbases': ['savoirs.Search']},
+            'date_max': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'date_min': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'search_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['savoirs.Search']", 'unique': 'True', 'primary_key': 'True'}),
+            'titre': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'})
+        },
+        'savoirs.harvestlog': {
+            'Meta': {'object_name': 'HarvestLog'},
+            'added': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'context': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'processed': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['savoirs.Record']", 'null': 'True', 'blank': 'True'}),
+            'updated': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'savoirs.listset': {
+            'Meta': {'object_name': 'ListSet'},
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'server': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'spec': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+            'validated': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+        },
+        'savoirs.pagestatique': {
+            'Meta': {'object_name': 'PageStatique'},
+            'contenu': ('django.db.models.fields.TextField', [], {}),
+            'id': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
+            'titre': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'savoirs.profile': {
+            'Meta': {'object_name': 'Profile'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'serveurs': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['savoirs.Serveur']", 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
+        },
+        'savoirs.record': {
+            'Meta': {'object_name': 'Record'},
+            'abstract': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'alt_title': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'contributor': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'creation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'creator': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'disciplines': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['savoirs.Discipline']", 'symmetrical': 'False', 'blank': 'True'}),
+            'format': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+            'isbn': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'issued': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'language': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'last_checksum': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'last_update': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'listsets': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['savoirs.ListSet']", 'null': 'True', 'blank': 'True'}),
+            'modified': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'orig_lang': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'pays': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['datamaster_modeles.Pays']", 'symmetrical': 'False', 'blank': 'True'}),
+            'publisher': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'regions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['datamaster_modeles.Region']", 'symmetrical': 'False', 'blank': 'True'}),
+            'server': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'source': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'subject': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'thematiques': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['datamaster_modeles.Thematique']", 'symmetrical': 'False', 'blank': 'True'}),
+            'title': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'type': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'uri': ('django.db.models.fields.CharField', [], {'max_length': '255', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+            'validated': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+        },
+        'savoirs.ressourcesearch': {
+            'Meta': {'object_name': 'RessourceSearch', '_ormbases': ['savoirs.Search']},
+            'auteur': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'publisher': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'search_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['savoirs.Search']", 'unique': 'True', 'primary_key': 'True'}),
+            'sujet': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'titre': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'})
+        },
+        'savoirs.search': {
+            'Meta': {'object_name': 'Search'},
+            'alerte_courriel': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'derniere_alerte': ('django.db.models.fields.DateField', [], {'null': 'True'}),
+            'discipline': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['savoirs.Discipline']", 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'q': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'region': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['datamaster_modeles.Region']", 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'savoirs.serveur': {
+            'Meta': {'object_name': 'Serveur'},
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'})
+        },
+        'savoirs.sourceactualite': {
+            'Meta': {'object_name': 'SourceActualite'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'type': ('django.db.models.fields.CharField', [], {'default': "'actu'", 'max_length': '10'}),
+            'url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
+        }
+    }
+
+    complete_apps = ['savoirs']
index 64a3789..6c03e22 100644 (file)
@@ -6,6 +6,7 @@ import operator
 import os
 import pytz
 import random
+import textwrap
 import uuid
 import vobject
 from pytz.tzinfo import AmbiguousTimeError, NonExistentTimeError
@@ -16,12 +17,14 @@ from babel.dates import get_timezone_name
 from caldav.lib import error
 from django.contrib.auth.models import User
 from django.contrib.contenttypes.models import ContentType
+from django.core.mail import EmailMultiAlternatives
 from django.core.urlresolvers import reverse
 from django.db import models
 from django.db.models import Q, Max
 from django.db.models.signals import pre_delete
-from django.utils.encoding import smart_unicode
+from django.utils.encoding import smart_unicode, smart_str
 from djangosphinx.models import SphinxQuerySet, SearchError
+from markdown2 import markdown
 
 from datamaster_modeles.models import Region, Pays, Thematique
 from savoirs.globals import META
@@ -666,10 +669,16 @@ class GlobalSearchResults(object):
         self.sites = sites
         self.sites_auf = sites_auf
 
+    def __nonzero__(self):
+        return bool(self.actualites or self.appels or self.evenements or 
+                    self.ressources or self.chercheurs or self.sites or self.sites_auf)
+
 class Search(models.Model):
     user = models.ForeignKey(User, editable=False)
     content_type = models.ForeignKey(ContentType, editable=False)
     nom = models.CharField(max_length=100, verbose_name="nom de la recherche")
+    alerte_courriel = models.BooleanField(verbose_name="Envoyer une alerte courriel")
+    derniere_alerte = models.DateField(verbose_name="Date d'envoi de la dernière alerte courriel", null=True, editable=False)
     q = models.CharField(max_length=100, blank=True, verbose_name="rechercher dans tous les champs")
     discipline = models.ForeignKey(Discipline, blank=True, null=True)
     region = models.ForeignKey(Region, blank=True, null=True, verbose_name='région',
@@ -685,7 +694,7 @@ class Search(models.Model):
                 if isinstance(value, datetime.date):
                     params[field.name] = value.strftime('%d/%m/%Y')
                 else:
-                    params[field.name] = value
+                    params[field.name] = smart_str(value)
         return urlencode(params)
     
     class Meta:
@@ -704,7 +713,7 @@ class Search(models.Model):
             return self
         return model.objects.get(id=self.id)
                 
-    def run(self):
+    def run(self, min_date=None, max_date=None):
         from chercheurs.models import Chercheur
         from sitotheque.models import Site
 
@@ -732,6 +741,19 @@ class Search(models.Model):
             ressources = ressources.filter_region(self.region)
             chercheurs = chercheurs.filter_region(self.region)
             sites = sites.filter_region(self.region)
+        if min_date:
+            actualites = actualites.filter_date(min=min_date)
+            evenements = evenements.filter_debut(min=min_date)
+            ressources = ressources.filter_modified(min=min_date)
+            chercheurs = chercheurs.filter_date_modification(min=min_date)
+            sites = sites.filter_date_maj(min=min_date)
+        if max_date:
+            actualites = actualites.filter_date(max=max_date)
+            evenements = evenements.filter_debut(max=max_date)
+            ressources = ressources.filter_modified(max=max_date)
+            chercheurs = chercheurs.filter_date_modification(max=max_date)
+            sites = sites.filter_date_maj(max=max_date)
+
         try:
             sites_auf = google_search(0, self.q)['results']
         except:
@@ -755,12 +777,100 @@ class Search(models.Model):
             url += '/region/%d' % self.region.id
         url += '/recherche/'
         if self.q:
-            url += '?' + urlencode({'q': self.q})
+            url += '?' + urlencode({'q': smart_str(self.q)})
         return url
 
     def rss_url(self):
         return None
 
+    def send_email_alert(self):
+        """Envoie une alerte courriel correspondant à cette recherche"""
+        yesterday = datetime.date.today() - datetime.timedelta(days=1)
+        if self.derniere_alerte is not None:
+            results = self.as_leaf_class().run(min_date=self.derniere_alerte, max_date=yesterday)
+            if results:
+                subject = 'Savoirs en partage - ' + self.nom
+                from_email = 'contact-savoirsenpartage@auf.org'
+                to_email = self.user.email
+                text_content = u'Voici les derniers résultats correspondant à votre recherche sauvegardée.\n\n'
+                text_content += self.as_leaf_class().get_email_alert_content(results)
+                text_content += u'''
+                
+Pour modifier votre abonnement aux alertes courriel de Savoirs en partage,
+rendez-vous sur le [gestionnaire de recherches sauvegardées](%s%s)''' % (SITE_ROOT_URL, reverse('recherches'))
+                html_content = '<div style="font-family: Arial, sans-serif">\n' + markdown(smart_str(text_content)) + '</div>\n'
+                msg = EmailMultiAlternatives(subject, text_content, from_email, [to_email])
+                msg.attach_alternative(html_content, "text/html")
+                msg.send()
+        self.derniere_alerte = yesterday
+        self.save()
+        return
+
+    def get_email_alert_content(self, results):
+        content = ''
+        if results.chercheurs:
+            content += u'\n### Nouveaux chercheurs\n\n'
+            for chercheur in results.chercheurs:
+                content += u'-   [%s %s](%s%s)  \n' % (chercheur.nom.upper(),
+                                                       chercheur.prenom,
+                                                       SITE_ROOT_URL,
+                                                       chercheur.get_absolute_url())
+                content += u'    %s\n\n' % chercheur.etablissement_display
+        if results.ressources:
+            content += u'\n### Nouvelles ressources\n\n'
+            for ressource in results.ressources:
+                content += u'-   [%s](%s%s)\n\n' % (ressource.title,
+                                                    SITE_ROOT_URL,
+                                                    ressource.get_absolute_url())
+                if ressource.description:
+                    content += '\n'
+                    content += ''.join(['    %s\n' % line for line in textwrap.wrap(ressource.description)])
+                    content += '\n'
+
+        if results.actualites:
+            content += u'\n### Nouvelles actualités\n\n'
+            for actualite in results.actualites:
+                content += u'-  [%s](%s%s)\n\n' % (actualite.titre,
+                                                   SITE_ROOT_URL,
+                                                   actualite.get_absolute_url())
+                if actualite.texte:
+                    content += '\n'
+                    content += ''.join(['    %s\n' % line for line in textwrap.wrap(actualite.texte)])
+                    content += '\n'
+        if results.appels:
+            content += u"\n### Nouveaux appels d'offres\n\n"
+            for appel in results.appels:
+                content += u'-   [%s](%s%s)\n\n' % (appel.titre,
+                                                    SITE_ROOT_URL,
+                                                    appel.get_absolute_url())
+                if appel.texte:
+                    content += '\n'
+                    content += ''.join(['    %s\n' % line for line in textwrap.wrap(appel.texte)])
+                    content += '\n'
+        if results.evenements:
+            content += u"\n### Nouveaux évènements\n\n"
+            for evenement in results.evenements:
+                content += u'-   [%s](%s%s)  \n' % (evenement.titre,
+                                                    SITE_ROOT_URL,
+                                                    evenement.get_absolute_url())
+                content += u'    où ? : %s, %s, %s  \n' % (evenement.adresse, evenement.ville, evenement.pays and evenement.pays.nom)
+                content += evenement.debut.strftime(u'    quand ? : %d/%m/%Y %H:%M  \n')
+                content += u'    durée ? : %s\n\n' % evenement.duration_display()
+                content += u'    quoi ? : '
+                content += '\n             '.join(textwrap.wrap(evenement.description))
+                content += '\n\n'
+        if results.sites:
+            content += u"\n### Nouveaux sites\n\n"
+            for site in results.sites:
+                content += u'-   [%s](%s%s)\n\n' % (site.titre,
+                                                    SITE_ROOT_URL,
+                                                    site.get_absolute_url())
+                if site.description:
+                    content += '\n'
+                    content += ''.join(['    %s\n' % line for line in textwrap.wrap(site.description)])
+                    content += '\n'
+        return content
+
 class RessourceSearch(Search):
     auteur = models.CharField(max_length=100, blank=True, verbose_name="auteur ou contributeur")
     titre = models.CharField(max_length=100, blank=True)
@@ -771,7 +881,7 @@ class RessourceSearch(Search):
         verbose_name = 'recherche de ressources'
         verbose_name_plural = "recherches de ressources"
 
-    def run(self):
+    def run(self, min_date=None, max_date=None):
         results = Record.objects
         if self.q:
             results = results.search(self.q)
@@ -787,6 +897,10 @@ class RessourceSearch(Search):
             results = results.filter_discipline(self.discipline)
         if self.region:
             results = results.filter_region(self.region)
+        if min_date:
+            results = results.filter_modified(min=min_date)
+        if max_date:
+            results = results.filter_modified(max=max_date)
         if not self.q:
             """Montrer les résultats les plus récents si on n'a pas fait
                une recherche par mots-clés."""
@@ -801,6 +915,18 @@ class RessourceSearch(Search):
         qs = self.query_string()
         return reverse('rss_ressources') + ('?' + qs if qs else '')
 
+    def get_email_alert_content(self, results):
+        content = ''
+        for ressource in results:
+            content += u'-   [%s](%s%s)\n\n' % (ressource.title,
+                                                SITE_ROOT_URL,
+                                                ressource.get_absolute_url())
+            if ressource.description:
+                content += '\n'
+                content += ''.join(['    %s\n' % line for line in textwrap.wrap(ressource.description)])
+                content += '\n'
+        return content
+
 class ActualiteSearchBase(Search):
     date_min = models.DateField(blank=True, null=True, verbose_name="depuis le")
     date_max = models.DateField(blank=True, null=True, verbose_name="jusqu'au")
@@ -808,7 +934,7 @@ class ActualiteSearchBase(Search):
     class Meta:
         abstract = True
 
-    def run(self):
+    def run(self, min_date=None, max_date=None):
         results = Actualite.objects
         if self.q:
             results = results.search(self.q)
@@ -820,16 +946,32 @@ class ActualiteSearchBase(Search):
             results = results.filter_date(min=self.date_min)
         if self.date_max:
             results = results.filter_date(max=self.date_max)
+        if min_date:
+            results = results.filter_date(min=min_date)
+        if max_date:
+            results = results.filter_date(max=max_date)
         return results.all()
 
+    def get_email_alert_content(self, results):
+        content = ''
+        for actualite in results:
+            content += u'-  [%s](%s%s)\n\n' % (actualite.titre,
+                                               SITE_ROOT_URL,
+                                               actualite.get_absolute_url())
+            if actualite.texte:
+                content += '\n'
+                content += ''.join(['    %s\n' % line for line in textwrap.wrap(actualite.texte)])
+                content += '\n'
+        return content
+
 class ActualiteSearch(ActualiteSearchBase):
 
     class Meta:
         verbose_name = "recherche d'actualités"
         verbose_name_plural = "recherches d'actualités"
         
-    def run(self):
-        return super(ActualiteSearch, self).run().filter_type('actu')
+    def run(self, min_date=None, max_date=None):
+        return super(ActualiteSearch, self).run(min_date=min_date, max_date=max_date).filter_type('actu')
 
     def url(self):
         qs = self.query_string()
@@ -845,8 +987,8 @@ class AppelSearch(ActualiteSearchBase):
         verbose_name = "recherche d'appels d'offres"
         verbose_name_plural = "recherches d'appels d'offres"
 
-    def run(self):
-        return super(AppelSearch, self).run().filter_type('appel')
+    def run(self, min_date=None, max_date=None):
+        return super(AppelSearch, self).run(min_date=min_date, max_date=max_date).filter_type('appels')
 
     def url(self):
         qs = self.query_string()
@@ -866,7 +1008,7 @@ class EvenementSearch(Search):
         verbose_name = "recherche d'évènements"
         verbose_name_plural = "recherches d'évènements"
 
-    def run(self):
+    def run(self, min_date=None, max_date=None):
         results = Evenement.objects
         if self.q:
             results = results.search(self.q)
@@ -882,6 +1024,10 @@ class EvenementSearch(Search):
             results = results.filter_debut(min=self.date_min)
         if self.date_max:
             results = results.filter_debut(max=self.date_max)
+        if min_date:
+            results = results.filter_debut(min=min_date)
+        if max_date:
+            results = results.filter_debut(max=max_date)
         return results.all()
 
     def url(self):
@@ -891,3 +1037,17 @@ class EvenementSearch(Search):
     def rss_url(self):
         qs = self.query_string()
         return reverse('rss_agenda') + ('?' + qs if qs else '')
+
+    def get_email_alert_content(self, results):
+        content = ''
+        for evenement in results:
+            content += u'-   [%s](%s%s)  \n' % (evenement.titre,
+                                                SITE_ROOT_URL,
+                                                evenement.get_absolute_url())
+            content += u'    où ? : %s, %s, %s  \n' % (evenement.adresse, evenement.ville, evenement.pays and evenement.pays.nom)
+            content += evenement.debut.strftime(u'    quand ? : %d/%m/%Y %H:%M  \n')
+            content += u'    durée ? : %s\n\n' % evenement.duration_display()
+            content += u'    quoi ? : '
+            content += '\n             '.join(textwrap.wrap(evenement.description))
+            content += '\n\n'
+        return content
index 269efc5..2b243c4 100644 (file)
@@ -21,7 +21,7 @@ class FilChercheurs(Feed):
 
     def items(self, search):
         min_date = date.today() - timedelta(days=30)
-        return search.run().order_by('-date_modification').filter_date_modification(min=min_date)
+        return search.run(min_date=min_date).order_by('-date_modification')
 
     def item_title(self, chercheur):
         return unicode(chercheur)
@@ -47,7 +47,7 @@ class FilRessources(Feed):
 
     def items(self, search):
         min_date = date.today() - timedelta(days=30)
-        return search.run().order_by('-modified').filter_modified(min=min_date)
+        return search.run(min_date=min_date).order_by('-modified')
 
     def item_title(self, ressource):
         return ressource.title
@@ -75,7 +75,7 @@ class FilActualitesBase(Feed):
 
     def items(self, search):
         min_date = date.today() - timedelta(days=30)
-        return search.run().filter_date(min=min_date).order_by('-date')
+        return search.run(min_date=min_date).order_by('-date')
 
     def item_title(self, actualite):
         return actualite.titre
@@ -119,7 +119,7 @@ class FilEvenements(Feed):
     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')
+        return search.run(min_date=min_date, max_date=max_date).order_by('-debut')
 
     def item_title(self, evenement):
         return evenement.titre
@@ -140,8 +140,8 @@ class FilSites(Feed):
         return search_form.save(commit=False)
 
     def items(self, search):
-        min_date = date.today() - timedelta(days=365)
-        return search.run().filter_date_maj(min=min_date)
+        min_date = date.today() - timedelta(days=30)
+        return search.run(min_date=min_date)
 
     def item_title(self, site):
         return site.titre
index 6eb6994..b745e27 100644 (file)
@@ -340,6 +340,22 @@ def supprimer_recherche(request, id):
     }, context_instance=RequestContext(request))
 
 @login_required
+def activer_alerte(request, id):
+    """Activer une alerte courriel"""
+    recherche = get_object_or_404(Search, id=id, user=request.user)
+    recherche.alerte_courriel = True
+    recherche.save()
+    return HttpResponseRedirect(reverse('recherches'))
+
+@login_required
+def desactiver_alerte(request, id):
+    """Désactiver une alerte courriel"""
+    recherche = get_object_or_404(Search, id=id, user=request.user)
+    recherche.alerte_courriel = False
+    recherche.save()
+    return HttpResponseRedirect(reverse('recherches'))
+
+@login_required
 def evenement_moderation(request):
     events = Evenement.objects.filter(approuve = False)
     return render_to_response ("savoirs/evenement_moderation.html", \
diff --git a/auf_savoirs_en_partage/scripts/alertes_courriel.in b/auf_savoirs_en_partage/scripts/alertes_courriel.in
new file mode 100644 (file)
index 0000000..39c5614
--- /dev/null
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+# -*- encoding: utf-8 -*-
+
+import sys, os, glob, time
+sys.path[0:0] = [
+  "%(directory)s",
+  "%(directory)s/auf_savoirs_en_partage",
+  ]
+os.environ['DJANGO_SETTINGS_MODULE'] = 'production'
+for d in glob.glob("%(directory)s/eggs/*"):
+    sys.path[0:0] = [d,]
+for d in glob.glob("%(directory)s/parts/*"):
+    sys.path[0:0] = [d,]
+
+from savoirs.models import Search
+
+recherches = Search.objects.filter(alerte_courriel=True)
+for recherche in recherches:
+    recherche.send_email_alert()
index 3a97ced..c48eee9 100644 (file)
@@ -13,4 +13,4 @@ class SiteSearchForm(forms.ModelForm):
 class SiteSearchEditForm(SiteSearchForm):
 
     class Meta(SiteSearchForm.Meta):
-        fields = ['nom'] + SiteSearchForm.Meta.fields
+        fields = ['nom', 'alerte_courriel'] + SiteSearchForm.Meta.fields
index 68691b2..6182738 100644 (file)
@@ -1,4 +1,7 @@
 # -*- encoding: utf-8 -*-
+import textwrap
+
+from django.conf import settings
 from django.core.urlresolvers import reverse
 from django.db import models
 from django.db.models import Q
@@ -109,7 +112,7 @@ class SiteSearch(Search):
         verbose_name = "recherche de sites"
         verbose_name_plural = "recherches de sites"
 
-    def run(self):
+    def run(self, min_date=None, max_date=None):
         results = Site.objects
         if self.q:
             results = results.search(self.q)
@@ -121,6 +124,10 @@ class SiteSearch(Search):
             results = results.filter_pays(pays=self.pays)
         if not self.q:
             results = results.order_by('-date_maj')
+        if min_date:
+            results = results.filter_date_maj(min=min_date)
+        if max_date:
+            results = results.filter_date_maj(max=max_date)
         return results.all()
 
     def url(self):
@@ -130,3 +137,15 @@ class SiteSearch(Search):
     def rss_url(self):
         qs = self.query_string()
         return reverse('rss_sites') + ('?' + qs if qs else '')
+
+    def get_email_alert_content(self, results):
+        content = ''
+        for site in results:
+            content += u'-   [%s](%s%s)\n\n' % (site.titre,
+                                                settings.SITE_ROOT_URL,
+                                                site.get_absolute_url())
+            if site.description:
+                content += '\n'
+                content += ''.join(['    %s\n' % line for line in textwrap.wrap(site.description)])
+                content += '\n'
+        return content
index 68acff9..303dbf1 100644 (file)
             {% if recherche.rss_url %}
             <a href="{{ recherche.rss_url }}">Fil RSS</a>
             {% endif %}
+            {% if recherche.alerte_courriel %}
+            <a href="{% url desactiver_alerte recherche.id %}" class="alerte active">Alerte courriel</a>
+            {% else %}
+            <a href="{% url activer_alerte recherche.id %}" class="alerte">Alerte courriel</a>
+            {% endif %}
             <a href="{% url supprimer_recherche recherche.id %}">Supprimer</a>
             <a href="{% url editer_recherche recherche.id %}">Modifier</a>
         </div>
index 5215f36..77f7425 100644 (file)
@@ -1,4 +1,4 @@
+<div>où ? : {{ obj.adresse }}, {{ obj.ville }}, {{ obj.pays.nom }}</div>
+<div>quand ? : {{ obj.debut|date:"d F Y H:m" }}</div>
+<div>durée ? : {{ obj.duration_display }}</div>
 <p>quoi ? : {{ obj.description }}</p>
-<p>où ? : {{ obj.adresse }}, {{ obj.ville }}, {{ obj.pays.nom }}</p>
-<p>quand ? : {{ obj.debut|date:"d F Y H:m" }}</p>
-<p>durée ? : {{ obj.duration_display }}</p>
index 17c24ea..215df62 100644 (file)
@@ -138,6 +138,8 @@ urlpatterns = sep_patterns + patterns(
     (r'^recherches/(?P<type>[^/]*)/sauvegarder/$', 'savoirs.views.sauvegarder_recherche', {}, 'sauvegarder_recherche'),
     (r'^recherches/(?P<id>\d+)/supprimer/$', 'savoirs.views.supprimer_recherche', {}, 'supprimer_recherche'),
     (r'^recherches/(?P<id>\d+)/editer/$', 'savoirs.views.editer_recherche', {}, 'editer_recherche'),
+    (r'^recherches/(?P<id>\d+)/activer-alerte/$', 'savoirs.views.activer_alerte', {}, 'activer_alerte'),
+    (r'^recherches/(?P<id>\d+)/desactiver-alerte/$', 'savoirs.views.desactiver_alerte', {}, 'desactiver_alerte'),
 )
 
 if settings.DEBUG:
index bc94421..c4841af 100644 (file)
@@ -1,6 +1,6 @@
 [buildout]
 newest = false
-parts = django articles harvest sphinx_config
+parts = django articles harvest sphinx_config alertes_courriel
 find-links = http://pypi.auf.org/caldav/
     http://pypi.auf.org/auf_references_client/
     http://pypi.auf.org/auf_references_modeles/
@@ -35,6 +35,7 @@ eggs = auf_references_client==0.4.9
     South==0.7.3
     django_exportateur==1.0
     python-dateutil==1.5
+    markdown2
     auf.django.admingroup
     MySQL-python
     simplejson
@@ -60,3 +61,9 @@ template = import_chercheurs.in
 recipe = buildout_script
 template_dir = ${buildout:directory}/auf_savoirs_en_partage/scripts/
 template = sphinx.conf.py.in
+
+[alertes_courriel]
+recipe = buildout_script
+template_dir = ${buildout:directory}/auf_savoirs_en_partage/scripts/
+template = alertes_courriel.in
+