21 |
21 |
22 import logging |
22 import logging |
23 logger = logging.getLogger(__name__) |
23 logger = logging.getLogger(__name__) |
24 |
24 |
25 def tagtranslation(request): |
25 def tagtranslation(request): |
|
26 """ |
|
27 Vue donnant des traductions de label de tag pour une langue. |
|
28 la recherche se fait dans les objets :class:`hdalab.models.DbpediaFieldsTranslation`. |
|
29 |
|
30 Paramêtres GET: |
|
31 :var lang: La langue demandée |
|
32 :var labels: Un ou plusieurs labels de tag (séparateur : ",") |
|
33 |
|
34 Réponse (application/json): |
|
35 Un dictionnaire sous la forme :: |
|
36 |
|
37 { |
|
38 "<label1>": "<translation>", |
|
39 "<label2>": "<translation>" |
|
40 } |
|
41 |
|
42 """ |
26 |
43 |
27 lang = request.GET.get('lang',request.LANGUAGE_CODE) |
44 lang = request.GET.get('lang',request.LANGUAGE_CODE) |
28 labels = request.GET.get('labels',None) |
45 labels = request.GET.get('labels',None) |
29 |
46 |
30 if not labels: |
47 if not labels: |
44 |
61 |
45 translations = dict([(t.master.label, t.label) for t in translationqs]) |
62 translations = dict([(t.master.label, t.label) for t in translationqs]) |
46 |
63 |
47 return HttpResponse(content=json.dumps(translations), content_type='application/json') |
64 return HttpResponse(content=json.dumps(translations), content_type='application/json') |
48 |
65 |
49 def subcat(category, globtags, level, max_level ): |
66 |
|
67 |
|
68 def subcat(category, globtags, level, max_level): |
|
69 """ |
|
70 Méthode récursive utilisée pour reconstruire un arbre de catégories. |
|
71 |
|
72 :param object category: La catégorie racine. |
|
73 :param dict globtags: dictionnaire flobal des tags. |
|
74 :param int level: Niveau de récursion. |
|
75 :param int max_level: Niveau maximum de récursion (level <= max_level) |
|
76 |
|
77 :returns: L'arbre des catégories. |
|
78 """ |
50 # recursive function used by cattree |
79 # recursive function used by cattree |
51 catlabel = category.label |
80 catlabel = category.label |
52 tags = Tag.objects.filter(wp_categories__wp_category = category).distinct() |
81 tags = Tag.objects.filter(wp_categories__wp_category = category).distinct() |
53 taglabels = [k for k in dict([(t.label,t.label) for t in tags])] |
82 taglabels = [k for k in dict([(t.label,t.label) for t in tags])] |
54 resobj = { |
83 resobj = { |
68 subcats = WpCategory.objects.filter(parent_categories__parent_category = category) |
97 subcats = WpCategory.objects.filter(parent_categories__parent_category = category) |
69 resobj['themes'] += [subcat(subcats[i], globtags, level + 1, max_level ) for i in range(len(subcats))] |
98 resobj['themes'] += [subcat(subcats[i], globtags, level + 1, max_level ) for i in range(len(subcats))] |
70 return resobj |
99 return resobj |
71 |
100 |
72 def cleantags(category): |
101 def cleantags(category): |
|
102 """ |
|
103 Methode annexe qui nettoie recursivement un arbre de catégorie. elle effectue les actions suivantes: |
|
104 - retire les clefs correspondant à des listes vides ('contents' et 'themes') |
|
105 - trie les listes 'themes' par label |
|
106 - trie les listes 'contents' par score |
|
107 |
|
108 :param category: la catégorie racine où commencer le traitement. |
|
109 |
|
110 """ |
73 if category.has_key('contents') and len(category['contents']) == 0: |
111 if category.has_key('contents') and len(category['contents']) == 0: |
74 del category['contents'] |
112 del category['contents'] |
75 if category.has_key('contents'): |
113 if category.has_key('contents'): |
76 category['contents'] = sorted(category['contents'], key=lambda content: -content['score']) |
114 category['contents'] = sorted(category['contents'], key=lambda content: -content['score']) |
77 if category.has_key('themes'): |
115 if category.has_key('themes'): |
84 if len(category['themes']) == 0: |
122 if len(category['themes']) == 0: |
85 del category['themes'] |
123 del category['themes'] |
86 return category |
124 return category |
87 |
125 |
88 def cattree(request): |
126 def cattree(request): |
|
127 """ |
|
128 Contruit l'arbre de catégorie pour un label. |
|
129 Les contenus attachés aux noeud sont des listes de fiches hdalab triées par score. |
|
130 Le score pour une fiche est fonction de ses tags, de leur ordre, de leur présence dnas l'arbre des catégories et de leur hauteur dans l'arbre des catégories. |
|
131 |
|
132 Paramêtres GET : |
|
133 :var label: Le label |
|
134 |
|
135 Réponse (application/json): |
|
136 Un json représentant l'arbre des catégories avec pour chaque noeud une liste ordonnée de fiches liée à la catégorie. |
|
137 |
|
138 exemple :: |
|
139 |
|
140 { |
|
141 "label": "<label1>", |
|
142 "themes": [ { "label": "<label1.1>", "themes": [...], "contents": [...]}, ...], |
|
143 "contents": [ |
|
144 { |
|
145 "organization": "Ciclic", |
|
146 "description": "Ciclic propose...", |
|
147 "score": 7, |
|
148 "title": "Vocabulaire de l'analyse filmique...", |
|
149 "url": "http://upopi.ciclic.fr/vocabulaire/definition/sceance-11", |
|
150 "hda_id": "5879", |
|
151 "organization_url": "http://www.ciclic.fr/", |
|
152 "id": 14852 |
|
153 }, |
|
154 ... |
|
155 ] |
|
156 } |
|
157 """ |
89 # Gets the category tree from a label |
158 # Gets the category tree from a label |
90 ROOT_MAX_TAG_ORDER = 8 |
159 ROOT_MAX_TAG_ORDER = 8 |
91 MAX_TAG_ORDER = 8 |
160 MAX_TAG_ORDER = 8 |
92 MAX_LEVEL = 3 |
161 MAX_LEVEL = 3 |
93 LEVEL_COEFF = 5 |
162 LEVEL_COEFF = 5 |
186 |
260 |
187 return HttpResponse(content=json.dumps(resobj), content_type='application/json') |
261 return HttpResponse(content=json.dumps(resobj), content_type='application/json') |
188 |
262 |
189 |
263 |
190 def tagsearch(request): |
264 def tagsearch(request): |
|
265 """ |
|
266 Vue permettant la recherche dans les tag. |
|
267 La recherche se fait dans les objets :class:`hdabo.models.Tag`, :class:`hdalab.models.DbpediaFields` et :class:`hdalab.models.DbpediaFieldsTranslation`. |
|
268 |
|
269 Paramêtres GET: |
|
270 :var (str) term: Le terme à rechercher. |
|
271 :var (str) lang: La langue dans laquelle il faut faire la recherche. |
|
272 :var (int) count: Le nombre maximum de résultat. |
|
273 :var (bool) count_notices: Ajoute ou pas le nombre de notices par tag. |
|
274 |
|
275 Réponse (application/json): |
|
276 Une liste comprenant les résultats de la recherche. |
|
277 |
|
278 exemple :: |
|
279 |
|
280 [ |
|
281 { |
|
282 "original_label": "Cathédrale Notre-Dame de Chartres", |
|
283 "url": "http://fr.wikipedia.org/wiki/Cath%C3%A9drale_Notre-Dame_de_Chartres", |
|
284 "abstract": "La cathédrale Notre-Dame de Chartres ...", |
|
285 "value": "Cathédrale Notre-Dame de Chartres", |
|
286 "thumbnail": "http://commons.wikimedia.org/wiki/Special:FilePath/Chartres_Cath+Gare.JPG?width=300", |
|
287 "nb": 7 |
|
288 }, |
|
289 { |
|
290 "original_label": "Cathédrale Notre-Dame de Paris", |
|
291 "url": "http://fr.wikipedia.org/wiki/Cath%C3%A9drale_Notre-Dame_de_Paris", |
|
292 "abstract": "La cathédrale Notre-Dame de Paris...", |
|
293 "value": "Cathédrale Notre-Dame de Paris", |
|
294 "thumbnail": "http://commons.wikimedia.org/wiki/Special:FilePath/Notre_Dame_de_Paris_DSC_0846w.jpg?width=300", |
|
295 "nb": 6 |
|
296 }, |
|
297 ... |
|
298 ] |
|
299 """ |
191 |
300 |
192 q = request.GET.get('term',None) |
301 q = request.GET.get('term',None) |
193 maxcount = int(request.GET.get('count','40')) |
302 maxcount = int(request.GET.get('count','40')) |
194 lang = request.GET.get('lang',request.LANGUAGE_CODE) |
303 lang = request.GET.get('lang',request.LANGUAGE_CODE) |
195 count_notices_str = request.REQUEST.get("count_notices") |
304 count_notices_str = request.REQUEST.get("count_notices") |
250 if q is None or resobj['value'].lower().find(lq) != -1: |
359 if q is None or resobj['value'].lower().find(lq) != -1: |
251 res.append(resobj) |
360 res.append(resobj) |
252 |
361 |
253 return HttpResponse(content=json.dumps(res), content_type='application/json') |
362 return HttpResponse(content=json.dumps(res), content_type='application/json') |
254 |
363 |
|
364 |
|
365 |
255 def catsearch(request): |
366 def catsearch(request): |
|
367 """ |
|
368 Vue permettant la recherche de catégorie. On se restreint aux catégories qui sont aussi des tags. |
|
369 |
|
370 Paramêtres GET: |
|
371 :var (str) term: Le terme à rechercher. |
|
372 |
|
373 Réponse (application/json): |
|
374 Une liste comprenant le résultat de la recherche. |
|
375 |
|
376 exemple :: |
|
377 |
|
378 [ |
|
379 { "value": "1982 au cinéma" }, |
|
380 { "value": "Cinéma italien" }, |
|
381 { "value": "2003 au cinéma" }, |
|
382 ... |
|
383 ] |
|
384 |
|
385 """ |
|
386 |
256 |
387 |
257 q = request.GET.get('term',None) |
388 q = request.GET.get('term',None) |
258 |
389 |
259 # On ne récupère que les catégories qui sont également des tags |
390 # On ne récupère que les catégories qui sont également des tags |
260 qrx = '(\\m|\\b)%s'%q |
391 qrx = '(\\m|\\b)%s'%q |
266 |
397 |
267 res = [{'value':t.label} for t in qs] |
398 res = [{'value':t.label} for t in qs] |
268 |
399 |
269 return HttpResponse(content=json.dumps(res), content_type='application/json') |
400 return HttpResponse(content=json.dumps(res), content_type='application/json') |
270 |
401 |
|
402 |
|
403 |
271 def filter(request): |
404 def filter(request): |
|
405 """ |
|
406 Vue permettant de filtrer par facette des fiches HDA. |
|
407 Cette méthode est en fait un simple wrapper pour la méthode `filter_generic`. |
|
408 |
|
409 Paramêtres GET: |
|
410 :var lang: la langue de recherche (défaut: fr-fr). |
|
411 :var period: Période dans laquelle doit se faire la recherche. |
|
412 :var label: un mot-clef. |
|
413 :var country: Une liste de pays limitant la recherche. |
|
414 :var contentlist: Liste de fiches limitant la recherche. |
|
415 :var mto: Ordre maximum des tags (défaut: 12). |
|
416 :var contentcount: nombre de fiches maximum (défaut : 8). |
|
417 :var tagcount: nombre maximum de tag (défaut: 30). |
|
418 |
|
419 Réponse (application/json): |
|
420 Un objet comprenant le résultat de la recherche. |
|
421 """ |
|
422 |
272 |
423 |
273 lang = request.GET.get('lang',request.LANGUAGE_CODE) |
424 lang = request.GET.get('lang',request.LANGUAGE_CODE) |
274 periode = request.GET.get('period',None) |
425 periode = request.GET.get('period',None) |
275 label = request.GET.get('label', None) |
426 label = request.GET.get('label', None) |
276 country = request.GET.get('country', None) |
427 country = request.GET.get('country', None) |
283 |
434 |
284 return HttpResponse(content=outputstr, content_type='application/json') |
435 return HttpResponse(content=outputstr, content_type='application/json') |
285 |
436 |
286 |
437 |
287 def filter_generic(lang="fr-fr", periode=None, label=None, country=None, contentlist=None, max_tag_order=12, content_count=8, tag_count=30): |
438 def filter_generic(lang="fr-fr", periode=None, label=None, country=None, contentlist=None, max_tag_order=12, content_count=8, tag_count=30): |
|
439 """ |
|
440 Méthode de recherche par facette sur les fiches HDA. |
|
441 |
|
442 :param str lang: La langue de recherche (défaut: "fr-fr"). |
|
443 :param str period: Période d'année limitant la recherche. Le format est `<année-début>,<année-fin>` (défaut: None). |
|
444 :param str label: Limite la recherche à un label de tag (défaut: None). |
|
445 :param str country: Liste de pays où limiter la recherche. Le format est `<uri dbpedia pays 1>,<uri dbpedia pays 2>...` (défaut: None). |
|
446 :param str contentlist: Liste d'id de fiche HDA (:class:`hdabo.models.Datasheet`) limitant la recherche. Le format est `<id1>,<id2>...` (défaut: None) |
|
447 :param int max_tag_order: Limite le nombre maximum de tag par fiche (défaut: 12). |
|
448 :param int content_count: Limite le nombre de fiches résultat (défaut: 8). |
|
449 :param int tag_count: Limite le nombre de tag dans le résultat (défaut: 30). |
|
450 :rtype: string |
|
451 :returns: Un objet json sérialisé comprenant les résultats de la recherche pour les différentes facettes. |
|
452 |
|
453 |
|
454 Clefs de l'objet: |
|
455 - :count: Nombre total de fiches. |
|
456 - :disciplines: Liste de disciplines artistiques en relation avec les reesultats, triée par score. |
|
457 - :countries: Objet dont les clef sont des uri dbpedia de pays et les valeurs sont le nombre de fiches. |
|
458 - :tags: Liste de tag triés par score. |
|
459 - :sparkline: Liste d'année avec un score, triés par année. Le score est lié au nombre de fiche dont le contenu couvre l'année en question. |
|
460 - :contents: Liste de fiches HDA répondant à la recherche, classée par score. Chaque fiche comporte une liste de tag. |
|
461 - :tagtranslations: Objet donnant les traductions de label de tag rencontrés dans les résultats. |
|
462 |
|
463 exemple:: |
|
464 |
|
465 { |
|
466 "count": 936, |
|
467 "disciplines": [ |
|
468 { |
|
469 "translated_label": "Peinture", |
|
470 "score": 936, |
|
471 "label": "Peinture" |
|
472 }, |
|
473 { |
|
474 "translated_label": "Sculpture", |
|
475 "score": 88, |
|
476 "label": "Sculpture" |
|
477 }, |
|
478 ... |
|
479 ], |
|
480 "countries": { |
|
481 "http://fr.dbpedia.org/resource/Iran": 1, |
|
482 "http://fr.dbpedia.org/resource/Espagne": 16, |
|
483 ... |
|
484 }, |
|
485 "tags": [ |
|
486 { |
|
487 "url": "http://fr.dbpedia.org/resource/Portrait", |
|
488 "id": 63452, |
|
489 "translated_label": "Portrait", |
|
490 "score": 179, |
|
491 "wkpd_url": "http://fr.wikipedia.org/wiki/Portrait", |
|
492 "label": "Portrait", |
|
493 "thumbnail": "http://commons.wikimedia.org/wiki/Special:FilePath/Fayum02.jpg?width=300" |
|
494 }, |
|
495 ... |
|
496 ], |
|
497 "sparkline": [ |
|
498 { |
|
499 "score": 2, |
|
500 "year": -600 |
|
501 }, |
|
502 { |
|
503 "score": 4, |
|
504 "year": -500 |
|
505 }, |
|
506 ... |
|
507 { |
|
508 "score": 18, |
|
509 "year": 2001 |
|
510 } |
|
511 ], |
|
512 "contents": [ |
|
513 { |
|
514 "description": "Le palais Fesch, ...", |
|
515 "title": "Histoires bibliques", |
|
516 "url": "http://www.musee-fesch.com/index.php/musee_fesch/content/view/ef_catalogue_sommaire/1513/%28node_id_theme%29/33459", |
|
517 "tags": [ |
|
518 { |
|
519 "url": "http://fr.dbpedia.org/resource/Peinture", |
|
520 "id": 54648, |
|
521 "translated_label": "Peinture", |
|
522 "wkpd_url": "http://fr.wikipedia.org/wiki/Peinture", |
|
523 "label": "Peinture", |
|
524 "order": 1, |
|
525 "match": true |
|
526 }, |
|
527 ... |
|
528 ], |
|
529 "score": 23, |
|
530 "coords": { |
|
531 "city_name": "Ajaccio", |
|
532 "latitude": 41.916667, |
|
533 "longitude": 8.733333 |
|
534 }, |
|
535 "hda_id": "4448", |
|
536 "id": 13855 |
|
537 }, |
|
538 { |
|
539 "description": "...", |
|
540 "title": "Le XIXe siècle", |
|
541 "url": "http://www.grandpalais.fr/fr/article/le-xixe-siecle", |
|
542 "tags": [ ... ], |
|
543 "score": 22, |
|
544 "hda_id": "5217", |
|
545 "id": 13582 |
|
546 }, |
|
547 ... |
|
548 ], |
|
549 "tagtranslations": { |
|
550 "Paul Cézanne": "Paul Cézanne", |
|
551 "Politique": "Politique", |
|
552 "Poésie": "Poésie", |
|
553 "Religion": "Religion", |
|
554 "Empereur": "Empereur", |
|
555 "Saint": "Saint", |
|
556 ... |
|
557 } |
|
558 } |
|
559 |
|
560 """ |
288 |
561 |
289 no_translate_langs = [ 'fr' ] |
562 no_translate_langs = [ 'fr' ] |
290 |
563 |
291 key_parts = ("filter",lang,periode,label,country,contentlist,max_tag_order,content_count,tag_count) |
564 key_parts = ("filter",lang,periode,label,country,contentlist,max_tag_order,content_count,tag_count) |
292 key_parts = [unicode(p).encode("ascii", "ignore") for p in key_parts] |
565 key_parts = [unicode(p).encode("ascii", "ignore") for p in key_parts] |
472 return outputstr |
745 return outputstr |
473 |
746 |
474 |
747 |
475 |
748 |
476 def subtree(tree): |
749 def subtree(tree): |
|
750 """ |
|
751 Methode récursive permettant de remplir un arbre ce catégories avec les fiches HDA correspondantes. |
|
752 |
|
753 :param tree: L'arbre de catégorie |
|
754 |
|
755 :returns:Un arbre de catégorie rempli de fiches HDA. |
|
756 |
|
757 format du paramêtre `tree` :: |
|
758 |
|
759 { |
|
760 "label": "secteur urbain", |
|
761 "contents": [ |
|
762 { "label": "banlieue", |
|
763 "contents": [ |
|
764 { "label": "faubourg" } |
|
765 ] }, |
|
766 { "label": "îlot" }, |
|
767 ... |
|
768 ] |
|
769 } |
|
770 |
|
771 Exemple de retour :: |
|
772 |
|
773 |
|
774 { |
|
775 "label": "secteur urbain", |
|
776 "contents": [ |
|
777 { |
|
778 "score": 6, |
|
779 "organization": "Institut national de l'audiovisuel ( INA )", |
|
780 "description": "Pour faire face à la ...", |
|
781 "title": "La construction des grands ensembles de banlieue : l'exemple de Sarcelles", |
|
782 "url": "http://fresques.ina.fr/jalons/fiche-media/InaEdu01075/la-construction-des-grands-ensembles-de-banlieue--l-exemple-de-sarcelles", |
|
783 "hda_id": "2090", |
|
784 "organization_url": "http://www.ina.fr", |
|
785 "id": 12360 |
|
786 }, |
|
787 { |
|
788 "score": 6, |
|
789 "organization": "Maison de banlieue et d'architecture", |
|
790 "description": "La Maison de banlieue et d'architecture...", |
|
791 "title": "Des ensembles assez grands. Mémoire et projets en Essonne", |
|
792 "url": "http://maisondebanlieue.fr/wp-content/uploads/2011/05/Cahier11_grands_ensembles.pdf", |
|
793 "hda_id": "5893", |
|
794 "organization_url": "http://www.maisondebanlieue.fr/", |
|
795 "id": 14821 |
|
796 }, |
|
797 ... |
|
798 ] |
|
799 "themes": [ |
|
800 { |
|
801 "label": "faubourg", |
|
802 "content": [...], |
|
803 "themes": [...] |
|
804 }, |
|
805 ... |
|
806 ] |
|
807 } |
|
808 |
|
809 """ |
477 MAX_TAG_ORDER = 16 |
810 MAX_TAG_ORDER = 16 |
478 label = tree['label'] |
811 label = tree['label'] |
479 sub = tree.get('contents',[]) |
812 sub = tree.get('contents',[]) |
480 |
813 |
481 datasheets = Datasheet.objects.filter(validated = True, taggedsheet__tag__label__iexact = label, taggedsheet__order__lte = MAX_TAG_ORDER).annotate(tagorder=Min('taggedsheet__order')).select_related('organisation').distinct() |
814 datasheets = Datasheet.objects.filter(validated = True, taggedsheet__tag__label__iexact = label, taggedsheet__order__lte = MAX_TAG_ORDER).annotate(tagorder=Min('taggedsheet__order')).select_related('organisation').distinct() |