mirror of
https://github.com/Redume/Kekkai.git
synced 2025-02-23 12:43:12 +03:00
refactor(chart): Added docstring in each file and function. Fixed long lines
This commit is contained in:
parent
c5d3d9fe9b
commit
ed8cf91558
7 changed files with 190 additions and 30 deletions
|
@ -1,9 +1,27 @@
|
|||
import asyncpg
|
||||
import yaml
|
||||
"""
|
||||
This module initializes the database connection pool using asyncpg.
|
||||
|
||||
config = yaml.safe_load(open("config.yaml", "r"))
|
||||
It reads database configuration from a YAML file and creates a connection pool
|
||||
that can be used throughout the application for database operations.
|
||||
"""
|
||||
|
||||
import asyncpg
|
||||
|
||||
from chart.utils.load_config import load_config
|
||||
|
||||
config = load_config('config.yaml')
|
||||
|
||||
async def create_pool() -> asyncpg.pool.Pool:
|
||||
"""
|
||||
Creates and returns a connection pool for the PostgreSQL database.
|
||||
|
||||
The function uses configuration settings loaded from a YAML file to
|
||||
establish the connection pool. The pool allows multiple database
|
||||
connections to be reused efficiently across the application.
|
||||
|
||||
Returns:
|
||||
asyncpg.pool.Pool: The connection pool object.
|
||||
"""
|
||||
pool = await asyncpg.create_pool(
|
||||
user=config['database']['user'],
|
||||
password=config['database']['password'],
|
||||
|
@ -14,4 +32,4 @@ async def create_pool() -> asyncpg.pool.Pool:
|
|||
max_size=20
|
||||
)
|
||||
|
||||
return pool
|
||||
return pool
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
"""
|
||||
This module provides functionality to generate currency rate charts
|
||||
based on historical data retrieved from the database.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from matplotlib import pyplot as plt
|
||||
|
@ -5,7 +10,29 @@ 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):
|
||||
async def create_chart(
|
||||
from_currency: str,
|
||||
conv_currency: str,
|
||||
start_date: str,
|
||||
end_date: str
|
||||
) -> (str, None):
|
||||
"""
|
||||
Generates a line chart of currency rates for a given date range.
|
||||
|
||||
The chart shows the exchange rate trend between `from_currency` and
|
||||
`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
|
||||
file name. If data is invalid or insufficient, the function returns `None`.
|
||||
|
||||
Args:
|
||||
from_currency (str): The base currency (e.g., "USD").
|
||||
conv_currency (str): The target currency (e.g., "EUR").
|
||||
start_date (str): The start date in the format 'YYYY-MM-DD'.
|
||||
end_date (str): The end date in the format 'YYYY-MM-DD'.
|
||||
|
||||
Returns:
|
||||
str | None: The name of the saved chart file, or `None` if the operation fails.
|
||||
"""
|
||||
pool = await create_pool()
|
||||
|
||||
if not validate_date(start_date) or not validate_date(end_date):
|
||||
|
@ -58,6 +85,15 @@ async def create_chart(from_currency: str, conv_currency: str, start_date: str,
|
|||
|
||||
|
||||
def validate_date(date_str: str) -> bool:
|
||||
"""
|
||||
Validates whether the provided string is a valid date in the format 'YYYY-MM-DD'.
|
||||
|
||||
Args:
|
||||
date_str (str): The date string to validate.
|
||||
|
||||
Returns:
|
||||
bool: `True` if the string is a valid date, `False` otherwise.
|
||||
"""
|
||||
try:
|
||||
datetime.strptime(date_str, '%Y-%m-%d')
|
||||
return True
|
||||
|
|
|
@ -1,9 +1,23 @@
|
|||
"""
|
||||
This module provides a function to generate a unique name for chart files.
|
||||
"""
|
||||
import datetime
|
||||
import random
|
||||
import string
|
||||
|
||||
|
||||
async def generate_unique_name(currency_pair: str, date: datetime) -> str:
|
||||
"""
|
||||
Generates a unique name for a chart file based on the currency pair,
|
||||
current date, and a random suffix.
|
||||
|
||||
Args:
|
||||
currency_pair (str): A string representing the currency pair (e.g., "USD_EUR").
|
||||
date (datetime.datetime): The current datetime object.
|
||||
|
||||
Returns:
|
||||
str: A unique name in the format "CURRENCYPAIR_YYYYMMDD_RANDOMSUFFIX".
|
||||
"""
|
||||
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}"
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
"""
|
||||
This is the main application file for the chart service using FastAPI.
|
||||
|
||||
The application serves static files, provides endpoints for generating charts,
|
||||
and integrates with Plausible Analytics for tracking usage.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
import uvicorn
|
||||
import yaml
|
||||
|
||||
from fastapi import FastAPI
|
||||
from starlette.staticfiles import StaticFiles
|
||||
|
||||
from chart.middleware.plausible_analytics import PlausibleAnalytics
|
||||
from chart.routes import get_chart, get_chart_period
|
||||
from chart.utils.load_config import load_config
|
||||
|
||||
app = FastAPI()
|
||||
config = yaml.safe_load(open('config.yaml'))
|
||||
config = load_config('config.yaml')
|
||||
|
||||
if not os.path.exists('../charts'):
|
||||
os.mkdir('../charts')
|
||||
|
@ -23,11 +29,16 @@ app.include_router(get_chart_period.router)
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
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
|
||||
)
|
||||
|
|
|
@ -1,12 +1,25 @@
|
|||
import httpx
|
||||
import yaml
|
||||
|
||||
from user_agents import parse as ua_parse
|
||||
"""
|
||||
This module provides middleware for integrating Plausible Analytics
|
||||
into a FastAPI application.
|
||||
"""
|
||||
from http import HTTPStatus
|
||||
|
||||
config = yaml.safe_load(open('../config.yaml'))
|
||||
import httpx
|
||||
from user_agents import parse as ua_parse
|
||||
|
||||
from chart.utils.load_config import load_config
|
||||
|
||||
config = load_config('config.yaml')
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
class PlausibleAnalytics:
|
||||
"""
|
||||
Middleware for sending events to Plausible Analytics.
|
||||
|
||||
This middleware intercepts each incoming request, collects metadata such as
|
||||
user-agent, request path, and response status, and sends it as an event to
|
||||
Plausible Analytics.
|
||||
"""
|
||||
async def __call__(self, request, call_next):
|
||||
response = await call_next(request)
|
||||
|
||||
|
@ -23,8 +36,10 @@ class PlausibleAnalytics:
|
|||
"props": {
|
||||
"method": request.method,
|
||||
"statusCode": response.status_code,
|
||||
"browser": f"{user_agent_parsed.browser.family} {user_agent_parsed.browser.version_string}",
|
||||
"os": f"{user_agent_parsed.os.family} {user_agent_parsed.os.version_string}",
|
||||
"browser": f"{user_agent_parsed.browser.family} "
|
||||
f"{user_agent_parsed.browser.version_string}",
|
||||
"os": f"{user_agent_parsed.os.family} "
|
||||
f"{user_agent_parsed.os.version_string}",
|
||||
"source": request.headers.get('referer', 'direct'),
|
||||
},
|
||||
}
|
||||
|
@ -40,7 +55,12 @@ class PlausibleAnalytics:
|
|||
"User-Agent": request.headers.get('user-agent', 'unknown'),
|
||||
},
|
||||
)
|
||||
except httpx.RequestError as e:
|
||||
print(f"Request error while sending event to Plausible: {e}")
|
||||
except httpx.HTTPStatusError as e:
|
||||
print(f"HTTP status error while sending event to Plausible: {e}")
|
||||
# pylint: disable=broad-exception-caught
|
||||
except Exception as e:
|
||||
print(f"Error sending event to Plausible: {e}")
|
||||
print(f"Unexpected error sending event to Plausible: {e}")
|
||||
|
||||
return response
|
||||
|
|
|
@ -1,34 +1,60 @@
|
|||
"""
|
||||
This module contains the route for retrieving a chart based on a given currency pair and date range.
|
||||
It defines the `/api/getChart/` endpoint that processes requests for generating charts.
|
||||
"""
|
||||
from fastapi import APIRouter, status, Request, Response
|
||||
from pydantic import BaseModel
|
||||
|
||||
from chart.function.create_chart import create_chart
|
||||
from chart.routes.get_chart_period import prepare_chart_response
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
class ChartRequestParams(BaseModel):
|
||||
"""
|
||||
A Pydantic model that represents the request parameters for generating a chart.
|
||||
|
||||
This model is used to validate and group the request parameters:
|
||||
from_currency, conv_currency, start_date, and end_date.
|
||||
"""
|
||||
from_currency: str
|
||||
conv_currency: str
|
||||
start_date: str
|
||||
end_date: str
|
||||
|
||||
@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,
|
||||
params: ChartRequestParams
|
||||
):
|
||||
"""
|
||||
Fetches a chart for a given currency pair and date range.
|
||||
|
||||
if not from_currency or not conv_currency:
|
||||
:param response: The response object used for returning the HTTP response.
|
||||
:param request: The request object containing details about the incoming request.
|
||||
:param params: Contains the request parameters:
|
||||
from_currency, conv_currency, start_date, and end_date.
|
||||
:return: A chart or an error message if the request is invalid.
|
||||
"""
|
||||
if not params.from_currency or not params.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:
|
||||
if not params.start_date or not params.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)
|
||||
chart = await create_chart(
|
||||
params.from_currency,
|
||||
params.conv_currency,
|
||||
params.start_date,
|
||||
params.end_date
|
||||
)
|
||||
return await prepare_chart_response(response, request, chart)
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
"""
|
||||
This module defines the route for fetching a chart for a specific currency pair and period.
|
||||
|
||||
It includes the endpoint `/api/getChart/{period}` which allows users to request a chart for
|
||||
a given currency pair (from_currency and conv_currency)
|
||||
over a specified time period (week, month, quarter, or year).
|
||||
"""
|
||||
from datetime import datetime
|
||||
import dateutil.relativedelta
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from fastapi import APIRouter, status, Request, Response
|
||||
|
||||
|
@ -15,7 +22,20 @@ async def get_chart_period(
|
|||
conv_currency: str = None,
|
||||
period: str = None,
|
||||
):
|
||||
"""
|
||||
Fetches a chart for a given currency pair and a specific period.
|
||||
|
||||
The period can be one of the following: 'week', 'month', 'quarter', 'year'.
|
||||
Based on the selected period, it calculates the start date and retrieves the chart data.
|
||||
|
||||
:param response: The response object used to set status and message.
|
||||
:param request: The request object used to retrieve details of the incoming request.
|
||||
:param from_currency: The base currency in the pair (e.g., 'USD').
|
||||
:param conv_currency: The target currency in the pair (e.g., 'EUR').
|
||||
:param period: The time period for which the chart is requested
|
||||
(e.g., 'week', 'month', 'quarter', 'year').
|
||||
:return: A response containing the chart URL or an error message if parameters are invalid.
|
||||
"""
|
||||
if not from_currency or not conv_currency:
|
||||
response.status_code = status.HTTP_400_BAD_REQUEST
|
||||
return {
|
||||
|
@ -39,7 +59,7 @@ async def get_chart_period(
|
|||
years = -1
|
||||
|
||||
end_date = datetime.now()
|
||||
start_date = end_date + dateutil.relativedelta.relativedelta(months=month, days=days, years=years)
|
||||
start_date = end_date + relativedelta(months=month, days=days, years=years)
|
||||
|
||||
chart = await create_chart(
|
||||
from_currency,
|
||||
|
@ -51,7 +71,22 @@ async def get_chart_period(
|
|||
return await prepare_chart_response(response, request, chart)
|
||||
|
||||
|
||||
async def prepare_chart_response(response: Response, request: Request, chart_name: str):
|
||||
async def prepare_chart_response(
|
||||
response: Response,
|
||||
request: Request,
|
||||
chart_name: str
|
||||
):
|
||||
"""
|
||||
Prepares the response to return the URL of the generated chart.
|
||||
|
||||
If the chart data is not found, it returns a 404 error with an appropriate message.
|
||||
Otherwise, it returns a URL to access the chart image.
|
||||
|
||||
:param response: The response object used to set status and message.
|
||||
:param request: The request object used to retrieve details of the incoming request.
|
||||
:param chart_name: The name of the generated chart (used to build the URL).
|
||||
:return: A dictionary with the chart URL or an error message if no chart is found.
|
||||
"""
|
||||
if not chart_name:
|
||||
response.status_code = status.HTTP_404_NOT_FOUND
|
||||
return {'message': 'No data found.', 'status_code': status.HTTP_404_NOT_FOUND}
|
||||
|
|
Loading…
Add table
Reference in a new issue