query.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. #!/usr/bin/env python
  2. '''
  3. searx is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as published by
  5. the Free Software Foundation, either version 3 of the License, or
  6. (at your option) any later version.
  7. searx is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with searx. If not, see < http://www.gnu.org/licenses/ >.
  13. (C) 2014 by Thomas Pointhuber, <thomas.pointhuber@gmx.at>
  14. '''
  15. from searx.languages import language_codes
  16. from searx.engines import (
  17. categories, engines, engine_shortcuts
  18. )
  19. import string
  20. import re
  21. VALID_LANGUAGE_CODE = re.compile(r'^[a-z]{2,3}(\-[A-Z]{2})?$')
  22. class RawTextQuery(object):
  23. """parse raw text query (the value from the html input)"""
  24. def __init__(self, query, disabled_engines):
  25. self.query = query
  26. self.disabled_engines = []
  27. if disabled_engines:
  28. self.disabled_engines = disabled_engines
  29. self.query_parts = []
  30. self.engines = []
  31. self.languages = []
  32. self.specific = False
  33. # parse query, if tags are set, which
  34. # change the serch engine or search-language
  35. def parse_query(self):
  36. self.query_parts = []
  37. # split query, including whitespaces
  38. raw_query_parts = re.split(r'(\s+)', self.query)
  39. parse_next = True
  40. for query_part in raw_query_parts:
  41. if not parse_next:
  42. self.query_parts[-1] += query_part
  43. continue
  44. parse_next = False
  45. # part does only contain spaces, skip
  46. if query_part.isspace()\
  47. or query_part == '':
  48. parse_next = True
  49. self.query_parts.append(query_part)
  50. continue
  51. # this force a language
  52. if query_part[0] == ':':
  53. lang = query_part[1:].lower()
  54. # user may set a valid, yet not selectable language
  55. if VALID_LANGUAGE_CODE.match(lang):
  56. self.languages.append(lang)
  57. parse_next = True
  58. # check if any language-code is equal with
  59. # declared language-codes
  60. for lc in language_codes:
  61. lang_id, lang_name, country, english_name = map(unicode.lower, lc)
  62. # if correct language-code is found
  63. # set it as new search-language
  64. if lang == lang_id\
  65. or lang_id.startswith(lang)\
  66. or lang == lang_name\
  67. or lang == english_name\
  68. or lang.replace('_', ' ') == country:
  69. parse_next = True
  70. self.languages.append(lang_id)
  71. # to ensure best match (first match is not necessarily the best one)
  72. if lang == lang_id:
  73. break
  74. # this force a engine or category
  75. if query_part[0] == '!' or query_part[0] == '?':
  76. prefix = query_part[1:].replace('-', ' ')
  77. # check if prefix is equal with engine shortcut
  78. if prefix in engine_shortcuts:
  79. parse_next = True
  80. self.engines.append({'category': 'none',
  81. 'name': engine_shortcuts[prefix]})
  82. # check if prefix is equal with engine name
  83. elif prefix in engines:
  84. parse_next = True
  85. self.engines.append({'category': 'none',
  86. 'name': prefix})
  87. # check if prefix is equal with categorie name
  88. elif prefix in categories:
  89. # using all engines for that search, which
  90. # are declared under that categorie name
  91. parse_next = True
  92. self.engines.extend({'category': prefix,
  93. 'name': engine.name}
  94. for engine in categories[prefix]
  95. if (engine.name, prefix) not in self.disabled_engines)
  96. if query_part[0] == '!':
  97. self.specific = True
  98. # append query part to query_part list
  99. self.query_parts.append(query_part)
  100. def changeSearchQuery(self, search_query):
  101. if len(self.query_parts):
  102. self.query_parts[-1] = search_query
  103. else:
  104. self.query_parts.append(search_query)
  105. def getSearchQuery(self):
  106. if len(self.query_parts):
  107. return self.query_parts[-1]
  108. else:
  109. return ''
  110. def getFullQuery(self):
  111. # get full querry including whitespaces
  112. return string.join(self.query_parts, '')
  113. class SearchQuery(object):
  114. """container for all the search parameters (query, language, etc...)"""
  115. def __init__(self, query, engines, categories, lang, safesearch, pageno, time_range):
  116. self.query = query
  117. self.engines = engines
  118. self.categories = categories
  119. self.lang = lang
  120. self.safesearch = safesearch
  121. self.pageno = pageno
  122. self.time_range = time_range
  123. def __str__(self):
  124. return str(self.query) + ";" + str(self.engines)