From 853b2023a063ca2956fc8a522938ad681fbb2e48 Mon Sep 17 00:00:00 2001 From: Mark Reynolds Date: Feb 20 2017 13:51:25 +0000 Subject: Add functionality for dealing with issues with no milestone We need a way find issues that do not have a milestone set yet, and we need a way to find tickets that have specific milestones as well as no milestone. For example: When new tickets are created they don't have a milestone, but we also have a NEEDS_TRIAGE milestone. So to find all the issues that need to triaged(aka have a milestone set) we need to find both types. This patch adds a new "No milestone" button to the roadmap page, and it allows you to search issues by using multiple milestones(including "none"): http://localhost.localdomain:5000/TEST_PROJ/issues?milestone=none&milestone=0.0 We can save this as a custom report for finding all the issues that need to be triaged. --- diff --git a/pagure/lib/__init__.py b/pagure/lib/__init__.py index 52599bd..040f042 100644 --- a/pagure/lib/__init__.py +++ b/pagure/lib/__init__.py @@ -2028,7 +2028,7 @@ def search_issues( closed=False, tags=None, assignee=None, author=None, private=None, priority=None, milestones=None, count=False, offset=None, limit=None, search_pattern=None, custom_search=None, - updated_after=None): + updated_after=None, no_milestones=None): ''' Retrieve one or more issues associated to a project with the given criterias. @@ -2083,6 +2083,8 @@ def search_issues( :kwarg updated_after: datetime's date format (e.g. 2016-11-15) used to filter issues updated after that date :type updated_after: str or None + :kwarg no_milestones: Request issues that do not have a milestone set yet + :type None, True, or False :return: A single Issue object if issueid is specified, a list of Project objects otherwise. @@ -2127,6 +2129,7 @@ def search_issues( query = query.filter( model.Issue.priority == priority ) + if tags is not None and tags != []: if isinstance(tags, basestring): tags = [tags] @@ -2225,10 +2228,23 @@ def search_issues( ) ) - if milestones is not None and milestones != []: + if no_milestones and milestones is not None and milestones != []: + # Asking for issues with no milestone or a specific milestone + if isinstance(milestones, basestring): + milestones = [milestones] + query = query.filter( + (model.Issue.milestone == None) | + (model.Issue.milestone.in_(milestones)) + ) + elif no_milestones: + # Asking for issues without a milestone + query = query.filter( + model.Issue.milestone == None + ) + elif milestones is not None and milestones != []: + # Asking for a single specific milestone if isinstance(milestones, basestring): milestones = [milestones] - query = query.filter( model.Issue.milestone.in_(milestones) ) diff --git a/pagure/templates/roadmap.html b/pagure/templates/roadmap.html index 12d54fe..81669e6 100644 --- a/pagure/templates/roadmap.html +++ b/pagure/templates/roadmap.html @@ -79,7 +79,7 @@ All Milestones + No Milestone @@ -141,6 +151,109 @@ {% endif %} + +{% if no_stones %} +
+
+ + + + + + {% if status and status|lower == 'closed' %} + + {% else %} + + {% endif %} + + + + + + + + {% for issue in issues |sort(attribute='priority') %} + {% if status is none or status|lower == 'all' or issue.status == status %} + + + + + + + + + {% endif %} + {% else %} + + + + {% endfor %} + +
No Milestone + OpenedClosedModified + Priority + + Assignee + Status
+ #{{ issue.id }} + {% if issue.private %} + + {% endif %} + + {{ issue.title | noJS("img") | safe }} + +    + {% if issue.comments|count > 0 %} + + + {{issue.comments|count}} + + {% endif %} + {% for tag in issue.tags%} + {{tag.tag}} + {% endfor%} + + {{ + issue.date_created | humanize}} + + {% if status and status|lower == 'closed' %} + {{ + issue.closed_at | humanize}} + {% else %} + {{ + issue.last_updated | humanize}} + {% endif %} + + {% if issue.priority %} + {{ repo.priorities[issue.priority | string] }} + {% endif %} + + {% if issue.assignee %} + {{ issue.assignee.default_email | avatar(16) | safe }} + {{ issue.assignee.user }} + {% else %} + unassigned + {% endif %} + + {{ issue.status }} +
No issues found
+
+
+{% endif %} + {% for milestone in milestones %} {% if issues[milestone] %}
diff --git a/pagure/ui/issues.py b/pagure/ui/issues.py index 25b633b..b00aa29 100644 --- a/pagure/ui/issues.py +++ b/pagure/ui/issues.py @@ -590,6 +590,7 @@ def view_issues(repo, username=None, namespace=None): assignee = flask.request.args.get('assignee', None) author = flask.request.args.get('author', None) search_pattern = flask.request.args.get('search_pattern', None) + milestones = flask.request.args.getlist('milestone', None) # Custom fields custom_keys = flask.request.args.getlist('ckeys') @@ -599,6 +600,11 @@ def view_issues(repo, username=None, namespace=None): for idx, key in enumerate(custom_keys): custom_search[key] = custom_values[idx] + if "none" in milestones: + no_stone = True + milestones.remove("none") + else: + no_stone = False search_string = search_pattern extra_fields, search_pattern = pagure.lib.tokenize_search_string( search_pattern) @@ -646,6 +652,8 @@ def view_issues(repo, username=None, namespace=None): limit=flask.g.limit, search_pattern=search_pattern, custom_search=custom_search, + milestones=milestones, + no_milestones=no_stone ) issues_cnt = pagure.lib.search_issues( SESSION, @@ -730,6 +738,7 @@ def view_roadmap(repo, username=None, namespace=None): milestones = flask.request.args.getlist('milestone', None) tags = flask.request.args.getlist('tag', None) all_stones = flask.request.args.get('all_stones') + no_stones = flask.request.args.get('no_stones') repo = flask.g.repo @@ -746,6 +755,25 @@ def view_roadmap(repo, username=None, namespace=None): if flask.g.repo_committer: private = None + if no_stones: + # Return only issues that do not have a milestone set + issues = pagure.lib.search_issues( + SESSION, + repo, + no_milestones=True, + status=status if status.lower() != 'all' else None, + ) + return flask.render_template( + 'roadmap.html', + select='issues', + repo=repo, + username=username, + status=status, + no_stones=True, + issues=issues, + tags=tags, + ) + all_milestones = sorted(list(repo.milestones.keys())) active_milestones = pagure.lib.get_active_milestones(SESSION, repo)