Opened 16 years ago
Last modified 5 years ago
#7342 new enhancement
Add filter parameters to milestone view
Reported by: | Owned by: | ||
---|---|---|---|
Priority: | normal | Milestone: | unscheduled |
Component: | roadmap | Version: | 0.10.4 |
Severity: | normal | Keywords: | |
Cc: | ivan@…, franz.mayer@… | Branch: | |
Release Notes: | |||
API Changes: | |||
Internal Changes: |
Description
Milestone view support only one milestone per page. Roadmap can display several milestones, but there is almost no control on order and scope of shown stats. The only option is to show completed milestones (show=all) or not.
I propose to extend "show=all" to allow queries like "show=^0.5" or "show=0.5|0.5.1", but it can create a conflict if somebody defines milestone named "all".
It can be convenient to extend milestone view as well to allow URLs like /milestone/6.0|6.2|6.4
Attached is a simple patch to select several milestones in roadmap using piped /roadmap?milestone=6.0|6.2|6.4 syntax. No advanced filtering using ~= or ^=
Attachments (4)
Change History (17)
by , 16 years ago
Attachment: | 0.10.4.roadmap.py.txt added |
---|
by , 16 years ago
Attachment: | 0.11.roadmap.py.txt added |
---|
comment:1 by , 16 years ago
Milestone: | → 2.0 |
---|---|
Owner: | removed |
comment:2 by , 16 years ago
Component: | general → roadmap |
---|
For example, the prefs form, in addition to the Show already completed milestones checkbox could have a Filter by name: field, which would accept a glob pattern. If you fill it with e.g. 0.11*
, the roadmap module would show all the Milestone starting with 0.11.
comment:3 by , 16 years ago
Cc: | added |
---|
comment:8 by , 13 years ago
RoadmapPlugin might suit your needs (uses largely code of RoadmapFilterPlugin
); but it's only available in English and German.
comment:9 by , 9 years ago
Cc: | added |
---|
Is there any plan to implement this feature?
When many milestones are present (like at t.e.o or at our environment) I find it very useful to filter by milestone name (like ^1.1
, which displays all milestones starting with "1.1"); this feature is already implemented by RoadmapPlugin. Maybe the filter can be implemented as in reports (with Drop-Down-Box staring, contains etc).
comment:11 by , 6 years ago
-
trac/ticket/templates/roadmap.html
diff -r 4f798415e628 trac/ticket/templates/roadmap.html
a b 40 40 <label for="hidenoduedate">${ 41 41 _("Hide milestones with no due date")}</label> 42 42 </div> 43 <div> 44 <label for="filter">${ 45 _("Filter:")}</label> 46 <input type="input" id="filter" name="filter" value="${filter}"/> 47 </div> 43 48 <div class="buttons"> 44 49 <input type="submit" value="${_('Update')}"/> 45 50 </div> -
trac/ticket/roadmap.py
diff -r 4f798415e628 trac/ticket/roadmap.py
a b 27 27 from trac.perm import IPermissionRequestor 28 28 from trac.resource import * 29 29 from trac.search import ISearchSource, search_to_regexps, shorten_result 30 from trac.util import as_bool, partition30 from trac.util import as_bool, MultiPartFilter, partition 31 31 from trac.util.datefmt import (datetime_now, format_date, format_datetime, 32 32 from_utimestamp, get_datetime_format_hint, 33 33 parse_date, pretty_timedelta, to_datetime, … … 484 484 def process_request(self, req): 485 485 req.perm.require('ROADMAP_VIEW') 486 486 487 filter = req.args.get('filter', '') 487 488 show = req.args.getlist('show') 488 489 if 'all' in show: 489 490 show = ['completed'] … … 494 495 if m.due is not None or m.completed] 495 496 milestones = [m for m in milestones 496 497 if 'MILESTONE_VIEW' in req.perm(m.resource)] 498 if filter: 499 f = MultiPartFilter(filter) 500 milestones = [m for m in milestones 501 if f.matches(m.name)] 497 502 498 503 stats = [] 499 504 queries = [] … … 524 529 'milestone_stats': stats, 525 530 'queries': queries, 526 531 'show': show, 532 'filter': filter, 527 533 } 528 534 add_stylesheet(req, 'common/css/roadmap.css') 529 535 return 'roadmap.html', data -
trac/util/__init__.py
diff -r 4f798415e628 trac/util/__init__.py
a b 1388 1388 else: 1389 1389 the_list[index] = item_to_add 1390 1390 1391 1392 class Filter(object): 1393 """A simple text filter with optional leading operator: 1394 ^ ("starts with") 1395 !^ ("does not start with") 1396 $ ("ends with") 1397 !$ ("does not end with") 1398 ~ ("contains") 1399 !~ ("does not contain") 1400 ! ("does not equal") 1401 The default operator is "equals". 1402 """ 1403 1404 def __init__(self, pattern): 1405 for op in ('^', '!^', '$', '!$', '~', '!~', '!'): 1406 if pattern.startswith(op): 1407 self.op = op 1408 self.needle = pattern[len(op):] 1409 break 1410 else: 1411 self.op = '' 1412 self.needle = pattern 1413 self.func = { 1414 '^': lambda text: text.startswith(self.needle), 1415 '!^': lambda text: not text.startswith(self.needle), 1416 '$': lambda text: text.endswith(self.needle), 1417 '!$': lambda text: not text.endswith(self.needle), 1418 '~': lambda text: self.needle in text, 1419 '!~': lambda text: self.needle not in text, 1420 '': lambda text: text == self.needle, 1421 '!': lambda text: text != self.needle, 1422 }[self.op] 1423 1424 def matches(self, text): 1425 """Returns True if the text matches this filter""" 1426 return self.func(text) 1427 1428 1429 class MultiPartFilter(object): 1430 """A text filter consisting of multiple parts.""" 1431 1432 def __init__(self, filters): 1433 if isinstance(filters, basestring): 1434 filters = map(Filter, to_list(filters, ' ')) 1435 self.excludes = [f for f in filters if f.op.startswith('!')] 1436 self.includes = [f for f in filters if not f.op.startswith('!')] 1437 1438 def matches(self, text): 1439 """Returns True if the text matches this filter.""" 1440 for exclude in self.excludes: 1441 if not exclude.matches(text): 1442 return False 1443 for include in self.includes: 1444 if include.matches(text): 1445 return True 1446 return not any(self.includes) 1447 1391 1448 1392 1449 # Imports for backward compatibility (at bottom to avoid circular dependencies) 1393 1450 from trac.core import TracError
comment:12 by , 6 years ago
Please submit patches as attachments, per guidelines in TracDev/SubmittingPatches, rather than pasting inline.
by , 6 years ago
Attachment: | MilestoneFilter-trunk@16826.patch added |
---|
by , 5 years ago
Attachment: | Screen Shot 2020-04-29 at 18.08.56.jpg added |
---|
comment:13 by , 5 years ago
I'm sorry for the very long delay to review this.
The query filter operators look similar to TracQuery#QueryLanguage.
I think ultimately we probably want a filter like on the TicketQuery page:
Maybe the roadmap could have query controls for Name, Due Date and Completed Date, similar to the TicketQuery module.
Otherwise, we are introducing a new query language that will be difficult to remember, especially lacking autocomplete for the field.
Well, maybe it's okay as an interim, but it seems like we should support globs (comment:2). Maybe a tooltip on hover of the field or help icon would be sufficient to help with the syntax. Some form of autocomplete would probably be ideal. Is this particular query language similar to that used elsewhere?
Why not. However, the current patch requires editing the page URL, which is not really convenient. For this to be included, some more thought should be given to the user interface side.