Merge pull request #4 from Redume/feat/improved-graphics
Some checks are pending
Create and publish a Docker image / detect what files changed (push) Waiting to run
Create and publish a Docker image / build-and-push-server (push) Blocked by required conditions
Create and publish a Docker image / build-and-push-chart (push) Blocked by required conditions
Create and publish a Docker image / build-and-push-CR (push) Blocked by required conditions
Create and publish a Docker image / build-and-push-web (push) Blocked by required conditions
Deploy docs / detect what files changed (push) Waiting to run
Deploy docs / deploy (push) Blocked by required conditions

Feat/improved graphics
This commit is contained in:
Данил 2025-02-19 14:29:06 +03:00 committed by GitHub
commit 2c4a15a3fd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 85 additions and 69 deletions

View file

@ -6,98 +6,112 @@ based on historical data retrieved from the database.
from datetime import datetime from datetime import datetime
from matplotlib import pyplot as plt from matplotlib import pyplot as plt
from scipy.interpolate import make_interp_spline
import numpy as np
from function.gen_unique_name import generate_unique_name from function.gen_unique_name import generate_unique_name
from database.server import create_pool from database.server import create_pool
async def create_chart( async def create_chart(
from_currency: str, from_currency: str,
conv_currency: str, conv_currency: str,
start_date: str, start_date: str,
end_date: str end_date: str
) -> (str, None): ) -> (str, None):
""" """
Generates a line chart of currency rates for a given date range. Generates a line chart of currency rates for a given date range.
The chart shows the exchange rate trend between `from_currency` and The chart shows the exchange rate trend between `from_currency` and
`conv_currency` within the specified `start_date` and `end_date` range. `conv_currency` within the specified `start_date` and `end_date` range.
The generated chart is saved as a PNG file, and the function returns the The generated chart is saved as a PNG file, and the function returns the
file name. If data is invalid or insufficient, the function returns `None`. file name. If data is invalid or insufficient, the function returns `None`.
Args: Args:
from_currency (str): The base currency (e.g., "USD"). from_currency (str): The base currency (e.g., "USD").
conv_currency (str): The target currency (e.g., "EUR"). conv_currency (str): The target currency (e.g., "EUR").
start_date (str): The start date in the format 'YYYY-MM-DD'. start_date (str): The start date in the format 'YYYY-MM-DD'.
end_date (str): The end date in the format 'YYYY-MM-DD'. end_date (str): The end date in the format 'YYYY-MM-DD'.
Returns: Returns:
str | None: The name of the saved chart file, or `None` if the operation fails. str | None: The name of the saved chart file, or `None` if the operation fails.
""" """
pool = await create_pool() pool = await create_pool()
if not validate_date(start_date) or not validate_date(end_date): if not validate_date(start_date) or not validate_date(end_date):
return None return None
start_date_obj = datetime.strptime(start_date, '%Y-%m-%d').date() start_date_obj = datetime.strptime(start_date, '%Y-%m-%d').date()
end_date_obj = datetime.strptime(end_date, '%Y-%m-%d').date() end_date_obj = datetime.strptime(end_date, '%Y-%m-%d').date()
async with pool.acquire() as conn: async with pool.acquire() as conn:
data = await conn.fetch( data = await conn.fetch(
'SELECT date, rate FROM currency ' 'SELECT date, rate FROM currency '
'WHERE (date BETWEEN $1 AND $2) AND from_currency = $3 AND conv_currency = $4 ORDER BY date', 'WHERE (date BETWEEN $1 AND $2) ' +
start_date_obj, 'AND from_currency = $3 AND conv_currency = $4 ORDER BY date',
end_date_obj, start_date_obj,
from_currency.upper(), end_date_obj,
conv_currency.upper() from_currency.upper(),
) conv_currency.upper()
)
if not data or len(data) <= 1: if not data or len(data) <= 1:
return None return None
date, rate = [], [] date, rate = [], []
fig = plt.gcf()
for row in data: for row in data:
date.append(str(row['date'])) date.append(row[0])
rate.append(row['rate']) rate.append(row[1])
width = 18.5 + (len(date) // 5) * 3
fig.set_size_inches(width, 9.5) spline = make_interp_spline(range(len(date)), rate, k=2)
x = np.arange(len(date))
newx_2 = np.linspace(0, len(date) - 1, 200)
newy_2 = spline(newx_2)
fig, ax = plt.subplots(figsize=(15, 6))
for label in (ax.get_xticklabels() + ax.get_yticklabels()):
label.set_fontsize(10)
ax.set_xticks(np.linspace(0, len(date) - 1, 10))
ax.set_xticklabels(
[
date[int(i)].strftime('%Y-%m-%d')
for i in np.linspace(0, len(date) - 1, 10).astype(int)
]
)
name = await generate_unique_name(
f'{from_currency.upper()}_{conv_currency.upper()}',
datetime.now()
)
if rate[0] < rate[-1]: if rate[0] < rate[-1]:
plt.plot(date, rate, color='green', marker='o') plt.plot(newx_2, newy_2, color='green')
elif rate[0] > rate[-1]: elif rate[0] > rate[-1]:
plt.plot(date, rate, color='red', marker='o') plt.plot(newx_2, newy_2, color='red')
else: else:
plt.plot(date, rate, color='grey') plt.plot(newx_2, newy_2, color='grey')
plt.xlabel('Date') plt.savefig(f'../charts/{name}.png')
plt.ylabel('Rate') fig.clear()
return name
name = await generate_unique_name(
f'{from_currency.upper()}_{conv_currency.upper()}',
datetime.now()
)
fig.savefig(f'../charts/{name}.png')
fig.clear()
return name
def validate_date(date_str: str) -> bool: def validate_date(date_str: str) -> bool:
""" """
Validates whether the provided string is a valid date in the format 'YYYY-MM-DD'. Validates whether the provided string is a valid date in the format 'YYYY-MM-DD'.
Args: Args:
date_str (str): The date string to validate. date_str (str): The date string to validate.
Returns: Returns:
bool: `True` if the string is a valid date, `False` otherwise. bool: `True` if the string is a valid date, `False` otherwise.
""" """
try: try:
datetime.strptime(date_str, '%Y-%m-%d') datetime.strptime(date_str, '%Y-%m-%d')
return True return True
except ValueError: except ValueError:
return False return False

View file

@ -5,3 +5,5 @@ fastapi[standard]~=0.115.2
starlette~=0.40.0 starlette~=0.40.0
user_agents==2.2.0 user_agents==2.2.0
asyncpg~=0.30.0 asyncpg~=0.30.0
scipy~=1.15.2
numpy~=2.2.0