prevention identifier ambigue
[auf_savoirs_en_partage_django.git] / auf_savoirs_en_partage / savoirs / lib / sep.py
CommitLineData
0cc5f772 1# -*- encoding: utf-8 -*-
23b5b3d5 2import simplejson, re, datetime, operator, hashlib
ecc62927 3from savoirs.globals import *
d972b61d 4from savoirs.models import Record, ListSet
0cc5f772 5
23b5b3d5 6class SEPEncoder:
7 """
8 Classe permettant de d'encoder et de décoder les données moissonnées.
9 """
10 separator = ", "
11
e923e22c 12 def propre(self, str):
13 """Retoune une chaîne de caractère propre utf-8
14 Elle permet de corrgier les problèmes d'encodage."""
15 if str is None:
16 return u""
17 else:
18 return str.replace(u"\x92", u"´")
19
23b5b3d5 20 def encode(self, field, data):
e923e22c 21 """Encode la structure de donnée moissonnée pour la BD"""
23b5b3d5 22 if field in META.keys() and META[field]['type'] == 'array':
23 return self.separator.join(data)
24 else:
25 return data
26
27 def decode(self, field, data):
e923e22c 28 """Décode la structure provenant de la BD"""
23b5b3d5 29 if field in META.keys() and META[field]['type'] == 'array':
30 return data.split(self.separator)
31 else:
32 return data
33
e923e22c 34 def menage(self,):
35 """Applique sur tous les records, la fonction de corrections
36 de string sur les données moissonnées"""
37 for r in Record.objects.all():
38 for k in META.keys ():
39 v = getattr (r, k)
40 setattr (r, k, self.propre(v))
41 r.save()
0cc5f772
CR
42
43class SEP:
44 """
23b5b3d5 45 Classe utilisée pour réaliser manipuler les données moisonnées.
0cc5f772 46 """
0cc5f772 47
23b5b3d5 48 encoder = SEPEncoder()
0cc5f772 49
23b5b3d5 50 ############################################################################
51 # MÉTHODES INTERNES
52 ############################################################################
0cc5f772 53
23b5b3d5 54 def _load (self, id):
55 """Recupérer la structure de métadonnées pour un record selon un `id`."""
56 r = Record.objects.get(id = id)
57 meta = {}
58 for k in META.keys ():
59 if hasattr (r, k):
60 v = getattr (r, k)
61 if v is not None:
62 meta[k] = self.encoder.decode(k, v)
63 return meta
64
d972b61d 65 # traitement spécial pour certaines clef de la structure
66 def listsets(self, record, value):
67
68 # doit avoir un id pour créer les relations multivaluées
69 record.save()
70 for set in [ls for ls in ListSet.objects.all() if ls.spec in value]:
71 record.listsets.add(set)
e923e22c 72
c88d78dc 73 def _update_record(self, r, metadata):
23b5b3d5 74 for k in metadata.keys ():
d972b61d 75 if hasattr(self, k):
76 method = getattr(self, k)
77 method(r, metadata[k])
78 else:
79 setattr (r, k, self.encoder.encode(k, metadata[k]))
80
23b5b3d5 81 r.last_checksum = hashlib.md5(str(metadata)).hexdigest()
82 r.last_update = datetime.datetime.today()
e923e22c 83
84 # stocke des chaînes de caractères propre en BD en provenance
85 # des données moissonnées
86 for k in META.keys ():
87 v = getattr (r, k)
88 setattr (r, k, self.encoder.propre(v))
89
23b5b3d5 90 r.save()
c88d78dc 91
92
93 def _save (self, metadata):
94 r = Record ()
95 self._update_record(r, metadata)
23b5b3d5 96 return r.id
97
98 def _modify (self, id, metadata):
99 r = Record.objects.get(id = id)
100
101 # test si le fichier a été modifié
102 if hashlib.md5(str(metadata)).hexdigest() == r.last_checksum:
103 return False
23b5b3d5 104
c88d78dc 105 self._update_record(r, metadata)
106
23b5b3d5 107 return True
108
109 def _combine (self, result_lists, op):
110 scores = {}
111 simple_sets = []
112
113 for list in result_lists:
114 simple_sets.append (set([x[0] for x in list]))
115 for (id, score) in list:
116 if scores.get (id) is None:
117 scores[id] = 0
118 scores[id] += score
119
120 matches = []
121 for s in simple_sets:
122 if op == "|":
123 matches = set(matches) | s
124 elif op == "&":
125 if len (matches) == 0:
126 matches = s
127 else:
128 matches = set(matches) & s
129 #print "EE", matches
130
131 return [(x, scores[x]) for x in matches]
132
133
134 def _text_search (self, q, fields = None):
135 if fields is None:
136 fields = [x for x in META.keys() if META[x].get("text_search", False)]
137
138 w = re.compile (r'\W+', re.U)
139 words = w.split (q)
140
141 matches = []
142 suffix = ""
143 if len(fields)==1 and fields[0] == "subject":
144 suffix = " IN BOOLEAN MODE"
145
146 for k in fields:
147 matches.append ("MATCH(`%s`) AGAINST ('%s'%s)" % (k, " ".join(words), suffix))
148 m = "+".join (matches)
149
9eda5d6c 150 q = "SELECT r.id, (%s) AS score FROM savoirs_record AS r \
151 LEFT JOIN savoirs_record_listsets AS rl ON r.id = rl.record_id \
152 JOIN savoirs_listset AS l ON rl.listset_id = l.spec \
153 WHERE (%s) AND r.validated = 1 AND l.validated = 1 \
8346a35e 154 GROUP BY r.id \
a3a69209 155 HAVING score > 0 ORDER BY score DESC" % (m, m)
23b5b3d5 156
157 from django.db import connection, transaction
158 cursor = connection.cursor()
159 cursor.execute(q)
160 rc = cursor.fetchall()
0cc5f772
CR
161 return rc
162
23b5b3d5 163 ############################################################################
164 # API
165 ############################################################################
166
0cc5f772
CR
167 def add (self, metadata):
168 """Ajouter la ressource définie par `metadata`. Si on trouve une
169 ressource avec le même `identifier`, on le met a jour.
170
171 Retourne l'id de la ressource créée ou mise à jour.
172 """
23b5b3d5 173 added = updated = False
0cc5f772
CR
174 exists = self.search (q = {URI: metadata[URI]})
175 if len (exists) > 0:
8b95ddc9 176 id = exists[0][0]
23b5b3d5 177 updated = self.update (int(id), metadata)
0cc5f772 178 else:
23b5b3d5 179 added = True
180 id = self._save (metadata)
181 return {'record_id': id, 'added':added, 'updated':updated}
182
183 def delete (self, id):
184 """Supprime la ressource identifiée par `id`.
185 """
186 r = Record.objects.get(id = id)
187 r.delete()
0cc5f772
CR
188
189 def update (self, id, metadata):
190 """Met a jour la ressource identifiée par `id`, avec les données de
191 `metadata`. Une exception est levée si elle n'existe pas.
192 """
193 if self.get (int(id)) is not None:
23b5b3d5 194 return self._modify (int(id), metadata)
0cc5f772
CR
195 else:
196 raise Exception ("Objet inexistant")
23b5b3d5 197 return False
0cc5f772 198
23b5b3d5 199 def get (self, id):
200 """Recupérer la structure de métadonnées pour la ressource identifiée
201 par `id`. `id` peut être une liste si on veut les structures de
202 plusieurs ressources.
0cc5f772 203 """
23b5b3d5 204 if isinstance (id, tuple) or isinstance (id, list):
205 rc = []
206 for i in id:
207 try:
208 i = i[0]
209 except: pass
210 rc.append (self._load (int(i)))
211 else:
212 rc = self._load (int(id))
213 return rc
0cc5f772 214
23b5b3d5 215 def ids (self):
216 """ Retourner la liste complète des ids des ressources."""
217 return [x.id for x in Record.objects.all()]
0cc5f772 218
23b5b3d5 219 def search (self, q):
220 """Effectue une recherche multi-critères, en fonction du dictionnaire
221 `q`. Retourne une list d'`id`s uniquement. Les données pour chaque
222 résultat doivent être chargées ulterieurement.
223 """
224 rc = []
225 sets = []
0cc5f772 226
23b5b3d5 227 if len (q) > 0:
228 # Recherche "simple"
f991eb01 229 ww = q.get ("q", "").strip()
23b5b3d5 230 if len (ww) > 0:
231 s = self._text_search (ww)
232 if len(s) > 0:
8f17344b 233 rc.extend(s)
23b5b3d5 234 # Recherche URL
235 elif q.get (URI) is not None:
236 s = []
237 try:
238 s.append((Record.objects.get(uri__iexact = q.get(URI)).id, 1))
239 rc.append(s)
240 except: pass
241 # Recherche avancée
242 else:
f991eb01 243 creator = q.get ("creator", "")
244 title = q.get ("title", "")
245 description = q.get ("description", "")
246 subject = q.get ("subject", "")
23b5b3d5 247
248 if len (creator) > 0:
249 sets.append (self._text_search (creator, [CREATOR, CONTRIBUTOR]))
250 if len (title) > 0:
251 sets.append (self._text_search (title, [TITLE, ALT_TITLE]))
252 if len (description) > 0:
253 sets.append (self._text_search (description, [DESCRIPTION, ABSTRACT]))
254 if len (subject) > 0:
255 sets.append (self._text_search (subject, [SUBJECT,]))
256 rc = self._combine (sets, q.get ("operator", "|"))
257 rc.sort (key = operator.itemgetter(1), reverse = True)
258
259 if len(rc) > 0:
260 rc = [x[0] for x in rc]
261
262 else:
263 rc = self.ids()
23b5b3d5 264 return rc