Skip to content

Commit bf6080d

Browse files
authored
Merge pull request #9 from Redume/feat/support-typst
feat: Support for typst to create a graph
2 parents 7ad75ea + 441cce9 commit bf6080d

File tree

15 files changed

+531
-279
lines changed

15 files changed

+531
-279
lines changed

chart/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
This microservice generates graphs of currency exchange rates using Python and the `matplotlib` library.
1+
This microservice generates graphs of currency exchange rates using Python and the `matplotlib` and `typst` library.
22
It retrieves historical exchange rate data from an internal database and visualizes the data in the form of line charts
33

44

@@ -11,4 +11,5 @@ It retrieves historical exchange rate data from an internal database and visuali
1111
- [encode/uvicorn](https://github.com/encode/uvicorn)[BSD-3-Clause](https://github.com/encode/uvicorn/blob/master/LICENSE.md)
1212
- [fastapi/fastapi](https://github.com/fastapi/fastapi)[MIT](https://github.com/fastapi/fastapi/blob/master/LICENSE)
1313
- [selwin/python-user-agents](https://github.com/selwin/python-user-agents)[MIT](https://github.com/selwin/python-user-agents/blob/master/LICENSE.txt)
14-
- [MagicStack/asyncpg](https://github.com/MagicStack/asyncpg)—- [Apache-2.0](https://github.com/MagicStack/asyncpg/blob/master/LICENSE)
14+
- [MagicStack/asyncpg](https://github.com/MagicStack/asyncpg)[Apache-2.0](https://github.com/MagicStack/asyncpg/blob/master/LICENSE)
15+
- [messense/typst-py](https://github.com/messense/typst-py/)[Apache-2.0](https://github.com/messense/typst-py/blob/main/LICENSE)

chart/chart_backend/matplotlib.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
"""
2+
Module for generating smoothed currency exchange rate charts.
3+
4+
This module provides an asynchronous function to create a visual
5+
representation of historical currency exchange rates. The function
6+
uses spline interpolation to smooth data points and matplotlib
7+
to generate a JPEG chart image returned as a binary stream.
8+
9+
It requires the Currency schema for input parameters and uses
10+
matplotlib and scipy for plotting and interpolation.
11+
"""
12+
13+
from datetime import datetime
14+
from io import BytesIO
15+
from typing import Optional
16+
17+
import numpy as np
18+
from database.server import Database
19+
from matplotlib import pyplot as plt
20+
from schemas.currency import Currency
21+
from scipy.interpolate import make_interp_spline
22+
from utils.config.get_dsn import get_dsn
23+
from utils.config.load_config import load_config
24+
25+
config = load_config("config.hjson")
26+
27+
28+
async def create_chart(
29+
currency: Currency, dates: list, rates: list
30+
) -> Optional[BytesIO]:
31+
"""
32+
Generates a smoothed currency exchange rate chart image
33+
from historical data.
34+
35+
This asynchronous function receives historical exchange rate data
36+
for aspecified currency pair over a given date range.
37+
It applies spline interpolation to smooth the rate curve,
38+
then creates a matplotlib plot
39+
with formatted date labels on the x-axis. The resulting plot is saved
40+
as a JPEG image in a binary stream.
41+
42+
Args:
43+
currency (Currency): An object containing details about
44+
the currency pair and period.
45+
dates (list): A list of datetime objects
46+
representing the dates of the data points.
47+
rates (list): A list of float values representing
48+
exchange rates corresponding to the dates.
49+
50+
Returns:
51+
Optional[BytesIO]: A binary stream containing
52+
the JPEG image of the chart,
53+
or None if the interpolation fails or there is insufficient data.
54+
"""
55+
x_values = np.arange(len(dates))
56+
try:
57+
spline = make_interp_spline(x_values, rates, k=2)
58+
except ValueError:
59+
return None
60+
61+
new_x = np.linspace(0, len(dates) - 1, 200)
62+
new_y = spline(new_x)
63+
64+
fig, ax = plt.subplots(figsize=(15, 6))
65+
66+
ax.set_xticks(np.linspace(0, len(dates) - 1, 10))
67+
current_year = datetime.now().year
68+
ax.set_xticklabels(
69+
[
70+
(
71+
dates[int(i)].strftime("%m-%d")
72+
if dates[int(i)].year == current_year
73+
else dates[int(i)].strftime("%Y-%m-%d")
74+
)
75+
for i in np.linspace(0, len(dates) - 1, 10).astype(int)
76+
],
77+
)
78+
79+
ax.tick_params(axis="both", labelsize=10)
80+
81+
if rates[0] < rates[-1]:
82+
color = "green"
83+
elif rates[0] > rates[-1]:
84+
color = "red"
85+
else:
86+
color = "grey"
87+
88+
ax.plot(new_x, new_y, color=color, linewidth=2)
89+
90+
ax.spines["top"].set_visible(False)
91+
ax.spines["right"].set_visible(False)
92+
93+
buf = BytesIO()
94+
fig.savefig(buf, format="jpeg", bbox_inches="tight")
95+
plt.close(fig)
96+
buf.seek(0)
97+
98+
return buf

chart/chart_backend/typst.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""
2+
Module for generating currency exchange rate charts using Typst.
3+
4+
This module provides an asynchronous function that creates a currency
5+
exchange rate chart based on historical data for a specified currency pair.
6+
It compiles the chart using the Typst template engine and returns the chart
7+
as a PNG image in a binary stream.
8+
9+
The function expects a list of datetime objects for dates and corresponding
10+
exchange rates, along with currency details and display parameters.
11+
"""
12+
13+
import json
14+
from datetime import datetime
15+
from io import BytesIO
16+
from typing import Optional
17+
18+
import typst
19+
from database.server import Database
20+
from schemas.currency import Currency
21+
from utils.config.get_dsn import get_dsn
22+
from utils.config.load_config import load_config
23+
24+
config = load_config("config.hjson")
25+
26+
27+
async def create_chart(
28+
currency: Currency, dates: list, rates: list
29+
) -> Optional[BytesIO]:
30+
"""
31+
Generates a currency exchange rate chart based on historical data.
32+
33+
This function takes historical currency exchange rate data
34+
for a specified period,
35+
prepares the data, and compiles a chart image using Typst.
36+
It returns a binary stream containing the chart image in PNG format.
37+
38+
Args:
39+
currency (Currency):
40+
An object containing currency pair details and settings.
41+
dates (list):
42+
A list of datetime objects representing observation dates.
43+
rates (list):
44+
A list of exchange rate values corresponding to the dates.
45+
46+
Returns:
47+
Optional[BytesIO]: A binary stream
48+
with the generated PNG chart imageб
49+
or None if no data is available
50+
or an error occurs during generation.
51+
"""
52+
dpi = 1
53+
54+
compiler = typst.Compiler(
55+
"./typst_template/simple.typ",
56+
sys_inputs={
57+
"from_currency": currency.from_currency.upper(),
58+
"conv_currency": currency.conv_currency.upper(),
59+
"x": json.dumps([d.timestamp() for d in dates]),
60+
"y": json.dumps(rates),
61+
"dpi": str(dpi),
62+
"width": str(currency.width),
63+
"period": currency.period if currency.period is not None else "",
64+
},
65+
)
66+
67+
return BytesIO(compiler.compile(output=None, ppi=dpi, format="png"))

0 commit comments

Comments
 (0)