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