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