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