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