query.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  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 re
  20. import string
  21. import sys
  22. if sys.version_info[0] == 3:
  23. unicode = str
  24. VALID_LANGUAGE_CODE = re.compile(r'^[a-z]{2,3}(-[a-zA-Z]{2})?$')
  25. class RawTextQuery(object):
  26. """parse raw text query (the value from the html input)"""
  27. def __init__(self, query, disabled_engines):
  28. self.query = query
  29. self.disabled_engines = []
  30. if disabled_engines:
  31. self.disabled_engines = disabled_engines
  32. self.query_parts = []
  33. self.engines = []
  34. self.languages = []
  35. self.specific = False
  36. # parse query, if tags are set, which
  37. # change the serch engine or search-language
  38. def parse_query(self):
  39. self.query_parts = []
  40. # split query, including whitespaces
  41. raw_query_parts = re.split(r'(\s+)', self.query)
  42. parse_next = True
  43. for query_part in raw_query_parts:
  44. if not parse_next:
  45. self.query_parts[-1] += query_part
  46. continue
  47. parse_next = False
  48. # part does only contain spaces, skip
  49. if query_part.isspace()\
  50. or query_part == '':
  51. parse_next = True
  52. self.query_parts.append(query_part)
  53. continue
  54. # this force a language
  55. if query_part[0] == ':':
  56. lang = query_part[1:].lower().replace('_', '-')
  57. # user may set a valid, yet not selectable language
  58. if VALID_LANGUAGE_CODE.match(lang):
  59. self.languages.append(lang)
  60. parse_next = True
  61. # check if any language-code is equal with
  62. # declared language-codes
  63. for lc in language_codes:
  64. lang_id, lang_name, country, english_name = map(unicode.lower, lc)
  65. # if correct language-code is found
  66. # set it as new search-language
  67. if lang == lang_id\
  68. or lang_id.startswith(lang)\
  69. or lang == lang_name\
  70. or lang == english_name\
  71. or lang.replace('-', ' ') == country:
  72. parse_next = True
  73. self.languages.append(lang_id)
  74. # to ensure best match (first match is not necessarily the best one)
  75. if lang == lang_id:
  76. break
  77. # this force a engine or category
  78. if query_part[0] == '!' or query_part[0] == '?':
  79. prefix = query_part[1:].replace('-', ' ').replace('_', ' ')
  80. # check if prefix is equal with engine shortcut
  81. if prefix in engine_shortcuts:
  82. parse_next = True
  83. self.engines.append({'category': 'none',
  84. 'name': engine_shortcuts[prefix]})
  85. # check if prefix is equal with engine name
  86. elif prefix in engines:
  87. parse_next = True
  88. self.engines.append({'category': 'none',
  89. 'name': prefix})
  90. # check if prefix is equal with categorie name
  91. elif prefix in categories:
  92. # using all engines for that search, which
  93. # are declared under that categorie name
  94. parse_next = True
  95. self.engines.extend({'category': prefix,
  96. 'name': engine.name}
  97. for engine in categories[prefix]
  98. if (engine.name, prefix) not in self.disabled_engines)
  99. if query_part[0] == '!':
  100. self.specific = True
  101. # append query part to query_part list
  102. self.query_parts.append(query_part)
  103. def changeSearchQuery(self, search_query):
  104. if len(self.query_parts):
  105. self.query_parts[-1] = search_query
  106. else:
  107. self.query_parts.append(search_query)
  108. def getSearchQuery(self):
  109. if len(self.query_parts):
  110. return self.query_parts[-1]
  111. else:
  112. return ''
  113. def getFullQuery(self):
  114. # get full querry including whitespaces
  115. return string.join(self.query_parts, '')
  116. class SearchQuery(object):
  117. """container for all the search parameters (query, language, etc...)"""
  118. def __init__(self, query, engines, categories, lang, safesearch, pageno, time_range):
  119. self.query = query.encode('utf-8')
  120. self.engines = engines
  121. self.categories = categories
  122. self.lang = lang
  123. self.safesearch = safesearch
  124. self.pageno = pageno
  125. self.time_range = time_range
  126. def __str__(self):
  127. return str(self.query) + ";" + str(self.engines)