1
1
mirror of https://github.com/chubin/cheat.sh.git synced 2024-11-22 18:17:38 +03:00
cheat.sh/lib/search.py
2022-04-18 16:33:00 +00:00

121 lines
3.1 KiB
Python

"""
Very naive search implementation. Just a placeholder.
Exports:
find_answer_by_keyword()
It should be implemented on the adapter basis:
1. adapter.search(keyword) returns list of matching answers
* maybe with some initial weight
2. ranking is done
3. sorted results are returned
4. eage page are cut by keyword
5. results are paginated
Configuration parameters:
search.limit
"""
import re
from config import CONFIG
from routing import get_answers, get_topics_list
def _limited_entry():
return {
'topic_type': 'LIMITED',
"topic": "LIMITED",
'answer': "LIMITED TO %s ANSWERS" % CONFIG['search.limit'],
'format': "code",
}
def _parse_options(options):
"""Parse search options string into optiond_dict
"""
if options is None:
return {}
search_options = {
'insensitive': 'i' in options,
'word_boundaries': 'b' in options,
'recursive': 'r' in options,
}
return search_options
def match(paragraph, keyword, options=None, options_dict=None):
"""Search for each keyword from `keywords` in `page`
and if all of them are found, return `True`.
Otherwise return `False`.
Several keywords can be joined together using ~
For example: ~ssh~passphrase
"""
if keyword is None:
return True
if '~' in keyword:
keywords = keyword.split('~')
else:
keywords = [keyword]
if options_dict is None:
options_dict = _parse_options(options)
for kwrd in keywords:
if not kwrd:
continue
regex = re.escape(kwrd)
if options_dict["word_boundaries"]:
regex = r"\b%s\b" % kwrd
if options_dict["insensitive"]:
if not re.search(regex, paragraph, re.IGNORECASE):
return False
else:
if not re.search(regex, paragraph):
return False
return True
def find_answers_by_keyword(directory, keyword, options="", request_options=None):
"""
Search in the whole tree of all cheatsheets or in its subtree `directory`
by `keyword`
"""
options_dict = _parse_options(options)
answers_found = []
for topic in get_topics_list(skip_internal=True, skip_dirs=True):
if not topic.startswith(directory):
continue
subtopic = topic[len(directory):]
if not options_dict["recursive"] and '/' in subtopic:
continue
answer_dicts = get_answers(topic, request_options=request_options)
for answer_dict in answer_dicts:
answer_text = answer_dict.get('answer', '')
# Temporary hotfix:
# In some cases answer_text may be 'bytes' and not 'str'
if type(b"") == type(answer_text):
answer_text = answer_text.decode("utf-8")
if match(answer_text, keyword, options_dict=options_dict):
answers_found.append(answer_dict)
if len(answers_found) > CONFIG['search.limit']:
answers_found.append(
_limited_entry()
)
break
return answers_found