aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcin Kuzminski <marcin@python-works.com>2012-12-06 23:55:12 +0100
committerMarcin Kuzminski <marcin@python-works.com>2012-12-06 23:55:12 +0100
commite72e4e231838587e0d51f6ab33346b870c11106a (patch)
tree0d8915d2c955804a77e3e5e66986a22767453bca
parent3e4e16e41709230c6f8ab3f2afe9809a6929410e (diff)
final implementation of #210 journal filtering.
--HG-- branch : beta
-rw-r--r--rhodecode/controllers/admin/admin.py19
-rw-r--r--rhodecode/controllers/journal.py20
-rw-r--r--rhodecode/lib/helpers.py21
-rw-r--r--rhodecode/templates/admin/admin.html41
-rw-r--r--rhodecode/templates/journal/journal.html56
-rw-r--r--rhodecode/tests/functional/test_admin.py14
6 files changed, 117 insertions, 54 deletions
diff --git a/rhodecode/controllers/admin/admin.py b/rhodecode/controllers/admin/admin.py
index c5ffe1f5..32444fbb 100644
--- a/rhodecode/controllers/admin/admin.py
+++ b/rhodecode/controllers/admin/admin.py
@@ -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 = []
for term in qry:
field = term.fieldname
- val = term.text
+ val = (term.text if not isinstance(term, query.DateRange)
+ else [term.startdate, term.enddate])
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
diff --git a/rhodecode/controllers/journal.py b/rhodecode/controllers/journal.py
index 7b34e0c8..ed0bd2d2 100644
--- a/rhodecode/controllers/journal.py
+++ b/rhodecode/controllers/journal.py
@@ -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
log = logging.getLogger(__name__)
@@ -65,9 +66,14 @@ class JournalController(BaseController):
.options(joinedload(UserFollowing.follows_repository))\
.all()
+ #FILTERING
+ c.search_term = request.GET.get('filter')
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,9 +147,15 @@ 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)\
- .order_by(UserLog.action_date.desc())
+ .options(joinedload(UserLog.repository))
+ #filter
+ try:
+ journal = _journal_filter(journal, c.search_term)
+ except:
+ # we want this to crash for now
+ raise
+ journal = journal.filter(filtering_criterion)\
+ .order_by(UserLog.action_date.desc())
else:
journal = []
diff --git a/rhodecode/lib/helpers.py b/rhodecode/lib/helpers.py
index ebff0185..d6e244fa 100644
--- a/rhodecode/lib/helpers.py
+++ b/rhodecode/lib/helpers.py
@@ -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_status):
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*"
+ '''))
diff --git a/rhodecode/templates/admin/admin.html b/rhodecode/templates/admin/admin.html
index c3b814ea..80c4b3b4 100644
--- a/rhodecode/templates/admin/admin.html
+++ b/rhodecode/templates/admin/admin.html
@@ -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 '*'
- "repositroy:vcs*" - search everything starting with 'vcs'
- "repository:*vcs*" - search for repository containing 'vcs'
- Use AND / OR operators in queries
- "repository:vcs OR repository:test"
- "username:test AND repository:test*"
- 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);
});
-fix_q_filter_width(YUD.get('q_filter').value.length);
+fix_j_filter_width(YUD.get('j_filter').value.length);
</script>
</%def>
diff --git a/rhodecode/templates/journal/journal.html b/rhodecode/templates/journal/journal.html
index b8faf820..0d41db06 100644
--- a/rhodecode/templates/journal/journal.html
+++ b/rhodecode/templates/journal/journal.html
@@ -4,7 +4,15 @@
${_('Journal')} - ${c.rhodecode_name}
</%def>
<%def name="breadcrumbs()">
- ${c.rhodecode_name}
+ <h5>
+ <form id="filter_form">
+ <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...')}"/>
+ <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"/>
+ ${_('journal')} - ${ungettext('%s entry', '%s entries', c.journal_pager.item_count) % (c.journal_pager.item_count)}
+ </form>
+ ${h.end_form()}
+ </h5>
</%def>
<%def name="page_nav()">
${self.menu('home')}
@@ -18,18 +26,18 @@
<div class="box box-left">
<!-- box / title -->
<div class="title">
- <h5>${_('Journal')}</h5>
- <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>
- </li>
- <li>
- <span><a href="${h.url('journal_rss', api_key=c.rhodecode_user.api_key)}"><img class="icon" title="${_('RSS feed')}" alt="${_('RSS feed')}" src="${h.url('/images/icons/rss_16.png')}"/></a></span>
- </li>
- <li>
- <span><a href="${h.url('journal_atom', api_key=c.rhodecode_user.api_key)}"><img class="icon" title="${_('ATOM feed')}" alt="${_('ATOM feed')}" src="${h.url('/images/icons/atom.png')}"/></a></span>
- </li>
- </ul>
+ ${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>
+ </li>
+ <li>
+ <span><a href="${h.url('journal_rss', api_key=c.rhodecode_user.api_key)}"><img class="icon" title="${_('RSS feed')}" alt="${_('RSS feed')}" src="${h.url('/images/icons/rss_16.png')}"/></a></span>
+ </li>
+ <li>
+ <span><a href="${h.url('journal_atom', api_key=c.rhodecode_user.api_key)}"><img class="icon" title="${_('ATOM feed')}" alt="${_('ATOM feed')}" src="${h.url('/images/icons/atom.png')}"/></a></span>
+ </li>
+ </ul>
</div>
<div id="journal">${c.journal_data}</div>
</div>
@@ -106,6 +114,26 @@
</div>
<script type="text/javascript">
+
+ YUE.on('j_filter','click',function(){
+ var jfilter = YUD.get('j_filter');
+ if(YUD.hasClass(jfilter, 'initial')){
+ jfilter.value = '';
+ }
+ });
+ var fix_j_filter_width = function(len){
+ YUD.setStyle(YUD.get('j_filter'),'width',Math.max(80, len*6.50)+'px');
+ }
+ 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('j_filter').value;
+ window.location = "${url.current(filter='__FILTER__')}".replace('__FILTER__',val);
+ });
+ fix_j_filter_width(YUD.get('j_filter').value.length);
+
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();
diff --git a/rhodecode/tests/functional/test_admin.py b/rhodecode/tests/functional/test_admin.py
index 84935bea..1b2840f8 100644
--- a/rhodecode/tests/functional/test_admin.py
+++ b/rhodecode/tests/functional/test_admin.py
@@ -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
+ response.mustcontain('187 entries')
+
+ def test_filter_journal_filter_on_date(self):
+ self.log_user()
+ response = self.app.get(url(controller='admin/admin', action='index',
+ filter='date:20121010'))
+ response.mustcontain('47 entries')
+
+ def test_filter_journal_filter_on_date_2(self):
+ self.log_user()
+ response = self.app.get(url(controller='admin/admin', action='index',
+ filter='date:20121020'))
+ response.mustcontain('17 entries') \ No newline at end of file