From 6515e7d86b4407d534589fd0ac85fa953df4c50b Mon Sep 17 00:00:00 2001 From: Igor Chubin Date: Sun, 22 Nov 2020 19:00:44 +0000 Subject: [PATCH] Show all cheat sheets when several found --- lib/cheat_wrapper.py | 4 +- lib/frontend/ansi.py | 18 +++++--- lib/routing.py | 100 ++++++++++++++++++++++++++++--------------- lib/search.py | 11 ++--- 4 files changed, 85 insertions(+), 48 deletions(-) diff --git a/lib/cheat_wrapper.py b/lib/cheat_wrapper.py index 73fc74a..5a5fa62 100644 --- a/lib/cheat_wrapper.py +++ b/lib/cheat_wrapper.py @@ -11,7 +11,7 @@ Exports: import re import json -from routing import get_answer_dict, get_topics_list +from routing import get_answers, get_topics_list from search import find_answers_by_keyword from languages_data import LANGUAGE_ALIAS, rewrite_editor_section_name import postprocessing @@ -98,7 +98,7 @@ def cheat_wrapper(query, request_options=None, output_format='ansi'): answers = find_answers_by_keyword( topic, keyword, options=search_options, request_options=request_options) else: - answers = [get_answer_dict(topic, request_options=request_options)] + answers = get_answers(topic, request_options=request_options) answers = [ postprocessing.postprocess( diff --git a/lib/frontend/ansi.py b/lib/frontend/ansi.py index 3880af5..276d58b 100644 --- a/lib/frontend/ansi.py +++ b/lib/frontend/ansi.py @@ -101,6 +101,10 @@ def _visualize(answers, request_options, search_mode=False): if color_style not in CONFIG['frontend.styles']: color_style = '' + # if there is more than one answer, + # show the source of the answer + multiple_answers = len(answers) > 1 + found = True result = "" for answer_dict in answers: @@ -109,14 +113,16 @@ def _visualize(answers, request_options, search_mode=False): answer = answer_dict['answer'] found = found and not topic_type == 'unknown' - if search_mode and topic != 'LIMITED': + if multiple_answers and topic != 'LIMITED': + section_name = f"{topic_type}:{topic}" + if not highlight: - result += "\n[%s]\n" % topic + result += f"#[{section_name}]\n" else: - result += "\n%s%s %s %s%s\n" % ( - colored.bg('dark_gray'), colored.attr("res_underlined"), - topic, - colored.attr("res_underlined"), colored.attr('reset')) + result += "".join([ + "\n", colored.bg('dark_gray'), colored.attr("res_underlined"), + f" {section_name} ", + colored.attr("res_underlined"), colored.attr('reset'), "\n"]) if answer_dict['format'] in ['ansi', 'text']: result += answer diff --git a/lib/routing.py b/lib/routing.py index 1247c43..c23f5fc 100644 --- a/lib/routing.py +++ b/lib/routing.py @@ -4,12 +4,13 @@ Queries routing and caching. Exports: get_topics_list() - get_answer_dict() + get_answers() """ -from __future__ import print_function -import re import random +import re +from typing import Any, Dict, List + import cache import adapter.cheat_sheets import adapter.cmd @@ -83,20 +84,28 @@ class Router(object): self._cached_topics_list = answer return answer - def get_topic_type(self, topic): + def get_topic_type(self, topic: str) -> List[str]: """ - Return topic type for `topic` or "unknown" if topic can't be determined. + Return list of topic types for `topic` + or ["unknown"] if topic can't be determined. """ - def __get_topic_type(topic): + def __get_topic_type(topic: str) -> List[str]: + result = [] for regexp, route in self.routing_table: if re.search(regexp, topic): if route in self._adapter: if self._adapter[route].is_found(topic): - return route + result.append(route) else: - return route - return CONFIG["routing.default"] + result.append(route) + if not result: + return [CONFIG["routing.default"]] + + # cut the default route off, if there are more than one route found + if len(result) > 1: + return result[:-1] + return result if topic not in self._cached_topic_type: self._cached_topic_type[topic] = __get_topic_type(topic) @@ -111,7 +120,9 @@ class Router(object): def handle_if_random_request(self, topic): """ - Check if the `query` if a :random one, if yes we check its correctness and then randomly select a topic, based on the provided prefix. + Check if the `query` if a :random one, + if yes we check its correctness and then randomly select a topic, + based on the provided prefix. """ @@ -152,60 +163,79 @@ class Router(object): #Here if not a random requst, we just forward the topic return topic - def get_answer_dict(self, topic, request_options=None): + def get_answers(self, topic: str, request_options:Dict[str, str] = None) -> List[Dict[str, Any]]: """ - Find cheat sheet for the topic. + Find cheat sheets for the topic. Args: `topic` (str): the name of the topic of the cheat sheet Returns: - answer_dict: the answer dictionary + [answer_dict]: list of answers (dictionaries) """ + # if topic specified as :, + # cut off + topic_type = "" + if re.match("[^/]+:", topic): + topic_type, topic = topic.split(":", 1) + topic = self.handle_if_random_request(topic) - topic_type = self.get_topic_type(topic) + topic_types = self.get_topic_type(topic) + + # if topic_type is specified explicitly, + # show pages only of that type + if topic_type and topic_type in topic_types: + topic_types = [topic_type] # 'question' queries are pretty expensive, that's why they should be handled # in a special way: # we do not drop the old style cache entries and try to reuse them if possible - if topic_type == 'question': + if topic_types == ['question']: answer = cache.get('q:' + topic) if answer: if isinstance(answer, dict): - return answer - return { + return [answer] + return [{ 'topic': topic, 'topic_type': 'question', 'answer': answer, 'format': 'text+code', - } + }] - answer = self._get_page_dict(topic, topic_type, request_options=request_options) + answer = self._get_page_dict(topic, topic_types[0], request_options=request_options) if answer.get("cache", True): cache.put('q:' + topic, answer) - return answer + return [answer] # Try to find cacheable queries in the cache. # If answer was not found in the cache, resolve it in a normal way and save in the cache - cache_needed = self._adapter[topic_type].is_cache_needed() - if cache_needed: - answer = cache.get(topic) - if not isinstance(answer, dict): - answer = None - if answer: - return answer + answers = [] + for topic_type in topic_types: - answer = self._get_page_dict(topic, topic_type, request_options=request_options) - if isinstance(answer, dict): - if "cache" in answer: - cache_needed = answer["cache"] + cache_entry_name = f"{topic_type}:{topic}" + cache_needed = self._adapter[topic_type].is_cache_needed() - if cache_needed and answer: - cache.put(topic, answer) - return answer + if cache_needed: + answer = cache.get(cache_entry_name) + if not isinstance(answer, dict): + answer = None + if answer: + answers.append(answer) + + answer = self._get_page_dict(topic, topic_type, request_options=request_options) + if isinstance(answer, dict): + if "cache" in answer: + cache_needed = answer["cache"] + + if cache_needed and answer: + cache.put(cache_entry_name, answer) + + answers.append(answer) + + return answers # pylint: disable=invalid-name _ROUTER = Router() get_topics_list = _ROUTER.get_topics_list -get_answer_dict = _ROUTER.get_answer_dict +get_answers = _ROUTER.get_answers diff --git a/lib/search.py b/lib/search.py index dc95aea..e4beaa3 100644 --- a/lib/search.py +++ b/lib/search.py @@ -22,7 +22,7 @@ Configuration parameters: import re from config import CONFIG -from routing import get_answer_dict, get_topics_list +from routing import get_answers, get_topics_list def _limited_entry(): return { @@ -100,10 +100,11 @@ def find_answers_by_keyword(directory, keyword, options="", request_options=None if not options_dict["recursive"] and '/' in subtopic: continue - answer_dict = get_answer_dict(topic, request_options=request_options) - answer_text = answer_dict.get('answer', '') - if match(answer_text, keyword, options_dict=options_dict): - answers_found.append(answer_dict) + answer_dicts = get_answers(topic, request_options=request_options) + for answer_dict in answer_dicts: + answer_text = answer_dict.get('answer', '') + if match(answer_text, keyword, options_dict=options_dict): + answers_found.append(answer_dict) if len(answers_found) > CONFIG['search.limit']: answers_found.append(