Add tools for currency conversion in search results (#536)

This implements a method for converting between various currencies. When a user
searches "<currency A> to <currency B>" (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.
main
Vansh Comar 2021-12-07 11:26:13 +05:30 committed by GitHub
parent 10a15e06e1
commit 7bea6349a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 159 additions and 4 deletions

View File

@ -15,8 +15,8 @@ from app.models.endpoint import Endpoint
from app.request import Request, TorError from app.request import Request, TorError
from app.utils.bangs import resolve_bang from app.utils.bangs import resolve_bang
from app.utils.misc import read_config_bool, get_client_ip, get_request_url 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 add_ip_card, bold_search_terms,\
from app.utils.results import bold_search_terms add_currency_card, check_currency
from app.utils.search import * from app.utils.search import *
from app.utils.session import generate_user_key, valid_user_session from app.utils.session import generate_user_key, valid_user_session
from bs4 import BeautifulSoup as bsoup from bs4 import BeautifulSoup as bsoup
@ -318,6 +318,12 @@ def search():
html_soup = bsoup(str(response), 'html.parser') html_soup = bsoup(str(response), 'html.parser')
response = add_ip_card(html_soup, get_client_ip(request)) 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( return render_template(
'display.html', 'display.html',
newest_version=newest_version, newest_version=newest_version,

View File

@ -187,6 +187,10 @@ path {
color: var(--whoogle-dark-text) !important; 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; color: var(--whoogle-dark-secondary-text) !important;
} }
.cb:focus {
color: var(--whoogle-dark-contrast-text) !important;
}

View File

@ -12,3 +12,30 @@
height: 40px; height: 40px;
width: 50px; 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;
}

View File

@ -175,6 +175,10 @@ path {
border-bottom: 0px; border-bottom: 0px;
} }
.ip-text-div, .update_available { .ip-text-div, .update_available, .cb_label, .cb {
color: var(--whoogle-secondary-text) !important; color: var(--whoogle-secondary-text) !important;
} }
.cb:focus {
color: var(--whoogle-text) !important;
}

View File

@ -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));
}

View File

@ -38,4 +38,5 @@
<script src="{{ cb_url('autocomplete.js') }}"></script> <script src="{{ cb_url('autocomplete.js') }}"></script>
<script src="{{ cb_url('utils.js') }}"></script> <script src="{{ cb_url('utils.js') }}"></script>
<script src="{{ cb_url('keyboard.js') }}"></script> <script src="{{ cb_url('keyboard.js') }}"></script>
<script src="{{ cb_url('currency.js') }}"></script>
</html> </html>

View File

@ -223,3 +223,107 @@ def add_ip_card(html_soup: BeautifulSoup, ip: str) -> BeautifulSoup:
# Inserting the element # Inserting the element
ref_element.insert_before(ip_tag) ref_element.insert_before(ip_tag)
return html_soup 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