Modify

Opened 2 years ago

Last modified 13 months ago

#9983 assigned enhancement

Exclude filter for display doesn't work & Parent ticket should be always on Top of it's childs

Reported by: rochi Owned by: ChrisNelson
Priority: normal Component: TracJsGanttPlugin
Severity: normal Keywords:
Cc: Trac Release: 0.11

Description

If I want to display only open tickets with

display!=status:closed

The closed tickets are still displayed.

Attachments (3)

tracjsgantt_patch.diff (6.4 KB) - added by rochi 2 years ago.
See comment:7
sort.patch (2.5 KB) - added by ChrisNelson 2 years ago.
Balance of previous patch which implements sorting
tracjsgantt_patch_12998.diff (4.6 KB) - added by rochi 13 months ago.
Actual Patch for 12998

Download all attachments as: .zip

Change History (18)

comment:1 in reply to: ↑ description Changed 2 years ago by ChrisNelson

Replying to rochi:

If I want to display only open tickets with

display!=status:closed

The closed tickets are still displayed.

The filter specifies what tickets to include. The way it is written there really isn't a way to negate that to exclude tickets. We might add an "hide" filter (the opposite of display) so you could

hide=status:closed

comment:2 Changed 2 years ago by rochi

In your code comments you are talking about TracQuery so I was a little bit confused.
That would be a variant.

But, I've also tried:

display=status:new|status:assigned|status:accepted

It shows also not all posible tickets.

comment:3 follow-up: Changed 2 years ago by rochi

I've done some changes for the filter algorithm, it's not perfect and there are a lof of TODOs...

# Process each part into the display filter
                displayFilter = {}
                for f in displayList:
                    field, value = f.split(':')
                    if field in displayFilter:
                        displayFilter[field].append(value)
                    else:
                        displayFilter[field] = [value]

                # Filter the tickets
                displayTickets = []
                self.env.log.debug("Tickets available: %s",
                           len(self.tickets))
                for ticket in self.tickets:
                    # Default to showing every ticket
                    display = True
                    # Process each element and disable display if all
                    # filters fails to match. ((or) disjunction)
                    for f in displayFilter:
                        for v in displayFilter[f]:
                            if ticket[f] == v:
                                #self.env.log.debug("Display filter match '%s': '%s' for ticket #%s",
                                 #   f,
                                 #   v,
                                 #   ticket['id'])
                                display = True
                                break
                            #self.env.log.debug("Display filter '%s' not match: '%s' for ticket #%s",
                            #    f,
                            #    v,
                            #    ticket['id'])
                            display = False

                    if display:
                        displayTickets.append(ticket)

                # Sort the tickets
                displayTickets.sort(self._compare_tickets)

Further work:

  • disjunction for fields with same key otherwise conjunction.

There is still a problem with the order of subtasks, they are sometimes placed far away from their parents.

comment:4 in reply to: ↑ 3 ; follow-up: Changed 2 years ago by ChrisNelson

Replying to rochi:

I've done some changes for the filter algorithm, it's not perfect and there are a lof of TODOs...

Thanks for working on this.

                     if field in displayFilter:
                         displayFilter[field].append(value)
                     else:
                         displayFilter[field] = [value]

I wonder if value shouldn't be split on |. You can do id=1|2|3, so I think it would be natural to do display=owner:curly|moe.

Further work:

  • disjunction for fields with same key otherwise conjunction.

There is still a problem with the order of subtasks, they are sometimes placed far away from their parents.

That may be deliberate. My original use case for filters was: schedule everything then show me what I should do next" so I sort by date after filtering. You could add a displaySort option to control that.

comment:5 in reply to: ↑ 4 Changed 2 years ago by rochi

Replying to ChrisNelson:

I wonder if value shouldn't be split on |. You can do id=1|2|3, so I think it would be natural to do display=owner:curly|moe.

You are right, but I think it's better to support both.

That may be deliberate. My original use case for filters was: schedule everything then show me what I should do next" so I sort by date after filtering. You could add a displaySort option to control that.

You missed my point, move the next task to the top is the correct behavior, but on a parent-child relation the parent have also move up.
This can be a variant:

  • first item to schedule
    • child from parent next to schedule
  • parent
    • other childs

comment:6 Changed 2 years ago by rochi

Support for: disjunction for fields with same key otherwise conjunction

                # Process each part into the display filter
                displayFilter = {}
                for f in displayList:
                    field, value = f.split(':')
                    if field in displayFilter:
                        displayFilter[field].append(value)
                    else:
                        displayFilter[field] = [value]

                # Filter the tickets
                displayTickets = []
                self.env.log.debug("Tickets available: %s",
                           len(self.tickets))
                for ticket in self.tickets:
                    # Default to showing every ticket
                    fieldDisplay = True
                    # Process each element and disable display if all
                    # filters fails to match. ((or) disjunction)
                    for f in displayFilter:
                        display = True
                        for v in displayFilter[f]:
                            if ticket[f] == v:
                                #self.env.log.debug("Display filter match '%s': '%s' for ticket #%s",
                                 #   f,
                                 #   v,
                                 #   ticket['id'])
                                display = True
                                break
                            #self.env.log.debug("Display filter '%s' not match: '%s' for ticket #%s",
                            #    f,
                            #    v,
                            #    ticket['id'])
                            display = False
                        fieldDisplay = fieldDisplay & display
                    if fieldDisplay:
                        displayTickets.append(ticket)

                # Sort the tickets
                displayTickets.sort(self._compare_tickets)

comment:7 follow-up: Changed 2 years ago by rochi

Ok, I've finished my improvements (see patch)

New features are:

  • Filter with 'display'
    • display:status=new|status=assigned or by status=new|assigned
  • The parent is now always on top of its childs and still considereds the schedule
    • The first item in schedule is than the second item on front because the parent is always the first item

I'm sorry for some code snippets looking strange, but I'm very new to python.

Changed 2 years ago by rochi

See comment:7

comment:8 in reply to: ↑ 7 Changed 2 years ago by ChrisNelson

Replying to rochi:

Ok, I've finished my improvements (see patch)

Thanks! I'll try to review it in the next few days.

New features are:

  • Filter with 'display'
    • display:status=new|status=assigned or by status=new|assigned
  • The parent is now always on top of its childs and still considereds the schedule
    • The first item in schedule is than the second item on front because the parent is always the first item

I'm sorry for some code snippets looking strange, but I'm very new to python.

No problem. If the problems are minor, I'll clean them up. If I don't understand something, I'll let you know.

comment:9 Changed 2 years ago by ChrisNelson

  • Status changed from new to assigned

I tried to apply this patch and can't get it to work. I end up with:

            # If no display filter, just display all tickets
            if not options.get('display') or options['display'] == '':
                displayTickets = self.tickets
            # Otherwise, process the filter
            else:
                # Build the list of display filters from the configured value
                # The general form is
                # 'display=field:value|field:value...'. Split on pipe
                # to get each part
                displayList = options['display'].split('|')

                # Process each part into the display filter
                displayFilter = {}
                for f in displayList:
                    field, value = f.split(':')
                    values = value.split('|')
                    if field in displayFilter:
                        displayFilter[field].extend(values)
                    else:
                        displayFilter[field] = values

Note that the display filter is parsed twice on '|': once for multiple fields (field1:value1|field2:value2) and once for multiple values (field1:value1|value2).

I imagine both could be supported if we kept track of the previous field so that if the second split failed, we'd use the first field. That is, field1:value1|value2 would parse to field1:value1 and value2 and when the split of value2 on : failed, the code would know to look back to field1. This is more than I can do right now.

comment:10 follow-up: Changed 2 years ago by rochi

That ist only the processing of the filters, right?
The real ticket filtering looks like:

# Filter the tickets
                displayTickets = []
                self.env.log.debug("Tickets available: %s",
                           len(self.tickets))
                self.ticketsByID.clear()
                for ticket in self.tickets:
                    # Default to showing every ticket
                    fieldDisplay = True
                    # Process each element and disable display if all
                    # filters fails to match. ((or) disjunction)
                    for f in displayFilter:
                        display = True
                        for v in displayFilter[f]:
                            if ticket[f] == v:
                                #self.env.log.debug("Display filter match '%s': '%s' for ticket #%s",
                                #   f,
                                #   v,
                                #   ticket['id'])
                                display = True
                                break
                            #self.env.log.debug("Display filter '%s' not match: '%s' for ticket #%s",
                            #    f,
                            #    v,
                            #    ticket['id'])
                            display = False
                        fieldDisplay = fieldDisplay & display
                    if fieldDisplay:
                        displayTickets.append(ticket)
                        self.ticketsByID[ticket['id']] = ticket

                # Sort the tickets by date and successor dependencies
                displayTickets.sort(self._compare_tickets)

and comes directly after the filter processing.

What is yout specific issue?
I have this code now running several months without problems.

comment:11 in reply to: ↑ 10 Changed 2 years ago by anonymous

Replying to rochi:

That ist only the processing of the filters, right?
...
What is yout specific issue?
I have this code now running several months without problems.

I have have a chart that shows 7 tasks if I don't add a display option.

  • If I use display=owner:owner1|owner:owner2 I get "No tasks selected".
  • If I use display=owner:owner1|owner2, I get "need more than 1 value to unpack" from field, value = f.split(':')

comment:12 Changed 2 years ago by ChrisNelson

(In [11704]) Allow display=field:val1|val2... Refs #9983.

comment:13 Changed 2 years ago by anonymous

After [11704], I can do

  • display=field1:value1|field2:value2
  • display=field1:value1|field1:value2
  • display=field1:value1|value2

and all work as expected.

Changed 2 years ago by ChrisNelson

Balance of previous patch which implements sorting

comment:14 Changed 2 years ago by ChrisNelson

I'm not yet comfortable enough with the sorting additions to put them on the main line but I attached a patch which should implement them on top of my commit.

comment:15 Changed 13 months ago by rochi

  • Summary changed from Exclude filter for display doesn't work to Exclude filter for display doesn't work & Parent ticket should be always on Top of it's childs
  • Type changed from defect to enhancement

Changed 13 months ago by rochi

Actual Patch for 12998

Add Comment

Modify Ticket

Action
as assigned .
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.