Recherches sauvegardées
authorEric Mc Sween <eric.mcsween@gmail.com>
Wed, 9 Mar 2011 19:17:17 +0000 (14:17 -0500)
committerEric Mc Sween <eric.mcsween@gmail.com>
Wed, 9 Mar 2011 19:17:17 +0000 (14:17 -0500)
Demande #1178

28 files changed:
auf_savoirs_en_partage/chercheurs/forms.py
auf_savoirs_en_partage/chercheurs/migrations/0006_auto__add_chercheursearch.py [new file with mode: 0644]
auf_savoirs_en_partage/chercheurs/models.py
auf_savoirs_en_partage/chercheurs/views.py
auf_savoirs_en_partage/media/css/global.css
auf_savoirs_en_partage/savoirs/fixtures/tests.yaml
auf_savoirs_en_partage/savoirs/forms.py
auf_savoirs_en_partage/savoirs/migrations/0010_auto__add_search__add_appelsearch__add_evenementsearch__add_actualites.py [new file with mode: 0644]
auf_savoirs_en_partage/savoirs/models.py
auf_savoirs_en_partage/savoirs/tests.py
auf_savoirs_en_partage/savoirs/views.py
auf_savoirs_en_partage/sitotheque/forms.py
auf_savoirs_en_partage/sitotheque/migrations/0003_auto__add_sitesearch.py [new file with mode: 0644]
auf_savoirs_en_partage/sitotheque/models.py
auf_savoirs_en_partage/sitotheque/views.py
auf_savoirs_en_partage/templates/chercheurs/index.html
auf_savoirs_en_partage/templates/chercheurs/perso.html
auf_savoirs_en_partage/templates/savoirs/actualite_index.html
auf_savoirs_en_partage/templates/savoirs/appels_index.html
auf_savoirs_en_partage/templates/savoirs/editer_recherche.html [new file with mode: 0644]
auf_savoirs_en_partage/templates/savoirs/evenement_index.html
auf_savoirs_en_partage/templates/savoirs/recherche.html
auf_savoirs_en_partage/templates/savoirs/recherches.html [new file with mode: 0644]
auf_savoirs_en_partage/templates/savoirs/ressource_index.html
auf_savoirs_en_partage/templates/savoirs/sauvegarder_recherche.html [new file with mode: 0644]
auf_savoirs_en_partage/templates/savoirs/supprimer_recherche.html [new file with mode: 0644]
auf_savoirs_en_partage/templates/sites/index.html
auf_savoirs_en_partage/urls.py

index a737296..e656c38 100644 (file)
@@ -317,81 +317,21 @@ class ChercheurFormGroup(object):
             self.expertises.save()
             return self.chercheur.instance
 
-class RepertoireSearchForm (forms.Form):
-    q = forms.CharField(required=False, label="Rechercher dans tous les champs")
-    nom = forms.CharField(required=False, label="Nom")
-    domaine = forms.ModelChoiceField(queryset=Groupe.objects.all(), required=False, label="Domaine de recherche", empty_label="Tous")
-    groupe_recherche = forms.CharField(required=False, label="Groupe de recherche",
-                                       help_text="ou Laboratoire, ou Groupement inter-universitaire")
-    statut = forms.ChoiceField(choices=(('','Tous'),)+STATUT_CHOICES+(('expert','Expert'),), required=False, label="Statut")
-    discipline = forms.ModelChoiceField(queryset=Discipline.objects.all(), required=False, label="Discipline", empty_label="Toutes")
-    pays = forms.ModelChoiceField(queryset=Pays.objects.all(), required=False, label="Pays", empty_label="Tous")
-    region = forms.ModelChoiceField(queryset=Region.objects.all(), required=False, label="Région", empty_label="Toutes",
-                                    help_text="La région est ici définie au sens, non strictement géographique, du Bureau régional de l'AUF de référence.")
-    nord_sud = forms.ChoiceField(choices=(('', 'Tous'), ('Nord', 'Nord'), ('Sud', 'Sud')), required=False, label="Nord/Sud",
-                                 help_text="Distinction d'ordre géopolitique et économique, non géographique, qui conditionne souvent l'attribution de soutiens par les agences internationales: on entend par Nord les pays développés, par Sud les pays en développement (pays les moins avancés, pays émergents et pays à économies en transition)")
-    activites_francophonie = forms.ChoiceField(required=False, label="Activités en Francophonie", choices=(
-        ('', '---------'),
-        ('instance_auf', "Membre d'une instance de l'AUF"),
-        ('expert_oif', "Sollicité par l'OIF"),
-        ('association_francophone', "Membre d'une association ou d'une société savante francophone"),
-        ('reseau_institutionnel', "Membre des instances d'un réseau institutionnel de l'AUF")
-    ))
-    genre = forms.ChoiceField(choices=((('', 'Tous'),) + GENRE_CHOICES), required=False, label="Genre")
-
-    def __init__(self, data=None, region=None):
-        super(RepertoireSearchForm, self).__init__(data)
-        if region:
-            pays = self.fields['pays']
-            pays.queryset = pays.queryset.filter(region=region)
-
-    def get_query_set(self):
-        chercheurs = Chercheur.objects
-        if self.is_valid():
-            q = self.cleaned_data["q"]
-            if q:
-                chercheurs = chercheurs.search(q)
-            nom = self.cleaned_data['nom']
-            if nom:
-                chercheurs = chercheurs.add_to_query('@(nom,prenom) ' + nom)
-            groupe_recherche = self.cleaned_data['groupe_recherche']
-            if groupe_recherche:
-                chercheurs = chercheurs.add_to_query('@groupe_recherche ' + groupe_recherche)
-            discipline = self.cleaned_data['discipline']
-            if discipline:
-                chercheurs = chercheurs.filter_discipline(discipline)
-            region = self.cleaned_data['region']
-            if region:
-                chercheurs = chercheurs.filter_region(region)
-            statut = self.cleaned_data["statut"]
-            if statut:
-                if statut == "expert":
-                    chercheurs = chercheurs.filter_expert()
-                else:
-                    chercheurs = chercheurs.filter_statut(statut)
-            domaine = self.cleaned_data["domaine"]
-            if domaine:
-                chercheurs = chercheurs.filter_groupe(domaine)
-            pays = self.cleaned_data["pays"]
-            if pays:
-                chercheurs = chercheurs.filter_pays(pays)
-            nord_sud = self.cleaned_data['nord_sud']
-            if nord_sud:
-                chercheurs = chercheurs.filter_nord_sud(nord_sud)
-            genre = self.cleaned_data['genre']
-            if genre:
-                chercheurs = chercheurs.filter_genre(genre)
-            activites_francophonie = self.cleaned_data['activites_francophonie']
-            if activites_francophonie == 'instance_auf':
-                chercheurs = chercheurs.filter(membre_instance_auf=True)
-            elif activites_francophonie == 'expert_oif':
-                chercheurs = chercheurs.filter(expert_oif=True)
-            elif activites_francophonie == 'association_francophone':
-                chercheurs = chercheurs.filter(membre_association_francophone=True)
-            elif activites_francophonie == 'reseau_institutionnel':
-                chercheurs = chercheurs.filter(membre_reseau_institutionnel=True)
-        return chercheurs.all()
-    
+class ChercheurSearchForm(forms.ModelForm):
+    """Formulaire de recherche pour les chercheurs."""
+
+    class Meta:
+        model = ChercheurSearch
+        fields = ['q', 'nom_chercheur', 'domaine', 'groupe_recherche', 'statut',
+                  'discipline', 'pays', 'region', 'nord_sud',
+                  'activites_francophonie', 'genre']
+
+class ChercheurSearchEditForm(ChercheurSearchForm):
+    """Formulaire d'édition d'une recherche sauvegardée."""
+
+    class Meta(ChercheurSearchForm.Meta):
+        fields = ['nom'] + ChercheurSearchForm.Meta.fields
+
 class SendPasswordForm(forms.Form):
     email = forms.EmailField(required=True, label="Adresse électronique")
     def clean_email(self):
@@ -419,3 +359,4 @@ class SetPasswordForm(forms.Form):
 
 class AuthenticationForm(DjangoAuthenticationForm):
     username = forms.CharField(label='Courriel')
+
diff --git a/auf_savoirs_en_partage/chercheurs/migrations/0006_auto__add_chercheursearch.py b/auf_savoirs_en_partage/chercheurs/migrations/0006_auto__add_chercheursearch.py
new file mode 100644 (file)
index 0000000..67ef17b
--- /dev/null
@@ -0,0 +1,320 @@
+# 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 model 'ChercheurSearch'
+        db.create_table('chercheurs_chercheursearch', (
+            ('search_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['savoirs.Search'], unique=True, primary_key=True)),
+            ('nom_chercheur', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)),
+            ('domaine', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['chercheurs.Groupe'], null=True, blank=True)),
+            ('groupe_recherche', self.gf('django.db.models.fields.CharField')(max_length=100, null=True, blank=True)),
+            ('statut', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)),
+            ('pays', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['datamaster_modeles.Pays'], null=True, blank=True)),
+            ('nord_sud', self.gf('django.db.models.fields.CharField')(max_length=4, blank=True)),
+            ('activites_francophonie', self.gf('django.db.models.fields.CharField')(max_length=25, blank=True)),
+            ('genre', self.gf('django.db.models.fields.CharField')(max_length=1, blank=True)),
+        ))
+        db.send_create_signal('chercheurs', ['ChercheurSearch'])
+
+
+    def backwards(self, orm):
+        
+        # Deleting model 'ChercheurSearch'
+        db.delete_table('chercheurs_chercheursearch')
+
+
+    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', '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'})
+        },
+        'chercheurs.chercheur': {
+            'Meta': {'ordering': "['nom', 'prenom']", 'object_name': 'Chercheur', '_ormbases': ['chercheurs.Personne']},
+            'attestation': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'date_creation': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'db_column': "'date_creation'", 'blank': 'True'}),
+            'date_modification': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'db_column': "'date_modification'", 'blank': 'True'}),
+            'diplome': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+            'discipline': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['savoirs.Discipline']", 'null': 'True', 'db_column': "'discipline'"}),
+            'etablissement': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['datamaster_modeles.Etablissement']", 'null': 'True', 'db_column': "'etablissement'", 'blank': 'True'}),
+            'etablissement_autre_nom': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'etablissement_autre_pays': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'etablissement_autre_pays'", 'null': 'True', 'db_column': "'etablissement_autre_pays'", 'to': "orm['datamaster_modeles.Pays']"}),
+            'expert_oif': ('django.db.models.fields.NullBooleanField', [], {'null': 'True'}),
+            'expert_oif_dates': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'expert_oif_details': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'expertises_auf': ('django.db.models.fields.NullBooleanField', [], {'null': 'True'}),
+            'groupe_recherche': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'groupes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['chercheurs.Groupe']", 'symmetrical': 'False', 'through': "'ChercheurGroupe'", 'blank': 'True'}),
+            'membre_association_francophone': ('django.db.models.fields.NullBooleanField', [], {'null': 'True'}),
+            'membre_association_francophone_details': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'membre_instance_auf': ('django.db.models.fields.NullBooleanField', [], {'null': 'True'}),
+            'membre_instance_auf_dates': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'membre_instance_auf_fonction': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'membre_instance_auf_nom': ('django.db.models.fields.CharField', [], {'max_length': '10', 'blank': 'True'}),
+            'membre_reseau_institutionnel': ('django.db.models.fields.NullBooleanField', [], {'null': 'True'}),
+            'membre_reseau_institutionnel_dates': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'membre_reseau_institutionnel_fonction': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'membre_reseau_institutionnel_nom': ('django.db.models.fields.CharField', [], {'max_length': '15', 'blank': 'True'}),
+            'mots_cles': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+            'nationalite': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nationalite'", 'null': 'True', 'db_column': "'nationalite'", 'to': "orm['datamaster_modeles.Pays']"}),
+            'personne_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['chercheurs.Personne']", 'unique': 'True', 'primary_key': 'True'}),
+            'statut': ('django.db.models.fields.CharField', [], {'max_length': '36'}),
+            'thematique': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['datamaster_modeles.Thematique']", 'null': 'True', 'db_column': "'thematique'"}),
+            'theme_recherche': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'url_blog': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'url_reseau_social': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'url_site_web': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'chercheurs.chercheurgroupe': {
+            'Meta': {'object_name': 'ChercheurGroupe'},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_column': "'actif'"}),
+            'chercheur': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chercheurs.Chercheur']", 'db_column': "'chercheur'"}),
+            'date_inscription': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'date_modification': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'blank': 'True'}),
+            'groupe': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chercheurs.Groupe']", 'db_column': "'groupe'"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True', 'db_column': "'id'"})
+        },
+        'chercheurs.chercheursearch': {
+            'Meta': {'object_name': 'ChercheurSearch', '_ormbases': ['savoirs.Search']},
+            'activites_francophonie': ('django.db.models.fields.CharField', [], {'max_length': '25', 'blank': 'True'}),
+            'domaine': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chercheurs.Groupe']", 'null': 'True', 'blank': 'True'}),
+            'genre': ('django.db.models.fields.CharField', [], {'max_length': '1', 'blank': 'True'}),
+            'groupe_recherche': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+            'nom_chercheur': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'nord_sud': ('django.db.models.fields.CharField', [], {'max_length': '4', 'blank': 'True'}),
+            'pays': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['datamaster_modeles.Pays']", 'null': 'True', 'blank': 'True'}),
+            'search_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['savoirs.Search']", 'unique': 'True', 'primary_key': 'True'}),
+            'statut': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'})
+        },
+        'chercheurs.expertise': {
+            'Meta': {'object_name': 'Expertise'},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_column': "'actif'"}),
+            'chercheur': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'expertises'", 'to': "orm['chercheurs.Chercheur']"}),
+            'date': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True', 'db_column': "'id'"}),
+            'lieu': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'organisme_demandeur': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'organisme_demandeur_visible': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+        },
+        'chercheurs.groupe': {
+            'Meta': {'object_name': 'Groupe'},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_column': "'actif'"}),
+            'bulletin': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True', 'db_column': "'id'"}),
+            'liste_diffusion': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'nom'"}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'chercheurs.personne': {
+            'Meta': {'ordering': "['nom', 'prenom']", 'object_name': 'Personne'},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'adresse_postale': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'afficher_courriel': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'commentaire': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'courriel': ('django.db.models.fields.EmailField', [], {'max_length': '128'}),
+            'date_naissance': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'fonction': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'genre': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'prenom': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'salutation': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'sousfonction': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'telephone': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'})
+        },
+        'chercheurs.publication': {
+            'Meta': {'object_name': 'Publication'},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'annee': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'auteurs': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'chercheur': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publications'", 'to': "orm['chercheurs.Chercheur']"}),
+            'editeur': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'lieu_edition': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'nb_pages': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'publication_affichage': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'revue': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'titre': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'chercheurs.these': {
+            'Meta': {'object_name': 'These'},
+            'annee': ('django.db.models.fields.IntegerField', [], {}),
+            'chercheur': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['chercheurs.Chercheur']", 'unique': 'True', 'primary_key': 'True'}),
+            'directeur': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'etablissement': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'nb_pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'titre': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'blank': 'True'})
+        },
+        '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.etablissement': {
+            'Meta': {'ordering': "('nom',)", 'object_name': 'Etablissement', 'db_table': "u'ref_etablissement'"},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'adresse': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'cedex': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}),
+            'code_implantation': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'code_gere_etablissement'", 'to_field': "'code'", 'db_column': "'code_implantation'", 'to': "orm['datamaster_modeles.Implantation']"}),
+            'code_postal': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}),
+            'fax': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}),
+            'implantation': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'gere_etablissement'", 'db_column': "'implantation'", 'to': "orm['datamaster_modeles.Implantation']"}),
+            'membre': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'membre_adhesion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'pays': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['datamaster_modeles.Pays']", 'db_column': "'pays'"}),
+            'province': ('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'"}),
+            'responsable_genre': ('django.db.models.fields.CharField', [], {'max_length': '1', 'blank': 'True'}),
+            'responsable_nom': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'responsable_prenom': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'telephone': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'ville': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
+        },
+        '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'}),
+            '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'}),
+            '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'}),
+            '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.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.search': {
+            'Meta': {'object_name': 'Search'},
+            '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']"})
+        }
+    }
+
+    complete_apps = ['chercheurs']
index a6cc1ed..ac094cc 100644 (file)
@@ -1,13 +1,17 @@
 # -*- encoding: utf-8 -*-
 import hashlib
+
 from datamaster_modeles.models import *
 from django.conf import settings
+from django.contrib.auth.models import User
+from django.core.urlresolvers import reverse as url
 from django.db import models
 from django.db.models import Q
 from django.utils.encoding import smart_str
 from django.utils.hashcompat import sha_constructor
 from djangosphinx.models import SphinxSearch
-from savoirs.models import Discipline, SEPManager, SEPSphinxQuerySet, SEPQuerySet
+
+from savoirs.models import Discipline, SEPManager, SEPSphinxQuerySet, SEPQuerySet, Search
 
 GENRE_CHOICES = (('m', 'Homme'), ('f', 'Femme'))
 class Personne(models.Model):
@@ -367,3 +371,66 @@ class ChercheurGroupe(models.Model):
 
     def __unicode__(self):
         return u"%s - %s" % (self.chercheur, self.groupe)
+
+class ChercheurSearch(Search):
+    nom_chercheur = models.CharField(max_length=100, blank=True, verbose_name='nom')
+    domaine = models.ForeignKey(Groupe, blank=True, null=True, verbose_name='domaine de recherche')
+    groupe_recherche = models.CharField(max_length=100, blank=True, null=True, 
+                                        verbose_name='groupe de recherche',
+                                        help_text='ou Laboratoire, ou Groupement inter-universitaire')
+    statut = models.CharField(max_length=100, blank=True, choices=STATUT_CHOICES + (('expert', 'Expert'),))
+    pays = models.ForeignKey(Pays, blank=True, null=True)
+    nord_sud = models.CharField(max_length=4, blank=True, choices=(('Nord', 'Nord'), ('Sud', 'Sud')),
+                                verbose_name='Nord/Sud',
+                                help_text="Distinction d'ordre géopolitique et économique, non géographique, qui conditionne souvent l'attribution de soutiens par les agences internationales: on entend par Nord les pays développés, par Sud les pays en développement (pays les moins avancés, pays émergents et pays à économies en transition)")
+    activites_francophonie = models.CharField(
+        max_length=25, blank=True, verbose_name='activités en Francophonie',
+        choices=(('instance_auf', "Membre d'une instance de l'AUF"),
+                 ('expert_oif', "Sollicité par l'OIF"),
+                 ('association_francophone', "Membre d'une association ou d'une société savante francophone"),
+                 ('reseau_institutionnel', "Membre des instances d'un réseau institutionnel de l'AUF"))
+    ) 
+    genre = models.CharField(max_length=1, blank=True, choices=GENRE_CHOICES)
+
+    class Meta:
+        verbose_name = 'recherche de chercheurs'
+        verbose_name_plural = 'recherches de chercheurs'
+
+    def run(self):
+        results = Chercheur.objects
+        if self.q:
+            results = results.search(self.q)
+        if self.nom_chercheur:
+            results = results.add_to_query('@(nom,prenom) ' + self.nom_chercheur)
+        if self.groupe_recherche:
+            results = results.add_to_query('@groupe_recherche ' + self.groupe_recherche)
+        if self.discipline:
+            results = results.filter_discipline(self.discipline)
+        if self.region:
+            results = results.filter_region(self.region)
+        if self.statut:
+            if self.statut == "expert":
+                results = results.filter_expert()
+            else:
+                results = results.filter_statut(self.statut)
+        if self.domaine:
+            results = results.filter_groupe(self.domaine)
+        if self.pays:
+            results = results.filter_pays(self.pays)
+        if self.nord_sud:
+            results = results.filter_nord_sud(self.nord_sud)
+        if self.genre:
+            results = results.filter_genre(self.genre)
+        if self.activites_francophonie == 'instance_auf':
+            results = results.filter(membre_instance_auf=True)
+        elif self.activites_francophonie == 'expert_oif':
+            results = results.filter(expert_oif=True)
+        elif self.activites_francophonie == 'association_francophone':
+            results = results.filter(membre_association_francophone=True)
+        elif self.activites_francophonie == 'reseau_institutionnel':
+            results = results.filter(membre_reseau_institutionnel=True)
+        return results.all()
+
+    def url(self):
+        qs = self.query_string()
+        return url('chercheurs') + ('?' + qs if qs else '')
index 161b645..0a356d0 100644 (file)
@@ -1,30 +1,32 @@
 # -*- encoding: utf-8 -*-
 from chercheurs.decorators import chercheur_required
-from chercheurs.forms import RepertoireSearchForm, SetPasswordForm, ChercheurFormGroup, AuthenticationForm
+from chercheurs.forms import ChercheurSearchForm, SetPasswordForm, ChercheurFormGroup, AuthenticationForm
 from chercheurs.models import Chercheur
 from chercheurs.utils import get_django_user_for_email
-from datamaster_modeles.models import Etablissement
+from datamaster_modeles.models import Etablissement, Region
 from django.conf import settings
 from django.shortcuts import render_to_response
-from django.http import HttpResponseRedirect, HttpResponse
+from django.http import HttpResponseRedirect, HttpResponse, HttpResponseForbidden, HttpResponseNotFound
 from django.template import Context, RequestContext
 from django.template.loader import get_template
 from django.core.urlresolvers import reverse as url
 from django.core.mail import send_mail
 from django.contrib.auth import REDIRECT_FIELD_NAME
 from django.contrib.auth import login as auth_login
+from django.contrib.auth.decorators import login_required
 from django.contrib.sites.models import RequestSite, Site
 from django.utils import simplejson
 from django.utils.http import int_to_base36, base36_to_int
 from django.views.decorators.cache import never_cache
 from django.contrib.auth import authenticate, login
 from django.shortcuts import get_object_or_404
-from savoirs.models import PageStatique
+from savoirs.models import PageStatique, Discipline
 
 def index(request):
     """Répertoire des chercheurs"""
-    search_form = RepertoireSearchForm(request.GET)
-    chercheurs = search_form.get_query_set().select_related('etablissement')
+    search_form = ChercheurSearchForm(request.GET)
+    search = search_form.save(commit=False)
+    chercheurs = search.run().select_related('etablissement')
     sort = request.GET.get('tri')
     if sort is not None and sort.endswith('_desc'):
         sort = sort[:-5]
@@ -47,6 +49,7 @@ def index(request):
         entete = u'<h1>Répertoire des chercheurs</h1>'
 
     nb_chercheurs = chercheurs.count()
+
     return render_to_response("chercheurs/index.html",
                               dict(chercheurs=chercheurs, nb_chercheurs=nb_chercheurs, 
                                    search_form=search_form, entete=entete),
@@ -206,4 +209,3 @@ def login(request, template_name='registration/login.html', redirect_field_name=
         'site_name': current_site.name,
     }, context_instance=RequestContext(request))
 login = never_cache(login)
-
index c536739..097cd3e 100644 (file)
@@ -57,7 +57,7 @@ body { padding: 0; margin: 0; font-size: 9pt; font-family: Verdana, Arial, sans-
                     
 #flash-message { margin: 10px 20px; background: #f3e3e7; color: #b03d5e; padding: 1em; font-size: 120%;
                  border: 1px solid #b03d5e; -moz-border-radius: 5px; -webkit-border-radius: 5px; }
-#contenu { position: relative; padding: 5px 20px; background: white; }
+#contenu { position: relative; padding: 5px 20px; }
 #contenu img.top, .resultats img.top { height:10px; position:relative; top:-10px; left:0;}
 #contenu img.bottom, .resultats img.bottom{ height:10px; position:relative; bottom:-10px; left:0;}
 
@@ -130,6 +130,9 @@ tr.odd { background:#ddd; }
 
 dt { font-weight: bold; }
 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; }
 
 /* Styles inline */
 
index c23a327..6cf8096 100644 (file)
     implantation_bureau: 1
     actif: 1
 
+- model: datamaster_modeles.Pays
+  pk: AO
+  fields:
+    id: 1
+    code_iso3: AGO
+    code_bureau: BAC
+    nom: Angola
+    region: 1
+    nord_sud: Sud
+    actif: 1
+
 - model: savoirs.Discipline
   pk: 1
   fields:
     date: "2010-01-02"
     visible: 1
     source: 2
+
+- model: chercheurs.Groupe
+  pk: 1
+  fields:
+    nom: "Littératures au Sud"
+    url: "www.llcd.auf.org/article2.html"
+    actif: 1
index 2fd2873..dcdc324 100644 (file)
@@ -7,7 +7,7 @@ from django.db import models
 from django.contrib.admin import widgets
 from django.utils.safestring import mark_safe
 from datamaster_modeles.models import Thematique, Pays, Region
-from models import Evenement, Discipline, Record, Actualite
+from savoirs.models import Evenement, Discipline, Record, Actualite, RessourceSearch, ActualiteSearch, EvenementSearch, Search
 from savoirs.lib.recherche import build_search_regexp
 from savoirs.admin import EvenementAdminForm
 import settings
@@ -24,7 +24,7 @@ class SEPDateField(forms.DateField):
         # Nous recevons les dates en format français
         format = '%d/%m/%Y'
         self.widget = forms.DateInput(attrs={'class': 'date'}, format=format)
-        self.input_formats = [format,]
+        self.input_formats = [format]
 
 class SEPSplitDateTimeWidget(forms.MultiWidget):
     
@@ -51,122 +51,51 @@ class SEPDateTimeField(forms.DateTimeField):
 
 # Formulaires de recherche
 
-class RecordSearchForm(forms.Form):
+class RessourceSearchForm(forms.ModelForm):
     """Formulaire de recherche pour les ressources."""
 
-    q = forms.CharField(required=False, label="Rechercher dans tous les champs")
-    auteur = forms.CharField(required=False, label="Auteur ou contributeur")
-    titre = forms.CharField(required=False, label="Titre")
-    sujet = forms.CharField(required=False, label="Sujet")
-    publisher = forms.CharField(required=False, label="Éditeur")
-    discipline = forms.ModelChoiceField(queryset=Discipline.objects.all(), required=False, label="Discipline", empty_label="Toutes")
-    region = forms.ModelChoiceField(queryset=Region.objects.all(), required=False, label="Région", empty_label="Toutes",
-                                    help_text="La région est ici définie au sens, non strictement géographique, du Bureau régional de l'AUF de référence.")
-
-    def get_query_set(self):
-        """Retourne l'ensemble des ressources qui correspondent aux valeurs
-           entrées dans le formulaire."""
-        records = Record.objects
-        if self.is_valid():
-            q = self.cleaned_data['q']
-            if q:
-                records = records.search(q)
-            auteur = self.cleaned_data['auteur']
-            if auteur:
-                records = records.add_to_query('@(creator,contributor) ' + auteur)
-            titre = self.cleaned_data['titre']
-            if titre:
-                records = records.add_to_query('@title ' + titre)
-            sujet = self.cleaned_data['sujet']
-            if sujet:
-                records = records.add_to_query('@subject ' + sujet)
-            publisher = self.cleaned_data['publisher']
-            if publisher:
-                records = records.add_to_query('@publisher ' + publisher)
-            discipline = self.cleaned_data['discipline']
-            if discipline:
-                records = records.filter_discipline(discipline)
-            region = self.cleaned_data['region']
-            if region:
-                records = records.filter_region(region)
-
-            if not q:
-                """Montrer les résultats les plus récents si on n'a pas fait
-                   une recherche par mots-clés."""
-                records = records.order_by('-id')
-        return records.all()
-
-class ActualiteSearchForm(forms.Form):
-    """Formulaire de recherche pour les actualités."""
+    class Meta:
+        model = RessourceSearch
+        fields = ['q', 'auteur', 'titre', 'sujet', 'publisher', 'discipline', 'region']
+
+class RessourceSearchEditForm(RessourceSearchForm):
+    """Formulaire d'édition de recherche sauvegardée."""
 
-    q = forms.CharField(required=False, label="Rechercher dans tous les champs")
-    date_min = SEPDateField(required=False, label="Depuis le")
+    class Meta(RessourceSearchForm.Meta):
+        fields = ['nom'] + RessourceSearchForm.Meta.fields
+
+class ActualiteSearchForm(forms.ModelForm):
+    """Formulaire de recherche pour les actualités."""
+    date_min = SEPDateField(required=False, label="Depuis le") 
     date_max = SEPDateField(required=False, label="Jusqu'au") 
-    discipline = forms.ModelChoiceField(queryset=Discipline.objects.all(), required=False, label="Discipline", empty_label="Toutes")
-    region = forms.ModelChoiceField(queryset=Region.objects.all(), required=False, label="Région", empty_label="Toutes",
-                                    help_text="La région est ici définie au sens, non strictement géographique, du Bureau régional de l'AUF de référence.")
-
-    def get_query_set(self):
-        """Retourne l'ensemble des actualités qui correspondent aux valeurs
-           entrées dans le formulaire."""
-        actualites = Actualite.objects
-        if self.is_valid():
-            q = self.cleaned_data['q']
-            if q:
-                actualites = actualites.search(q)
-            discipline = self.cleaned_data['discipline']
-            if discipline:
-                actualites = actualites.filter_discipline(discipline)
-            region = self.cleaned_data['region']
-            if region:
-                actualites = actualites.filter_region(region)
-            date_min = self.cleaned_data['date_min']
-            if date_min:
-                actualites = actualites.filter_date(min=date_min)
-            date_max = self.cleaned_data['date_max']
-            if date_max:
-                actualites = actualites.filter_date(max=date_max)
-        return actualites.all()
+
+    class Meta:
+        model = ActualiteSearch
+        fields = ['q', 'date_min', 'date_max', 'discipline', 'region']
     
-class EvenementSearchForm(forms.Form):
-    """Formulaire de recherche pour les évènements."""
+class ActualiteSearchEditForm(ActualiteSearchForm):
+
+    class Meta(ActualiteSearchForm.Meta):
+        fields = ['nom'] + ActualiteSearchForm.Meta.fields
 
-    q = forms.CharField(required=False, label="Rechercher dans tous les champs")
-    titre = forms.CharField(required=False, label="Intitulé")
-    type = forms.ChoiceField(required=False, choices=(('', 'Tous'),)+Evenement.TYPE_CHOICES)
+class EvenementSearchForm(forms.ModelForm):
+    """Formulaire de recherche pour les évènements."""
     date_min = SEPDateField(required=False, label="Depuis le") 
     date_max = SEPDateField(required=False, label="Jusqu'au") 
-    discipline = forms.ModelChoiceField(queryset=Discipline.objects.all(), required=False, label="Discipline", empty_label="Toutes")
-    region = forms.ModelChoiceField(queryset=Region.objects.all(), required=False, label="Région", empty_label="Toutes",
-                                    help_text="La région est ici définie au sens, non strictement géographique, du Bureau régional de l'AUF de référence.")
-    
-    def get_query_set(self):
-        """Retourne l'ensemble des évènements qui correspondent aux valeurs
-           entrées dans le formulaire."""
-        evenements = Evenement.objects
-        if self.is_valid():
-            query = self.cleaned_data['q']
-            if query:
-                evenements = evenements.search(query)
-            titre = self.cleaned_data['titre']
-            if titre:
-                evenements = evenements.add_to_query('@titre ' + titre)
-            discipline = self.cleaned_data['discipline']
-            if discipline:
-                evenements = evenements.filter_discipline(discipline)
-            region = self.cleaned_data['region']
-            if region:
-                evenements = evenements.filter_region(region)
-            type = self.cleaned_data['type']
-            if type:
-                evenements = evenements.filter_type(type)
-            date_min = self.cleaned_data['date_min']
-            if date_min:
-                evenements = evenements.filter_debut(min=date_min)
-            date_max = self.cleaned_data['date_max']
-            if date_max:
-                evenements = evenements.filter_debut(max=date_max)
-        return evenements.all()
+
+    class Meta:
+        model = EvenementSearch
+        fields = ['q', 'type', 'date_min', 'date_max', 'discipline', 'region']
+
+class EvenementSearchEditForm(EvenementSearchForm):
+
+    class Meta(EvenementSearchForm.Meta):
+        fields = ['nom'] + EvenementSearchForm.Meta.fields
+
+class SearchEditForm(forms.ModelForm):
+
+    class Meta:
+        model = Search
 
 ###
 
diff --git a/auf_savoirs_en_partage/savoirs/migrations/0010_auto__add_search__add_appelsearch__add_evenementsearch__add_actualites.py b/auf_savoirs_en_partage/savoirs/migrations/0010_auto__add_search__add_appelsearch__add_evenementsearch__add_actualites.py
new file mode 100644 (file)
index 0000000..5956bac
--- /dev/null
@@ -0,0 +1,363 @@
+# 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 model 'Search'
+        db.create_table('savoirs_search', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
+            ('content_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['contenttypes.ContentType'])),
+            ('nom', self.gf('django.db.models.fields.CharField')(max_length=100)),
+            ('q', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)),
+            ('discipline', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['savoirs.Discipline'], null=True, blank=True)),
+            ('region', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['datamaster_modeles.Region'], null=True, blank=True)),
+        ))
+        db.send_create_signal('savoirs', ['Search'])
+
+        # Adding model 'AppelSearch'
+        db.create_table('savoirs_appelsearch', (
+            ('search_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['savoirs.Search'], unique=True, primary_key=True)),
+            ('date_min', self.gf('django.db.models.fields.DateField')(null=True, blank=True)),
+            ('date_max', self.gf('django.db.models.fields.DateField')(null=True, blank=True)),
+        ))
+        db.send_create_signal('savoirs', ['AppelSearch'])
+
+        # Adding model 'EvenementSearch'
+        db.create_table('savoirs_evenementsearch', (
+            ('search_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['savoirs.Search'], unique=True, primary_key=True)),
+            ('titre', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)),
+            ('type', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)),
+            ('date_min', self.gf('django.db.models.fields.DateField')(null=True, blank=True)),
+            ('date_max', self.gf('django.db.models.fields.DateField')(null=True, blank=True)),
+        ))
+        db.send_create_signal('savoirs', ['EvenementSearch'])
+
+        # Adding model 'ActualiteSearch'
+        db.create_table('savoirs_actualitesearch', (
+            ('search_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['savoirs.Search'], unique=True, primary_key=True)),
+            ('date_min', self.gf('django.db.models.fields.DateField')(null=True, blank=True)),
+            ('date_max', self.gf('django.db.models.fields.DateField')(null=True, blank=True)),
+        ))
+        db.send_create_signal('savoirs', ['ActualiteSearch'])
+
+        # Adding model 'RessourceSearch'
+        db.create_table('savoirs_ressourcesearch', (
+            ('search_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['savoirs.Search'], unique=True, primary_key=True)),
+            ('auteur', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)),
+            ('titre', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)),
+            ('sujet', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)),
+            ('publisher', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)),
+        ))
+        db.send_create_signal('savoirs', ['RessourceSearch'])
+
+
+    def backwards(self, orm):
+        
+        # Deleting model 'Search'
+        db.delete_table('savoirs_search')
+
+        # Deleting model 'AppelSearch'
+        db.delete_table('savoirs_appelsearch')
+
+        # Deleting model 'EvenementSearch'
+        db.delete_table('savoirs_evenementsearch')
+
+        # Deleting model 'ActualiteSearch'
+        db.delete_table('savoirs_actualitesearch')
+
+        # Deleting model 'RessourceSearch'
+        db.delete_table('savoirs_ressourcesearch')
+
+
+    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', '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'}),
+            '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'}),
+            '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'}),
+            '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': "'d7dd9768-4a73-11e0-862a-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'},
+            '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']
index b9e0845..2e02294 100644 (file)
@@ -8,17 +8,21 @@ import pytz
 import random
 import uuid
 import vobject
+from urllib import urlencode
+
 from backend_config import RESOURCES
 from babel.dates import get_timezone_name
 from caldav.lib import error
-from babel.dates import get_timezone_name
-from datamaster_modeles.models import Region, Pays, Thematique
 from django.contrib.auth.models import User
+from django.contrib.contenttypes.models import ContentType
+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 djangosphinx.models import SphinxQuerySet, SearchError
+
+from datamaster_modeles.models import Region, Pays, Thematique
 from savoirs.globals import META
 from settings import CALENDRIER_URL, SITE_ROOT_URL
 
@@ -617,3 +621,225 @@ class PageStatique(models.Model):
 
     class Meta:
         verbose_name_plural = 'pages statiques'
+
+# Recherches
+
+class GlobalSearchResults(object):
+
+    def __init__(self, actualites=None, appels=None, evenements=None, 
+                 ressources=None, chercheurs=None, sites=None, sites_auf=None):
+        self.actualites = actualites
+        self.appels = appels
+        self.evenements = evenements
+        self.ressources = ressources
+        self.chercheurs = chercheurs
+        self.sites = sites
+        self.sites_auf = 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")
+    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',
+                               help_text="La région est ici définie au sens, non strictement géographique, du Bureau régional de l'AUF de référence.")
+
+    def query_string(self):
+        params = dict()
+        for field in self._meta.fields:
+            if field.name in ['id', 'user', 'nom', 'search_ptr', 'content_type']:
+                continue
+            value = getattr(self, field.column)
+            if value:
+                if isinstance(value, datetime.date):
+                    params[field.name] = value.strftime('%d/%m/%Y')
+                else:
+                    params[field.name] = value
+        return urlencode(params)
+    
+    class Meta:
+        verbose_name = 'recherche transversale'
+        verbose_name_plural = "recherches transversales"
+
+    def save(self):
+        if (not self.content_type_id):
+            self.content_type = ContentType.objects.get_for_model(self.__class__)
+        super(Search, self).save()
+
+    def as_leaf_class(self):
+        content_type = self.content_type
+        model = content_type.model_class()
+        if(model == Search):
+            return self
+        return model.objects.get(id=self.id)
+                
+    def run(self):
+        from chercheurs.models import Chercheur
+        from sitotheque.models import Site
+
+        results = object()
+        actualites = Actualite.objects
+        evenements = Evenement.objects
+        ressources = Record.objects
+        chercheurs = Chercheur.objects
+        sites = Site.objects
+        if self.q:
+            actualites = actualites.search(self.q)
+            evenements = evenements.search(self.q)
+            ressources = ressources.search(self.q)
+            chercheurs = chercheurs.search(self.q)
+            sites = sites.search(self.q)
+        if self.discipline:
+            actualites = actualites.filter_discipline(self.discipline)
+            evenements = evenements.filter_discipline(self.discipline)
+            ressources = ressources.filter_discipline(self.discipline)
+            chercheurs = chercheurs.filter_discipline(self.discipline)
+            sites = sites.filter_discipline(self.discipline)
+        if self.region:
+            actualites = actualites.filter_region(self.region)
+            evenements = evenements.filter_region(self.region)
+            ressources = ressources.filter_region(self.region)
+            chercheurs = chercheurs.filter_region(self.region)
+            sites = sites.filter_region(self.region)
+        try:
+            sites_auf = google_search(0, self.q)['results']
+        except:
+            sites_auf = []
+
+        return GlobalSearchResults(
+            actualites=actualites.order_by('-date').filter_type('actu'),
+            appels=actualites.order_by('-date').filter_type('appels'),
+            evenements=evenements.order_by('-debut'),
+            ressources=ressources.order_by('-id'),
+            chercheurs=chercheurs.order_by('-date_modification'),
+            sites=sites.order_by('-date_maj'),
+            sites_auf=sites_auf
+        )
+
+    def url(self):
+        url = ''
+        if self.discipline:
+            url += '/discipline/%d' % self.discipline.id
+        if self.region:
+            url += '/region/%d' % self.region.id
+        url += '/recherche/'
+        if self.q:
+            url += '?' + urlencode({'q': self.q})
+        return url
+
+class RessourceSearch(Search):
+    auteur = models.CharField(max_length=100, blank=True, verbose_name="auteur ou contributeur")
+    titre = models.CharField(max_length=100, blank=True)
+    sujet = models.CharField(max_length=100, blank=True)
+    publisher = models.CharField(max_length=100, blank=True, verbose_name="éditeur")
+
+    class Meta:
+        verbose_name = 'recherche de ressources'
+        verbose_name_plural = "recherches de ressources"
+
+    def run(self):
+        results = Record.objects
+        if self.q:
+            results = results.search(self.q)
+        if self.auteur:
+            results = results.add_to_query('@(creator,contributor) ' + self.auteur)
+        if self.titre:
+            results = results.add_to_query('@title ' + self.titre)
+        if self.sujet:
+            results = results.add_to_query('@subject ' + self.sujet)
+        if self.publisher:
+            results = results.add_to_query('@publisher ' + self.publisher)
+        if self.discipline:
+            results = results.filter_discipline(self.discipline)
+        if self.region:
+            results = results.filter_region(self.region)
+        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')
+        return results.all()
+
+    def url(self):
+        qs = self.query_string()
+        return reverse('ressources') + ('?' + qs if qs else '')
+
+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")
+
+    class Meta:
+        abstract = True
+
+    def run(self):
+        results = Actualite.objects
+        if self.q:
+            results = results.search(self.q)
+        if self.discipline:
+            results = results.filter_discipline(self.discipline)
+        if self.region:
+            results = results.filter_region(self.region)
+        if self.date_min:
+            results = results.filter_date(min=self.date_min)
+        if self.date_max:
+            results = results.filter_date(max=self.date_max)
+        return results.all()
+
+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 url(self):
+        qs = self.query_string()
+        return reverse('actualites') + ('?' + qs if qs else '')
+
+class AppelSearch(ActualiteSearchBase):
+
+    class Meta:
+        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 url(self):
+        qs = self.query_string()
+        return reverse('appels') + ('?' + qs if qs else '')
+
+class EvenementSearch(Search):
+    titre = models.CharField(max_length=100, blank=True, verbose_name="Intitulé")
+    type = models.CharField(max_length=100, blank=True, choices=Evenement.TYPE_CHOICES)
+    date_min = models.DateField(blank=True, null=True, verbose_name="depuis le")
+    date_max = models.DateField(blank=True, null=True, verbose_name="jusqu'au")
+
+    class Meta:
+        verbose_name = "recherche d'évènements"
+        verbose_name_plural = "recherches d'évènements"
+
+    def run(self):
+        results = Evenement.objects
+        if self.q:
+            results = results.search(self.q)
+        if self.titre:
+            results = results.add_to_query('@titre ' + self.titre)
+        if self.discipline:
+            results = results.filter_discipline(self.discipline)
+        if self.region:
+            results = results.filter_region(self.region)
+        if self.type:
+            results = results.filter_type(self.type)
+        if self.date_min:
+            results = results.filter_debut(min=self.date_min)
+        if self.date_max:
+            results = results.filter_debut(max=self.date_max)
+        return results.all()
+
+    def url(self):
+        qs = self.query_string()
+        return reverse('agenda') + ('?' + qs if qs else '')
+
index cbd3db0..7eed10a 100644 (file)
@@ -24,12 +24,30 @@ class PageLoadTest(TestCase):
 
     def test_ressources(self):
         self.check_status_200('/ressources/')
+        self.check_status_200('/ressources/', {
+            'q': "recherche textuelle",
+            'auteur': 'Un auteur',
+            'titre': 'Un titre',
+            'sujet': 'Un sujet',
+            'publisher': "Jean l'éditeur",
+            'discipline': 1,
+            'region': 1
+        })
 
     def test_ressource(self):
         self.check_status_200('/ressources/1/')
 
     def test_agenda(self):
         self.check_status_200('/agenda/')
+        self.check_status_200('/agenda/', {
+            'q': 'foo',
+            'titre': 'bar',
+            'type': 'Colloque',
+            'date_min': '18/01/2001',
+            'date_max': '20/01/2001',
+            'discipline': 1,
+            'region': 1
+        })
         self.check_status_200('/agenda/evenements/utilisation/')
         self.check_status_200('/agenda/evenements/creer/')
 
@@ -38,6 +56,13 @@ class PageLoadTest(TestCase):
         
     def test_actualites(self):
         self.check_status_200('/actualites/')
+        self.check_status_200('/actualites/', {
+            'q': 'mots-clés',
+            'date_min': '01/01/2011',
+            'date_max': '31/12/2011',
+            'discipline': 1,
+            'region': 1
+        })
         self.check_status_200('/rss/actualites/')
 
     def test_actualite(self):
@@ -46,10 +71,30 @@ class PageLoadTest(TestCase):
 
     def test_appels(self):
         self.check_status_200('/appels/')
+        self.check_status_200('/appels/', {
+            'q': 'mots-clés',
+            'date_min': '01/01/2011',
+            'date_max': '31/12/2011',
+            'discipline': 1,
+            'region': 1
+        })
         self.check_status_200('/rss/appels/')
 
     def test_chercheurs(self):
         self.check_status_200('/chercheurs/')
+        self.check_status_200('/chercheurs/', {
+            'q': 'texte texte',
+            'nom_chercheur': 'Ted Kennedy',
+            'domaine': 1,
+            'groupe_recherche': 'Le groupe',
+            'statut': 'expert',
+            'discipline': 1,
+            'pays': 'AO',
+            'region': 1,
+            'nord_sud': 'Nord',
+            'activites_francophonie': 'instance_auf',
+            'genre': 'm'
+        })
         self.check_status_200('/chercheurs/', dict(tri='nom'))
         self.check_status_200('/chercheurs/', dict(tri='nom_desc'))
         self.check_status_200('/chercheurs/', dict(tri='etablissement'))
@@ -59,6 +104,12 @@ class PageLoadTest(TestCase):
 
     def test_sites(self):
         self.check_status_200('/sites/')
+        self.check_status_200('/sites/', {
+            'q': 'recherche',
+            'discipline': 1,
+            'pays': 'AO',
+            'region': 1
+        })
 
     def test_sites_auf(self):
         self.check_status_200('/sites-auf/')
index 12491f1..a47c03b 100644 (file)
@@ -7,57 +7,42 @@ from django.shortcuts import render_to_response, get_object_or_404, redirect
 from django.template import Context, RequestContext
 from django.http import HttpResponse, HttpResponseRedirect
 from django.contrib.auth.decorators import login_required
+from django.contrib.contenttypes.models import ContentType
 from django.core.urlresolvers import reverse
 from django.shortcuts import get_object_or_404
 from django.utils.safestring import mark_safe
 from django import forms
 from django.conf import settings
+
+import backend_config
+from chercheurs.forms import ChercheurSearch, ChercheurSearchEditForm
+from chercheurs.models import Chercheur
 from lib.recherche import google_search, excerpt_function
 from lib import sep
 from lib.calendrier import evenements, evenement_info, combine
+from savoirs.forms import *
 from savoirs.globals import configuration
-from savoirs.models import build_time_zone_choices
-import backend_config
-from forms import *
-from models import *
-from chercheurs.models import Chercheur
+from savoirs.models import *
 from sitotheque.models import Site
+from sitotheque.forms import SiteSearch, SiteSearchEditForm
 
 # Accueil
 
 def index(request, discipline=None, region=None):
     """Page d'accueil"""
-    actualites = Actualite.objects
-    evenements = Evenement.objects
-    ressources = Record.objects
-    chercheurs = Chercheur.objects
-    sites = Site.objects
-    if discipline:
-        discipline = Discipline.objects.get(pk=discipline)
-        actualites = actualites.filter_discipline(discipline)
-        evenements = evenements.filter_discipline(discipline)
-        ressources = ressources.filter_discipline(discipline)
-        chercheurs = chercheurs.filter_discipline(discipline)
-        sites = sites.filter_discipline(discipline)
-    if region:
-        region = Region.objects.get(pk=region)
-        actualites = actualites.filter_region(region)
-        evenements = evenements.filter_region(region)
-        ressources = ressources.filter_region(region)
-        chercheurs = chercheurs.filter_region(region)
-        sites = sites.filter_region(region)
-    actualites_actu = actualites.filter_type('actu').order_by('-date')[0:4]
-    appels = actualites.filter_type('appels').order_by('-date')[0:4]
-    evenements = evenements.order_by('-debut')[0:4]
-    ressources = ressources.order_by('-id')[0:4]
-    chercheurs = chercheurs.order_by('-date_modification')[0:10]
-    sites = sites.order_by('-date_maj')[0:4]
-    return render_to_response(
-        "savoirs/index.html",
-        dict(actualites=actualites_actu, appels=appels, evenements=evenements,
-             caldav_url=configuration['calendrier_publique'],
-             ressources=ressources, chercheurs=chercheurs, sites=sites),
-        context_instance = RequestContext(request))
+    discipline_obj = discipline and get_object_or_404(Discipline, pk=discipline)
+    region_obj = region and get_object_or_404(Region, pk=region)
+    search = Search(discipline=discipline_obj, region=region_obj)
+    results = search.run()
+    return render_to_response("savoirs/index.html", dict(
+        actualites=results.actualites[0:4], 
+        appels=results.appels[0:4], 
+        evenements=results.evenements[0:4],
+        ressources=results.ressources[0:4],
+        chercheurs=results.chercheurs[0:10],
+        sites=results.sites[0:4],
+        caldav_url=configuration['calendrier_publique']
+    ), context_instance = RequestContext(request))
 
 # sous-menu droite
 def a_propos(request):
@@ -89,40 +74,20 @@ def recherche(request, discipline=None, region=None):
             kwargs['region'] = region
         return HttpResponseRedirect(reverse('savoirs.views.index', kwargs=kwargs))
 
-    actualites = Actualite.objects.search(query).order_by('-date')
-    evenements = Evenement.objects.search(query).order_by('-debut')
-    ressources = Record.objects.search(query)
-    chercheurs = Chercheur.objects.search(query).order_by('-date_modification')
-    sites = Site.objects.search(query)
-    if discipline:
-        discipline = Discipline.objects.get(pk=discipline)
-        actualites = actualites.filter_discipline(discipline)
-        evenements = evenements.filter_discipline(discipline)
-        ressources = ressources.filter_discipline(discipline)
-        chercheurs = chercheurs.filter_discipline(discipline)
-        sites = sites.filter_discipline(discipline)
-    if region:
-        region = Region.objects.get(pk=region)
-        actualites = actualites.filter_region(region)
-        evenements = evenements.filter_region(region)
-        ressources = ressources.filter_region(region)
-        chercheurs = chercheurs.filter_region(region)
-        sites = sites.filter_region(region)
-    try:
-        sites_auf = google_search(0, query)['results']
-    except:
-        sites_auf = []
-    actualites_actu = actualites.filter_type('actu')
-    appels = actualites.filter_type('appels')
+    # Effectuer la recherche
+    discipline_obj = discipline and get_object_or_404(Discipline, pk=discipline)
+    region_obj = region and get_object_or_404(Region, pk=region)
+    search = Search(q=query, discipline=discipline_obj, region=region_obj)
+    results = search.run()
 
     # Bâtissons une query string pour les liens vers les briques
     params = {}
     if query:
         params['q'] = query
     if discipline:
-        params['discipline'] = unicode(discipline.id)
+        params['discipline'] = unicode(discipline)
     if region:
-        params['region'] = unicode(region.id)
+        params['region'] = unicode(region)
     if params:
         briques_query_string = mark_safe('?' + '&'.join(k + '=' + v.replace('"', '&quot;') for (k, v) in params.iteritems()))
     else:
@@ -130,18 +95,17 @@ def recherche(request, discipline=None, region=None):
         
     excerpt = excerpt_function(Record.objects, query)
 
-    return render_to_response(
-        "savoirs/recherche.html",
-        dict(q=query, excerpt=excerpt,
-             ressources=ressources[0:5], total_ressources=ressources.count(), 
-             evenements=evenements[0:5], total_evenements=evenements.count(),
-             chercheurs=chercheurs[0:10], total_chercheurs=chercheurs.count(),
-             actualites=actualites_actu[0:5], total_actualites=actualites_actu.count(),
-             appels=appels[0:5], total_appels=appels.count(),
-             sites=sites[0:5], total_sites=sites.count(),
-             sites_auf=sites_auf[0:5], briques_query_string=briques_query_string),
-        context_instance = RequestContext(request)
-    )
+    return render_to_response("savoirs/recherche.html", dict(
+        q=query, excerpt=excerpt,
+        ressources=results.ressources[0:5], total_ressources=results.ressources.count(), 
+        evenements=results.evenements[0:5], total_evenements=results.evenements.count(),
+        chercheurs=results.chercheurs[0:10], total_chercheurs=results.chercheurs.count(),
+        actualites=results.actualites[0:5], total_actualites=results.actualites.count(),
+        appels=results.appels[0:5], total_appels=results.appels.count(),
+        sites=results.sites[0:5], total_sites=results.sites.count(),
+        sites_auf=results.sites_auf[0:5], 
+        briques_query_string=briques_query_string
+    ), context_instance = RequestContext(request))
 
 def sites_auf(request):
     q = request.GET.get('q')
@@ -156,8 +120,9 @@ def sites_auf(request):
 
 # ressources
 def ressource_index(request):
-    search_form = RecordSearchForm(request.GET)
-    ressources = search_form.get_query_set()
+    search_form = RessourceSearchForm(request.GET)
+    search = search_form.save(commit=False)
+    ressources = search.run()
     nb_resultats = ressources.count()
     excerpt = excerpt_function(Record.objects, search_form.cleaned_data['q'])
     try:
@@ -165,12 +130,11 @@ def ressource_index(request):
         entete = p.contenu
     except PageStatique.DoesNotExist:
         entete = '<h1>Ressources</h1>'
-    return render_to_response(
-        "savoirs/ressource_index.html", 
-        dict(search_form=search_form, ressources=ressources,
-             nb_resultats=nb_resultats, excerpt=excerpt, entete=entete),
-        context_instance=RequestContext(request)
-    )
+
+    return render_to_response("savoirs/ressource_index.html", dict(
+        search_form=search_form, ressources=ressources,
+        nb_resultats=nb_resultats, excerpt=excerpt, entete=entete
+    ), context_instance=RequestContext(request))
 
 def ressource_retrieve(request, id):
     """Notice OAI de la ressource"""
@@ -197,7 +161,8 @@ def informations (request):
 # actualités
 def actualite_index(request, type='actu'):
     search_form = ActualiteSearchForm(request.GET)
-    actualites = search_form.get_query_set().filter_type(type)
+    search = search_form.save(commit=False)
+    actualites = search.run().filter_type(type)
     excerpt = excerpt_function(Actualite.objects, search_form.cleaned_data['q'])
     if type == 'appels':
         template = "savoirs/appels_index.html"
@@ -214,11 +179,10 @@ def actualite_index(request, type='actu'):
         except PageStatique.DoesNotExist:
             entete = '<h1>Actualités</h1>'
 
-    return render_to_response(
-        template,
-        dict(actualites=actualites, search_form=search_form,
-             excerpt=excerpt, nb_resultats=actualites.count(), entete=entete),
-        context_instance = RequestContext(request))
+    return render_to_response(template, dict(
+        actualites=actualites, search_form=search_form,
+        excerpt=excerpt, nb_resultats=actualites.count(), entete=entete
+    ), context_instance = RequestContext(request))
 
 def actualite(request, id):
     actualite = get_object_or_404(Actualite, pk=id)
@@ -229,7 +193,8 @@ def actualite(request, id):
 # agenda
 def evenement_index(request):
     search_form = EvenementSearchForm(request.GET)
-    evenements = search_form.get_query_set()
+    search = search_form.save(commit=False)
+    evenements = search.run()
     excerpt = excerpt_function(Evenement.objects, search_form.cleaned_data['q'])
     
     try:
@@ -282,6 +247,94 @@ def page_statique(request, id):
     return render_to_response('savoirs/page_statique.html', dict(page=page),
                               context_instance=RequestContext(request))
     
+# recherches sauvegardées
+
+@login_required
+def recherches(request):
+    types = []
+    for model in [Search, ChercheurSearch, RessourceSearch,
+                  ActualiteSearch, AppelSearch, EvenementSearch, SiteSearch]:
+        content_type = ContentType.objects.get_for_model(model)
+        recherches = model.objects.filter(user=request.user, content_type=content_type)
+        if recherches.count() > 0:
+            types.append({'label': model._meta.verbose_name_plural.capitalize(),
+                          'recherches': recherches})
+
+    return render_to_response('savoirs/recherches.html', dict(
+        types=types
+    ), context_instance=RequestContext(request))
+
+@login_required
+def sauvegarder_recherche(request, type):
+    """Sauvegarde une recherche"""
+    if type == 'ressources':
+        form_class = RessourceSearchEditForm
+    elif type in ['actualites', 'appels']:
+        form_class = ActualiteSearchEditForm
+    elif type == 'sites':
+        form_class = SiteSearchEditForm
+    elif type == 'chercheurs':
+        form_class = ChercheurSearchEditForm
+    elif type == 'evenements':
+        form_class = EvenementSearchEditForm
+    else:
+        form_class = SearchEditForm
+
+    if request.POST:
+        form = form_class(request.POST)
+        if form.is_valid():
+            search = form.save(commit=False)
+            search.user = request.user
+            search.save()
+            request.flash['message'] = 'Votre recherche a été sauvegardée.'
+            return HttpResponseRedirect(search.url())
+    else:
+        form = form_class(initial=dict(request.GET.iteritems()))
+    return render_to_response("savoirs/sauvegarder_recherche.html", dict(
+        form=form
+    ), context_instance=RequestContext(request))
+
+@login_required
+def editer_recherche(request, id):
+    """Éditer une recherche"""
+    recherche = get_object_or_404(Search, id=id, user=request.user).as_leaf_class()
+    if isinstance(recherche, RessourceSearch):
+        form_class = RessourceSearchEditForm
+    elif isinstance(recherche, ActualiteSearchBase):
+        form_class = ActualiteSearchEditForm
+    elif isinstance(recherche, SiteSearch):
+        form_class = SiteSearchEditForm
+    elif isinstance(recherche, ChercheurSearch):
+        form_class = ChercheurSearchEditForm
+    elif isinstance(recherche, EvenementSearch):
+        form_class = EvenementSearchEditForm
+    else:
+        form_class = SearchEditForm
+
+    if request.POST:
+        form = form_class(request.POST, instance=recherche)
+        if form.is_valid():
+            form.save()
+            return HttpResponseRedirect(reverse('recherches'))
+    else:
+        form = form_class(instance=recherche)
+    return render_to_response('savoirs/editer_recherche.html', dict(
+        form=form
+    ), context_instance=RequestContext(request))
+
+@login_required
+def supprimer_recherche(request, id):
+    """Supprimer une recherche"""
+    recherche = get_object_or_404(Search, id=id, user=request.user)
+    if request.POST:
+        if request.POST.get('confirmation'):
+            request.flash['message'] = 'La recherche a été supprimée.'
+            recherche.delete()
+        return HttpResponseRedirect(reverse('recherches'))
+    return render_to_response('savoirs/supprimer_recherche.html', {
+        'recherche': recherche
+    }, context_instance=RequestContext(request))
+
 @login_required
 def evenement_moderation(request):
     events = Evenement.objects.filter(approuve = False)
index acb3a92..3a97ced 100644 (file)
@@ -1,35 +1,16 @@
 # -*- encoding: utf-8 -*-
 from datamaster_modeles.models import Discipline, Region
 from django import forms
-from models import *
+from sitotheque.models import *
 from savoirs.lib.recherche import build_search_regexp
 
-class SiteSearchForm(forms.Form):
-    q = forms.CharField(required=False, label="Rechercher dans tous les champs")
-    discipline = forms.ModelChoiceField(queryset=Discipline.objects.all(), required=False, label="Discipline", empty_label="Toutes")
-    pays = forms.ModelChoiceField(queryset=Pays.objects.all(), required=False, label="Pays", empty_label="Tous")
-    region = forms.ModelChoiceField(queryset=Region.objects.all(), required=False, label="Région", empty_label="Toutes",
-                                    help_text="La région est ici définie au sens, non strictement géographique, du Bureau régional de l'AUF de référence.")
+class SiteSearchForm(forms.ModelForm):
 
-    def get_query_set(self):
-        """Retourne l'ensemble des sites qui correspondent aux valeurs
-           entrées dans le formulaire."""
-        sites = Site.objects
-        if self.is_valid():
-            q = self.cleaned_data["q"]
-            if q:
-                sites = sites.search(q)
-            discipline = self.cleaned_data['discipline']
-            if discipline:
-                sites = sites.filter_discipline(discipline)
-            region = self.cleaned_data['region']
-            if region:
-                sites = sites.filter_region(region)
-            pays = self.cleaned_data["pays"]
-            if pays:
-                sites = sites.filter_pays(pays=pays)
+    class Meta:
+        model = SiteSearch
+        fields = ['q', 'discipline', 'pays', 'region']
 
-            if not q:
-                sites = sites.order_by('-date_maj')
+class SiteSearchEditForm(SiteSearchForm):
 
-        return sites.all()
+    class Meta(SiteSearchForm.Meta):
+        fields = ['nom'] + SiteSearchForm.Meta.fields
diff --git a/auf_savoirs_en_partage/sitotheque/migrations/0003_auto__add_sitesearch.py b/auf_savoirs_en_partage/sitotheque/migrations/0003_auto__add_sitesearch.py
new file mode 100644 (file)
index 0000000..ea42b54
--- /dev/null
@@ -0,0 +1,195 @@
+# 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 model 'SiteSearch'
+        db.create_table('sitotheque_sitesearch', (
+            ('search_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['savoirs.Search'], unique=True, primary_key=True)),
+            ('pays', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['datamaster_modeles.Pays'], null=True, blank=True)),
+        ))
+        db.send_create_signal('sitotheque', ['SiteSearch'])
+
+
+    def backwards(self, orm):
+        
+        # Deleting model 'SiteSearch'
+        db.delete_table('sitotheque_sitesearch')
+
+
+    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', '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'}),
+            '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'}),
+            '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'}),
+            '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.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.search': {
+            'Meta': {'object_name': 'Search'},
+            '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']"})
+        },
+        'sitotheque.site': {
+            'Meta': {'object_name': 'Site'},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'auteur': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'date_maj': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'blank': 'True'}),
+            'date_publication': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'discipline': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['savoirs.Discipline']", 'symmetrical': 'False', 'blank': 'True'}),
+            'editeur': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'mots_cles': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'pays': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['datamaster_modeles.Pays']", 'null': 'True', 'db_column': "'pays'", 'blank': 'True'}),
+            'recherche_google': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'regions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'sites'", 'blank': 'True', 'to': "orm['datamaster_modeles.Region']"}),
+            'thematique': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['datamaster_modeles.Thematique']", 'symmetrical': 'False', 'blank': 'True'}),
+            'titre': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '2', 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
+        },
+        'sitotheque.sitesearch': {
+            'Meta': {'object_name': 'SiteSearch', '_ormbases': ['savoirs.Search']},
+            'pays': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['datamaster_modeles.Pays']", 'null': 'True', 'blank': 'True'}),
+            'search_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['savoirs.Search']", 'unique': 'True', 'primary_key': 'True'})
+        }
+    }
+
+    complete_apps = ['sitotheque']
index 4f82414..196b8f2 100644 (file)
@@ -1,9 +1,11 @@
 # -*- encoding: utf-8 -*-
-from datamaster_modeles.models import *
+from django.core.urlresolvers import reverse
 from django.db import models
 from django.db.models import Q
 from djangosphinx.models import SphinxSearch
-from savoirs.models import Discipline, SEPManager, SEPSphinxQuerySet, SEPQuerySet
+
+from datamaster_modeles.models import *
+from savoirs.models import Discipline, SEPManager, SEPSphinxQuerySet, SEPQuerySet, Search
 
 TYPE_SITE_CHOICES = (
     ('RV', 'Revue en ligne'), 
@@ -16,7 +18,7 @@ TYPE_SITE_CHOICES = (
     ('SC', 'Site culturel'),
     ('SI', 'Site d\'information'),
     ('AU', 'Autre type de site'),
-    )
+)
 
 class SiteQuerySet(SEPQuerySet):
 
@@ -87,3 +89,29 @@ class Site(models.Model):
 
     def assigner_disciplines(self, disciplines):
         self.discipline.add(*disciplines)
+
+class SiteSearch(Search):
+    pays = models.ForeignKey(Pays, blank=True, null=True)
+
+    class Meta:
+        verbose_name = "recherche de sites"
+        verbose_name_plural = "recherches de sites"
+
+    def run(self):
+        results = Site.objects
+        if self.q:
+            results = results.search(self.q)
+        if self.discipline:
+            results = results.filter_discipline(self.discipline)
+        if self.region:
+            results = results.filter_region(self.region)
+        if self.pays:
+            results = results.filter_pays(pays=self.pays)
+        if not self.q:
+            results = results.order_by('-date_maj')
+        return results.all()
+
+    def url(self):
+        qs = self.query_string()
+        return reverse('sites') + ('?' + qs if qs else '')
+
index 8671792..6e75f75 100644 (file)
@@ -1,15 +1,16 @@
 # -*- encoding: utf-8 -*-
+from django.contrib.auth.decorators import login_required
+from django.db.models import Q
 from django.shortcuts import render_to_response
 from django.template import Context, RequestContext
-from django.db.models import Q
 
 from savoirs.lib.recherche import excerpt_function
 from sitotheque.models import Site
-from sitotheque.forms import SiteSearchForm
+from sitotheque.forms import SiteSearchForm, SiteSearchEditForm
 
 def index(request):
     search_form = SiteSearchForm(request.GET)
-    sites = search_form.get_query_set()
+    sites = search_form.save(commit=False).run()
     excerpt = excerpt_function(Site.objects, search_form.cleaned_data['q'])
     nb_sites = sites.count()
     return render_to_response("sites/index.html",
index e8a287e..64d3846 100644 (file)
 </form>
 
 <h2>Résultats</h2>
-<div class="sous-titre"> {{nb_chercheurs}} chercheurs correspondant à votre recherche</div>
+<div class="sous-titre"> 
+    {{ nb_chercheurs }} chercheurs correspondant à votre recherche
+    {% if user.is_authenticated %}
+    (<a href="{% url sauvegarder_recherche "chercheurs" %}?{{ request.META.QUERY_STRING }}">Sauvegarder cette recherche</a>)
+    {% endif %}
+</div>
 
 <div class="pagination">{% paginate %}</div>
 <table id="repertoire">
index 8fbdfe8..55aad59 100644 (file)
@@ -4,6 +4,7 @@
 
 <ul class="actions">
     <li><a href="{% url chercheurs.views.index %}">Tous les chercheurs</a></li>
+    <li><a href="{% url recherches %}">Recherches sauvegardées</a></li>
     <li><a href="{% url chercheurs.views.edit %}">Éditer votre fiche</a></li>
     <li><a href="{% url chercheurs-password-change %}">Modifier votre mot de passe</a></li>
     <li><a href="{% url savoirs.views.evenement_ajout %}">Ajouter un événement à l'agenda</a></li>
index 2e1bca9..440fdf6 100644 (file)
 </form>
 
 <h2>Résultats</h2>
-<div class="sous-titre">{{ nb_resultats }} actualités correspondant à votre recherche</div>
+<div class="sous-titre">
+    {{ nb_resultats }} actualités correspondant à votre recherche
+    {% if user.is_authenticated %}
+    (<a href="{% url sauvegarder_recherche "actualites" %}?{{ request.META.QUERY_STRING }}">Sauvegarder cette recherche</a>)
+    {% endif %}
+</div>
 
 <div class="pagination">{% paginate %}</div>
 {% for actualite in actualites %}
index c5001a2..fa3ad23 100644 (file)
 </form>
 
 <h2>Résultats</h2>
-<div class="sous-titre">{{ nb_resultats }} appels d'offres correspondant à votre recherche</div>
+<div class="sous-titre">
+    {{ nb_resultats }} appels d'offres correspondant à votre recherche
+    {% if user.is_authenticated %}
+    (<a href="{% url sauvegarder_recherche "appels" %}?{{ request.META.QUERY_STRING }}">Sauvegarder cette recherche</a>)
+    {% endif %}
+</div>
 
 <div class="pagination">{% paginate %}</div>
 {% for actualite in actualites %}
diff --git a/auf_savoirs_en_partage/templates/savoirs/editer_recherche.html b/auf_savoirs_en_partage/templates/savoirs/editer_recherche.html
new file mode 100644 (file)
index 0000000..e6d9587
--- /dev/null
@@ -0,0 +1,17 @@
+{% extends "container_base.html" %}
+
+{% block contenu %}
+<div class="cadre">
+  <h1>Modifier une recherche sauvegardée</h1>
+
+  <form action="" method="post">
+    <table>
+      {% include "render_form.html" %}
+      <tr>
+        <td></td>
+        <td><input type="submit" value="Enregistrer"></td>
+      </tr>
+    </table>
+  </form>
+</div>
+{% endblock %}
index 54f4292..0187f56 100644 (file)
 </form>
 
 <h2>Résultats</h2>
-<div class="sous-titre">{{ nb_resultats }} évènements correspondant à votre recherche</div>
+<div class="sous-titre">
+    {{ nb_resultats }} évènements correspondant à votre recherche
+    {% if user.is_authenticated %}
+    (<a href="{% url sauvegarder_recherche "evenements" %}?{{ request.META.QUERY_STRING }}">Sauvegarder cette recherche</a>)
+    {% endif %}
+</div>
 
 <div class="pagination">{% paginate %}</div>
 {% for evenement in evenements %}
index 8fecd52..9344bc9 100644 (file)
@@ -2,8 +2,12 @@
 {% load sep %}
 
 {% block contenu %}
-<div class="texte">
-    <h1>Résultats correspondant à «&nbsp;{{ q }}&nbsp;»</h1>
+<h1>Résultats correspondant à «&nbsp;{{ q }}&nbsp;»</h1>
+{% if user.is_authenticated %}
+<div class="sous-titre">
+    <a href="{% url sauvegarder_recherche "transversale" %}{{ briques_query_string }}">Sauvegarder cette recherche</a>
+</div>
+{% endif %}
     
 {% if ressources %}
     <h2>Ressources ({{ ressources|length }} sur {{ total_ressources }})</h2>
@@ -88,6 +92,5 @@
     <h2>Aucun résultat</h2>
     <p><a href="{% url savoirs.views.index %}">Retour à la page d'accueil</a></p>
 {% endif %}
-</div>
 
 {% endblock %}
diff --git a/auf_savoirs_en_partage/templates/savoirs/recherches.html b/auf_savoirs_en_partage/templates/savoirs/recherches.html
new file mode 100644 (file)
index 0000000..6b5e8da
--- /dev/null
@@ -0,0 +1,26 @@
+{% extends "container_base.html" %}
+
+{% block contenu %}
+<h1>Recherches sauvegardées</h1>
+
+{% if types %}
+{% for type in types %}
+<h2>{{ type.label }}</h2>
+<ul class="items-and-actions">
+    {% for recherche in type.recherches %}
+    <li>
+        <a href="{{ recherche.url }}">{{ recherche.nom }}</a>
+        <div class="item-actions">
+            <a href="{% url supprimer_recherche recherche.id %}">Supprimer</a>
+            <a href="{% url editer_recherche recherche.id %}">Modifier</a>
+        </div>
+    </li>
+    {% endfor %}
+</ul>
+</table>
+{% endfor %}
+{% else %}
+<p>Aucune recherche sauvegardée.</p>
+{% endif %}
+
+{% endblock %}
index abfa5a9..e83cfe1 100644 (file)
 </form>
 
 <h2>Résultats</h2>
-<div class="sous-titre">{{ nb_resultats }} ressources correspondant à votre recherche</div>
+<div class="sous-titre">
+    {{ nb_resultats }} ressources correspondant à votre recherche
+    {% if user.is_authenticated %}
+    (<a href="{% url sauvegarder_recherche "ressources" %}?{{ request.META.QUERY_STRING }}">Sauvegarder cette recherche</a>)
+    {% endif %}
+</div>
 
 <div class="pagination">{% paginate %}</div>
 {% for ressource in ressources %}
diff --git a/auf_savoirs_en_partage/templates/savoirs/sauvegarder_recherche.html b/auf_savoirs_en_partage/templates/savoirs/sauvegarder_recherche.html
new file mode 100644 (file)
index 0000000..0ca6885
--- /dev/null
@@ -0,0 +1,17 @@
+{% extends "container_base.html" %}
+
+{% block contenu %}
+<div class="cadre">
+    <h1>Sauvegarder une recherche</h1>
+
+    <form action="" method="post">
+        <table>
+            {% include "render_form.html" %}
+            <tr>
+                <td></td>
+                <td><input type="submit" value="Enregistrer"></td>
+            </tr>
+        </table>
+    </form>
+</div>
+{% endblock %}
diff --git a/auf_savoirs_en_partage/templates/savoirs/supprimer_recherche.html b/auf_savoirs_en_partage/templates/savoirs/supprimer_recherche.html
new file mode 100644 (file)
index 0000000..128fc7b
--- /dev/null
@@ -0,0 +1,12 @@
+{% extends "container_base.html" %}
+
+{% block contenu %}
+<div class="cadre">
+    <h1>Supprimer une recherche</h1>
+    <form method="post" action="">
+        <p>Voulez-vous vraiment supprimer la recherche <em>{{ recherche.nom }}</em>&nbsp;?</p>
+        <input type="submit" name="confirmation" value="Oui">
+        <input type="submit" name="annulation" value="Non">
+    </form>
+</div>
+{% endblock %}
index 420d6eb..2b56a60 100644 (file)
 </form>
 
 <h2>Résultats</h2>
-<div class="sous-titre">{{nb_sites}} site{{ nb_sites|pluralize }} correspondant à votre recherche</div>
+<div class="sous-titre">
+    {{nb_sites}} site{{ nb_sites|pluralize }} correspondant à votre recherche
+    {% if user.is_authenticated %}
+    (<a href="{% url sauvegarder_recherche "sites" %}?{{ request.META.QUERY_STRING }}">Sauvegarder cette recherche</a>)
+    {% endif %}
+</div>
 
 <div class="pagination">{% paginate %}</div>
 {% for site in sites %}
index aaacff7..6c79f7e 100644 (file)
@@ -134,6 +134,11 @@ urlpatterns = sep_patterns + patterns(
     (r'^json/get/$', 'savoirs.views.json_get'),
     (r'^json/set/$', 'savoirs.views.json_set'),
 
+    # recherches sauvegardées
+    (r'^recherches/$', 'savoirs.views.recherches', {}, 'recherches'),
+    (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'),
 )
 
 if settings.DEBUG: