Remis en place le filtre pour les ressources et activités non-validées.
[auf_savoirs_en_partage_django.git] / auf_savoirs_en_partage / savoirs / models.py
1 # -*- encoding: utf-8 -*-
2 import simplejson, uuid, datetime, caldav, vobject, uuid
3 from django.contrib.auth.models import User
4 from django.db import models
5 from django.db.models import Q
6 from django.db.models.signals import pre_delete
7 from timezones.fields import TimeZoneField
8 from auf_savoirs_en_partage.backend_config import RESOURCES
9 from savoirs.globals import META
10 from settings import CALENDRIER_URL
11 from datamaster_modeles.models import Thematique, Pays, Region
12 from lib.calendrier import combine
13 from caldav.lib import error
14
15 class Discipline(models.Model):
16 id = models.IntegerField(primary_key=True, db_column='id_discipline')
17 nom = models.CharField(max_length=765, db_column='nom_discipline')
18
19 def __unicode__ (self):
20 return self.nom
21
22 class Meta:
23 db_table = u'discipline'
24 ordering = ["nom",]
25
26 class SourceActualite(models.Model):
27 nom = models.CharField(max_length=255)
28 url = models.CharField(max_length=255)
29
30 def __unicode__(self,):
31 return u"%s" % self.nom
32
33 class ActualiteManager(models.Manager):
34
35 def get_query_set(self):
36 return ActualiteQuerySet(self.model)
37
38 def search(self, text):
39 return self.get_query_set().search(text)
40
41 class ActualiteQuerySet(models.query.QuerySet):
42
43 def search(self, text):
44 return self.filter(Q(titre__icontains=text) | Q(texte__icontains=text))
45
46 class Actualite(models.Model):
47 id = models.AutoField(primary_key=True, db_column='id_actualite')
48 titre = models.CharField(max_length=765, db_column='titre_actualite')
49 texte = models.TextField(db_column='texte_actualite')
50 url = models.CharField(max_length=765, db_column='url_actualite')
51 date = models.DateField(db_column='date_actualite')
52 visible = models.BooleanField(db_column='visible_actualite', default = False)
53 ancienid = models.IntegerField(db_column='ancienId_actualite', blank = True, null = True)
54 source = models.ForeignKey(SourceActualite, blank = True, null = True)
55
56 objects = ActualiteManager()
57
58 def __unicode__ (self):
59 return "%s" % (self.titre)
60
61 class Meta:
62 db_table = u'actualite'
63 ordering = ["-date",]
64
65 class Evenement(models.Model):
66 uid = models.CharField(max_length = 255, default = str(uuid.uuid1()))
67 approuve = models.BooleanField(default = False)
68 titre = models.CharField(max_length=255)
69 discipline = models.ForeignKey('Discipline', related_name = "discipline",
70 blank = True, null = True)
71 discipline_secondaire = models.ForeignKey('Discipline', related_name = \
72 "discipline_secondaire",
73 verbose_name = \
74 "Discipline secondaire",
75 blank = True, null = True)
76 mots_cles = models.TextField('Mots-Clés', blank = True, null = True)
77 type = models.CharField(max_length = 255, choices = \
78 (('Colloque', 'Colloque'),
79 ('Conférence', 'Conférence'),
80 ('Appel à contribution', 'Appel à contribution'),
81 ('Journée d\'étude', 'Journée d\'étude'),
82 (None, 'Autre')
83 )) #TODO: choices
84 fuseau = TimeZoneField(verbose_name = 'Fuseau horaire')
85 debut = models.DateTimeField(default = datetime.datetime.now)
86 fin = models.DateTimeField(default = datetime.datetime.now)
87 lieu = models.TextField()
88 description = models.TextField(blank = True, null = True)
89 #fichiers = TODO?
90 contact = models.TextField(blank = True, null = True)
91 url = models.CharField(max_length=255, blank = True, null = True)
92
93 def __unicode__(self,):
94 return "[%s] %s" % (self.uid, self.titre)
95
96 def save(self, *args, **kwargs):
97 """Sauvegarde l'objet dans django et le synchronise avec caldav s'il a été
98 approuvé"""
99 self.update_vevent()
100 super(Evenement, self).save(*args, **kwargs)
101
102 # methodes de commnunications avec CALDAV
103 def as_ical(self,):
104 """Retourne l'evenement django sous forme d'objet icalendar"""
105 cal = vobject.iCalendar()
106 cal.add('vevent')
107
108 # fournit son propre uid
109 if self.uid in [None, ""]:
110 self.uid = str(uuid.uuid1())
111
112 cal.vevent.add('uid').value = self.uid
113
114 cal.vevent.add('summary').value = self.titre
115
116 if self.mots_cles is None:
117 kw = []
118 else:
119 kw = self.mots_cles.split(",")
120
121 try:
122 kw.append(self.discipline.nom)
123 kw.append(self.discipline_secondaire.nom)
124 kw.append(self.type)
125 except: pass
126
127 kw = [x.strip() for x in kw if len(x.strip()) > 0 and x is not None]
128 for k in kw:
129 cal.vevent.add('x-auf-keywords').value = k
130
131 description = self.description
132 if len(kw) > 0:
133 if len(self.description) > 0:
134 description += "\n"
135 description += u"Mots-clés: " + ", ".join(kw)
136
137 cal.vevent.add('dtstart').value = combine(self.debut, self.fuseau)
138 cal.vevent.add('dtend').value = combine(self.fin, self.fuseau)
139 cal.vevent.add('created').value = combine(datetime.datetime.now(), "UTC")
140 cal.vevent.add('dtstamp').value = combine(datetime.datetime.now(), "UTC")
141 if len(description) > 0:
142 cal.vevent.add('description').value = description
143 if len(self.contact) > 0:
144 cal.vevent.add('contact').value = self.contact
145 if len(self.url) > 0:
146 cal.vevent.add('url').value = self.url
147 if len(self.lieu) > 0:
148 cal.vevent.add('location').value = self.lieu
149 return cal
150
151 def update_vevent(self,):
152 """Essaie de créer l'évènement sur le serveur ical.
153 En cas de succès, l'évènement local devient donc inactif et approuvé"""
154 try:
155 if self.approuve:
156 event = self.as_ical()
157 client = caldav.DAVClient(CALENDRIER_URL)
158 cal = caldav.Calendar(client, url = CALENDRIER_URL)
159 e = caldav.Event(client, parent = cal, data = event.serialize(), id=self.uid)
160 e.save()
161 except:
162 self.approuve = False
163
164 def delete_vevent(self,):
165 """Supprime l'evenement sur le serveur caldav"""
166 try:
167 if self.approuve:
168 event = self.as_ical()
169 client = caldav.DAVClient(CALENDRIER_URL)
170 cal = caldav.Calendar(client, url = CALENDRIER_URL)
171 e = cal.event(self.uid)
172 e.delete()
173 except error.NotFoundError:
174 pass
175
176
177 # Surcharge du comportement de suppression
178 # La méthode de connexion par signals est préférable à surcharger la méthode delete()
179 # car dans le cas de la suppression par lots, cell-ci n'est pas invoquée
180 def delete_vevent(sender, instance, *args, **kwargs):
181 instance.delete_vevent()
182
183 pre_delete.connect(delete_vevent, sender = Evenement)
184
185
186 class ListSet(models.Model):
187 spec = models.CharField(primary_key = True, max_length = 255)
188 name = models.CharField(max_length = 255)
189 server = models.CharField(max_length = 255)
190 validated = models.BooleanField(default = True)
191
192 def __unicode__(self,):
193 return self.name
194
195 class RecordManager(models.Manager):
196
197 def get_query_set(self):
198 return RecordQuerySet(self.model)
199
200 def search(self, text):
201 return self.get_query_set().search(text)
202
203 def validated(self):
204 return self.get_query_set().validated()
205
206 class RecordQuerySet(models.query.QuerySet):
207
208 def search(self, text):
209 qs = self
210 words = text.split()
211
212 # Ne garder que les ressources qui contiennent tous les mots
213 # demandés.
214 for word in words:
215 qs = qs.filter(Q(title__icontains=word) | Q(description__icontains=word) |
216 Q(creator__icontains=word) | Q(contributor__icontains=word) |
217 Q(subject__icontains=word))
218
219 # On donne un point pour chaque mot présent dans le titre.
220 score_expr = ' + '.join(['(title LIKE %s)'] * len(words))
221 score_params = ['%' + word + '%' for word in words]
222 return qs.extra(
223 select={'score': score_expr},
224 select_params=score_params
225 ).order_by('-score')
226
227 def search_auteur(self, text):
228 qs = self
229 for word in text.split():
230 qs = qs.filter(Q(creator__icontains=word) | Q(contributor__icontains=word))
231 return qs
232
233 def search_sujet(self, text):
234 qs = self
235 for word in text.split():
236 qs = qs.filter(subject__icontains=word)
237 return qs
238
239 def search_titre(self, text):
240 qs = self
241 for word in text.split():
242 qs = qs.filter(title__icontains=word)
243 return qs
244
245 def validated(self):
246 """Ne garder que les ressources validées et qui sont soit dans aucun
247 listset ou au moins dans un listset validé."""
248 qs = self.filter(validated=True)
249 qs = qs.extra(where=['''((savoirs_record.id NOT IN (SELECT record_id FROM savoirs_record_listsets)) OR
250 ((SELECT MAX(l.validated) FROM savoirs_listset l
251 INNER JOIN savoirs_record_listsets rl ON rl.listset_id = l.spec
252 WHERE rl.record_id = savoirs_record.id) = TRUE))'''])
253 return qs
254
255 class Record(models.Model):
256
257 #fonctionnement interne
258 id = models.AutoField(primary_key = True)
259 server = models.CharField(max_length = 255)
260 last_update = models.CharField(max_length = 255)
261 last_checksum = models.CharField(max_length = 255)
262 validated = models.BooleanField(default = True)
263
264 #OAI
265 title = models.TextField(null = True, blank = True)
266 creator = models.TextField(null = True, blank = True)
267 description = models.TextField(null = True, blank = True)
268 modified = models.CharField(max_length = 255, null = True, blank = True)
269 identifier = models.CharField(max_length = 255, null = True, blank = True, unique = True)
270 uri = models.CharField(max_length = 255, null = True, blank = True, unique = True)
271 source = models.TextField(null = True, blank = True)
272 contributor = models.TextField(null = True, blank = True)
273 subject = models.TextField(null = True, blank = True)
274 publisher = models.TextField(null = True, blank = True)
275 type = models.TextField(null = True, blank = True)
276 format = models.TextField(null = True, blank = True)
277 language = models.TextField(null = True, blank = True)
278
279 listsets = models.ManyToManyField(ListSet, null = True, blank = True)
280
281 #SEP 2 (aucune données récoltées)
282 alt_title = models.TextField(null = True, blank = True)
283 abstract = models.TextField(null = True, blank = True)
284 creation = models.CharField(max_length = 255, null = True, blank = True)
285 issued = models.CharField(max_length = 255, null = True, blank = True)
286 isbn = models.TextField(null = True, blank = True)
287 orig_lang = models.TextField(null = True, blank = True)
288
289 # Metadata AUF multivaluées
290 disciplines = models.ManyToManyField(Discipline)
291 thematiques = models.ManyToManyField(Thematique)
292 pays = models.ManyToManyField(Pays)
293 regions = models.ManyToManyField(Region)
294
295 # Manager
296 objects = RecordManager()
297
298 def est_complet(self,):
299 """teste si le record à toutes les données obligatoires"""
300 return self.disciplines.count() > 0 and \
301 self.thematiques.count() > 0 and \
302 self.pays.count() > 0 and \
303 self.regions.count() > 0
304
305 def __unicode__(self):
306 return "[%s] %s" % (self.server, self.title)
307
308 class Serveur(models.Model):
309 """Identification d'un serveur d'ou proviennent les références"""
310 nom = models.CharField(primary_key = True, max_length = 255)
311
312 def __unicode__(self,):
313 return self.nom
314
315 def conf_2_db(self,):
316 for k in RESOURCES.keys():
317 s, created = Serveur.objects.get_or_create(nom=k)
318 s.nom = k
319 s.save()
320
321 class Profile(models.Model):
322 user = models.ForeignKey(User, unique=True)
323 serveurs = models.ManyToManyField(Serveur, null = True, blank = True)
324
325 class HarvestLog(models.Model):
326 context = models.CharField(max_length = 255)
327 name = models.CharField(max_length = 255)
328 date = models.DateTimeField(auto_now = True)
329 added = models.IntegerField(null = True, blank = True)
330 updated = models.IntegerField(null = True, blank = True)
331 processed = models.IntegerField(null = True, blank = True)
332 record = models.ForeignKey(Record, null = True, blank = True)
333
334 @staticmethod
335 def add(message):
336 logger = HarvestLog()
337 if message.has_key('record_id'):
338 message['record'] = Record.objects.get(id=message['record_id'])
339 del(message['record_id'])
340
341 for k,v in message.items():
342 setattr(logger, k, v)
343 logger.save()