refactor(chart): Move functions to separate files or folders to improve code structure.

This commit is contained in:
Данил 2024-12-14 23:22:44 +03:00
parent 110effaeb2
commit 85acbbb16c
5 changed files with 177 additions and 175 deletions

View file

@ -0,0 +1,54 @@
from datetime import datetime
from matplotlib import pyplot as plt
from chart.function.gen_unique_name import generate_unique_name
from ..database.server import create_pool
async def create_chart(from_currency: str, conv_currency: str, start_date: str, end_date: str) -> (str, None):
pool = await create_pool()
start_date_obj = datetime.strptime(start_date, '%Y-%m-%d').date()
end_date_obj = datetime.strptime(end_date, '%Y-%m-%d').date()
async with pool.acquire() as conn:
data = await conn.fetch(
'SELECT date, rate FROM currency '
'WHERE (date BETWEEN $1 AND $2) AND from_currency = $3 AND conv_currency = $4',
start_date_obj,
end_date_obj,
from_currency.upper(),
conv_currency.upper()
)
if not data or len(data) <= 1:
return None
date, rate = [], []
for row in data:
date.append(str(row['date']))
rate.append(row['rate'])
if rate[0] < rate[-1]:
plt.plot(date, rate, color='green', marker='o')
elif rate[0] > rate[-1]:
plt.plot(date, rate, color='red', marker='o')
else:
plt.plot(date, rate, color='grey')
plt.xlabel('Date')
plt.ylabel('Rate')
fig = plt.gcf()
fig.set_size_inches(18.5, 9.5)
name = await generate_unique_name(
f'{from_currency.upper()}_{conv_currency.upper()}',
datetime.now()
)
fig.savefig(f'../charts/{name}.png')
fig.clear()
return name

View file

@ -0,0 +1,11 @@
import datetime
import random
import string
async def generate_unique_name(currency_pair: str, date: datetime) -> str:
date_str = date.strftime("%Y%m%d")
random_suffix = ''.join(random.choices(string.ascii_uppercase + string.digits, k=6))
unique_name = f"{currency_pair}_{date_str}_{random_suffix}"
return unique_name

View file

@ -1,34 +1,16 @@
import os
import psycopg2
import uvicorn
import yaml
import matplotlib.pyplot as plt
import datetime
import dateutil.relativedelta
import random
import string
from fastapi import FastAPI, Response, status, Request
from psycopg2.extras import DictCursor
from fastapi import FastAPI
from starlette.staticfiles import StaticFiles
from middleware.plausible_analytics import PlausibleAnalytics
from chart.middleware.plausible_analytics import PlausibleAnalytics
from chart.routes import get_chart, get_chart_period
app = FastAPI()
config = yaml.safe_load(open('../config.yaml'))
con = psycopg2.connect(host=config['database']['host'],
user=config['database']['user'],
password=config['database']['password'],
database=config['database']['name'],
port=config['database']['port'])
cur = con.cursor(cursor_factory=DictCursor)
con.autocommit = True
config = yaml.safe_load(open('config.yaml'))
if not os.path.exists('../charts'):
os.mkdir('../charts')
@ -36,158 +18,16 @@ if not os.path.exists('../charts'):
app.mount('/static/charts', StaticFiles(directory='../charts/'))
app.middleware('http')(PlausibleAnalytics())
@app.get("/api/getChart/", status_code=status.HTTP_201_CREATED)
async def get_chart(
response: Response,
request: Request,
from_currency: str = None,
conv_currency: str = None,
start_date: str = None,
end_date: str = None,
):
if not from_currency or not conv_currency:
response.status_code = status.HTTP_400_BAD_REQUEST
return {
'status': status.HTTP_400_BAD_REQUEST,
'message': 'The from_currency and conv_currency fields are required.',
}
elif not start_date and not end_date:
response.status_code = status.HTTP_400_BAD_REQUEST
return {
'status': status.HTTP_400_BAD_REQUEST,
'message': 'The start_date and end_date fields are required.',
}
chart = await create_chart(from_currency, conv_currency, start_date, end_date)
if not chart:
response.status_code = status.HTTP_404_NOT_FOUND
return {'message': 'No data found.', 'status_code': status.HTTP_404_NOT_FOUND}
host = request.headers.get("host")
url_schema = request.url.scheme
return {
'status': status.HTTP_400_BAD_REQUEST,
'message': f'{url_schema}://{host}/static/charts/{chart}.png',
}
@app.get("/api/getChart/{period}", status_code=status.HTTP_201_CREATED)
async def get_chart_period(
response: Response,
request: Request,
from_currency: str = None,
conv_currency: str = None,
period: str = None,
):
if not from_currency or not conv_currency:
response.status_code = status.HTTP_400_BAD_REQUEST
return {
'status': status.HTTP_400_BAD_REQUEST,
'message': 'The from_currency and conv_currency fields are required.',
}
if period not in ['week', 'month', 'quarter', 'year']:
response.status_code = status.HTTP_400_BAD_REQUEST
return {'message': 'Invalid period.', 'status_code': status.HTTP_400_BAD_REQUEST}
days, month, years = 0, 0, 0
if period == 'week':
days = -7
elif period == 'month':
month = -1
elif period == 'quarter':
month = -3
elif period == 'year':
years = -1
end_date = datetime.datetime.now()
start_date = end_date + dateutil.relativedelta.relativedelta(months=month, days=days, years=years)
chart = await create_chart(from_currency,
conv_currency,
start_date.strftime('%Y-%m-%d'),
end_date.strftime('%Y-%m-%d')
)
if not chart:
response.status_code = status.HTTP_404_NOT_FOUND
return {'message': 'No data found.', 'status_code': status.HTTP_404_NOT_FOUND}
host = request.headers.get("host")
url_schema = request.url.scheme
return {
'status': status.HTTP_201_CREATED,
'message': f'{url_schema}://{host}/static/charts/{chart}.png',
}
async def create_chart(from_currency: str, conv_currency: str, start_date: str, end_date: str) -> (str, None):
cur.execute('SELECT date, rate FROM currency WHERE (date BETWEEN %s AND %s) '
'AND from_currency = %s AND conv_currency = %s ORDER BY date',
[
start_date, end_date,
from_currency.upper(), conv_currency.upper()
])
con.commit()
data = cur.fetchall()
if not data or len(data) <= 1:
return None
date, rate = [], []
for i in range(len(data)):
date.append(str(data[i][0]))
rate.append(data[i][1])
if rate[0] < rate[-1]:
plt.plot(date, rate, color='green', marker='o')
elif rate[0] > rate[-1]:
plt.plot(date, rate, color='red', marker='o')
else:
plt.plot(date, rate, color='grey')
plt.xlabel('Date')
plt.ylabel('Rate')
fig = plt.gcf()
fig.set_size_inches(18.5, 9.5)
name = await generate_unique_name(
f'{from_currency.upper()}_{conv_currency.upper()}',
datetime.datetime.now()
)
fig.savefig(f'../charts/{name}.png')
fig.clear()
return name
async def generate_unique_name(currency_pair: str, date: datetime) -> str:
date_str = date.strftime("%Y%m%d")
random_suffix = ''.join(random.choices(string.ascii_uppercase + string.digits, k=6))
unique_name = f"{currency_pair}_{date_str}_{random_suffix}"
return unique_name
app.include_router(get_chart.router)
app.include_router(get_chart_period.router)
if __name__ == '__main__':
uvicorn.run(app,
uvicorn.run(
app,
host=config['server']['host'],
port=3030,
ssl_keyfile=config['server']['ssl']['private_key']
if config['server']['ssl']['work']
else None,
ssl_certfile=config['server']['ssl']['cert']
if config['server']['ssl']['work']
else None
ssl_keyfile=config['server']['ssl']['private_key'] if config['server']['ssl']['work'] else None,
ssl_certfile=config['server']['ssl']['cert'] if config['server']['ssl']['work'] else None
)

34
chart/routes/get_chart.py Normal file
View file

@ -0,0 +1,34 @@
from fastapi import APIRouter, status, Request, Response
from chart.function.create_chart import create_chart
from chart.routes.get_chart_period import prepare_chart_response
router = APIRouter()
@router.get("/api/getChart/", status_code=status.HTTP_201_CREATED)
async def get_chart(
response: Response,
request: Request,
from_currency: str = None,
conv_currency: str = None,
start_date: str = None,
end_date: str = None,
):
if not from_currency or not conv_currency:
response.status_code = status.HTTP_400_BAD_REQUEST
return {
'status': status.HTTP_400_BAD_REQUEST,
'message': 'The from_currency and conv_currency fields are required.',
}
elif not start_date and not end_date:
response.status_code = status.HTTP_400_BAD_REQUEST
return {
'status': status.HTTP_400_BAD_REQUEST,
'message': 'The start_date and end_date fields are required.',
}
chart = await create_chart(from_currency, conv_currency, start_date, end_date)
return await prepare_chart_response(response, request, chart)

View file

@ -0,0 +1,63 @@
from datetime import datetime
import dateutil.relativedelta
from fastapi import APIRouter, status, Request, Response
from chart.function.create_chart import create_chart
router = APIRouter()
@router.get("/api/getChart/{period}", status_code=status.HTTP_201_CREATED)
async def get_chart_period(
response: Response,
request: Request,
from_currency: str = None,
conv_currency: str = None,
period: str = None,
):
if not from_currency or not conv_currency:
response.status_code = status.HTTP_400_BAD_REQUEST
return {
'status': status.HTTP_400_BAD_REQUEST,
'message': 'The from_currency and conv_currency fields are required.',
}
if period not in ['week', 'month', 'quarter', 'year']:
response.status_code = status.HTTP_400_BAD_REQUEST
return {'message': 'Invalid period.', 'status_code': status.HTTP_400_BAD_REQUEST}
days, month, years = 0, 0, 0
if period == 'week':
days = -7
elif period == 'month':
month = -1
elif period == 'quarter':
month = -3
elif period == 'year':
years = -1
end_date = datetime.now()
start_date = end_date + dateutil.relativedelta.relativedelta(months=month, days=days, years=years)
chart = await create_chart(from_currency,
conv_currency,
start_date.strftime('%Y-%m-%d'),
end_date.strftime('%Y-%m-%d')
)
return await prepare_chart_response(response, request, chart)
async def prepare_chart_response(response: Response, request: Request, chart_name: str):
if not chart_name:
response.status_code = status.HTTP_404_NOT_FOUND
return {'message': 'No data found.', 'status_code': status.HTTP_404_NOT_FOUND}
host = request.headers.get("host")
url_schema = request.url.scheme
return {
'status': status.HTTP_201_CREATED,
'message': f'{url_schema}://{host}/static/charts/{chart_name}.png',
}