Skip to content
This repository has been archived by the owner on Jan 14, 2025. It is now read-only.

Refactor codebase for improved readability and maintainability #22

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions src/bonbast/helpers/click_callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@
def print_version(ctx, param, value):
if not value or ctx.resilient_parsing:
return
else:
click.echo(bonbast_version)
ctx.exit()
click.echo(bonbast_version)
ctx.exit()


def parse_show_only(ctx, param, value):
Expand Down
10 changes: 5 additions & 5 deletions src/bonbast/helpers/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
class Singleton(type):
_instances = {}

def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
def __call__(self, *args, **kwargs):
if self not in self._instances:
self._instances[self] = super(Singleton, self).__call__(*args, **kwargs)
return self._instances[self]


def format_toman(price: float) -> str:
Expand All @@ -31,7 +31,7 @@ def wrapper_retry(*args, **kwargs):
for _ in range(retry_count):
try:
return func(*args, **kwargs)
except: # noqa
except Exception:
if retry_delay is not None:
time.sleep(retry_delay)

Expand Down
47 changes: 30 additions & 17 deletions src/bonbast/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ def get_prices(show_only: List[str] = None):
token = token_manager.generate()
try:
response = get_prices_from_api(token.value)
if show_only is not None and len(show_only) > 0:
if show_only is not None and show_only:
amirmalekian marked this conversation as resolved.
Show resolved Hide resolved
response = list(response)
for index, item in enumerate(response):
response[index] = [item for item in item if item.code.lower() in show_only]
response[index] = [
item for item in item if item.code.lower() in show_only]
response = tuple(response)

return response
Expand Down Expand Up @@ -76,7 +77,6 @@ def cli(ctx, show_only):
def live(ctx):
if ctx.invoked_subcommand is None:
print('Show full table with live prices / up and down arrows')
pass


# ================ bonbast live graph ================
Expand All @@ -101,7 +101,8 @@ def live_simple(interval, show_only):
collections = get_prices(show_only)

prices_text = Text()
prices_text.append(f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}\n\n', style='bold')
prices_text.append(
f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}\n\n', style='bold')

for collection_index, collection in enumerate(collections):
for index, model in enumerate(collection):
Expand Down Expand Up @@ -160,28 +161,35 @@ def live_currency(interval, currency):
# add a row with the new information
if type(item) is Currency or type(item) is Coin:
sell_symbol = f' {get_change_char(item.sell, old_model.sell)}' if old_model is not None else ''
sell_str = (item.formatted_sell if item.sell is not None else '-') + sell_symbol
sell_color = get_color(item.sell, None if old_model is None else old_model.sell)
sell_str = (
item.formatted_sell if item.sell is not None else '-') + sell_symbol
sell_color = get_color(
item.sell, None if old_model is None else old_model.sell)

buy_symbol = f' {get_change_char(item.buy, old_model.buy)}' if old_model is not None else ''
buy_str = (item.formatted_buy if item.buy is not None else '-') + buy_symbol
buy_color = get_color(item.buy, None if old_model is None else old_model.buy)
buy_str = (
item.formatted_buy if item.buy is not None else '-') + buy_symbol
buy_color = get_color(
item.buy, None if old_model is None else old_model.buy)

price = (
Text(sell_str, style=sell_color),
Text(buy_str, style=buy_color),
)
else:
price_char = f' {get_change_char(item.price, old_model.price)}' if old_model is not None else ''
price_str = (item.formatted_price if item.price is not None else '-') + price_char
price_color = get_color(item.price, None if old_model is None else old_model.price)
price_str = (
item.formatted_price if item.price is not None else '-') + price_char
price_color = get_color(
item.price, None if old_model is None else old_model.price)

price = (
Text(price_str, style=price_color),
)

table.add_row(
Text(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), style=DEFAULT_TEXT_COLOR),
Text(datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
style=DEFAULT_TEXT_COLOR),
Text(item.name, style=DEFAULT_TEXT_COLOR),
*price,
)
Expand All @@ -201,21 +209,26 @@ def live_currency(interval, currency):
@click.pass_context
def convert(ctx, source, destination, amount, only_buy, only_sell):
if source is None and destination is None:
raise click.BadOptionUsage('', 'Please specify source or destination currency')
raise click.BadOptionUsage(
'', 'Please specify source or destination currency')

if source is not None and destination is not None:
raise click.BadOptionUsage('', 'Don\'t use --source and --destination together')
raise click.BadOptionUsage(
'', 'Don\'t use --source and --destination together')

if only_buy and only_sell:
raise click.BadOptionUsage('', 'Don\'t use --only-buy and --only-sell together')
raise click.BadOptionUsage(
'', 'Don\'t use --only-buy and --only-sell together')

currencies_list, _, __ = get_prices()
if source is not None:
currency = next(currency for currency in currencies_list if currency.code.lower() == source.lower())
currency = next(
currency for currency in currencies_list if currency.code.lower() == source.lower())
buy = format_toman(int(amount * currency.buy))
sell = format_toman(int(amount * currency.sell))
else:
currency = next(currency for currency in currencies_list if currency.code.lower() == destination.lower())
currency = next(currency for currency in currencies_list if currency.code.lower(
) == destination.lower())
buy = format_price(amount / currency.sell)
sell = format_price(amount / currency.buy)

Expand Down Expand Up @@ -255,7 +268,7 @@ def export(pretty, expanded, show_only):
prices = {}
for item in items:
for model in item:
prices.update(model.to_json())
prices |= model.to_json()
amirmalekian marked this conversation as resolved.
Show resolved Hide resolved

if pretty:
pprint(prices, expand_all=expanded)
Expand Down
5 changes: 2 additions & 3 deletions src/bonbast/managers/storage_manager.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import contextlib
import os
import pathlib
import sys
Expand Down Expand Up @@ -56,7 +57,5 @@ def load_file(self) -> str:
def delete_file(self) -> None:
""" Delete the saved token from datadir
"""
try:
with contextlib.suppress(FileNotFoundError):
os.remove(self.file_path)
except FileNotFoundError:
pass
114 changes: 59 additions & 55 deletions src/bonbast/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ def get_token_from_main_page():
r = requests.get(BASE_URL, headers=headers)
r.raise_for_status()
except requests.exceptions.HTTPError as err:
raise SystemExit(err)
raise SystemExit(err) from err

search = re.search(r"param\s*=\s*\"(.+)\"", r.text, re.MULTILINE)
if search is None or search.group(1) is None:
if search is None or search[1] is None:
amirmalekian marked this conversation as resolved.
Show resolved Hide resolved
raise SystemExit('Error: token not found in the main page')

return search.group(1)
return search[1]


def get_prices_from_api(token: str) -> Tuple[List[Currency], List[Coin], List[Gold]]:
Expand Down Expand Up @@ -98,49 +98,43 @@ def get_prices_from_api(token: str) -> Tuple[List[Currency], List[Coin], List[Go
r.raise_for_status()
r = r.json()
except requests.exceptions.HTTPError as err:
raise SystemExit(err)
raise SystemExit(err) from err

if 'reset' in r:
raise ResetAPIError('Error: token is expired')

currencies: List[Currency] = []
coins: List[Coin] = []
golds: List[Gold] = []

for currency in Currency.VALUES:
if f'{currency}{BUY}' in r and f'{currency}{SELL}' in r:
currencies.append(Currency(
currency.upper(),
Currency.VALUES[currency],
sell=int(r[f'{currency}{SELL}']),
buy=int(r[f'{currency}{BUY}']),
))

for coin in Coin.VALUES:
if f'{coin}' in r and f'{coin}{BUY}' in r:
coins.append(Coin(
coin,
Coin.VALUES[coin],
sell=int(r[coin]),
buy=int(r[f'{coin}{BUY}']),
))

for gold in Gold.VALUES:
if f'{gold}' in r:
golds.append(Gold(
gold,
Gold.VALUES[gold],
price=int(r[gold])
))

currencies: List[Currency] = [
Currency(
currency.upper(),
Currency.VALUES[currency],
sell=int(r[f'{currency}{SELL}']),
buy=int(r[f'{currency}{BUY}']),
)
for currency in Currency.VALUES
if f'{currency}{BUY}' in r and f'{currency}{SELL}' in r
]
coins.extend(
amirmalekian marked this conversation as resolved.
Show resolved Hide resolved
Coin(
coin,
Coin.VALUES[coin],
sell=int(r[coin]),
buy=int(r[f'{coin}{BUY}']),
)
for coin in Coin.VALUES
if f'{coin}' in r and f'{coin}{BUY}' in r
)
golds.extend(
Gold(gold, Gold.VALUES[gold], price=int(r[gold]))
for gold in Gold.VALUES
if f'{gold}' in r
)
return currencies, coins, golds


def get_graph_data(
currency: str,
start_date: datetime = datetime.today() - timedelta(days=30),
end_date: datetime = datetime.today(),
) -> Dict[str, int]:
def get_graph_data(currency: str, start_date: datetime = datetime.now() - timedelta(days=30), end_date: datetime = datetime.now()) -> Dict[str, int]:
"""
This function will make a request to bonbast.com/graph and make them in two array.
"""
Expand All @@ -164,36 +158,45 @@ def get_graph_data(
}

try:
request = requests.get(f'{BASE_URL}/graph/{currency}/{start_date.date()}/{end_date.date()}', headers=headers)
request = requests.get(
f'{BASE_URL}/graph/{currency}/{start_date.date()}/{end_date.date()}', headers=headers)
request.raise_for_status()
except requests.exceptions.HTTPError as err:
raise SystemExit(err)
raise SystemExit(err) from err

soup = BeautifulSoup(request.text, 'html.parser')
for data in soup.find_all("script"):
# get variables from script and convert them to list
if "data: {" in data.text:
price_list = data.text.split("data: [")[1].split("]")[0].split(",")
date_list = data.text.split("labels: [")[1].split("]")[0].split(',')
return get_graph_data(data)


def get_graph_data(data):
price_list = data.text.split("data: [")[1].split("]")[0].split(",")
date_list = data.text.split("labels: [")[1].split("]")[0].split(',')

if len(price_list) != len(date_list):
raise SystemExit('Error: data inconsistency')
if len(price_list) != len(date_list):
raise SystemExit('Error: data inconsistency')

dic = {}
for i in range(len(price_list)):
price = int(price_list[i])
date = re.search(r'\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])', date_list[i]).group(0)
dic[date] = price
dic = {}
for i in range(len(price_list)):
price = int(price_list[i])
date = re.search(
r'\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])', date_list[i]
)[0]
dic[date] = price

return dic
return dic


def get_history(date: datetime = datetime.today() - timedelta(days=1)) -> List[Currency]:
def get_history(date: datetime = datetime.now() - timedelta(days=1)) -> List[Currency]:
amirmalekian marked this conversation as resolved.
Show resolved Hide resolved
if date.date() < datetime(2012, 10, 9).date():
raise SystemExit('Error: date is too far in the past. Date must be greater than 2012-10-09')
raise SystemExit(
'Error: date is too far in the past. Date must be greater than 2012-10-09')

if date.date() >= datetime.today().date():
raise SystemExit(f'Error: date must be less than today({date.today().date()}).')
if date.date() >= datetime.now().date():
raise SystemExit(
f'Error: date must be less than today({date.today().date()}).')

headers = {
'authority': 'bonbast.com',
Expand All @@ -212,17 +215,18 @@ def get_history(date: datetime = datetime.today() - timedelta(days=1)) -> List[C
}

try:
request = requests.get(f'{BASE_URL}/archive/{date.strftime("%Y/%m/%d")}', headers=headers)
request = requests.get(
f'{BASE_URL}/archive/{date.strftime("%Y/%m/%d")}', headers=headers)
request.raise_for_status()
except requests.exceptions.HTTPError as err:
raise SystemExit(err)
raise SystemExit(err) from err

soup = BeautifulSoup(request.text, 'html.parser')
tables = soup.findAll("table")

# first and second table are currencies
currencies: List[Currency] = []
for table in tables[0:1]:
for table in tables[:1]:
for row in table.findAll('tr')[1:]:
cells = row.findAll("td")
currencies.append(Currency(
Expand Down