@@ -30,19 +30,20 @@ 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 sqlalchemy.sql.expression import or_, and_
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
from rhodecode.lib.base import BaseController, render
from rhodecode.model.db import UserLog, User
from rhodecode.lib.utils2 import safe_int, remove_prefix, remove_suffix
from rhodecode.lib.indexers import JOURNAL_SCHEMA
from whoosh.qparser.dateparse import DateParserPlugin
log = logging.getLogger(__name__)
def _filter(user_log, search_term):
def _journal_filter(user_log, search_term):
"""
Filters sqlalchemy user_log based on search_term with whoosh Query language
http://packages.python.org/Whoosh/querylang.html
@@ -54,6 +55,7 @@ def _filter(user_log, search_term):
qry = None
if search_term:
qp = QueryParser('repository', schema=JOURNAL_SCHEMA)
qp.add_plugin(DateParserPlugin())
qry = qp.parse(unicode(search_term))
log.debug('Filtering using parsed query %r' % qry)
@@ -87,20 +89,25 @@ def _filter(user_log, search_term):
return wildcard_handler(field, val)
elif isinstance(term, query.Prefix):
return field.startswith(val)
elif isinstance(term, query.DateRange):
return and_(field >= val[0], field <= val[1])
return field == val
if isinstance(qry, (query.And, query.Term, query.Prefix, query.Wildcard)):
if isinstance(qry, (query.And, query.Term, query.Prefix, query.Wildcard,
query.DateRange)):
if not isinstance(qry, query.And):
qry = [qry]
for term in qry:
field = term.fieldname
val = term.text
val = (term.text if not isinstance(term, query.DateRange)
else [term.startdate, term.enddate])
user_log = user_log.filter(get_filterion(field, val, term))
elif isinstance(qry, query.Or):
filters = []
filters.append(get_filterion(field, val, term))
user_log = user_log.filter(or_(*filters))
@@ -122,7 +129,7 @@ class AdminController(BaseController):
#FILTERING
c.search_term = request.GET.get('filter')
try:
users_log = _filter(users_log, c.search_term)
users_log = _journal_filter(users_log, c.search_term)
except:
# we want this to crash for now
raise
@@ -42,6 +42,7 @@ from rhodecode.model.meta import Session
from sqlalchemy.sql.expression import func
from rhodecode.model.scm import ScmModel
from rhodecode.lib.utils2 import safe_int
from rhodecode.controllers.admin.admin import _journal_filter
@@ -65,9 +66,14 @@ class JournalController(BaseController):
.options(joinedload(UserFollowing.follows_repository))\
.all()
journal = self._get_journal_data(c.following)
c.journal_pager = Page(journal, page=p, items_per_page=20)
def url_generator(**kw):
return url.current(filter=c.search_term, **kw)
c.journal_pager = Page(journal, page=p, items_per_page=20, url=url_generator)
c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager)
c.journal_data = render('journal/journal_data.html')
@@ -141,8 +147,14 @@ class JournalController(BaseController):
if filtering_criterion is not None:
journal = self.sa.query(UserLog)\
.options(joinedload(UserLog.user))\
.options(joinedload(UserLog.repository))\
.filter(filtering_criterion)\
.options(joinedload(UserLog.repository))
#filter
journal = _journal_filter(journal, c.search_term)
journal = journal.filter(filtering_criterion)\
.order_by(UserLog.action_date.desc())
else:
journal = []
@@ -11,6 +11,7 @@ import math
import logging
import re
import urlparse
import textwrap
from datetime import datetime
from pygments.formatters.html import HtmlFormatter
@@ -1135,3 +1136,23 @@ def changeset_status_lbl(changeset_statu
def get_permission_name(key):
return dict(Permission.PERMS).get(key)
def journal_filter_help():
return _(textwrap.dedent('''
Example filter terms:
repository:vcs
username:marcin
action:*push*
ip:127.0.0.1
date:20120101
date:[20120101100000 TO 20120102]
Generate wildcards using '*' character:
"repositroy:vcs*" - search everything starting with 'vcs'
"repository:*vcs*" - search for repository containing 'vcs'
Optional AND / OR operators in queries
"repository:vcs OR repository:test"
"username:test AND repository:test*"
'''))
@@ -7,25 +7,8 @@
<%def name="breadcrumbs_links()">
<form id="filter_form">
<input class="q_filter_box ${'' if c.search_term else 'initial'}" id="q_filter" size="15" type="text" name="filter" value="${c.search_term or _('quick filter...')}"/>
<span class="tooltip" title="${h.tooltip(_('''
Example search query:
"repository:vcs"
"username:marcin"
You can use wildcards using '*'
Use AND / OR operators in queries
List of valid search filters:
repository:
username:
action:
ip:
date:
'''))}">?</span>
<input class="q_filter_box ${'' if c.search_term else 'initial'}" id="j_filter" size="15" type="text" name="filter" value="${c.search_term or _('journal filter...')}"/>
<span class="tooltip" title="${h.tooltip(h.journal_filter_help())}">?</span>
<input type='submit' value="${_('filter')}" class="ui-btn" style="padding:0px 2px 0px 2px;margin:0px"/>
${_('Admin journal')} - ${ungettext('%s entry', '%s entries', c.users_log.item_count) % (c.users_log.item_count)}
</form>
@@ -50,24 +33,24 @@
</div>
<script>
YUE.on('q_filter','click',function(){
var qfilter = YUD.get('q_filter');
if(YUD.hasClass(qfilter, 'initial')){
qfilter.value = '';
YUE.on('j_filter','click',function(){
var jfilter = YUD.get('j_filter');
if(YUD.hasClass(jfilter, 'initial')){
jfilter.value = '';
}
});
var fix_q_filter_width = function(len){
YUD.setStyle(YUD.get('q_filter'),'width',Math.max(80, len*6.50)+'px');
var fix_j_filter_width = function(len){
YUD.setStyle(YUD.get('j_filter'),'width',Math.max(80, len*6.50)+'px');
YUE.on('q_filter','keyup',function(){
fix_q_filter_width(YUD.get('q_filter').value.length);
YUE.on('j_filter','keyup',function(){
fix_j_filter_width(YUD.get('j_filter').value.length);
YUE.on('filter_form','submit',function(e){
YUE.preventDefault(e)
var val = YUD.get('q_filter').value;
var val = YUD.get('j_filter').value;
window.location = "${url.current(filter='__FILTER__')}".replace('__FILTER__',val);
</script>
</%def>
@@ -4,7 +4,15 @@
${_('Journal')} - ${c.rhodecode_name}
<%def name="breadcrumbs()">
${c.rhodecode_name}
<h5>
<input class="q_filter_box ${'' if c.search_term else 'initial'}" id="j_filter" size="15" type="text" name="filter" value="${c.search_term or _('quick filter...')}"/>
${_('journal')} - ${ungettext('%s entry', '%s entries', c.journal_pager.item_count) % (c.journal_pager.item_count)}
${h.end_form()}
</h5>
<%def name="page_nav()">
${self.menu('home')}
@@ -18,7 +26,7 @@
<div class="box box-left">
<!-- box / title -->
<div class="title">
<h5>${_('Journal')}</h5>
${self.breadcrumbs()}
<ul class="links">
<li>
<span><a id="refresh" href="${h.url('journal')}"><img class="icon" title="${_('Refresh')}" alt="${_('Refresh')}" src="${h.url('/images/icons/arrow_refresh.png')}"/></a></span>
@@ -106,6 +114,26 @@
<script type="text/javascript">
var show_my = function(e){
YUD.setStyle('watched','display','none');
YUD.setStyle('my','display','');
@@ -153,7 +181,7 @@
YUE.on('refresh','click',function(e){
ypjax(e.currentTarget.href,"journal",function(){
ypjax("${h.url.current(filter=c.search_term)}","journal",function(){
show_more_event();
tooltip_activate();
show_changeset_tooltip();
@@ -100,4 +100,16 @@ class TestAdminController(TestController
self.log_user()
response = self.app.get(url(controller='admin/admin', action='index',
filter='action:*pull_request*'))
response.mustcontain('187 entries')
\ No newline at end of file
def test_filter_journal_filter_on_date(self):
filter='date:20121010'))
response.mustcontain('47 entries')
def test_filter_journal_filter_on_date_2(self):
filter='date:20121020'))
response.mustcontain('17 entries')
Status change: