Mercurial > kallithea
changeset 3070:cc7eedb5323c beta
final implementation of #210 journal filtering.
author | Marcin Kuzminski <marcin@python-works.com> |
---|---|
date | Thu, 06 Dec 2012 23:55:12 +0100 |
parents | 9dca99ffd495 |
children | 7b0c19b00629 |
files | rhodecode/controllers/admin/admin.py rhodecode/controllers/journal.py rhodecode/lib/helpers.py rhodecode/templates/admin/admin.html rhodecode/templates/journal/journal.html rhodecode/tests/functional/test_admin.py |
diffstat | 6 files changed, 117 insertions(+), 54 deletions(-) [+] |
line wrap: on
line diff
--- a/rhodecode/controllers/admin/admin.py Thu Dec 06 21:57:24 2012 +0100 +++ b/rhodecode/controllers/admin/admin.py Thu Dec 06 23:55:12 2012 +0100 @@ -30,19 +30,20 @@ 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 @@ 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 @@ 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 @@ #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
--- a/rhodecode/controllers/journal.py Thu Dec 06 21:57:24 2012 +0100 +++ b/rhodecode/controllers/journal.py Thu Dec 06 23:55:12 2012 +0100 @@ -42,6 +42,7 @@ 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 @@ .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 @@ 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 = []
--- a/rhodecode/lib/helpers.py Thu Dec 06 21:57:24 2012 +0100 +++ b/rhodecode/lib/helpers.py Thu Dec 06 23:55:12 2012 +0100 @@ -11,6 +11,7 @@ import logging import re import urlparse +import textwrap from datetime import datetime from pygments.formatters.html import HtmlFormatter @@ -1135,3 +1136,23 @@ 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*" + '''))
--- a/rhodecode/templates/admin/admin.html Thu Dec 06 21:57:24 2012 +0100 +++ b/rhodecode/templates/admin/admin.html Thu Dec 06 23:55:12 2012 +0100 @@ -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>
--- a/rhodecode/templates/journal/journal.html Thu Dec 06 21:57:24 2012 +0100 +++ b/rhodecode/templates/journal/journal.html Thu Dec 06 23:55:12 2012 +0100 @@ -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();
--- a/rhodecode/tests/functional/test_admin.py Thu Dec 06 21:57:24 2012 +0100 +++ b/rhodecode/tests/functional/test_admin.py Thu Dec 06 23:55:12 2012 +0100 @@ -100,4 +100,16 @@ 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