Remplacé "champ disciplinaire" par "discipline"
[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):
82f25472
EMS
44 q = None
45 for word in text.split():
46 part = (Q(titre__icontains=word) | Q(texte__icontains=word) |
47 Q(regions__nom__icontains=word) | Q(disciplines__nom__icontains=word))
48 if q is None:
49 q = part
50 else:
51 q = q & part
52 return self.filter(q).distinct() if q is not None else self
2f9c4d6c 53
d15017b2 54class Actualite(models.Model):
4f262f90 55 id = models.AutoField(primary_key=True, db_column='id_actualite')
d15017b2
CR
56 titre = models.CharField(max_length=765, db_column='titre_actualite')
57 texte = models.TextField(db_column='texte_actualite')
58 url = models.CharField(max_length=765, db_column='url_actualite')
d15017b2 59 date = models.DateField(db_column='date_actualite')
f554ef70 60 visible = models.BooleanField(db_column='visible_actualite', default = False)
3b6456b0 61 ancienid = models.IntegerField(db_column='ancienId_actualite', blank = True, null = True)
ccbc4363 62 source = models.ForeignKey(SourceActualite, blank = True, null = True)
3a45eb64 63 disciplines = models.ManyToManyField(Discipline, blank=True, related_name="actualites")
a5f76eb4 64 regions = models.ManyToManyField(Region, blank=True, related_name="actualites", verbose_name='régions')
6ef8ead4 65
2f9c4d6c
EMS
66 objects = ActualiteManager()
67
d15017b2
CR
68 class Meta:
69 db_table = u'actualite'
70 ordering = ["-date",]
92c7413b 71
264a3210
EMS
72 def __unicode__ (self):
73 return "%s" % (self.titre)
74
75 def assigner_disciplines(self, disciplines):
76 self.disciplines.add(*disciplines)
77
78 def assigner_regions(self, regions):
79 self.regions.add(*regions)
80
4101cfc0
EMS
81class EvenementManager(models.Manager):
82
83 def get_query_set(self):
84 return EvenementQuerySet(self.model)
85
86 def search(self, text):
87 return self.get_query_set().search(text)
88
89class EvenementQuerySet(models.query.QuerySet):
90
91 def search(self, text):
0b1ddc11
EMS
92 q = None
93 for word in text.split():
94 part = (Q(titre__icontains=word) |
95 Q(mots_cles__icontains=word) |
96 Q(discipline__nom__icontains=word) |
97 Q(discipline_secondaire__nom__icontains=word) |
98 Q(type__icontains=word) |
99 Q(lieu__icontains=word) |
100 Q(description__icontains=word) |
82f25472
EMS
101 Q(contact__icontains=word) |
102 Q(regions__nom__icontains=word))
0b1ddc11
EMS
103 if q is None:
104 q = part
105 else:
106 q = q & part
82f25472 107 return self.filter(q).distinct() if q is not None else self
4101cfc0 108
7bbf600c
EMS
109 def search_titre(self, text):
110 qs = self
111 for word in text.split():
112 qs = qs.filter(titre__icontains=word)
113 return qs
114
92c7413b 115class Evenement(models.Model):
7bbf600c
EMS
116 TYPE_CHOICES = ((u'Colloque', u'Colloque'),
117 (u'Conférence', u'Conférence'),
118 (u'Appel à contribution', u'Appel à contribution'),
119 (u'Journée d\'étude', u'Journée d\'étude'),
120 (None, u'Autre'))
121
c5b3da8b 122 uid = models.CharField(max_length = 255, default = str(uuid.uuid1()))
a5f76eb4 123 approuve = models.BooleanField(default=False, verbose_name=u'approuvé')
92c7413b
CR
124 titre = models.CharField(max_length=255)
125 discipline = models.ForeignKey('Discipline', related_name = "discipline",
126 blank = True, null = True)
a5f76eb4
EMS
127 discipline_secondaire = models.ForeignKey('Discipline', related_name="discipline_secondaire",
128 verbose_name=u"discipline secondaire",
129 blank=True, null=True)
92c7413b 130 mots_cles = models.TextField('Mots-Clés', blank = True, null = True)
7bbf600c 131 type = models.CharField(max_length=255, choices=TYPE_CHOICES)
a5f76eb4 132 fuseau = TimeZoneField(verbose_name='fuseau horaire')
92c7413b
CR
133 debut = models.DateTimeField(default = datetime.datetime.now)
134 fin = models.DateTimeField(default = datetime.datetime.now)
135 lieu = models.TextField()
136 description = models.TextField(blank = True, null = True)
137 #fichiers = TODO?
138 contact = models.TextField(blank = True, null = True)
139 url = models.CharField(max_length=255, blank = True, null = True)
a5f76eb4 140 regions = models.ManyToManyField(Region, blank=True, related_name="evenements", verbose_name='régions')
92c7413b 141
4101cfc0
EMS
142 objects = EvenementManager()
143
144 class Meta:
145 ordering = ['-debut']
146
020f79a9 147 def __unicode__(self,):
148 return "[%s] %s" % (self.uid, self.titre)
149
73309469 150 def clean(self):
151 from django.core.exceptions import ValidationError
152 if self.debut > self.fin:
153 raise ValidationError('La date de fin ne doit pas être antérieure à la date de début')
154
b7a741ad 155 def save(self, *args, **kwargs):
156 """Sauvegarde l'objet dans django et le synchronise avec caldav s'il a été
157 approuvé"""
73309469 158 self.clean()
b7a741ad 159 self.update_vevent()
160 super(Evenement, self).save(*args, **kwargs)
161
162 # methodes de commnunications avec CALDAV
163 def as_ical(self,):
164 """Retourne l'evenement django sous forme d'objet icalendar"""
165 cal = vobject.iCalendar()
166 cal.add('vevent')
167
168 # fournit son propre uid
7f56d0d4 169 if self.uid in [None, ""]:
b7a741ad 170 self.uid = str(uuid.uuid1())
171
172 cal.vevent.add('uid').value = self.uid
173
174 cal.vevent.add('summary').value = self.titre
175
176 if self.mots_cles is None:
177 kw = []
178 else:
179 kw = self.mots_cles.split(",")
180
181 try:
182 kw.append(self.discipline.nom)
183 kw.append(self.discipline_secondaire.nom)
184 kw.append(self.type)
185 except: pass
186
79b400f0 187 kw = [x.strip() for x in kw if len(x.strip()) > 0 and x is not None]
b7a741ad 188 for k in kw:
189 cal.vevent.add('x-auf-keywords').value = k
190
191 description = self.description
192 if len(kw) > 0:
193 if len(self.description) > 0:
194 description += "\n"
028f548f 195 description += u"Mots-clés: " + ", ".join(kw)
b7a741ad 196
197 cal.vevent.add('dtstart').value = combine(self.debut, self.fuseau)
198 cal.vevent.add('dtend').value = combine(self.fin, self.fuseau)
199 cal.vevent.add('created').value = combine(datetime.datetime.now(), "UTC")
200 cal.vevent.add('dtstamp').value = combine(datetime.datetime.now(), "UTC")
79b400f0 201 if len(description) > 0:
b7a741ad 202 cal.vevent.add('description').value = description
203 if len(self.contact) > 0:
204 cal.vevent.add('contact').value = self.contact
205 if len(self.url) > 0:
206 cal.vevent.add('url').value = self.url
207 if len(self.lieu) > 0:
208 cal.vevent.add('location').value = self.lieu
209 return cal
210
211 def update_vevent(self,):
212 """Essaie de créer l'évènement sur le serveur ical.
213 En cas de succès, l'évènement local devient donc inactif et approuvé"""
214 try:
215 if self.approuve:
216 event = self.as_ical()
217 client = caldav.DAVClient(CALENDRIER_URL)
218 cal = caldav.Calendar(client, url = CALENDRIER_URL)
219 e = caldav.Event(client, parent = cal, data = event.serialize(), id=self.uid)
220 e.save()
221 except:
222 self.approuve = False
223
224 def delete_vevent(self,):
225 """Supprime l'evenement sur le serveur caldav"""
226 try:
227 if self.approuve:
228 event = self.as_ical()
229 client = caldav.DAVClient(CALENDRIER_URL)
230 cal = caldav.Calendar(client, url = CALENDRIER_URL)
231 e = cal.event(self.uid)
232 e.delete()
233 except error.NotFoundError:
234 pass
235
264a3210
EMS
236 def assigner_regions(self, regions):
237 self.regions.add(*regions)
238
239 def assigner_disciplines(self, disciplines):
240 if len(disciplines) == 1:
241 if self.discipline:
242 self.discipline_secondaire = disciplines[0]
243 else:
244 self.discipline = disciplines[0]
245 elif len(disciplines) >= 2:
246 self.discipline = disciplines[0]
247 self.discipline_secondaire = disciplines[1]
248
b7a741ad 249
250# Surcharge du comportement de suppression
251# La méthode de connexion par signals est préférable à surcharger la méthode delete()
252# car dans le cas de la suppression par lots, cell-ci n'est pas invoquée
253def delete_vevent(sender, instance, *args, **kwargs):
254 instance.delete_vevent()
255
256pre_delete.connect(delete_vevent, sender = Evenement)
257
258
d972b61d 259class ListSet(models.Model):
260 spec = models.CharField(primary_key = True, max_length = 255)
261 name = models.CharField(max_length = 255)
262 server = models.CharField(max_length = 255)
9eda5d6c 263 validated = models.BooleanField(default = True)
d972b61d 264
10d37e44 265 def __unicode__(self,):
266 return self.name
267
da44ce68
EMS
268class RecordManager(models.Manager):
269
270 def get_query_set(self):
271 return RecordQuerySet(self.model)
272
273 def search(self, text):
274 return self.get_query_set().search(text)
275
f153be1b
EMS
276 def validated(self):
277 return self.get_query_set().validated()
278
da44ce68
EMS
279class RecordQuerySet(models.query.QuerySet):
280
281 def search(self, text):
f12cc7fb 282 qs = self
da44ce68 283 words = text.split()
da44ce68 284
f12cc7fb
EMS
285 # Ne garder que les ressources qui contiennent tous les mots
286 # demandés.
0b1ddc11 287 q = None
f12cc7fb 288 for word in words:
82f25472 289 matching_pays = list(Pays.objects.filter(Q(nom__icontains=word) | Q(region__nom__icontains=word)).values_list('pk', flat=True))
0b1ddc11
EMS
290 part = (Q(title__icontains=word) | Q(description__icontains=word) |
291 Q(creator__icontains=word) | Q(contributor__icontains=word) |
292 Q(subject__icontains=word) | Q(disciplines__nom__icontains=word) |
82f25472 293 Q(regions__nom__icontains=word) | Q(pays__in=matching_pays))
0b1ddc11
EMS
294 if q is None:
295 q = part
296 else:
297 q = q & part
3c23982e
EMS
298 if q is not None:
299 qs = qs.filter(q).distinct()
f12cc7fb
EMS
300
301 # On donne un point pour chaque mot présent dans le titre.
3c23982e
EMS
302 if words:
303 score_expr = ' + '.join(['(title LIKE %s)'] * len(words))
304 score_params = ['%' + word + '%' for word in words]
305 qs = qs.extra(
306 select={'score': score_expr},
307 select_params=score_params
308 ).order_by('-score')
3c23982e 309 return qs
f12cc7fb
EMS
310
311 def search_auteur(self, text):
312 qs = self
313 for word in text.split():
314 qs = qs.filter(Q(creator__icontains=word) | Q(contributor__icontains=word))
315 return qs
316
317 def search_sujet(self, text):
318 qs = self
319 for word in text.split():
320 qs = qs.filter(subject__icontains=word)
321 return qs
322
323 def search_titre(self, text):
324 qs = self
325 for word in text.split():
326 qs = qs.filter(title__icontains=word)
327 return qs
328
f153be1b
EMS
329 def validated(self):
330 """Ne garder que les ressources validées et qui sont soit dans aucun
331 listset ou au moins dans un listset validé."""
332 qs = self.filter(validated=True)
82f25472
EMS
333 qs = qs.filter(Q(listsets__isnull=True) | Q(listsets__validated=True))
334 return qs.distinct()
f153be1b 335
0cc5f772 336class Record(models.Model):
23b5b3d5 337
338 #fonctionnement interne
0cc5f772 339 id = models.AutoField(primary_key = True)
a5f76eb4 340 server = models.CharField(max_length = 255, verbose_name=u'serveur')
23b5b3d5 341 last_update = models.CharField(max_length = 255)
342 last_checksum = models.CharField(max_length = 255)
a5f76eb4 343 validated = models.BooleanField(default=True, verbose_name=u'validé')
23b5b3d5 344
345 #OAI
346 title = models.TextField(null = True, blank = True)
347 creator = models.TextField(null = True, blank = True)
348 description = models.TextField(null = True, blank = True)
349 modified = models.CharField(max_length = 255, null = True, blank = True)
350 identifier = models.CharField(max_length = 255, null = True, blank = True, unique = True)
351 uri = models.CharField(max_length = 255, null = True, blank = True, unique = True)
352 source = models.TextField(null = True, blank = True)
353 contributor = models.TextField(null = True, blank = True)
354 subject = models.TextField(null = True, blank = True)
355 publisher = models.TextField(null = True, blank = True)
356 type = models.TextField(null = True, blank = True)
357 format = models.TextField(null = True, blank = True)
358 language = models.TextField(null = True, blank = True)
da9020f3 359
c88d78dc 360 listsets = models.ManyToManyField(ListSet, null = True, blank = True)
d972b61d 361
da9020f3 362 #SEP 2 (aucune données récoltées)
23b5b3d5 363 alt_title = models.TextField(null = True, blank = True)
364 abstract = models.TextField(null = True, blank = True)
365 creation = models.CharField(max_length = 255, null = True, blank = True)
366 issued = models.CharField(max_length = 255, null = True, blank = True)
367 isbn = models.TextField(null = True, blank = True)
368 orig_lang = models.TextField(null = True, blank = True)
da9020f3 369
370 # Metadata AUF multivaluées
a342f93a
EMS
371 disciplines = models.ManyToManyField(Discipline, blank=True)
372 thematiques = models.ManyToManyField(Thematique, blank=True, verbose_name='thématiques')
373 pays = models.ManyToManyField(Pays, blank=True)
374 regions = models.ManyToManyField(Region, blank=True, verbose_name='régions')
0cc5f772 375
da44ce68
EMS
376 # Manager
377 objects = RecordManager()
378
264a3210
EMS
379 def __unicode__(self):
380 return "[%s] %s" % (self.server, self.title)
381
382 def getServeurURL(self):
f98ad449 383 """Retourne l'URL du serveur de provenance"""
384 return RESOURCES[self.server]['url']
385
264a3210 386 def est_complet(self):
6d885e0c 387 """teste si le record à toutes les données obligatoires"""
388 return self.disciplines.count() > 0 and \
389 self.thematiques.count() > 0 and \
390 self.pays.count() > 0 and \
391 self.regions.count() > 0
392
264a3210
EMS
393 def assigner_regions(self, regions):
394 self.regions.add(*regions)
da9020f3 395
264a3210
EMS
396 def assigner_disciplines(self, disciplines):
397 self.disciplines.add(*disciplines)
398
399
6d885e0c 400class Serveur(models.Model):
b7a741ad 401 """Identification d'un serveur d'ou proviennent les références"""
6d885e0c 402 nom = models.CharField(primary_key = True, max_length = 255)
403
404 def __unicode__(self,):
405 return self.nom
406
407 def conf_2_db(self,):
408 for k in RESOURCES.keys():
409 s, created = Serveur.objects.get_or_create(nom=k)
410 s.nom = k
411 s.save()
412
413class Profile(models.Model):
414 user = models.ForeignKey(User, unique=True)
415 serveurs = models.ManyToManyField(Serveur, null = True, blank = True)
0cc5f772
CR
416
417class HarvestLog(models.Model):
23b5b3d5 418 context = models.CharField(max_length = 255)
419 name = models.CharField(max_length = 255)
0cc5f772 420 date = models.DateTimeField(auto_now = True)
23b5b3d5 421 added = models.IntegerField(null = True, blank = True)
422 updated = models.IntegerField(null = True, blank = True)
a85ba76e 423 processed = models.IntegerField(null = True, blank = True)
23b5b3d5 424 record = models.ForeignKey(Record, null = True, blank = True)
425
426 @staticmethod
427 def add(message):
428 logger = HarvestLog()
429 if message.has_key('record_id'):
430 message['record'] = Record.objects.get(id=message['record_id'])
431 del(message['record_id'])
432
433 for k,v in message.items():
434 setattr(logger, k, v)
435 logger.save()