#3598 Implement some filters for the roadmap page
Merged 5 years ago by ryanlerch. Opened 5 years ago by ryanlerch.
ryanlerch/pagure filter-milestones  into  master

@@ -15,7 +15,9 @@ 

                  repo=repo.name,

                  username=username,

                  namespace=repo.namespace) }}#roadmap-tab"

-                 class="btn btn-outline-primary">

+                 class="btn btn-outline-primary"

+                 title="Configure Milestones"

+                 data-toggle="tooltip">

              <i class="fa fa-cogs fa-fw"></i>

              Configure Milestones

          </a>
@@ -31,27 +33,176 @@ 

                  <div class="row">

                      <div class="col">

                          <div class="btn-group">

-                             <a class="btn btn-sm {% if milestones_status_select == 'active'

-                             %}btn-primary{% else %}btn-outline-primary{% endif %}"

-                                href="{{ url_for('ui_ns.view_roadmap',

-                                      repo=repo.name,

-                                      username=repo.username if repo.is_fork else None,

-                                      namespace=repo.namespace,

-                                     status='active') }}">

-                                 <i class="fa fa-clock-o"></i>

-                                 {{milestones_totals['active']}} Active

-                             </a>

-                             <a class="btn btn-sm {% if milestones_status_select == 'inactive'

-                             %}btn-primary{% else %}btn-outline-primary{% endif %}"

-                                href="{{ url_for('ui_ns.view_roadmap',

-                                      repo=repo.name,

-                                      username=repo.username if repo.is_fork else None,

-                                      namespace=repo.namespace,

-                                     status='inactive') }}">

-                                 <i class="fa fa-check-circle"></i>

-                                 {{milestones_totals['inactive']}} Inactive

-                             </a>

-                         </div>

+                             <div class="dropdown">

+                             {% if milestones_status_select == 'active' %}

+                                 <a class="btn btn-sm btn-outline-primary border-0 font-weight-bold dropdown-toggle" href="#" data-toggle="dropdown" id="issue-status-dropdown">

+                                     <span class="fa fa-fw fa-map-signs"></span> {{milestones_totals['active']}} Active Milestones

+                             {% elif milestones_status_select == 'inactive'%}

+                                 <a class="btn btn-sm btn-outline-secondary border-0 font-weight-bold dropdown-toggle" href="#" data-toggle="dropdown" id="issue-status-dropdown">

+                                     <span class="fa fa-fw fa-map-signs"></span> {{milestones_totals['inactive']}} Inactive Milestones

+                             {% elif milestones_status_select == 'all' %}

+                                 <a class="btn btn-sm btn-outline-secondary border-0 font-weight-bold dropdown-toggle" href="#" data-toggle="dropdown" id="issue-status-dropdown">

+                                     <span class="fa fa-fw fa-map-signs"></span> {{milestones_totals['inactive']+milestones_totals['active']}} All Milestones

+                             {% endif %}

+                                 </a>

+                                 <div class="dropdown-menu">

+                                 <a class="dropdown-item {% if milestones_status_select == 'active' %}active{%endif%}"

+                                         href="{{ url_for('ui_ns.view_roadmap',

+                                         repo=repo.name,

+                                         username=repo.username if repo.is_fork else None,

+                                         namespace=repo.namespace,

+                                        status='active',

+                                        keyword=keyword,) }}">

+                                        {{milestones_totals['active']}} Active Milestones

+                                 </a>

+ 

+                                 <a class="dropdown-item {% if milestones_status_select == 'inactive' %}active{%endif%}"

+                                     href="{{ url_for('ui_ns.view_roadmap',

+                                         repo=repo.name,

+                                         username=repo.username if repo.is_fork else None,

+                                         namespace=repo.namespace,

+                                         status='inactive',

+                                         keyword=keyword) }}">

+                                         {{milestones_totals['inactive']}} Inactive Milestones

+                                 </a>

+ 

+                                 <a class="dropdown-item {% if milestones_status_select == 'all' %}active{%endif%}"

+                                         href="{{ url_for('ui_ns.view_roadmap',

+                                         repo=repo.name,

+                                         username=repo.username if repo.is_fork else None,

+                                         namespace=repo.namespace,

+                                        status='all',

+                                        keyword=keyword) }}">

+                                        {{milestones_totals['inactive']+milestones_totals['active']}} All Milestones

+                                 </a>

+                                 </div>

+                             </div>

+                             <div class="btn-group">

+                                 <button class="btn btn-sm btn-outline-secondary border-0 dropdown-toggle" data-flip="false" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">

+                                 <span class="font-weight-bold">Filter{%if keyword %}:{% endif %}

+                                 </span>

+ 

+                                     {% if keyword %}

+                                     <i class="fa fa-search pl-2 pr-1" title="tagged"></i>

+                                     <span>{{ keyword }}</span>

+                                     {% endif %}

+                                     {% if onlyincomplete == True %}

+                                     <i class="fa fa-percent pl-2 pr-1" title="tagged"></i>

+                                     <span>only incomplete</span>

+                                     {% endif %}

+                                 </button>

+                                 <div class="dropdown-menu p-4" style="min-width:400px" aria-labelledby="dropdownMenuButton" id="filters-dropdown">

+                                 <div>

+                                     <form id="filters_form" action="{{ url_for('ui_ns.view_roadmap',

+                                         username=username,

+                                         namespace=repo.namespace,

+                                         repo=repo.name ) }}" method="GET">

+                                     <input type="hidden" name="status" value="{{ milestones_status_select or 'all' }}" />

+ 

+                                     <div class="form-group row mb-1" id="onlyincomplete-filter-group">

+                                         <label for="tags" class="col-auto align-self-center pl-1 pr-0"><i class="text-muted fa fa-fw fa-percent"></i> Show Incomplete only</label>

+                                         <input type="checkbox" name="onlyincomplete" value="True" {{'checked="checked"' if onlyincomplete}} class="ml-auto mr-1">

+                                     </div>

+ 

+                                     <div class="form-group row mb-2">

+                                         <label for="search_pattern" class="col-auto align-self-center pl-1 pr-0"><i class="text-muted fa fa-fw fa-search"></i></label>

+                                         <div class="col pl-1">

+                                         <input type="text" class="form-control"

+                                                 name="keyword" placeholder="Search"

+                                                 value="{{ keyword or '' }}" />

+                                         </div>

+                                     </div>

+                                     {#

+                                     <div class="form-group row mb-1" id="milestone-filter-group">

+                                         <label for="milestone" class="col-auto align-self-center pl-1 pr-0"><i class="text-muted fa fa-fw fa-map-signs"></i></label>

+                                         <div class="col pl-1">

+                                         <select name="milestone" id="milestone-selectize" placeholder="Milestone">

+                                             <option value=""></option>

+                                             <option value="none" {% if no_milestones %}selected="selected"{% endif %}>Milestone unset</option>

+                                             {% for stone in repo.milestones %}

+                                             {% if loop.first %}

+                                                 <optgroup label="Active">

+                                             {% endif %}

+                                             {% if repo.milestones[stone]['active']%}

+                                             <option value="{{ stone }}" {% if milestones[0] == stone %}selected="selected"{% endif %}>{{stone}}</option>

+                                             {% endif %}

+                                             {% if loop.last %}

+                                                 </optgroup>

+                                             {% endif %}

+                                             {% endfor %}

+                                             {% for stone in repo.milestones %}

+                                             {% if loop.first %}

+                                                 <optgroup label="Inactive">

+                                             {% endif %}

+                                             {% if not repo.milestones[stone]['active']%}

+                                             <option value="{{ stone }}" {% if milestones[0] == stone %}selected="selected"{% endif %}>{{stone}}</option>

+                                             {% endif %}

+                                             {% if loop.last %}

+                                                 </optgroup>

+                                             {% endif %}

+                                             {% endfor %}

+                                         </select>

+                                         </div>

+                                         <div class="col-auto pl-0 pr-1 pt-1">

+                                         <i class="fa fa-times fa-fw text-muted" id="milestone-selectize-reset"></i>

+                                         </div>

+                                     </div>

+ 

+                                     <div class="form-group row mb-1" id="priority-filter-group">

+                                         <label for="priority" class="col-auto align-self-center pl-1 pr-0"><i class="text-muted fa fa-fw fa-bolt"></i></label>

+                                         <div class="col pl-1">

+                                         <select name="priority" id="priority-selectize" placeholder="Priority">

+                                         {% for p in repo.priorities | sort %}

+                                             <option value="{{ p }}" {% if p | int == priority | int %}selected="selected"{% endif %}>{{repo.priorities[p | string]}}</option>

+                                         {% endfor %}

+                                         </select>

+                                         </div>

+                                         <div class="col-auto pl-0 pr-1 pt-1">

+                                         <i class="fa fa-times fa-fw text-muted" id="priority-selectize-reset"></i>

+                                         </div>

+                                     </div>

+ 

+                                     <div class="form-group row mb-1" id="assignee-filter-group">

+                                         <label for="assignee" class="col-auto align-self-center pl-1 pr-0"><i class="text-muted fa fa-fw fa-user-plus"></i></label>

+                                         <div class="col pl-1">

+                                         <select name="assignee" id="assignee-selectize" placeholder="Assignee">

+                                             {% if assignee %}

+                                             <option value="{{assignee}}" selected="selected">{{assignee}}</option>

+                                             {% endif %}

+                                         </select>

+                                         </div>

+                                         <div class="col-auto pl-0 pr-1 pt-1">

+                                         <i class="fa fa-times fa-fw text-muted" id="assignee-selectize-reset"></i>

+                                         </div>

+                                     </div>

+ 

+                                     <div class="form-group row mb-1" id="author-filter-group">

+                                         <label for="author" class="col-auto align-self-center pl-1 pr-0"><i class="text-muted fa fa-fw fa-user"></i></label>

+                                         <div class="col pl-1">

+                                         <select name="author" id="author-selectize" placeholder="Submitted by">

+                                             {% if author %}

+                                             <option value="{{author}}" selected="selected">{{author}}</option>

+                                             {% endif %}

+                                         </select>

+                                         </div>

+                                         <div class="col-auto pl-0 pr-1 pt-1">

+                                         <i class="fa fa-times fa-fw text-muted" id="author-selectize-reset"></i>

+                                         </div>

+                                     </div>#}

+ 

+                                     <input type="submit" class="btn btn-block btn-primary" value="Apply Filters" />

+ 

+                                     <a href="{{ url_for('ui_ns.view_roadmap',

+                                     repo=repo.name,

+                                     username=username,

+                                     namespace=repo.namespace,

+                                     status=milestones_status_select) }}" class="btn btn-link btn-block">Reset Filters</a>

+ 

+                                     </form>

+                                 </div>

+                                 </div>

+                             </div>

+                             </div>

                      </div>

                  </div>

              </div>

file modified
+27 -2
@@ -951,6 +951,14 @@ 

      """ List all issues associated to a repo as roadmap

      """

      milestones_status_arg = flask.request.args.get("status", "active")

+     milestones_keyword_arg = flask.request.args.get("keyword", None)

+     milestones_onlyincomplete_arg = \

+         flask.request.args.get("onlyincomplete", False)

+ 

+     if milestones_onlyincomplete_arg == 'True':

+         milestones_onlyincomplete_arg = True

+     else:

+         milestones_onlyincomplete_arg = False

  

      repo = flask.g.repo

  
@@ -970,13 +978,17 @@ 

      milestones_totals["inactive"] = 0

  

      for key in repo.milestones_keys:

+         if milestones_keyword_arg and milestones_keyword_arg not in key:

+             continue

          if repo.milestones[key]["active"]:

              milestones_totals["active"] += 1

-             if milestones_status_arg == "active":

+             if milestones_status_arg == "active" or \

+                milestones_status_arg == "all":

                  milestones_list.append(key)

          else:

              milestones_totals["inactive"] += 1

-             if milestones_status_arg == "inactive":

+             if milestones_status_arg == "inactive" or \

+                milestones_status_arg == "all":

                  milestones_list.append(key)

  

      issues = pagure.lib.search_issues(
@@ -997,6 +1009,17 @@ 

                  milestone_issues[issue.milestone][issue.status] += 1

                  milestone_issues[issue.milestone]["Total"] += 1

  

+     if milestones_onlyincomplete_arg:

+         for m in milestone_issues:

+             if milestone_issues[m]['Total'] == 0:

+                 continue

+             elif milestone_issues[m]['Total'] == milestone_issues[m]['Closed']:

+                 del milestone_issues[m]

+                 if repo.milestones[m]['active']:

+                     milestones_totals["active"] -= 1

+                 else:

+                     milestones_totals["inactive"] -= 1

+ 

      return flask.render_template(

          "repo_roadmap.html",

          select="roadmap",
@@ -1005,6 +1028,8 @@ 

          username=username,

          milestones=milestone_issues,

          milestones_totals=milestones_totals,

+         keyword=milestones_keyword_arg,

+         onlyincomplete=milestones_onlyincomplete_arg,

      )

  

  

Implements filters for the roadmap listing page, allowing
filtering by milestone name query, and to hide milestones
that are 100% complete.

see https://pagure.io/pagure/issue/3591 for more background discussion on this idea.

From the test results:

13:04:57 ======================================================================
13:04:57 FAIL: Enforce PEP-8 compliance on the codebase.
13:04:57 ----------------------------------------------------------------------
13:04:57 Traceback (most recent call last):
13:04:57   File "/root/pagure/tests/test_style.py", line 42, in test_code_with_flake8
13:04:57     self.assertEqual(proc.returncode, 0)
13:04:57 nose.proxy.AssertionError: 1 != 0
13:04:57 -------------------- >> begin captured stdout << ---------------------
13:04:57 (b"/root/pagure/pagure/ui/issues.py:955:80: E501 line too long (83 > 79 characters)\n/root/pagure/pagure/ui/issues.py:980:39: E713 test for membership should be 'not in'\n/root/pagure/pagure/ui/issues.py:1010:1: W293 blank line contains whitespace\n/root/pagure/pagure/ui/issues.py:1024:5: E303 too many blank lines (3)\n", None)
13:04:57 
13:04:57 --------------------- >> end captured stdout << ----------------------

Please fix.

rebased onto 6ab7d418a9ff3304297f83d143a124816d5461fc

5 years ago

Pretty please pagure-ci rebuild

@ryanlerch One more rebase and ready to merge :)

rebased onto b4021a7

5 years ago

Pull-Request has been merged by ryanlerch

5 years ago