# -*- coding: utf-8 -*- """ Copyright 2012-2014 Grégory Soutadé This file is part of Dynastie. Dynastie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Dynastie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Dynastie. If not, see . """ import re import unicodedata import os import operator import pickle from django.db import models from dynastie.models import Post class Search: MINIMUM_LETTERS = 3 def __init__(self): self.report = '' self.tagreg = re.compile('<[^>]+>') self.htmlreg = re.compile('&[^;]+;') self.numreg = re.compile('[0-9]+') self.pat = re.compile(r'\s+') self.replace_by_space = ('(', ')', '#', '\'', '{', '}', '[', ']', '-', '|', '\t', '\\', '_', '^' '=', '+', '$', '£', '%', 'µ', '*', ',', '?', ';', '.', '/', ':', '!', '§', '€', '²') # Imported from generator.py def _addReport(self, string, color=''): if color != '': self.report = self.report + '' self.report = self.report + '' + self.__class__.__name__ + ' : ' self.report = self.report + string if color != '': self.report = self.report + '' self.report = self.report + '
\n' def _addWarning(self, string): self.addReport(string, 'yellow') def _addError(self, string): self.addReport(string, 'red') def _saveDatabase(self, blog, hashtable): d = pickle.dumps(hashtable) f = open(blog.src_path + '/_search.db', 'wb') f.write(d) f.close() def _loadDatabase(self, blog): filename = blog.src_path + '/_search.db' if not os.path.exists(filename): print('No search index !') return None f = open(filename, 'rb') hashtable = pickle.load(f) f.close() return hashtable def _strip_accents(self, s): return ''.join((c for c in unicodedata.normalize('NFD', s) if unicodedata.category(c) != 'Mn')) def _remove_tag(self, content): content = self.htmlreg.sub('', content) content = self.numreg.sub('', content) content = content.replace('\n', '') content = content.replace('\r', '') content = content.replace('"', '') for c in self.replace_by_space: content = content.replace(c, ' ') content = self.tagreg.sub('', content) content = self.pat.sub(' ', content) return content def _prepare_string(self, content): content = self._remove_tag(content) content = self._strip_accents(content) return content def _indexContent(self, hashtable, index, content, word_weight): content = self._prepare_string(content) wordlist = content.split(' ') for word in wordlist: if len(word) < self.MINIMUM_LETTERS: continue word = word.lower() if not word in hashtable: hashtable[word] = [] if not index in hashtable[word]: hashtable[word].insert(0, [index, word_weight]) else: weight = hashtable[word][1] hashtable[word][1] = weight + word_weight def _index_file(self, hashtable, filename, index): try: post = Post.objects.get(pk=index) if post.published == False: return except: return f = open(filename, 'rb') content = f.read().decode('utf-8') f.close() self._indexContent(hashtable, index, content, 1) self._indexContent(hashtable, index, post.title, 5) def create_index(self, blog): hashtable = {} root = blog.src_path + '/_post' if os.path.exists(root): for post in os.listdir(root): # Not a post number if not re.search(self.numreg, post): continue self._index_file(hashtable, root + '/' + post, int(post)) self._saveDatabase(blog, hashtable) self._addReport('Search generated @ ' + blog.src_path + '/_search.db') return self.report def _index_post(self, blog, post, saveDatabase=True): hashtable = self._loadDatabase(blog) filename = blog.src_path + '/_post/' + str(post) if hashtable is None: return self.create_index(blog) self._index_file(hashtable, filename, int(post)) if saveDatabase: self._saveDatabase(blog, hashtable) def _remove_post(self, blog, post, saveDatabase=True): hashtable = self._loadDatabase(blog) if hashtable is None: return for k, v in hashtable.items(): # For tuples in values for t in v: if post == v[0]: v.remove(t) if saveDatabase: self._saveDatabase(blog, hashtable) def index_post(self, blog, post): return self._index_post(blog, post, True) def delete_post(self, blog, post): return self._remove_post(blog, post, True) def edit_post(self, blog, post, saveDatabase=True): self._remove_post(blog, post, False) self._index_post(blog, post, True) def search(self, blog, string): hashtable = self._loadDatabase(blog) string = self._prepare_string(string) wordlist = string.split(' ') res = {} for word in wordlist: if len(word) < Search.MINIMUM_LETTERS: continue word = word.lower() reg = re.compile('.*' + word + '.*') for key in hashtable.keys(): if reg.match(key): for post in hashtable[key]: res[post[0]] = res.get(post[0],0) + post[1] sorted_res = sorted(res.items(), key=operator.itemgetter(1), reverse=True) res = [sorted_res[i][0] for i in range(len(sorted_res))] return res