Exemple #1
0
def socli(query):
    """
    SoCLI Code
    :param query: Query to search on Stack Overflow.
    If google_search is true uses Google search to find the best result.
    Else, use Stack Overflow default search mechanism.
    :return:
    """
    query = printer.urlencode(query)
    try:
        if search.google_search:
            questions = search.get_questions_for_query_google(query)
            res_url = questions[0][2]  # Gets the first result
            display_results(res_url)
        else:
            questions = search.get_questions_for_query(query)
            res_url = questions[0][2]
            display_results(search.so_url + res_url)  # Returned URL is relative to SO homepage
    except UnicodeEncodeError as e:
        printer.showerror(e)
        printer.print_warning("\n\nEncoding error: Use \"chcp 65001\" command before using socli...")
        sys.exit(0)
    except requests.exceptions.ConnectionError:
        printer.print_fail("Please check your internet connectivity...")
    except Exception as e:
        printer.showerror(e)
        sys.exit(0)
Exemple #2
0
def del_datafile():
    """
    Deletes the data file
    :return:
    """
    global data_file
    try:
        os.remove(data_file)
    except FileNotFoundError:
        pr.print_warning("File not created.... Use socli -u to create a new configuration file.")
        exit(0)
Exemple #3
0
def set_api_key():
    """
    Sets a custom API Key
    :return:
    """
    global app_data
    api_key = pr.inputs("Type an API key to continue: ")
    if len(api_key) > 0:
        app_data["api_key"] = api_key
        save_datafile()
    pr.print_warning("\nAPI Key saved...")
Exemple #4
0
def wrongsyn(query):
    """
    Exits if query value is empty
    :param query:
    :return:
    """
    if query == "":
        printer.print_warning("Wrong syntax!...\n")
        printer.helpman()
        sys.exit(1)
    else:
        return
Exemple #5
0
def set_api_key():
    """
    Sets a custom API Key
    :return:
    """
    global app_data
    try:
        api_key = pr.inputs("Type an API key to continue (^C to abort): ")
        if len(api_key) > 0:
            app_data["api_key"] = api_key
            save_datafile()
        pr.print_warning("\nAPI Key saved...")
    except KeyboardInterrupt:
        print("Aborted.")
Exemple #6
0
def socli(query):
    """
    SoCLI Code
    :param query: Query to search on Stack Overflow.
    If search_enging == 'google' is true uses Google search to find the best result.
    Else, use Stack Overflow default search mechanism.
    :return:
    """
    query = printer.urlencode(query)
    try:
        if search.search_engine == 'google':
            questions = search.get_questions_for_query_google(query)
            res_url = questions[0][2]  # Gets the first result
            display_results(res_url)
        elif search.search_engine == 'ddg':
            questions = search.get_questions_for_query_duckduckgo(query)
            res_url = questions[0][2]
            display_results(search.so_url +
                            res_url)  # Returned URL is relative to SO homepage
        else:
            questions = search.get_questions_for_query(query)
            res_url = questions[0][2]
            display_results(search.so_url +
                            res_url)  # Returned URL is relative to SO homepage

    except IndexError:
        if len(questions) > 1:
            for i in range(1, len(questions)):
                res_url = questions[i][2]
                try:
                    display_results(res_url)
                except IndexError:
                    continue
                break
        printer.print_warning("No results found...")

    except UnicodeEncodeError as e:
        printer.showerror(e)
        printer.print_warning(
            "\n\nEncoding error: Use \"chcp 65001\" command before using socli..."
        )
        sys.exit(0)
    except requests.exceptions.ConnectionError:
        printer.print_fail("Please check your internet connectivity...")
    except Exception as e:
        printer.showerror(e)
        sys.exit(0)
Exemple #7
0
def retrieve_saved_profile():
    """
    Retrieves the user's saved profile after a "socli -u" command.
    Asks the user to enter a User ID and saves it if a previous file is not found.

    :return: The user's ID as an integer
    """
    global data_file
    global app_data
    user = None
    try:
        load_datafile()
        if "user" in app_data:
            user = app_data["user"]
        else:
            raise FileNotFoundError  # Manually raising to get value
    except json.JSONDecodeError:
        # This maybe some write failures
        del_datafile()
        pr.print_warning(
            "Error in parsing the data file, it will be now deleted. Please rerun the "
            "socli -u command.")
        exit(1)
    except FileNotFoundError:
        pr.print_warning("Default user not set...\n")
        try:
            # Code to execute when first time user runs socli -u
            app_data['user'] = int(
                pr.inputs("Enter your Stackoverflow User ID: "))
            save_datafile()
            user = app_data['user']
            pr.print_green("\nUserID saved...\n")
        except ValueError:
            pr.print_warning("\nUser ID must be an integer.")
            print(
                "\nFollow the instructions on this page to get your User ID: http://meta.stackexchange.com/a/111130"
            )
            exit(1)
    return user
Exemple #8
0
def main():
    """
    The main logic for how options in a command is checked.
    """
    global query
    namespace = parse_arguments(sys.argv[1:])
    search.load_user_agents()  # Populates the user agents array
    query_tag = ' '.join(namespace.browse)  # Tags

    # Query
    if namespace.query:
        # Query when args are present
        query = ' '.join(namespace.query)
    elif namespace.userQuery:
        # Query without any args
        query = ' '.join(namespace.userQuery)

    if namespace.help:
        # Display command line syntax
        printer.helpman()
        sys.exit(0)

    if namespace.debug:  # If --debug flag is present
        # Prints out error used for debugging
        printer.DEBUG = True
        debug_requests_on()

    if namespace.new:  # If --new flag is present
        # Opens StackOverflow website in the browser to create a  new question
        import webbrowser
        printer.print_warning("Opening stack overflow in your browser...")
        webbrowser.open(search.so_url + "/questions/ask")
        sys.exit(0)

    if namespace.api:  # If --api flag is present
        # Sets custom API key
        user_module.set_api_key()
        sys.exit(0)

    if namespace.user is not None:  # If --user flag is present
        # Stackoverflow user profile support
        if namespace.user != '(RANDOM_STRING_CONSTANT)':  # If user provided a user ID
            user_module.manual = 1  # Enabling manual mode
            user = namespace.user
        else:  # If user did not provide a user id
            user = user_module.retrieve_saved_profile()  # Reading saved user id from app data
        user_module.user_page(user)
        sys.exit(0)

    if namespace.delete:  # If --delete flag is present
        # Deletes user data
        user_module.del_datafile()
        printer.print_warning("Data files deleted...")
        sys.exit(0)

    if namespace.sosearch:  # If --sosearch flag is present
        # Disables google search
        search.google_search = False

    if namespace.tag:  # If --tag flag is present
        global tag
        search.google_search = False
        tag = namespace.tag
        has_tags()  # Adds tags to StackOverflow url (when not using google search.
    if namespace.open_url:
        import webbrowser
        open_in_browser=False
        display_condition=True
        url_to_use=namespace.open_url[0]
        if re.findall(r"^https:\/\/",url_to_use) !=[]:
            pass
        else:
            url_to_use="https://" + url_to_use
        try:
            if url_to_use == "https://stackoverflow.com/questions/":
                raise Exception('URL Error')
            if url_to_use == "https://www.stackoverflow.com/questions/":
                raise Exception('URL Error')
            requests.get(url_to_use)
        except Exception:
            printer.print_warning("Error, could be:\n- invalid url\n- url cannot be opened in socli\n- internet connection error")
            sys.exit(0)
        nostackoverflow=re.findall(r"stackoverflow\.com",url_to_use)
        if nostackoverflow == []:
            open_in_browser=True
            display_condition=False
            printer.print_warning("Your url is not a stack overflow url.\nOpening in your browser...")
        tag_matcher=re.findall(r"\/tag.+\/",url_to_use)
        blog_matcher=re.findall(r"blog",url_to_use)
        if tag_matcher != []:
            extracted_tag=""
            if re.findall(r"tagged",url_to_use) == []:
                extracted_tag=re.split(r"\/",url_to_use)[4]
            else:
                extracted_tag=re.split(r"\/",url_to_use)[5]
            open_in_browser=False
            display_condition=False
            tag=extracted_tag
            search.socli_interactive(tag)
        if blog_matcher != []:
            open_in_browser=True
            display_condition=False
            printer.print_warning("Your url belongs to blog")
            printer.print_warning("Opening in browser...")
        if display_condition:
            open_in_browser=False
            display_results(url_to_use)
        if open_in_browser:
            webbrowser.open(url_to_use)
    if namespace.res is not None:  # If --res flag is present
        # Automatically displays the result specified by the number
        question_number = namespace.res
        if namespace.query != [] or namespace.tag is not None:  # There must either be a tag or a query
            search.socli_manual_search(query, question_number)
        else:
            printer.print_warning('You must specify a query or a tag. For example, use: "socli -r 3 -q python for loop" '
                             'to retrieve the third result when searching about "python for loop". '
                             'You can also use "socli -r 3 -t python" '
                             'to retrieve the third result when searching for posts with the "python" tag.')

    if namespace.browse:
        # Browse mode
        search.google_search = False
        socli_browse_interactive(query_tag)
    elif namespace.query != [] or namespace.tag is not None:  # If query and tag are not both empty
        if namespace.interactive:
            search.socli_interactive(query)
        else:
            socli(query)
    elif query not in [' ', ''] and not (
            namespace.tag or namespace.res or namespace.interactive or namespace.browse):  # If there are no flags
        socli(query)
    else:
        # Help text for interactive mode
        if namespace.interactive and namespace.query == [] and namespace.tag is None:
            printer.print_warning('You must specify a query or a tag. For example, use: "socli -iq python for loop" '
                             'to search about "python for loop" in interactive mode. '
                             'You can also use "socli -it python" '
                             'to search posts with the "python" tag in interactive mode.')
        else:
            printer.helpman()
Exemple #9
0
def socli_browse_interactive(query_tag):
    """
    Interactive mode
    :return:
    """
    if sys.platform == 'win32':
        return socli_browse_interactive_windows(query_tag)

    class SelectQuestionPage(urwid.WidgetWrap):

        def display_text(self, index, question):
            question_text, question_desc, _ = question
            text = [
                ("warning", u"{}. {}\n".format(index, question_text)),
                question_desc + "\n",
            ]
            return text

        def __init__(self, questions):
            self.questions = questions
            self.cachedQuestions = [None for _ in range(10)]
            widgets = [self.display_text(i, q) for i, q in enumerate(questions)]
            self.questions_box = tui.ScrollableTextBox(widgets)
            self.header = tui.UnicodeText(('less-important', 'Select a question below:\n'))
            self.footerText = '0-' + str(len(self.questions) - 1) + ': select a question, any other key: exit.'
            self.errorText = tui.UnicodeText.to_unicode('Question numbers range from 0-' +
                                                        str(len(self.questions) - 1) +
                                                        ". Please select a valid question number.")
            self.footer = tui.UnicodeText(self.footerText)
            self.footerText = tui.UnicodeText.to_unicode(self.footerText)
            frame = urwid.Frame(header=self.header,
                                body=urwid.Filler(self.questions_box, height=('relative', 100), valign='top'),
                                footer=self.footer)
            urwid.WidgetWrap.__init__(self, frame)

        # Override parent method
        def selectable(self):
            return True

        def keypress(self, size, key):
            if key in '0123456789':
                try:
                    question_url = self.questions[int(key)][2]
                    self.footer.set_text(self.footerText)
                    self.select_question(question_url, int(key))
                except IndexError:
                    self.footer.set_text(self.errorText)
            elif key in {'down', 'up'}:
                self.questions_box.keypress(size, key)
            else:
                raise urwid.ExitMainLoop()

        def select_question(self, url, index):
            if self.cachedQuestions[index] is not None:
                tui.question_post = self.cachedQuestions[index]
                tui.MAIN_LOOP.widget = tui.question_post
            else:
                if not search.google_search:
                    url = search.so_url + url
                question_title, question_desc, question_stats, answers = search.get_question_stats_and_answer(url)
                question_post = tui.QuestionPage((answers, question_title, question_desc, question_stats, url))
                self.cachedQuestions[index] = question_post
                tui.MAIN_LOOP.widget = question_post

    tui.display_header = tui.Header()

    try:
        if search.google_search:
            questions = search.get_questions_for_query_google(query)
        else:
            # print('hurr')
            questions = search.get_questions_for_query(query_tag)
            # print(questions)

        question_page = SelectQuestionPage(questions)

        tui.MAIN_LOOP = tui.EditedMainLoop(question_page, printer.palette)
        tui.MAIN_LOOP.run()

    except UnicodeEncodeError:
        printer.print_warning("\n\nEncoding error: Use \"chcp 65001\" command before using socli...")
        sys.exit(0)
    except requests.exceptions.ConnectionError:
        printer.print_fail("Please check your internet connectivity...")
    except Exception as e:
        printer.showerror(e)
        # print("Hurra")
        print("exiting...")
        sys.exit(0)
Exemple #10
0
def socli_browse_interactive_windows(query_tag):
    """
    Interactive mode for -b browse
    :param query_tag:
    :return:
    """
    try:
        search_res = requests.get(search.so_burl + query_tag)
        search.captcha_check(search_res.url)
        soup = BeautifulSoup(search_res.text, 'html.parser')
        try:
            soup.find_all("div", class_="question-summary")[0]  # For explicitly raising exception
            tmp = (soup.find_all("div", class_="question-summary"))
            i = 0
            question_local_url = []
            print(printer.bold("\nSelect a question below:\n"))
            while i < len(tmp):
                if i == 10:
                    break  # limiting results
                question_text = ' '.join((tmp[i].a.get_text()).split())
                question_text = question_text.replace("Q: ", "")
                printer.print_warning(str(i + 1) + ". " + printer.display_str(question_text))
                q_tag = (soup.find_all("div", class_="question-summary"))[i]
                answers = [s.get_text() for s in q_tag.find_all("a", class_="post-tag")][0:]
                ques_tags = " ".join(str(x) for x in answers)
                question_local_url.append(tmp[i].a.get("href"))
                print("  " + printer.display_str(ques_tags) + "\n")
                i = i + 1
            try:
                op = int(printer.inputs("\nType the option no to continue or any other key to exit:"))
                while 1:
                    if (op > 0) and (op <= i):
                        display_results(search.so_burl + question_local_url[op - 1])
                        cnt = 1  # this is because the 1st post is the question itself
                        while 1:
                            global tmpsoup
                            qna = printer.inputs(
                                "Type " + printer.bold("o") + " to open in browser, " + printer.bold(
                                    "n") + " to next answer, " + printer.bold(
                                    "b") + " for previous answer or any other key to exit:")
                            if qna in ["n", "N"]:
                                try:
                                    answer = (tmpsoup.find_all("div", class_="js-post-body")[cnt + 1].get_text())
                                    printer.print_green("\n\nAnswer:\n")
                                    print("-------\n" + answer + "\n-------\n")
                                    cnt = cnt + 1
                                except IndexError:
                                    printer.print_warning(" No more answers found for this question. Exiting...")
                                    sys.exit(0)
                                continue
                            elif qna in ["b", "B"]:
                                if cnt == 1:
                                    printer.print_warning(" You cant go further back. You are on the first answer!")
                                    continue
                                answer = (tmpsoup.find_all("div", class_="js-post-body")[cnt - 1].get_text())
                                printer.print_green("\n\nAnswer:\n")
                                print("-------\n" + answer + "\n-------\n")
                                cnt = cnt - 1
                                continue
                            elif qna in ["o", "O"]:
                                import webbrowser
                                printer.print_warning("Opening in your browser...")
                                webbrowser.open(search.so_burl + question_local_url[op - 1])
                            else:
                                break
                        sys.exit(0)
                    else:
                        op = int(input("\n\nWrong option. select the option no to continue:"))
            except Exception as e:
                printer.showerror(e)
                printer.print_warning("\n Exiting...")
                sys.exit(0)
        except IndexError:
            printer.print_warning("No results found...")
            sys.exit(0)

    except UnicodeEncodeError:
        printer.print_warning("\n\nEncoding error: Use \"chcp 65001\" command before using socli...")
        sys.exit(0)
    except requests.exceptions.ConnectionError:
        printer.print_fail("Please check your internet connectivity...")
    except Exception as e:
        printer.showerror(e)
        sys.exit(0)
Exemple #11
0
def user_page(user_id):
    """
    Stack Overflow user profile browsing
    :param user_id:
    :return:
    """
    global app_data
    import stackexchange

    try:
        user_id = int(user_id)
    except ValueError:
        pr.print_warning("\nUser ID must be an integer.")
        print(
            "\nFollow the instructions on this page to get your User ID: http://meta.stackexchange.com/a/111130")
        exit(1)

    try:
        from urllib.error import URLError
    except ImportError:
        from urllib import URLError
    try:
        user_id = int(user_id)
    except ValueError:
        pr.print_warning("\nUser ID must be an integer.")
        print(
            "\nFollow the instructions on this page to get your User ID: http://meta.stackexchange.com/a/111130")
        exit(1)

    try:
        if "api_key" not in app_data:
            app_data["api_key"] = None
        userprofile = stackexchange.Site(stackexchange.StackOverflow, app_key=app_data["api_key"]).user(user_id)
        print(pr.bold("\n User: "******"\n\tReputations: " + userprofile.reputation.format())
        pr.print_warning("\n\tBadges:")
        print("\t\t   Gold: " + str(userprofile.gold_badges))
        print("\t\t Silver: " + str(userprofile.silver_badges))
        print("\t\t Bronze: " + str(userprofile.bronze_badges))
        print("\t\t  Total: " + str(userprofile.badge_total))
        pr.print_warning("\n\tStats:")
        total_questions = len(userprofile.questions.fetch())
        unaccepted_questions = len(userprofile.unaccepted_questions.fetch())
        accepted = total_questions - unaccepted_questions
        rate = accepted / float(total_questions) * 100
        print("\t\t Total Questions Asked: " + str(len(userprofile.questions.fetch())))
        print('\t\t        Accept rate is: %.2f%%.' % rate)
        # check if the user have answers and questions or no.
        if userprofile.top_answer_tags.fetch():
            print('\nMost experienced on %s.' % userprofile.top_answer_tags.fetch()[0].tag_name)
        else:
            print("You have 0 answers")
        if userprofile.top_question_tags.fetch():
            print('Most curious about %s.' % userprofile.top_question_tags.fetch()[0].tag_name)
        else:
            print("You have 0 questions")
    except URLError:
        pr.print_fail("Please check your internet connectivity...")
        exit(1)
    except Exception as e:
        pr.showerror(e)
        if str(e) == "400 [bad_parameter]: `key` doesn't match a known application":
            pr.print_warning("Wrong API key... Deleting the data file...")
            del_datafile()
            exit(1)
        elif str(e) in ("not enough values to unpack (expected 1, got 0)", "400 [bad_parameter]: ids"):
            global manual
            if manual == 1:
                pr.print_warning("Wrong user ID specified...")
                pr.helpman()
                exit(1)
            pr.print_warning("Wrong user ID... Deleting the data file...")
            del_datafile()
            exit(1)

        # Reaches here when rate limit exceeds
        pr.print_warning(
            "Stack Overflow exception. This might be caused due to the rate limiting: "
            "http://stackapps.com/questions/3055/is-there-a-limit-of-api-requests")
        print("Use http://stackapps.com/apps/oauth/register to register a new API key.")
        set_api_key()
        exit(1)
Exemple #12
0
def main():
    """
    The main logic for how options in a command is checked.
    """
    global query
    namespace = parse_arguments(sys.argv[1:])
    search.load_user_agents()  # Populates the user agents array
    query_tag = ' '.join(namespace.browse)  # Tags

    # Query
    if namespace.query:
        # Query when args are present
        query = ' '.join(namespace.query)
    elif namespace.userQuery:
        # Query without any args
        query = ' '.join(namespace.userQuery)

    if namespace.help:
        # Display command line syntax
        printer.helpman()
        sys.exit(0)

    if namespace.debug:  # If --debug flag is present
        # Prints out error used for debugging
        printer.DEBUG = True
        debug_requests_on()

    if namespace.new:  # If --new flag is present
        # Opens StackOverflow website in the browser to create a  new question
        import webbrowser
        printer.print_warning("Opening stack overflow in your browser...")
        webbrowser.open(search.so_url + "/questions/ask")
        sys.exit(0)

    if namespace.api:  # If --api flag is present
        # Sets custom API key
        user_module.set_api_key()
        sys.exit(0)

    if namespace.user is not None:  # If --user flag is present
        # Stackoverflow user profile support
        if namespace.user != '(RANDOM_STRING_CONSTANT)':  # If user provided a user ID
            user_module.manual = 1  # Enabling manual mode
            user = namespace.user
        else:  # If user did not provide a user id
            user = user_module.retrieve_saved_profile(
            )  # Reading saved user id from app data
        user_module.user_page(user)
        sys.exit(0)

    if namespace.delete:  # If --delete flag is present
        # Deletes user data
        user_module.del_datafile()
        printer.print_warning("Data files deleted...")
        sys.exit(0)

    if namespace.sosearch:  # If --sosearch flag is present
        # Disables google search
        search.google_search = False

    if namespace.tag:  # If --tag flag is present
        global tag
        search.google_search = False
        tag = namespace.tag
        has_tags(
        )  # Adds tags to StackOverflow url (when not using google search.

    if namespace.res is not None:  # If --res flag is present
        # Automatically displays the result specified by the number
        question_number = namespace.res
        if namespace.query != [] or namespace.tag is not None:  # There must either be a tag or a query
            search.socli_manual_search(query, question_number)
        else:
            printer.print_warning(
                'You must specify a query or a tag. For example, use: "socli -r 3 -q python for loop" '
                'to retrieve the third result when searching about "python for loop". '
                'You can also use "socli -r 3 -t python" '
                'to retrieve the third result when searching for posts with the "python" tag.'
            )

    if namespace.browse:
        # Browse mode
        search.google_search = False
        socli_browse_interactive(query_tag)
    elif namespace.query != [] or namespace.tag is not None:  # If query and tag are not both empty
        if namespace.interactive:
            search.socli_interactive(query)
        else:
            socli(query)
    elif query not in [
            ' ', ''
    ] and not (namespace.tag or namespace.res or namespace.interactive
               or namespace.browse):  # If there are no flags
        socli(query)
    else:
        # Help text for interactive mode
        if namespace.interactive and namespace.query == [] and namespace.tag is None:
            printer.print_warning(
                'You must specify a query or a tag. For example, use: "socli -iq python for loop" '
                'to search about "python for loop" in interactive mode. '
                'You can also use "socli -it python" '
                'to search posts with the "python" tag in interactive mode.')
        else:
            printer.helpman()