Bug: SEP plantait lorsqu'on omettait de donner une date de début ou de fin à un
[auf_savoirs_en_partage_django.git] / auf_savoirs_en_partage / savoirs / models.py
CommitLineData
92c7413b 1# -*- encoding: utf-8 -*-
27fe0d70 2import simplejson, uuid, datetime, caldav, vobject, uuid, random, operator, pytz, os
86983865 3from babel.dates import get_timezone_name
6d885e0c 4from django.contrib.auth.models import User
d15017b2 5from django.db import models
15261361 6from django.db.models import Q, Max
b7a741ad 7from django.db.models.signals import pre_delete
6d885e0c 8from auf_savoirs_en_partage.backend_config import RESOURCES
da9020f3 9from savoirs.globals import META
74b087e5 10from settings import CALENDRIER_URL, SITE_ROOT_URL
e3c3296e 11from datamaster_modeles.models import Thematique, Pays, Region
b7a741ad 12from lib.calendrier import combine
13from caldav.lib import error
d15017b2 14
15261361
EMS
15class RandomQuerySetMixin(object):
16 """Mixin pour les modèles.
17
18 ORDER BY RAND() est très lent sous MySQL. On a besoin d'une autre
19 méthode pour récupérer des objets au hasard.
20 """
21
22 def random(self, n=1):
23 """Récupère aléatoirement un nombre donné d'objets."""
bae03b7b
EMS
24 count = self.count()
25 positions = random.sample(xrange(count), min(n, count))
26 return [self[p] for p in positions]
15261361 27
d15017b2
CR
28class Discipline(models.Model):
29 id = models.IntegerField(primary_key=True, db_column='id_discipline')
30 nom = models.CharField(max_length=765, db_column='nom_discipline')
6ef8ead4
CR
31
32 def __unicode__ (self):
92c7413b 33 return self.nom
6ef8ead4 34
d15017b2
CR
35 class Meta:
36 db_table = u'discipline'
37 ordering = ["nom",]
38
79c398f6
CR
39class SourceActualite(models.Model):
40 nom = models.CharField(max_length=255)
41 url = models.CharField(max_length=255)
ccbc4363 42
43 def __unicode__(self,):
44 return u"%s" % self.nom
79c398f6 45
2f9c4d6c
EMS
46class ActualiteManager(models.Manager):
47
48 def get_query_set(self):
49 return ActualiteQuerySet(self.model)
50
da44ce68
EMS
51 def search(self, text):
52 return self.get_query_set().search(text)
53
c1b134f8
EMS
54 def filter_region(self, region):
55 return self.get_query_set().filter_region(region)
56
57 def filter_discipline(self, discipline):
58 return self.get_query_set().filter_discipline(discipline)
59
15261361 60class ActualiteQuerySet(models.query.QuerySet, RandomQuerySetMixin):
2f9c4d6c
EMS
61
62 def search(self, text):
82f25472
EMS
63 q = None
64 for word in text.split():
65 part = (Q(titre__icontains=word) | Q(texte__icontains=word) |
66 Q(regions__nom__icontains=word) | Q(disciplines__nom__icontains=word))
67 if q is None:
68 q = part
69 else:
70 q = q & part
71 return self.filter(q).distinct() if q is not None else self
2f9c4d6c 72
bae03b7b
EMS
73 def filter_discipline(self, discipline):
74 """Ne conserve que les actualités dans la discipline donnée.
75
76 Si ``disicipline`` est None, ce filtre n'a aucun effet."""
c2fcb90b
EMS
77 if discipline is None:
78 return self
f0692c02
EMS
79 if not isinstance(discipline, Discipline):
80 discipline = Discipline.objects.get(pk=discipline)
183c10a5
EMS
81 return self.filter(Q(disciplines=discipline) |
82 Q(titre__icontains=discipline.nom) |
83 Q(texte__icontains=discipline.nom)).distinct()
bae03b7b
EMS
84
85 def filter_region(self, region):
86 """Ne conserve que les actualités dans la région donnée.
87
88 Si ``region`` est None, ce filtre n'a aucun effet."""
c2fcb90b
EMS
89 if region is None:
90 return self
f0692c02
EMS
91 if not isinstance(region, Region):
92 region = Region.objects.get(pk=region)
183c10a5
EMS
93 return self.filter(Q(regions=region) |
94 Q(titre__icontains=region.nom) |
95 Q(texte__icontains=region.nom)).distinct()
bae03b7b 96
d15017b2 97class Actualite(models.Model):
4f262f90 98 id = models.AutoField(primary_key=True, db_column='id_actualite')
d15017b2
CR
99 titre = models.CharField(max_length=765, db_column='titre_actualite')
100 texte = models.TextField(db_column='texte_actualite')
101 url = models.CharField(max_length=765, db_column='url_actualite')
d15017b2 102 date = models.DateField(db_column='date_actualite')
f554ef70 103 visible = models.BooleanField(db_column='visible_actualite', default = False)
3b6456b0 104 ancienid = models.IntegerField(db_column='ancienId_actualite', blank = True, null = True)
ccbc4363 105 source = models.ForeignKey(SourceActualite, blank = True, null = True)
3a45eb64 106 disciplines = models.ManyToManyField(Discipline, blank=True, related_name="actualites")
a5f76eb4 107 regions = models.ManyToManyField(Region, blank=True, related_name="actualites", verbose_name='régions')
6ef8ead4 108
2f9c4d6c
EMS
109 objects = ActualiteManager()
110
d15017b2
CR
111 class Meta:
112 db_table = u'actualite'
113 ordering = ["-date",]
92c7413b 114
264a3210
EMS
115 def __unicode__ (self):
116 return "%s" % (self.titre)
117
118 def assigner_disciplines(self, disciplines):
119 self.disciplines.add(*disciplines)
120
121 def assigner_regions(self, regions):
122 self.regions.add(*regions)
123
4101cfc0
EMS
124class EvenementManager(models.Manager):
125
126 def get_query_set(self):
127 return EvenementQuerySet(self.model)
128
129 def search(self, text):
130 return self.get_query_set().search(text)
131
c1b134f8
EMS
132 def filter_region(self, region):
133 return self.get_query_set().filter_region(region)
134
135 def filter_discipline(self, discipline):
136 return self.get_query_set().filter_discipline(discipline)
137
15261361 138class EvenementQuerySet(models.query.QuerySet, RandomQuerySetMixin):
4101cfc0
EMS
139
140 def search(self, text):
0b1ddc11
EMS
141 q = None
142 for word in text.split():
143 part = (Q(titre__icontains=word) |
144 Q(mots_cles__icontains=word) |
145 Q(discipline__nom__icontains=word) |
146 Q(discipline_secondaire__nom__icontains=word) |
147 Q(type__icontains=word) |
148 Q(lieu__icontains=word) |
149 Q(description__icontains=word) |
82f25472
EMS
150 Q(contact__icontains=word) |
151 Q(regions__nom__icontains=word))
0b1ddc11
EMS
152 if q is None:
153 q = part
154 else:
155 q = q & part
82f25472 156 return self.filter(q).distinct() if q is not None else self
4101cfc0 157
7bbf600c
EMS
158 def search_titre(self, text):
159 qs = self
160 for word in text.split():
161 qs = qs.filter(titre__icontains=word)
162 return qs
163
bae03b7b
EMS
164 def filter_discipline(self, discipline):
165 """Ne conserve que les évènements dans la discipline donnée.
166
167 Si ``disicipline`` est None, ce filtre n'a aucun effet."""
c2fcb90b
EMS
168 if discipline is None:
169 return self
f0692c02
EMS
170 if not isinstance(discipline, Discipline):
171 discipline = Discipline.objects.get(pk=discipline)
183c10a5
EMS
172 return self.filter(Q(discipline=discipline) |
173 Q(discipline_secondaire=discipline) |
174 Q(titre__icontains=discipline.nom) |
175 Q(mots_cles__icontains=discipline.nom) |
176 Q(description__icontains=discipline.nom))
bae03b7b
EMS
177
178 def filter_region(self, region):
179 """Ne conserve que les évènements dans la région donnée.
180
181 Si ``region`` est None, ce filtre n'a aucun effet."""
c2fcb90b
EMS
182 if region is None:
183 return self
f0692c02
EMS
184 if not isinstance(region, Region):
185 region = Region.objects.get(pk=region)
183c10a5
EMS
186 return self.filter(Q(regions=region) |
187 Q(titre__icontains=region.nom) |
188 Q(mots_cles__icontains=region.nom) |
189 Q(description__icontains=region.nom) |
190 Q(lieu__icontains=region.nom)).distinct()
bae03b7b 191
86983865
EMS
192def build_time_zone_choices():
193 fr_names = set()
194 tzones = []
195 now = datetime.datetime.now()
196 for tzname in pytz.common_timezones:
197 tz = pytz.timezone(tzname)
198 fr_name = get_timezone_name(tz, locale='fr_FR')
199 if fr_name in fr_names:
200 continue
201 fr_names.add(fr_name)
202 offset = tz.utcoffset(now)
203 seconds = offset.seconds + offset.days * 86400
204 (hours, minutes) = divmod(seconds // 60, 60)
205 offset_str = 'UTC%+d:%d' % (hours, minutes) if minutes else 'UTC%+d' % hours
206 tzones.append((seconds, tzname, '%s - %s' % (offset_str, fr_name)))
207 tzones.sort()
208 return [(tz[1], tz[2]) for tz in tzones]
209
92c7413b 210class Evenement(models.Model):
7bbf600c
EMS
211 TYPE_CHOICES = ((u'Colloque', u'Colloque'),
212 (u'Conférence', u'Conférence'),
213 (u'Appel à contribution', u'Appel à contribution'),
214 (u'Journée d\'étude', u'Journée d\'étude'),
215 (None, u'Autre'))
86983865
EMS
216 TIME_ZONE_CHOICES = build_time_zone_choices()
217
74b087e5 218 uid = models.CharField(max_length=255, default=str(uuid.uuid1()))
a5f76eb4 219 approuve = models.BooleanField(default=False, verbose_name=u'approuvé')
92c7413b
CR
220 titre = models.CharField(max_length=255)
221 discipline = models.ForeignKey('Discipline', related_name = "discipline",
222 blank = True, null = True)
a5f76eb4
EMS
223 discipline_secondaire = models.ForeignKey('Discipline', related_name="discipline_secondaire",
224 verbose_name=u"discipline secondaire",
225 blank=True, null=True)
74b087e5 226 mots_cles = models.TextField('Mots-Clés', blank=True, null=True)
7bbf600c 227 type = models.CharField(max_length=255, choices=TYPE_CHOICES)
86983865 228 lieu = models.TextField()
74b087e5
EMS
229 debut = models.DateTimeField(default=datetime.datetime.now)
230 fin = models.DateTimeField(default=datetime.datetime.now)
86983865 231 fuseau = models.CharField(max_length=100, choices=TIME_ZONE_CHOICES, verbose_name='fuseau horaire')
74b087e5
EMS
232 description = models.TextField(blank=True, null=True)
233 contact = models.TextField(blank=True, null=True)
234 url = models.CharField(max_length=255, blank=True, null=True)
235 piece_jointe = models.FileField(upload_to='agenda/pj', blank=True, verbose_name='pièce jointe')
a5f76eb4 236 regions = models.ManyToManyField(Region, blank=True, related_name="evenements", verbose_name='régions')
92c7413b 237
4101cfc0
EMS
238 objects = EvenementManager()
239
240 class Meta:
241 ordering = ['-debut']
242
020f79a9 243 def __unicode__(self,):
244 return "[%s] %s" % (self.uid, self.titre)
245
27fe0d70
EMS
246 def piece_jointe_display(self):
247 return self.piece_jointe and os.path.basename(self.piece_jointe.name)
248
73309469 249 def clean(self):
250 from django.core.exceptions import ValidationError
251 if self.debut > self.fin:
252 raise ValidationError('La date de fin ne doit pas être antérieure à la date de début')
253
b7a741ad 254 def save(self, *args, **kwargs):
255 """Sauvegarde l'objet dans django et le synchronise avec caldav s'il a été
256 approuvé"""
73309469 257 self.clean()
b7a741ad 258 self.update_vevent()
259 super(Evenement, self).save(*args, **kwargs)
260
261 # methodes de commnunications avec CALDAV
262 def as_ical(self,):
263 """Retourne l'evenement django sous forme d'objet icalendar"""
264 cal = vobject.iCalendar()
265 cal.add('vevent')
266
267 # fournit son propre uid
7f56d0d4 268 if self.uid in [None, ""]:
b7a741ad 269 self.uid = str(uuid.uuid1())
270
271 cal.vevent.add('uid').value = self.uid
272
273 cal.vevent.add('summary').value = self.titre
274
275 if self.mots_cles is None:
276 kw = []
277 else:
278 kw = self.mots_cles.split(",")
279
280 try:
281 kw.append(self.discipline.nom)
282 kw.append(self.discipline_secondaire.nom)
283 kw.append(self.type)
284 except: pass
285
79b400f0 286 kw = [x.strip() for x in kw if len(x.strip()) > 0 and x is not None]
b7a741ad 287 for k in kw:
288 cal.vevent.add('x-auf-keywords').value = k
289
290 description = self.description
291 if len(kw) > 0:
292 if len(self.description) > 0:
293 description += "\n"
028f548f 294 description += u"Mots-clés: " + ", ".join(kw)
b7a741ad 295
7f214e0f
EMS
296 cal.vevent.add('dtstart').value = combine(self.debut, pytz.timezone(self.fuseau))
297 cal.vevent.add('dtend').value = combine(self.fin, pytz.timezone(self.fuseau))
b7a741ad 298 cal.vevent.add('created').value = combine(datetime.datetime.now(), "UTC")
299 cal.vevent.add('dtstamp').value = combine(datetime.datetime.now(), "UTC")
79b400f0 300 if len(description) > 0:
b7a741ad 301 cal.vevent.add('description').value = description
302 if len(self.contact) > 0:
303 cal.vevent.add('contact').value = self.contact
304 if len(self.url) > 0:
305 cal.vevent.add('url').value = self.url
306 if len(self.lieu) > 0:
307 cal.vevent.add('location').value = self.lieu
74b087e5
EMS
308 if self.piece_jointe:
309 url = self.piece_jointe.url
310 if not url.startswith('http://'):
311 url = SITE_ROOT_URL + url
312 cal.vevent.add('attach').value = url
b7a741ad 313 return cal
314
315 def update_vevent(self,):
316 """Essaie de créer l'évènement sur le serveur ical.
317 En cas de succès, l'évènement local devient donc inactif et approuvé"""
318 try:
319 if self.approuve:
320 event = self.as_ical()
321 client = caldav.DAVClient(CALENDRIER_URL)
322 cal = caldav.Calendar(client, url = CALENDRIER_URL)
323 e = caldav.Event(client, parent = cal, data = event.serialize(), id=self.uid)
324 e.save()
325 except:
326 self.approuve = False
327
328 def delete_vevent(self,):
329 """Supprime l'evenement sur le serveur caldav"""
330 try:
331 if self.approuve:
332 event = self.as_ical()
333 client = caldav.DAVClient(CALENDRIER_URL)
334 cal = caldav.Calendar(client, url = CALENDRIER_URL)
335 e = cal.event(self.uid)
336 e.delete()
337 except error.NotFoundError:
338 pass
339
264a3210
EMS
340 def assigner_regions(self, regions):
341 self.regions.add(*regions)
342
343 def assigner_disciplines(self, disciplines):
344 if len(disciplines) == 1:
345 if self.discipline:
346 self.discipline_secondaire = disciplines[0]
347 else:
348 self.discipline = disciplines[0]
349 elif len(disciplines) >= 2:
350 self.discipline = disciplines[0]
351 self.discipline_secondaire = disciplines[1]
352
b7a741ad 353
354# Surcharge du comportement de suppression
355# La méthode de connexion par signals est préférable à surcharger la méthode delete()
356# car dans le cas de la suppression par lots, cell-ci n'est pas invoquée
357def delete_vevent(sender, instance, *args, **kwargs):
358 instance.delete_vevent()
359
360pre_delete.connect(delete_vevent, sender = Evenement)
361
362
d972b61d 363class ListSet(models.Model):
364 spec = models.CharField(primary_key = True, max_length = 255)
365 name = models.CharField(max_length = 255)
366 server = models.CharField(max_length = 255)
9eda5d6c 367 validated = models.BooleanField(default = True)
d972b61d 368
10d37e44 369 def __unicode__(self,):
370 return self.name
371
da44ce68
EMS
372class RecordManager(models.Manager):
373
374 def get_query_set(self):
375 return RecordQuerySet(self.model)
376
377 def search(self, text):
378 return self.get_query_set().search(text)
379
f153be1b
EMS
380 def validated(self):
381 return self.get_query_set().validated()
382
c1b134f8
EMS
383 def filter_region(self, region):
384 return self.get_query_set().filter_region(region)
385
386 def filter_discipline(self, discipline):
387 return self.get_query_set().filter_discipline(discipline)
388
15261361 389class RecordQuerySet(models.query.QuerySet, RandomQuerySetMixin):
da44ce68
EMS
390
391 def search(self, text):
f12cc7fb 392 qs = self
da44ce68 393 words = text.split()
da44ce68 394
f12cc7fb
EMS
395 # Ne garder que les ressources qui contiennent tous les mots
396 # demandés.
0b1ddc11 397 q = None
f12cc7fb 398 for word in words:
82f25472 399 matching_pays = list(Pays.objects.filter(Q(nom__icontains=word) | Q(region__nom__icontains=word)).values_list('pk', flat=True))
0b1ddc11
EMS
400 part = (Q(title__icontains=word) | Q(description__icontains=word) |
401 Q(creator__icontains=word) | Q(contributor__icontains=word) |
402 Q(subject__icontains=word) | Q(disciplines__nom__icontains=word) |
4360ac5f
EMS
403 Q(regions__nom__icontains=word) | Q(pays__in=matching_pays) |
404 Q(publisher__icontains=word))
0b1ddc11
EMS
405 if q is None:
406 q = part
407 else:
408 q = q & part
3c23982e
EMS
409 if q is not None:
410 qs = qs.filter(q).distinct()
f12cc7fb
EMS
411
412 # On donne un point pour chaque mot présent dans le titre.
3c23982e
EMS
413 if words:
414 score_expr = ' + '.join(['(title LIKE %s)'] * len(words))
415 score_params = ['%' + word + '%' for word in words]
416 qs = qs.extra(
417 select={'score': score_expr},
418 select_params=score_params
419 ).order_by('-score')
3c23982e 420 return qs
f12cc7fb
EMS
421
422 def search_auteur(self, text):
423 qs = self
424 for word in text.split():
425 qs = qs.filter(Q(creator__icontains=word) | Q(contributor__icontains=word))
426 return qs
427
428 def search_sujet(self, text):
429 qs = self
430 for word in text.split():
431 qs = qs.filter(subject__icontains=word)
432 return qs
433
434 def search_titre(self, text):
435 qs = self
436 for word in text.split():
437 qs = qs.filter(title__icontains=word)
438 return qs
439
bae03b7b
EMS
440 def filter_discipline(self, discipline):
441 """Ne conserve que les ressources dans la discipline donnée.
442
443 Si ``disicipline`` est None, ce filtre n'a aucun effet."""
c2fcb90b
EMS
444 if discipline is None:
445 return self
f0692c02
EMS
446 if not isinstance(discipline, Discipline):
447 discipline = Discipline.objects.get(pk=discipline)
183c10a5
EMS
448 return self.filter(Q(disciplines=discipline) |
449 Q(title__icontains=discipline.nom) |
450 Q(description__icontains=discipline.nom) |
451 Q(subject__icontains=discipline.nom)).distinct()
bae03b7b
EMS
452
453 def filter_region(self, region):
454 """Ne conserve que les ressources dans la région donnée.
455
456 Si ``region`` est None, ce filtre n'a aucun effet."""
c2fcb90b
EMS
457 if region is None:
458 return self
f0692c02
EMS
459 if not isinstance(region, Region):
460 region = Region.objects.get(pk=region)
183c10a5
EMS
461 return self.filter(Q(pays__region=region) |
462 Q(regions=region) |
463 Q(title__icontains=region.nom) |
464 Q(description__icontains=region.nom) |
465 Q(subject__icontains=region.nom)).distinct()
bae03b7b 466
f153be1b
EMS
467 def validated(self):
468 """Ne garder que les ressources validées et qui sont soit dans aucun
469 listset ou au moins dans un listset validé."""
470 qs = self.filter(validated=True)
82f25472
EMS
471 qs = qs.filter(Q(listsets__isnull=True) | Q(listsets__validated=True))
472 return qs.distinct()
f153be1b 473
77b0fac0
EMS
474 def filter(self, *args, **kwargs):
475 """Gère des filtres supplémentaires pour l'admin.
476
477 C'est la seule façon que j'ai trouvée de contourner les mécanismes
478 de recherche de l'admin."""
479 search = kwargs.pop('admin_search', None)
480 search_titre = kwargs.pop('admin_search_titre', None)
481 search_sujet = kwargs.pop('admin_search_sujet', None)
482 search_description = kwargs.pop('admin_search_description', None)
483 search_auteur = kwargs.pop('admin_search_auteur', None)
484
485 if search:
486 qs = self
487 search_all = not (search_titre or search_description or search_sujet or search_auteur)
488 fields = []
489 if search_titre or search_all:
490 fields += ['title', 'alt_title']
491 if search_description or search_all:
492 fields += ['description', 'abstract']
493 if search_sujet or search_all:
494 fields += ['subject']
495 if search_auteur or search_all:
496 fields += ['creator', 'contributor']
497
498 for bit in search.split():
499 or_queries = [Q(**{field + '__icontains': bit}) for field in fields]
500 qs = qs.filter(reduce(operator.or_, or_queries))
501
502 if args or kwargs:
503 qs = super(RecordQuerySet, qs).filter(*args, **kwargs)
504 return qs
505 else:
506 return super(RecordQuerySet, self).filter(*args, **kwargs)
507
0cc5f772 508class Record(models.Model):
23b5b3d5 509
510 #fonctionnement interne
0cc5f772 511 id = models.AutoField(primary_key = True)
a5f76eb4 512 server = models.CharField(max_length = 255, verbose_name=u'serveur')
23b5b3d5 513 last_update = models.CharField(max_length = 255)
514 last_checksum = models.CharField(max_length = 255)
a5f76eb4 515 validated = models.BooleanField(default=True, verbose_name=u'validé')
23b5b3d5 516
517 #OAI
18dbd2cf
EMS
518 title = models.TextField(null=True, blank=True, verbose_name=u'titre')
519 creator = models.TextField(null=True, blank=True, verbose_name=u'auteur')
520 description = models.TextField(null=True, blank=True)
521 modified = models.CharField(max_length=255, null=True, blank=True)
23b5b3d5 522 identifier = models.CharField(max_length = 255, null = True, blank = True, unique = True)
523 uri = models.CharField(max_length = 255, null = True, blank = True, unique = True)
524 source = models.TextField(null = True, blank = True)
525 contributor = models.TextField(null = True, blank = True)
18dbd2cf 526 subject = models.TextField(null=True, blank=True, verbose_name='sujet')
23b5b3d5 527 publisher = models.TextField(null = True, blank = True)
528 type = models.TextField(null = True, blank = True)
529 format = models.TextField(null = True, blank = True)
530 language = models.TextField(null = True, blank = True)
da9020f3 531
c88d78dc 532 listsets = models.ManyToManyField(ListSet, null = True, blank = True)
d972b61d 533
da9020f3 534 #SEP 2 (aucune données récoltées)
23b5b3d5 535 alt_title = models.TextField(null = True, blank = True)
536 abstract = models.TextField(null = True, blank = True)
537 creation = models.CharField(max_length = 255, null = True, blank = True)
538 issued = models.CharField(max_length = 255, null = True, blank = True)
539 isbn = models.TextField(null = True, blank = True)
540 orig_lang = models.TextField(null = True, blank = True)
da9020f3 541
542 # Metadata AUF multivaluées
a342f93a
EMS
543 disciplines = models.ManyToManyField(Discipline, blank=True)
544 thematiques = models.ManyToManyField(Thematique, blank=True, verbose_name='thématiques')
545 pays = models.ManyToManyField(Pays, blank=True)
546 regions = models.ManyToManyField(Region, blank=True, verbose_name='régions')
0cc5f772 547
da44ce68
EMS
548 # Manager
549 objects = RecordManager()
550
18dbd2cf
EMS
551 class Meta:
552 verbose_name = 'ressource'
553
264a3210
EMS
554 def __unicode__(self):
555 return "[%s] %s" % (self.server, self.title)
556
557 def getServeurURL(self):
f98ad449 558 """Retourne l'URL du serveur de provenance"""
559 return RESOURCES[self.server]['url']
560
264a3210 561 def est_complet(self):
6d885e0c 562 """teste si le record à toutes les données obligatoires"""
563 return self.disciplines.count() > 0 and \
564 self.thematiques.count() > 0 and \
565 self.pays.count() > 0 and \
566 self.regions.count() > 0
567
264a3210
EMS
568 def assigner_regions(self, regions):
569 self.regions.add(*regions)
da9020f3 570
264a3210
EMS
571 def assigner_disciplines(self, disciplines):
572 self.disciplines.add(*disciplines)
264a3210 573
6d885e0c 574class Serveur(models.Model):
b7a741ad 575 """Identification d'un serveur d'ou proviennent les références"""
6d885e0c 576 nom = models.CharField(primary_key = True, max_length = 255)
577
578 def __unicode__(self,):
579 return self.nom
580
581 def conf_2_db(self,):
582 for k in RESOURCES.keys():
583 s, created = Serveur.objects.get_or_create(nom=k)
584 s.nom = k
585 s.save()
586
587class Profile(models.Model):
588 user = models.ForeignKey(User, unique=True)
589 serveurs = models.ManyToManyField(Serveur, null = True, blank = True)
0cc5f772
CR
590
591class HarvestLog(models.Model):
23b5b3d5 592 context = models.CharField(max_length = 255)
593 name = models.CharField(max_length = 255)
0cc5f772 594 date = models.DateTimeField(auto_now = True)
23b5b3d5 595 added = models.IntegerField(null = True, blank = True)
596 updated = models.IntegerField(null = True, blank = True)
a85ba76e 597 processed = models.IntegerField(null = True, blank = True)
23b5b3d5 598 record = models.ForeignKey(Record, null = True, blank = True)
599
600 @staticmethod
601 def add(message):
602 logger = HarvestLog()
603 if message.has_key('record_id'):
604 message['record'] = Record.objects.get(id=message['record_id'])
605 del(message['record_id'])
606
607 for k,v in message.items():
608 setattr(logger, k, v)
609 logger.save()