Changeset - a08624dd675e
[Not reviewed]
beta
0 3 0
Marcin Kuzminski - 13 years ago 2012-12-05 21:14:31
marcin@python-works.com
Implemented filtering of admin journal based on Whoosh Query language
ref #210
3 files changed with 117 insertions and 8 deletions:
0 comments (0 inline, 0 general)
rhodecode/controllers/admin/admin.py
Show inline comments
 
@@ -25,18 +25,89 @@
 

	
 
import logging
 

	
 
from pylons import request, tmpl_context as c
 
from pylons import request, tmpl_context as c, url
 
from sqlalchemy.orm import joinedload
 
from webhelpers.paginate import Page
 
from whoosh.qparser.default import QueryParser
 
from whoosh import query
 
from sqlalchemy.sql.expression import or_
 

	
 
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
 
from rhodecode.lib.base import BaseController, render
 
from rhodecode.model.db import UserLog
 
from rhodecode.lib.utils2 import safe_int
 
from rhodecode.model.db import UserLog, User
 
from rhodecode.lib.utils2 import safe_int, remove_prefix
 
from rhodecode.lib.indexers import JOURNAL_SCHEMA
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
def _filter(user_log, search_term):
 
    """
 
    Filters sqlalchemy user_log based on search_term with whoosh Query language
 
    http://packages.python.org/Whoosh/querylang.html
 

	
 
    :param user_log:
 
    :param search_term:
 
    """
 
    qry = None
 
    if search_term:
 
        qp = QueryParser('repository', schema=JOURNAL_SCHEMA)
 
        qry = qp.parse(unicode(search_term))
 
        log.debug('Filtering using query %r' % qry)
 

	
 
    def get_filterion(field, val, term):
 
        if field == 'repository':
 
            field = getattr(UserLog, 'repository_name')
 
        elif field == 'ip':
 
            field = getattr(UserLog, 'user_ip')
 
        elif field == 'date':
 
            field = getattr(UserLog, 'action_date')
 
        elif field == 'username':
 
            ##special case for username
 
            if isinstance(term, query.Wildcard):
 
                #only support wildcards with * at beggining
 
                val = remove_prefix(val, prefix='*')
 
                return getattr(UserLog, 'user_id').in_(
 
                    [x.user_id for x in
 
                     User.query().filter(User.username.endswith(val))])
 
            elif isinstance(term, query.Prefix):
 
                return getattr(UserLog, 'user_id').in_(
 
                    [x.user_id for x in
 
                     User.query().filter(User.username.startswith(val))])
 
            # term == exact match, case insensitive
 
            field = getattr(UserLog, 'user')
 
            val = User.get_by_username(val, case_insensitive=True)
 

	
 
        else:
 
            field = getattr(UserLog, field)
 

	
 
        #sql filtering
 
        if isinstance(term, query.Wildcard):
 
            return field.endsswith(val)
 
        elif isinstance(term, query.Prefix):
 
            return field.startswith(val)
 
        return field == val
 

	
 
    if isinstance(qry, (query.And, query.Term, query.Prefix, query.Wildcard)):
 
        if not isinstance(qry, query.And):
 
            qry = [qry]
 
        for term in qry:
 
            field = term.fieldname
 
            val = term.text
 
            user_log = user_log.filter(get_filterion(field, val, term))
 
    elif isinstance(qry, query.Or):
 
        filters = []
 
        for term in qry:
 
            field = term.fieldname
 
            val = term.text
 
            if isinstance(term, query.Term):
 
                filters.append(get_filterion(field, val, term))
 
        user_log = user_log.filter(or_(*filters))
 

	
 
    return user_log
 

	
 

	
 
class AdminController(BaseController):
 

	
 
    @LoginRequired()
 
@@ -45,14 +116,26 @@ class AdminController(BaseController):
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def index(self):
 

	
 
        users_log = UserLog.query()\
 
                .options(joinedload(UserLog.user))\
 
                .options(joinedload(UserLog.repository))\
 
                .order_by(UserLog.action_date.desc())
 
                .options(joinedload(UserLog.repository))
 

	
 
        #FILTERING
 
        c.search_term = request.GET.get('filter')
 
        try:
 
            users_log = _filter(users_log, c.search_term)
 
        except:
 
            # we want this to crash for now
 
            raise
 

	
 
        users_log = users_log.order_by(UserLog.action_date.desc())
 

	
 
        p = safe_int(request.params.get('page', 1), 1)
 
        c.users_log = Page(users_log, page=p, items_per_page=10)
 

	
 
        def url_generator(**kw):
 
            return url.current(filter=c.search_term, **kw)
 

	
 
        c.users_log = Page(users_log, page=p, items_per_page=10, url=url_generator)
 
        c.log_data = render('admin/admin_log.html')
 

	
 
        if request.environ.get('HTTP_X_PARTIAL_XHR'):
rhodecode/lib/indexers/__init__.py
Show inline comments
 
@@ -35,7 +35,7 @@ from string import strip
 
from shutil import rmtree
 

	
 
from whoosh.analysis import RegexTokenizer, LowercaseFilter, StopFilter
 
from whoosh.fields import TEXT, ID, STORED, NUMERIC, BOOLEAN, Schema, FieldType
 
from whoosh.fields import TEXT, ID, STORED, NUMERIC, BOOLEAN, Schema, FieldType, DATETIME
 
from whoosh.index import create_in, open_dir
 
from whoosh.formats import Characters
 
from whoosh.highlight import highlight, HtmlFormatter, ContextFragmenter
 
@@ -89,6 +89,15 @@ CHGSETS_SCHEMA = Schema(
 

	
 
CHGSET_IDX_NAME = 'CHGSET_INDEX'
 

	
 
# used only to generate queries in journal
 
JOURNAL_SCHEMA = Schema(
 
    username=TEXT(),
 
    date=DATETIME(),
 
    action=TEXT(),
 
    repository=TEXT(),
 
    ip=TEXT(),
 
)
 

	
 

	
 
class MakeIndex(BasePasterCommand):
 

	
rhodecode/templates/admin/admin.html
Show inline comments
 
@@ -6,7 +6,12 @@
 
</%def>
 

	
 
<%def name="breadcrumbs_links()">
 
    <form id="filter_form">
 
    <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${c.search_term or _('quick filter...')}"/>
 
    <input type='submit' value="${_('filter')}" class="ui-btn"/>
 
    ${_('Admin journal')}
 
    </form>
 
    ${h.end_form()}
 
</%def>
 

	
 
<%def name="page_nav()">
 
@@ -25,4 +30,16 @@
 
	    </div>
 
	</div>
 
</div>
 

	
 
<script>
 
YUE.on('q_filter','click',function(){
 
    YUD.get('q_filter').value = '';
 
});
 
YUE.on('filter_form','submit',function(e){
 
	YUE.preventDefault(e)
 
    var val = YUD.get('q_filter').value;
 
	window.location = "${url.current(filter='__FILTER__')}".replace('__FILTER__',val);
 
 });
 
</script>
 
</%def>
 

	
0 comments (0 inline, 0 general)