574eeb18f0bcf819098638da5d456a00644ab8de
[auf_savoirs_en_partage_django.git] / auf_savoirs_en_partage / savoirs / models.py
1 # -*- encoding: utf-8 -*-
2 import simplejson, uuid, datetime, caldav, vobject, uuid
3 from django.contrib.auth.models import User
4 from django.db import models
5 from django.db.models.signals import pre_delete
6 from timezones.fields import TimeZoneField
7 from auf_savoirs_en_partage.backend_config import RESOURCES
8 from savoirs.globals import META
9 from settings import CALENDRIER_URL
10 from datamaster_modeles.models import Thematique, Pays, Region
11 from lib.calendrier import combine
12 from caldav.lib import error
13
14 class Discipline(models.Model):
15 id = models.IntegerField(primary_key=True, db_column='id_discipline')
16 nom = models.CharField(max_length=765, db_column='nom_discipline')
17
18 def __unicode__ (self):
19 return self.nom
20
21 class Meta:
22 db_table = u'discipline'
23 ordering = ["nom",]
24
25 class SourceActualite(models.Model):
26 nom = models.CharField(max_length=255)
27 url = models.CharField(max_length=255)
28
29 def __unicode__(self,):
30 return u"%s" % self.nom
31
32 class ActualiteManager(models.Manager):
33
34 def get_query_set(self):
35 return ActualiteQuerySet(self.model)
36
37 def search(self, text):
38 return self.get_query_set().search(text)
39
40 class ActualiteQuerySet(models.query.QuerySet):
41
42 def search(self, text):
43 return self.filter(titre__icontains=text)
44
45 class Actualite(models.Model):
46 id = models.AutoField(primary_key=True, db_column='id_actualite')
47 titre = models.CharField(max_length=765, db_column='titre_actualite')
48 texte = models.TextField(db_column='texte_actualite')
49 url = models.CharField(max_length=765, db_column='url_actualite')
50 date = models.DateField(db_column='date_actualite')
51 visible = models.BooleanField(db_column='visible_actualite', default = False)
52 ancienid = models.IntegerField(db_column='ancienId_actualite', blank = True, null = True)
53 source = models.ForeignKey(SourceActualite, blank = True, null = True)
54
55 objects = ActualiteManager()
56
57 def __unicode__ (self):
58 return "%s" % (self.titre)
59
60 class Meta:
61 db_table = u'actualite'
62 ordering = ["-date",]
63
64 class Evenement(models.Model):
65 uid = models.CharField(max_length = 255, default = str(uuid.uuid1()))
66 approuve = models.BooleanField(default = False)
67 titre = models.CharField(max_length=255)
68 discipline = models.ForeignKey('Discipline', related_name = "discipline",
69 blank = True, null = True)
70 discipline_secondaire = models.ForeignKey('Discipline', related_name = \
71 "discipline_secondaire",
72 verbose_name = \
73 "Discipline secondaire",
74 blank = True, null = True)
75 mots_cles = models.TextField('Mots-Clés', blank = True, null = True)
76 type = models.CharField(max_length = 255, choices = \
77 (('Colloque', 'Colloque'),
78 ('Conférence', 'Conférence'),
79 ('Appel à contribution', 'Appel à contribution'),
80 ('Journée d\'étude', 'Journée d\'étude'),
81 (None, 'Autre')
82 )) #TODO: choices
83 fuseau = TimeZoneField(verbose_name = 'Fuseau horaire')
84 debut = models.DateTimeField(default = datetime.datetime.now)
85 fin = models.DateTimeField(default = datetime.datetime.now)
86 lieu = models.TextField()
87 description = models.TextField(blank = True, null = True)
88 #fichiers = TODO?
89 contact = models.TextField(blank = True, null = True)
90 url = models.CharField(max_length=255, blank = True, null = True)
91
92 def __unicode__(self,):
93 return "[%s] %s" % (self.uid, self.titre)
94
95 def save(self, *args, **kwargs):
96 """Sauvegarde l'objet dans django et le synchronise avec caldav s'il a été
97 approuvé"""
98 self.update_vevent()
99 super(Evenement, self).save(*args, **kwargs)
100
101 # methodes de commnunications avec CALDAV
102 def as_ical(self,):
103 """Retourne l'evenement django sous forme d'objet icalendar"""
104 cal = vobject.iCalendar()
105 cal.add('vevent')
106
107 # fournit son propre uid
108 if self.uid in [None, ""]:
109 self.uid = str(uuid.uuid1())
110
111 cal.vevent.add('uid').value = self.uid
112
113 cal.vevent.add('summary').value = self.titre
114
115 if self.mots_cles is None:
116 kw = []
117 else:
118 kw = self.mots_cles.split(",")
119
120 try:
121 kw.append(self.discipline.nom)
122 kw.append(self.discipline_secondaire.nom)
123 kw.append(self.type)
124 except: pass
125
126 kw = [x.strip() for x in kw if len(x.strip()) > 0 and x is not None]
127 for k in kw:
128 cal.vevent.add('x-auf-keywords').value = k
129
130 description = self.description
131 if len(kw) > 0:
132 if len(self.description) > 0:
133 description += "\n"
134 description += u"Mots-clés: " + ", ".join(kw)
135
136 cal.vevent.add('dtstart').value = combine(self.debut, self.fuseau)
137 cal.vevent.add('dtend').value = combine(self.fin, self.fuseau)
138 cal.vevent.add('created').value = combine(datetime.datetime.now(), "UTC")
139 cal.vevent.add('dtstamp').value = combine(datetime.datetime.now(), "UTC")
140 if len(description) > 0:
141 cal.vevent.add('description').value = description
142 if len(self.contact) > 0:
143 cal.vevent.add('contact').value = self.contact
144 if len(self.url) > 0:
145 cal.vevent.add('url').value = self.url
146 if len(self.lieu) > 0:
147 cal.vevent.add('location').value = self.lieu
148 return cal
149
150 def update_vevent(self,):
151 """Essaie de créer l'évènement sur le serveur ical.
152 En cas de succès, l'évènement local devient donc inactif et approuvé"""
153 try:
154 if self.approuve:
155 event = self.as_ical()
156 client = caldav.DAVClient(CALENDRIER_URL)
157 cal = caldav.Calendar(client, url = CALENDRIER_URL)
158 e = caldav.Event(client, parent = cal, data = event.serialize(), id=self.uid)
159 e.save()
160 except:
161 self.approuve = False
162
163 def delete_vevent(self,):
164 """Supprime l'evenement sur le serveur caldav"""
165 try:
166 if self.approuve:
167 event = self.as_ical()
168 client = caldav.DAVClient(CALENDRIER_URL)
169 cal = caldav.Calendar(client, url = CALENDRIER_URL)
170 e = cal.event(self.uid)
171 e.delete()
172 except error.NotFoundError:
173 pass
174
175
176 # Surcharge du comportement de suppression
177 # La méthode de connexion par signals est préférable à surcharger la méthode delete()
178 # car dans le cas de la suppression par lots, cell-ci n'est pas invoquée
179 def delete_vevent(sender, instance, *args, **kwargs):
180 instance.delete_vevent()
181
182 pre_delete.connect(delete_vevent, sender = Evenement)
183
184
185 class ListSet(models.Model):
186 spec = models.CharField(primary_key = True, max_length = 255)
187 name = models.CharField(max_length = 255)
188 server = models.CharField(max_length = 255)
189 validated = models.BooleanField(default = True)
190
191 def __unicode__(self,):
192 return self.name
193
194 class RecordManager(models.Manager):
195
196 def get_query_set(self):
197 return RecordQuerySet(self.model)
198
199 def search(self, text):
200 return self.get_query_set().search(text)
201
202 class RecordQuerySet(models.query.QuerySet):
203
204 def search(self, text):
205 words = text.split()
206 score_parts = []
207 score_params = []
208 for word in words:
209 score_parts.append('(title LIKE %s)')
210 score_params.append('%' + word + '%')
211 score_expr = ' + '.join(score_parts)
212 return self.extra(
213 select={'relevance': score_expr},
214 select_params=score_params,
215 where=[score_expr],
216 params=score_params
217 ).order_by('-relevance')
218
219 class Record(models.Model):
220
221 #fonctionnement interne
222 id = models.AutoField(primary_key = True)
223 server = models.CharField(max_length = 255)
224 last_update = models.CharField(max_length = 255)
225 last_checksum = models.CharField(max_length = 255)
226 validated = models.BooleanField(default = True)
227
228 #OAI
229 title = models.TextField(null = True, blank = True)
230 creator = models.TextField(null = True, blank = True)
231 description = models.TextField(null = True, blank = True)
232 modified = models.CharField(max_length = 255, null = True, blank = True)
233 identifier = models.CharField(max_length = 255, null = True, blank = True, unique = True)
234 uri = models.CharField(max_length = 255, null = True, blank = True, unique = True)
235 source = models.TextField(null = True, blank = True)
236 contributor = models.TextField(null = True, blank = True)
237 subject = models.TextField(null = True, blank = True)
238 publisher = models.TextField(null = True, blank = True)
239 type = models.TextField(null = True, blank = True)
240 format = models.TextField(null = True, blank = True)
241 language = models.TextField(null = True, blank = True)
242
243 listsets = models.ManyToManyField(ListSet, null = True, blank = True)
244
245 #SEP 2 (aucune données récoltées)
246 alt_title = models.TextField(null = True, blank = True)
247 abstract = models.TextField(null = True, blank = True)
248 creation = models.CharField(max_length = 255, null = True, blank = True)
249 issued = models.CharField(max_length = 255, null = True, blank = True)
250 isbn = models.TextField(null = True, blank = True)
251 orig_lang = models.TextField(null = True, blank = True)
252
253 # Metadata AUF multivaluées
254 disciplines = models.ManyToManyField(Discipline)
255 thematiques = models.ManyToManyField(Thematique)
256 pays = models.ManyToManyField(Pays)
257 regions = models.ManyToManyField(Region)
258
259 # Manager
260 objects = RecordManager()
261
262 def est_complet(self,):
263 """teste si le record à toutes les données obligatoires"""
264 return self.disciplines.count() > 0 and \
265 self.thematiques.count() > 0 and \
266 self.pays.count() > 0 and \
267 self.regions.count() > 0
268
269 def __unicode__(self):
270 return "[%s] %s" % (self.server, self.title)
271
272 class Serveur(models.Model):
273 """Identification d'un serveur d'ou proviennent les références"""
274 nom = models.CharField(primary_key = True, max_length = 255)
275
276 def __unicode__(self,):
277 return self.nom
278
279 def conf_2_db(self,):
280 for k in RESOURCES.keys():
281 s, created = Serveur.objects.get_or_create(nom=k)
282 s.nom = k
283 s.save()
284
285 class Profile(models.Model):
286 user = models.ForeignKey(User, unique=True)
287 serveurs = models.ManyToManyField(Serveur, null = True, blank = True)
288
289 class HarvestLog(models.Model):
290 context = models.CharField(max_length = 255)
291 name = models.CharField(max_length = 255)
292 date = models.DateTimeField(auto_now = True)
293 added = models.IntegerField(null = True, blank = True)
294 updated = models.IntegerField(null = True, blank = True)
295 processed = models.IntegerField(null = True, blank = True)
296 record = models.ForeignKey(Record, null = True, blank = True)
297
298 @staticmethod
299 def add(message):
300 logger = HarvestLog()
301 if message.has_key('record_id'):
302 message['record'] = Record.objects.get(id=message['record_id'])
303 del(message['record_id'])
304
305 for k,v in message.items():
306 setattr(logger, k, v)
307 logger.save()