From 2ac29514e2284a88382b0e02feab607f9f43f1a7 Mon Sep 17 00:00:00 2001 From: Patrick Uiterwijk Date: Feb 14 2017 07:57:11 +0000 Subject: [PATCH 1/3] Parse issue search strings for key:value arguments for custom fields searching Signed-off-by: Patrick Uiterwijk --- diff --git a/pagure/lib/__init__.py b/pagure/lib/__init__.py index 6731d21..4657093 100644 --- a/pagure/lib/__init__.py +++ b/pagure/lib/__init__.py @@ -3863,3 +3863,41 @@ def add_metadata_update_notif(session, issue, messages, user, ticketfolder): session.add(issue_comment) # Make sure we won't have SQLAlchemy error before we continue session.commit() + + +def tokenize_search_string(pattern): + """This function tokenizes search patterns into key:value and rest. + + It will also correctly parse key values between quotes. + """ + if pattern is None: + return {}, None + def finalize_token(token, custom_search): + if ':' in token: + # This was a "key:value" parameter + key, value = token.split(':', 1) + custom_search[key] = value + return '' + else: + # This was a token without colon, thus a search pattern + return '%s ' % token + + custom_search = {} + # Remaining is the remaining real search_pattern (aka, non-key:values) + remaining = '' + # Token is the current "search token" we are processing + token = '' + in_quotes = False + for char in pattern: + if char == ' ' and not in_quotes: + remaining += finalize_token(token, custom_search) + token = '' + elif char == '"': + in_quotes = not in_quotes + else: + token += char + + # Parse the final token + remaining += finalize_token(token, custom_search) + + return custom_search, remaining.strip() diff --git a/pagure/ui/issues.py b/pagure/ui/issues.py index 3220229..710b659 100644 --- a/pagure/ui/issues.py +++ b/pagure/ui/issues.py @@ -603,6 +603,11 @@ def view_issues(repo, username=None, namespace=None): for idx, key in enumerate(custom_keys): custom_search[key] = custom_values[idx] + search_string = search_pattern + extra_fields, search_pattern = pagure.lib.tokenize_search_string( + search_pattern) + custom_search.update(extra_fields) + repo = flask.g.repo if not repo.settings.get('issue_tracker', True): @@ -710,7 +715,7 @@ def view_issues(repo, username=None, namespace=None): priority=priority, total_page=total_page, add_report_form=pagure.forms.AddReportForm(), - search_pattern=search_pattern, + search_pattern=search_string, ) From bc617a6299d829f69f39fcdda1def52e6802fa80 Mon Sep 17 00:00:00 2001 From: Patrick Uiterwijk Date: Feb 14 2017 07:57:11 +0000 Subject: [PATCH 2/3] Add test for tokenization function Signed-off-by: Patrick Uiterwijk --- diff --git a/tests/test_pagure_lib.py b/tests/test_pagure_lib.py index 7de24ad..ddbcbc8 100644 --- a/tests/test_pagure_lib.py +++ b/tests/test_pagure_lib.py @@ -3214,6 +3214,25 @@ class PagureLibtests(tests.Modeltests): self.assertEqual(out, 'You are no longer watching this issue') + def test_tokenize_search_string(self): + """ Test the tokenize_search_string function. """ + # These are the tests performed to make sure we tokenize correctly. + # This is in the form: input string, custom fields, remaining pattern + tests = [ + ('test123', {}, 'test123'), + ('test:key test123', {'test': 'key'}, 'test123'), + ('test:"key with spaces" test123', {'test': 'key with spaces'}, + 'test123'), + ('test123 test:key test456', {'test': 'key'}, 'test123 test456'), + ('test123 test:"key with spaces" key2:value12 test456', + {'test': 'key with spaces', 'key2': 'value12'}, + 'test123 test456') + ] + for inp, flds, rem in tests: + self.assertEqual(pagure.lib.tokenize_search_string(inp), + (flds, rem)) + + if __name__ == '__main__': SUITE = unittest.TestLoader().loadTestsFromTestCase(PagureLibtests) unittest.TextTestRunner(verbosity=2).run(SUITE) From 5ee678277e81aeddd6691172d57c752fd035bc0e Mon Sep 17 00:00:00 2001 From: Patrick Uiterwijk Date: Feb 14 2017 07:57:11 +0000 Subject: [PATCH 3/3] Add test for custom issue fields searching Signed-off-by: Patrick Uiterwijk --- diff --git a/tests/test_pagure_flask_ui_issues.py b/tests/test_pagure_flask_ui_issues.py index fcb0d26..2cc2dd6 100644 --- a/tests/test_pagure_flask_ui_issues.py +++ b/tests/test_pagure_flask_ui_issues.py @@ -297,8 +297,24 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertTrue( '

\n 0 Open Issues' in output.data) - # Create issues to play with repo = pagure.lib.get_project(self.session, 'test') + # Create some custom fields to play with + msg = pagure.lib.set_custom_key_fields( + session=self.session, + project=repo, + fields=['test1'], + types=['text'], + data=[None] + ) + self.session.commit() + self.assertEqual(msg, 'List of custom fields updated') + + cfield = pagure.lib.get_custom_key( + session=self.session, + project=repo, + keyname='test1') + + # Create issues to play with msg = pagure.lib.new_issue( session=self.session, repo=repo, @@ -310,6 +326,14 @@ class PagureFlaskIssuestests(tests.Modeltests): self.session.commit() self.assertEqual(msg.title, 'Test issue') + msg = pagure.lib.set_custom_key_value( + session=self.session, + issue=msg, + key=cfield, + value='firstissue') + self.session.commit() + self.assertEqual(msg, 'Custom field test1 adjusted to firstissue') + msg = pagure.lib.new_issue( session=self.session, repo=repo, @@ -323,6 +347,14 @@ class PagureFlaskIssuestests(tests.Modeltests): self.session.commit() self.assertEqual(msg.title, 'Test invalid issue') + msg = pagure.lib.set_custom_key_value( + session=self.session, + issue=msg, + key=cfield, + value='second issue') + self.session.commit() + self.assertEqual(msg, 'Custom field test1 adjusted to second issue') + # Whole list output = self.app.get('/test/issues') self.assertEqual(output.status_code, 200) @@ -359,6 +391,20 @@ class PagureFlaskIssuestests(tests.Modeltests): self.assertTrue( '

\n 2 Issues' in output.data) + # Custom key searching + output = self.app.get( + '/test/issues?status=all&search_pattern=test1:firstissue') + self.assertEqual(output.status_code, 200) + self.assertIn('Issues - test - Pagure', output.data) + self.assertIn('1 Issues', output.data) + + # Custom key searching with space + output = self.app.get( + '/test/issues?status=all&search_pattern=test1:"second issue"') + self.assertEqual(output.status_code, 200) + self.assertIn('Issues - test - Pagure', output.data) + self.assertIn('1 Issues', output.data) + # All tickets - different pagination before = pagure.APP.config['ITEM_PER_PAGE'] pagure.APP.config['ITEM_PER_PAGE'] = 1