From 7bea6349a00c7df51005ecba181d412fcdf4a3bd Mon Sep 17 00:00:00 2001 From: Vansh Comar <69755265+vacom13@users.noreply.github.com> Date: Tue, 7 Dec 2021 11:26:13 +0530 Subject: [PATCH] Add tools for currency conversion in search results (#536) This implements a method for converting between various currencies. When a user searches " to " (including when prefixed by a specific amount), they are now presented with a table for quickly converting between the two. This makes use of the currency ratio returned as the first "card" in currency related searches, and the table is inserted into this same card. --- app/routes.py | 10 +++- app/static/css/dark-theme.css | 6 +- app/static/css/input.css | 27 +++++++++ app/static/css/light-theme.css | 6 +- app/static/js/currency.js | 9 +++ app/templates/display.html | 1 + app/utils/results.py | 104 +++++++++++++++++++++++++++++++++ 7 files changed, 159 insertions(+), 4 deletions(-) create mode 100644 app/static/js/currency.js diff --git a/app/routes.py b/app/routes.py index b949f65..bb15295 100644 --- a/app/routes.py +++ b/app/routes.py @@ -15,8 +15,8 @@ from app.models.endpoint import Endpoint from app.request import Request, TorError from app.utils.bangs import resolve_bang from app.utils.misc import read_config_bool, get_client_ip, get_request_url -from app.utils.results import add_ip_card -from app.utils.results import bold_search_terms +from app.utils.results import add_ip_card, bold_search_terms,\ + add_currency_card, check_currency from app.utils.search import * from app.utils.session import generate_user_key, valid_user_session from bs4 import BeautifulSoup as bsoup @@ -318,6 +318,12 @@ def search(): html_soup = bsoup(str(response), 'html.parser') response = add_ip_card(html_soup, get_client_ip(request)) + # Feature to display currency_card + conversion = check_currency(str(response)) + if conversion: + html_soup = bsoup(str(response), 'html.parser') + response = add_currency_card(html_soup, conversion) + return render_template( 'display.html', newest_version=newest_version, diff --git a/app/static/css/dark-theme.css b/app/static/css/dark-theme.css index 68b7cad..c30b151 100644 --- a/app/static/css/dark-theme.css +++ b/app/static/css/dark-theme.css @@ -187,6 +187,10 @@ path { color: var(--whoogle-dark-text) !important; } -.ip-text-div, .update_available { +.ip-text-div, .update_available, .cb_label, .cb { color: var(--whoogle-dark-secondary-text) !important; } + +.cb:focus { + color: var(--whoogle-dark-contrast-text) !important; +} diff --git a/app/static/css/input.css b/app/static/css/input.css index 96cc108..2da2aa3 100644 --- a/app/static/css/input.css +++ b/app/static/css/input.css @@ -12,3 +12,30 @@ height: 40px; width: 50px; } +.ZINbbc.xpd.O9g5cc.uUPGi input::-webkit-outer-spin-button, +input::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} + +.cb { + width: 40%; + overflow: hidden; + text-align: left; + line-height: 28px; + background: transparent; + border-radius: 6px; + border: 1px solid #5f6368; + font-size: 14px !important; + height: 36px; + padding: 0 0 0 12px; + margin: 10px 10px 10px 0; +} + +.conversion_box { + margin-top: 15px; +} + +.ZINbbc.xpd.O9g5cc.uUPGi input:focus-visible { + outline: 0; +} diff --git a/app/static/css/light-theme.css b/app/static/css/light-theme.css index de37157..bc17952 100644 --- a/app/static/css/light-theme.css +++ b/app/static/css/light-theme.css @@ -175,6 +175,10 @@ path { border-bottom: 0px; } -.ip-text-div, .update_available { +.ip-text-div, .update_available, .cb_label, .cb { color: var(--whoogle-secondary-text) !important; } + +.cb:focus { + color: var(--whoogle-text) !important; +} diff --git a/app/static/js/currency.js b/app/static/js/currency.js new file mode 100644 index 0000000..b669596 --- /dev/null +++ b/app/static/js/currency.js @@ -0,0 +1,9 @@ +const convert = (n1, n2, conversionFactor) => { + // id's for currency input boxes + let id1 = "cb" + n1; + let id2 = "cb" + n2; + // getting the value of the input box that just got filled + let inputBox = document.getElementById(id1).value; + // updating the other input box after conversion + document.getElementById(id2).value = ((inputBox * conversionFactor).toFixed(2)); +} diff --git a/app/templates/display.html b/app/templates/display.html index 3cd76b6..fe39df7 100644 --- a/app/templates/display.html +++ b/app/templates/display.html @@ -38,4 +38,5 @@ + diff --git a/app/utils/results.py b/app/utils/results.py index 0b445af..02757d7 100644 --- a/app/utils/results.py +++ b/app/utils/results.py @@ -223,3 +223,107 @@ def add_ip_card(html_soup: BeautifulSoup, ip: str) -> BeautifulSoup: # Inserting the element ref_element.insert_before(ip_tag) return html_soup + + +def check_currency(response: str) -> dict: + """Check whether the results have currency conversion + + Args: + response: Search query Result + + Returns: + dict: Consists of currency names and values + + """ + soup = BeautifulSoup(response, 'html.parser') + currency_link = soup.find('a', {'href': 'https://g.co/gfd'}) + if currency_link: + while 'class' not in currency_link.attrs or \ + 'ZINbbc' not in currency_link.attrs['class']: + currency_link = currency_link.parent + currency_link = currency_link.find_all(class_='BNeawe') + currency1 = currency_link[0].text + currency2 = currency_link[1].text + currency1 = currency1.rstrip('=').split(' ', 1) + currency2 = currency2.split(' ', 1) + if currency2[0][-3] == ',': + currency1[0] = currency1[0].replace('.', '') + currency1[0] = currency1[0].replace(',', '.') + currency2[0] = currency2[0].replace('.', '') + currency2[0] = currency2[0].replace(',', '.') + else: + currency1[0] = currency1[0].replace(',', '') + currency2[0] = currency2[0].replace(',', '') + return {'currencyValue1': float(currency1[0]), + 'currencyLabel1': currency1[1], + 'currencyValue2': float(currency2[0]), + 'currencyLabel2': currency2[1] + } + return {} + + +def add_currency_card(soup: BeautifulSoup, + conversion_details: dict) -> BeautifulSoup: + """Adds the currency conversion boxes + to response of the search query + + Args: + soup: Parsed search result + conversion_details: Dictionary of currency + related information + + Returns: + BeautifulSoup + """ + # Element before which the code will be changed + # (This is the 'disclaimer' link) + element1 = soup.find('a', {'href': 'https://g.co/gfd'}) + + while 'class' not in element1.attrs or \ + 'nXE3Ob' not in element1.attrs['class']: + element1 = element1.parent + + # Creating the conversion factor + conversion_factor = (conversion_details['currencyValue1'] / + conversion_details['currencyValue2']) + + # Creating a new div for the input boxes + conversion_box = soup.new_tag('div') + conversion_box['class'] = 'conversion_box' + + # Currency to be converted from + input_box1 = soup.new_tag('input') + input_box1['id'] = 'cb1' + input_box1['type'] = 'number' + input_box1['class'] = 'cb' + input_box1['value'] = conversion_details['currencyValue1'] + input_box1['oninput'] = f'convert(1, 2, {1 / conversion_factor})' + + label_box1 = soup.new_tag('label') + label_box1['for'] = 'cb1' + label_box1['class'] = 'cb_label' + label_box1.append(conversion_details['currencyLabel1']) + + br = soup.new_tag('br') + + # Currency to be converted to + input_box2 = soup.new_tag('input') + input_box2['id'] = 'cb2' + input_box2['type'] = 'number' + input_box2['class'] = 'cb' + input_box2['value'] = conversion_details['currencyValue2'] + input_box2['oninput'] = f'convert(2, 1, {conversion_factor})' + + label_box2 = soup.new_tag('label') + label_box2['for'] = 'cb2' + label_box2['class'] = 'cb_label' + label_box2.append(conversion_details['currencyLabel2']) + + conversion_box.append(input_box1) + conversion_box.append(label_box1) + conversion_box.append(br) + conversion_box.append(input_box2) + conversion_box.append(label_box2) + + element1.insert_before(conversion_box) + return soup