From f9fd108d6fa76b6a4edf76eb3bcea0bac5771d0d Mon Sep 17 00:00:00 2001 From: Sn3llius Date: Sun, 14 Apr 2024 10:31:20 +0200 Subject: [PATCH] added crypto dashboard project template --- .../README.md | 0 .../__init__.py | 0 .../assets/cryptos.csv | 181 ++++++++++++++++ .../components/__init__.py | 3 + .../components/balance_card.py | 200 ++++++++++++++++++ .../components/crypto_card.py | 152 +++++++++++++ .../components/crypto_chart.py | 106 ++++++++++ .../data_models.py | 26 +++ .../meta.json | 10 + .../pages/__init__.py | 1 + .../pages/dashboard_page.py | 195 +++++++++++++++++ .../thumbnail.svg | 97 +++++++++ 12 files changed, 971 insertions(+) create mode 100644 rio/snippets/snippet-files/project-template-Crypto Dashboard/README.md create mode 100644 rio/snippets/snippet-files/project-template-Crypto Dashboard/__init__.py create mode 100644 rio/snippets/snippet-files/project-template-Crypto Dashboard/assets/cryptos.csv create mode 100644 rio/snippets/snippet-files/project-template-Crypto Dashboard/components/__init__.py create mode 100644 rio/snippets/snippet-files/project-template-Crypto Dashboard/components/balance_card.py create mode 100644 rio/snippets/snippet-files/project-template-Crypto Dashboard/components/crypto_card.py create mode 100644 rio/snippets/snippet-files/project-template-Crypto Dashboard/components/crypto_chart.py create mode 100644 rio/snippets/snippet-files/project-template-Crypto Dashboard/data_models.py create mode 100644 rio/snippets/snippet-files/project-template-Crypto Dashboard/meta.json create mode 100644 rio/snippets/snippet-files/project-template-Crypto Dashboard/pages/__init__.py create mode 100644 rio/snippets/snippet-files/project-template-Crypto Dashboard/pages/dashboard_page.py create mode 100644 rio/snippets/snippet-files/project-template-Crypto Dashboard/thumbnail.svg diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/README.md b/rio/snippets/snippet-files/project-template-Crypto Dashboard/README.md new file mode 100644 index 00000000..e69de29b diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/__init__.py b/rio/snippets/snippet-files/project-template-Crypto Dashboard/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/assets/cryptos.csv b/rio/snippets/snippet-files/project-template-Crypto Dashboard/assets/cryptos.csv new file mode 100644 index 00000000..6dbc7edc --- /dev/null +++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/assets/cryptos.csv @@ -0,0 +1,181 @@ +date,bitcoin,litecoin,ethereum +2024-03-08 12:00:00,67599.0,87.74,3948.76 +2024-03-08 16:00:00,68263.0,87.6,3938.98 +2024-03-08 20:00:00,68842.0,86.95,3924.4 +2024-03-09 00:00:00,68315.0,88.36,3893.61 +2024-03-09 04:00:00,68412.0,88.97,3936.56 +2024-03-09 08:00:00,68641.0,88.96,3940.13 +2024-03-09 12:00:00,68389.0,88.0,3918.71 +2024-03-09 16:00:00,68436.0,89.93,3923.73 +2024-03-09 20:00:00,68337.0,88.9,3891.06 +2024-03-10 00:00:00,68508.0,90.95,3916.04 +2024-03-10 04:00:00,69159.0,89.48,3941.75 +2024-03-10 08:00:00,69503.0,89.2,3953.62 +2024-03-10 12:00:00,69752.0,88.79,3940.61 +2024-03-10 16:00:00,69366.0,87.78,3899.33 +2024-03-10 20:00:00,69523.0,88.38,3907.77 +2024-03-11 00:00:00,69076.0,87.52,3887.47 +2024-03-11 04:00:00,68570.0,86.75,3862.07 +2024-03-11 08:00:00,71280.0,90.65,4004.27 +2024-03-11 12:00:00,72115.0,94.93,4046.45 +2024-03-11 16:00:00,72226.0,102.02,4030.35 +2024-03-11 20:00:00,72017.0,104.71,4035.06 +2024-03-12 00:00:00,72131.0,103.82,4070.6 +2024-03-12 04:00:00,71593.0,99.12,4036.72 +2024-03-12 08:00:00,72187.0,98.91,4028.14 +2024-03-12 12:00:00,72089.0,97.66,4023.2 +2024-03-12 16:00:00,71862.0,96.77,3992.88 +2024-03-12 20:00:00,71437.0,97.94,3980.26 +2024-03-13 00:00:00,71467.0,97.44,3978.69 +2024-03-13 04:00:00,72050.0,98.17,4042.21 +2024-03-13 08:00:00,72908.0,98.27,4043.54 +2024-03-13 12:00:00,73107.0,97.48,4049.14 +2024-03-13 16:00:00,72569.0,95.74,3958.16 +2024-03-13 20:00:00,73111.0,96.38,3998.11 +2024-03-14 00:00:00,73098.0,97.21,4007.91 +2024-03-14 04:00:00,73286.0,95.97,3997.86 +2024-03-14 08:00:00,73331.0,95.52,3970.7 +2024-03-14 12:00:00,72876.0,95.85,3954.82 +2024-03-14 16:00:00,70916.0,93.94,3854.01 +2024-03-14 20:00:00,69740.0,91.78,3796.04 +2024-03-15 00:00:00,71420.0,94.04,3879.04 +2024-03-15 04:00:00,68664.0,89.91,3732.7 +2024-03-15 08:00:00,68629.0,89.76,3760.8 +2024-03-15 12:00:00,67677.0,87.53,3680.82 +2024-03-15 16:00:00,68239.0,88.42,3693.14 +2024-03-15 20:00:00,68583.0,88.74,3698.82 +2024-03-16 00:00:00,69498.0,89.76,3738.38 +2024-03-16 04:00:00,69291.0,89.51,3737.05 +2024-03-16 08:00:00,69319.0,89.99,3733.34 +2024-03-16 12:00:00,68363.0,89.76,3697.12 +2024-03-16 16:00:00,68440.0,88.16,3679.65 +2024-03-16 20:00:00,67088.0,84.97,3599.44 +2024-03-17 00:00:00,65292.0,83.99,3514.22 +2024-03-17 04:00:00,66510.0,86.17,3571.89 +2024-03-17 08:00:00,65608.0,82.61,3470.38 +2024-03-17 12:00:00,66914.0,85.94,3576.46 +2024-03-17 16:00:00,68035.0,86.48,3635.12 +2024-03-17 20:00:00,68388.0,86.41,3649.0 +2024-03-18 00:00:00,68425.0,86.0,3643.28 +2024-03-18 04:00:00,67990.0,85.31,3602.72 +2024-03-18 08:00:00,68234.0,85.67,3628.4 +2024-03-18 12:00:00,68166.0,84.97,3596.86 +2024-03-18 16:00:00,67379.0,82.97,3524.82 +2024-03-18 20:00:00,66965.0,82.18,3483.63 +2024-03-19 00:00:00,67709.0,87.45,3525.89 +2024-03-19 04:00:00,66034.0,83.51,3424.52 +2024-03-19 08:00:00,64569.0,80.54,3356.92 +2024-03-19 12:00:00,63098.0,79.22,3264.74 +2024-03-19 16:00:00,63858.0,80.88,3302.27 +2024-03-19 20:00:00,64449.0,81.12,3326.83 +2024-03-20 00:00:00,62133.0,78.69,3171.29 +2024-03-20 04:00:00,62290.0,79.4,3197.54 +2024-03-20 08:00:00,62932.0,81.55,3214.48 +2024-03-20 12:00:00,63262.0,81.1,3272.76 +2024-03-20 16:00:00,63682.0,80.73,3312.18 +2024-03-20 20:00:00,65883.0,83.86,3382.99 +2024-03-21 00:00:00,67819.0,84.8,3515.69 +2024-03-21 04:00:00,66484.0,85.01,3499.07 +2024-03-21 08:00:00,66964.0,84.9,3513.59 +2024-03-21 12:00:00,67065.0,85.36,3530.98 +2024-03-21 16:00:00,66494.0,86.19,3496.02 +2024-03-21 20:00:00,65380.0,86.13,3442.28 +2024-03-22 00:00:00,65536.0,85.81,3493.43 +2024-03-22 04:00:00,65942.0,85.58,3512.73 +2024-03-22 08:00:00,66131.0,85.3,3513.87 +2024-03-22 12:00:00,64260.0,82.78,3410.95 +2024-03-22 16:00:00,64084.0,83.02,3347.99 +2024-03-22 20:00:00,64153.0,83.24,3356.17 +2024-03-23 00:00:00,63509.0,83.16,3322.89 +2024-03-23 04:00:00,63516.0,83.98,3301.83 +2024-03-23 08:00:00,64518.0,84.21,3363.15 +2024-03-23 12:00:00,64555.0,86.15,3359.43 +2024-03-23 16:00:00,65080.0,86.46,3400.15 +2024-03-23 20:00:00,64957.0,87.04,3394.56 +2024-03-24 00:00:00,64286.0,85.63,3353.37 +2024-03-24 04:00:00,64098.0,86.91,3318.68 +2024-03-24 08:00:00,64403.0,86.67,3334.59 +2024-03-24 12:00:00,65247.0,87.78,3382.24 +2024-03-24 16:00:00,65627.0,89.54,3398.86 +2024-03-24 20:00:00,65892.0,89.44,3386.51 +2024-03-25 00:00:00,67311.0,89.79,3454.26 +2024-03-25 04:00:00,66984.0,89.79,3442.76 +2024-03-25 08:00:00,66871.0,89.49,3452.12 +2024-03-25 12:00:00,66827.0,88.77,3435.6 +2024-03-25 16:00:00,69919.0,91.47,3588.17 +2024-03-25 20:00:00,70950.0,91.33,3637.92 +2024-03-26 00:00:00,69939.0,90.23,3588.49 +2024-03-26 04:00:00,70497.0,91.01,3634.89 +2024-03-26 08:00:00,70674.0,91.48,3632.06 +2024-03-26 12:00:00,70719.0,91.32,3635.26 +2024-03-26 16:00:00,70240.0,88.76,3592.04 +2024-03-26 20:00:00,69732.0,95.53,3566.54 +2024-03-27 00:00:00,70082.0,95.79,3591.55 +2024-03-27 04:00:00,70407.0,97.13,3607.21 +2024-03-27 08:00:00,69697.0,95.66,3565.21 +2024-03-27 12:00:00,70166.0,97.34,3582.5 +2024-03-27 16:00:00,69014.0,94.04,3519.94 +2024-03-27 20:00:00,68689.0,93.65,3490.66 +2024-03-28 00:00:00,69436.0,93.68,3505.22 +2024-03-28 04:00:00,69192.0,95.37,3486.11 +2024-03-28 08:00:00,70319.0,96.22,3568.91 +2024-03-28 12:00:00,70643.0,94.8,3577.23 +2024-03-28 16:00:00,71434.0,94.99,3587.31 +2024-03-28 20:00:00,70858.0,93.86,3568.63 +2024-03-29 00:00:00,70710.0,94.16,3560.26 +2024-03-29 04:00:00,70429.0,94.64,3559.74 +2024-03-29 08:00:00,69803.0,93.44,3520.88 +2024-03-29 12:00:00,70179.0,102.77,3548.3 +2024-03-29 16:00:00,69279.0,104.78,3497.78 +2024-03-29 20:00:00,69618.0,105.73,3497.88 +2024-03-30 00:00:00,69919.0,109.27,3516.1 +2024-03-30 04:00:00,69865.0,105.25,3492.65 +2024-03-30 08:00:00,69960.0,104.69,3500.01 +2024-03-30 12:00:00,70203.0,103.47,3560.29 +2024-03-30 16:00:00,70043.0,102.35,3539.64 +2024-03-30 20:00:00,69939.0,102.24,3509.25 +2024-03-31 00:00:00,69702.0,102.9,3507.66 +2024-03-31 04:00:00,69939.0,102.83,3536.77 +2024-03-31 08:00:00,70303.0,102.61,3626.24 +2024-03-31 12:00:00,70389.0,102.22,3608.23 +2024-03-31 16:00:00,70364.0,102.81,3619.01 +2024-03-31 20:00:00,71070.0,104.4,3638.99 +2024-04-01 00:00:00,71247.0,105.14,3644.77 +2024-04-01 04:00:00,70631.0,110.3,3611.68 +2024-04-01 08:00:00,69712.0,108.78,3549.56 +2024-04-01 12:00:00,69535.0,104.47,3540.84 +2024-04-01 16:00:00,68524.0,98.75,3477.05 +2024-04-01 20:00:00,69433.0,99.36,3478.01 +2024-04-02 00:00:00,69786.0,99.57,3508.25 +2024-04-02 04:00:00,66811.0,95.75,3368.96 +2024-04-02 08:00:00,66583.0,98.74,3372.58 +2024-04-02 12:00:00,65429.0,102.03,3305.06 +2024-04-02 16:00:00,65095.0,106.09,3255.86 +2024-04-02 20:00:00,66122.0,107.54,3276.87 +2024-04-03 00:00:00,65440.0,106.93,3274.9 +2024-04-03 04:00:00,66252.0,103.1,3322.29 +2024-04-03 08:00:00,66255.0,101.75,3309.57 +2024-04-03 12:00:00,66185.0,100.53,3322.99 +2024-04-03 16:00:00,65925.0,98.79,3328.94 +2024-04-03 20:00:00,65895.0,97.69,3321.85 +2024-04-04 00:00:00,66124.0,98.78,3316.68 +2024-04-04 04:00:00,65660.0,97.86,3279.5 +2024-04-04 08:00:00,66056.0,101.3,3320.72 +2024-04-04 12:00:00,66407.0,100.3,3346.21 +2024-04-04 16:00:00,67805.0,99.65,3365.87 +2024-04-04 20:00:00,68678.0,98.96,3370.51 +2024-04-05 00:00:00,68542.0,97.86,3332.09 +2024-04-05 04:00:00,67833.0,98.38,3307.82 +2024-04-05 08:00:00,66957.0,98.84,3284.9 +2024-04-05 12:00:00,66485.0,96.62,3249.98 +2024-04-05 16:00:00,67989.0,97.78,3322.74 +2024-04-05 20:00:00,67572.0,99.26,3323.64 +2024-04-06 00:00:00,67979.0,98.17,3320.28 +2024-04-06 04:00:00,67758.0,98.3,3333.11 +2024-04-06 08:00:00,68156.0,99.87,3338.67 +2024-04-06 12:00:00,67745.0,102.03,3335.87 +2024-04-06 16:00:00,68158.0,100.67,3340.78 +2024-04-06 20:00:00,68316.0,100.09,3348.47 +2024-04-07 00:00:00,69001.0,101.24,3362.84 +2024-04-07 04:00:00,69449.0,104.42,3391.29 +2024-04-07 08:00:00,69388.0,103.41,3389.81 diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/__init__.py b/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/__init__.py new file mode 100644 index 00000000..8a152923 --- /dev/null +++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/__init__.py @@ -0,0 +1,3 @@ +from .balance_card import BalanceCard as BalanceCard +from .crypto_card import CryptoCard as CryptoCard +from .crypto_chart import CryptoChart as CryptoChart diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/balance_card.py b/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/balance_card.py new file mode 100644 index 00000000..43909524 --- /dev/null +++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/balance_card.py @@ -0,0 +1,200 @@ +from typing import * # type:ignore + +# +import pandas as pd +import plotly.express as px + +import rio + +from .. import data_models + +# + + +# +class BalanceCard(rio.Component): + """ + The MyBalance class is a component of a dashboard application, designed to handle and + display balance-related data. + + The class provides methods to calculate total balance at a given index, calculate the + percentual difference in balance between the last and second last balances, and create + visual sections for the dashboard. + These sections include a balance section displaying the total balance and the percentual + difference in balance, and a bar chart section displaying a bar chart with a given color + and hidden axes. + + The build method combines these sections into a single rio.Card component, creating a + complete balance component for the dashboard. + + Attributes: + data: A pandas DataFrame containing the data for the coins in MY_COINS. + """ + + data: pd.DataFrame + + def total_balance(self, idx: int) -> float: + """ + Calculates the total balance for a given index. + + This function iterates over the coins in MY_COINS, and for each coin, + it multiplies the coin's value by the value at the given index in the + data for that coin. It then adds these products to a total and returns + this total. + + Args: + idx (int): The index at which to calculate the total balance. + + Returns: + float: The total balance at the given index. + """ + + total = 0 + for coin in data_models.MY_COINS: + total += data_models.MY_COINS[coin][0] * self.data[coin].iloc[idx] + return total + + def percentual_differance_balance(self) -> float: + """ + Calculates the percentual difference in balance between the last and + second last balances. + + This function iterates over the coins in MY_COINS, and for each coin, + it multiplies the coin's value by the total balance at the last and + second last indices. It then calculates the percentual difference between + these two totals and returns this value. + + Returns: + float: The percentual difference between the last and second last balances. + """ + + total_last = 0 + total_second_last = 0 + epsilon = 0.0000001 + + for coin in data_models.MY_COINS: + total_last += data_models.MY_COINS[coin][0] * self.total_balance(idx=-1) + total_second_last += data_models.MY_COINS[coin][0] * self.total_balance( + idx=-2 + ) + + # epsilon ensures that the denominator is never zero + return ((total_last - total_second_last) / (total_second_last + epsilon)) * 100 + + def balance_section(self) -> rio.Component: + """ + Creates a balance section for the dashboard. + + This function creates a section that displays the total balance and the + percentual difference in balance. The total balance is displayed in bold, + and the percentual difference is displayed in green if it's positive and + in red if it's negative. The section is returned as a Column component + from the rio library. + + Returns: + rio.Component: A Column component from the rio library, which includes + the total balance, the percentual difference in balance, and some + text and spacing elements. + """ + + return rio.Column( + rio.Text( + "My Balance", + style=rio.TextStyle(font_size=1.2, font_weight="bold"), + align_x=0, + ), + rio.Spacer(height=1), + rio.Text("Total Balance", style="dim", align_x=0), + rio.Row( + rio.Text( + f"{self.total_balance(idx=-1):,.2f} USD", + style=rio.TextStyle(font_size=1.2, font_weight="bold"), + align_x=0, + ), + rio.Text( + f"({self.percentual_differance_balance():.2f} %)", + style=rio.TextStyle( + fill=( + rio.Color.GREEN + if self.percentual_differance_balance() > 0 + else rio.Color.RED + ) + ), + ), + spacing=1, + ), + spacing=1, + align_y=1, + ) + + def bar_chart_section(self, name: str, color: str) -> rio.Component: + """ + Creates a bar chart section for the dashboard. + + This function creates a bar chart with the given color and hiden axes. + The function returns a Column component from the rio library, which includes + the Plot, the name of the section, and the total balance in USD. + + Args: + name (str): The name of the section. + color (str): The color of the bars in the bar chart. + + Returns: + rio.Component: A Column component from the rio library, which includes + the Plot, the name of the section, and the total balance in USD. + """ + + fig = px.bar( + data_models.BAR_CHART, + height=200, + width=200, + color_discrete_sequence=[color], + ) + # hide and lock down axes + fig.update_xaxes(visible=False, fixedrange=True) + fig.update_yaxes(visible=False, fixedrange=True) + # remove facet/subplot labels + fig.update_layout(annotations=[], overwrite=True) + + # strip down the rest of the plot + fig.update_layout( + showlegend=False, + margin=dict(t=10, l=10, b=10, r=10), + ) + + return rio.Column( + rio.Plot( + figure=fig, + height=5, + width=20, + background=self.session.theme.neutral_color, + ), + rio.Text(name, style="dim", align_x=0), + rio.Text( + f"{self.total_balance(idx=-1):,.2f} USD", + style=rio.TextStyle(font_size=1.2, font_weight="bold"), + justify="left", + ), + spacing=1, + align_y=1, + ) + + def build(self) -> rio.Component: + return rio.Card( + rio.Row( + # 1. Section + self.balance_section(), + rio.Separator(), + # 2. Section + self.bar_chart_section(name="Income", color="green"), + rio.Separator(), + self.bar_chart_section(name="Expenses", color="red"), + margin=1, + spacing=4, + align_x=0.5, + ), + height=13, + ) + + +# diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/crypto_card.py b/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/crypto_card.py new file mode 100644 index 00000000..7f652f3d --- /dev/null +++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/crypto_card.py @@ -0,0 +1,152 @@ +from typing import * # type:ignore + +# +import pandas as pd +import plotly.express as px + +import rio + +# + + +# +class CryptoCard(rio.Component): + """ + The CryptoCard class is a component of a dashboard application, designed to handle + and display cryptocurrency-related data. It uses the rio library to create + interactive dashboard components and pandas DataFrame to store cryptocurrency data. + + The build method creates a rio.Card component that displays a line plot of the last + 50 data points of the cryptocurrency, the cryptocurrency's logo, the name and ticker + symbol of the cryptocurrency, the amount of the cryptocurrency, and the amount of + the cryptocurrency in USD. The layout of the card is a grid with 4 rows and 2 columns. + + If there is no data available for the cryptocurrency, a message is printed to the console. + + + Attributes: + data: A pandas DataFrame that holds the cryptocurrency data. + coin: A string representing the name of the cryptocurrency. + coin_amount: A float representing the amount of the cryptocurrency. + coin_ticker: A string representing the ticker symbol of the cryptocurrency. + logo_url: A string representing the URL of the cryptocurrency's logo. + """ + + data: pd.DataFrame + coin: str + coin_amount: float + coin_ticker: str + color: str + logo_url: str + + def build(self) -> rio.Component: + fig = px.line( + self.data[self.coin].iloc[-50:], + color_discrete_sequence=[self.color], + height=200, + width=200, + ) + + # hide and lock down axes + fig.update_xaxes(visible=False, fixedrange=True) + fig.update_yaxes(visible=False, fixedrange=True) + + # remove facet/subplot labels + fig.update_layout(annotations=[], overwrite=True) + + # strip down the rest of the plot + fig.update_layout( + showlegend=False, + margin=dict(t=10, l=10, b=10, r=10), + ) + + grid = rio.Grid( + column_spacing=0.5, + row_spacing=1, + margin=2, + ) + + # Create a grid layout for the card + # The grid will have 4 rows and 2 columns + # Because the width of the plot is bigger, the second column will be wider + # like shown below: + + ############################################ + # Icon | Plot # + # Icon | Plot # + # Coin Ticker | Coin Amount # + # Coin Name | Coin Amount in USD # + ############################################ + + # Image with grid height 2 + grid.add( + rio.Image( + rio.URL( + self.logo_url + ), # logo_url = e.g. "https://cryptologos.cc/logos/bitcoin-btc-logo.svg?v=029" + height=2, + width=2, + align_y=0.5, + ), + row=0, + column=0, + ) + + # Plot with grid height 2 + grid.add( + rio.Plot( + figure=fig, + corner_radius=0, + height=4, + background=self.session.theme.neutral_color, + ), + row=0, + column=1, + height=2, + ) + + # Text with coin name and grid height 1 + grid.add( + rio.Text( + self.coin.capitalize(), + style=rio.TextStyle(font_size=1.2, font_weight="bold"), + align_x=0, + ), + row=2, + column=0, + ) + + # Text with coin amount and grid height 1 + grid.add( + rio.Text( + f"{self.coin_amount:.6f} {self.coin_ticker}", + align_x=0, + style=rio.TextStyle(font_size=1.2, font_weight="bold"), + ), + row=2, + column=1, + ) + + # Text with coin ticker and grid height 1 + grid.add( + rio.Row( + rio.Text( + self.coin_ticker, + ), + rio.Text(" / USD", style="dim"), + align_x=0, + ), + row=3, + column=0, + ) + # Text with coin amount in USD and grid height 1 + usd_amount = self.coin_amount * self.data[self.coin].iloc[-1] + grid.add(rio.Text(f"{usd_amount:,.2f} USD", align_x=0), row=3, column=1) + + return rio.Card( + grid, + height=13, + ) + + +# diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/crypto_chart.py b/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/crypto_chart.py new file mode 100644 index 00000000..f33aa446 --- /dev/null +++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/crypto_chart.py @@ -0,0 +1,106 @@ +from typing import * # type:ignore + +# +import pandas as pd +import plotly.express as px + +import rio + +from .. import data_models + +# + + +# +class CryptoChart(rio.Component): + data: pd.DataFrame + coin: str + logo_url: str = data_models.MY_COINS["bitcoin"][3] + color: str = data_models.MY_COINS["bitcoin"][2] + + def on_change_coin(self, ev: rio.DropdownChangeEvent) -> None: + """ + Handles the event of changing the selected coin. + + This function updates the coin, color and logo_url attributes based on the selected coin. + + Parameters: + ev (rio.DropdownChangeEvent): The event object containing the selected coin value. + """ + self.coin = ev.value + self.color = data_models.MY_COINS[self.coin][2] + self.logo_url = data_models.MY_COINS[self.coin][3] + + def build(self) -> rio.Component: + """ + Creates a Card component with the selected coin's line plot, logo, name, and dropdown. + + This function creates a line plot of the last 50 data points of the selected coin, + using the plotly express library. The plot is displayed in a Plot component from the + rio library. The Card component includes the coin's logo, name, and dropdown, as well + as the line plot. + + Returns: + rio.Card: A Card component with the selected coin's line plot, logo, name, and dropdown. + See the layout below: + + ############################################ + # Logo | Coin Name | Dropdown # + # Plot # + ############################################ + """ + + fig = px.line( + self.data[self.coin].iloc[-50:], + color_discrete_sequence=[self.color], + height=200, + width=200, + ) + + # Set x-axis labels to horizontal and remove labels + fig.update_xaxes(tickangle=0, title_text="") + fig.update_yaxes(title_text="") + + # strip down the rest of the plot + fig.update_layout( + showlegend=False, + margin=dict(t=10, l=10, b=10, r=10), + ) + + return rio.Card( + rio.Column( + rio.Row( + rio.Image( + rio.URL(self.logo_url), + height=2, + width=2, + ), + rio.Text( + self.coin.capitalize(), + style=rio.TextStyle(font_size=1.2, font_weight="bold"), + ), + rio.Dropdown( + options={ + value[1]: key for key, value in data_models.MY_COINS.items() + }, + on_change=self.on_change_coin, + ), + spacing=1, + align_x=0.1, + ), + rio.Plot( + figure=fig, + corner_radius=0, + height=20, + width=10, + background=self.session.theme.neutral_color, + # margin=1, + ), + spacing=1, + align_y=0.5, + margin=2, + ), + ) + + +# diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/data_models.py b/rio/snippets/snippet-files/project-template-Crypto Dashboard/data_models.py new file mode 100644 index 00000000..dd824dfe --- /dev/null +++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/data_models.py @@ -0,0 +1,26 @@ +from typing import * # type:ignore + +import pandas as pd + +BAR_CHART: pd.DataFrame = pd.DataFrame([1, 2, 3, 4, 5, 6, 7], columns=["data"]) + +MY_COINS: dict[str, Tuple[float, str, str, str]] = { + "bitcoin": ( + 13.344546, + "BTC", + "#f7931a", + "https://cryptologos.cc/logos/bitcoin-btc-logo.svg?v=029", + ), + "litecoin": ( + 40.21321, + "LTC", + "#365d99", + "https://cryptologos.cc/logos/litecoin-ltc-logo.svg?v=029", + ), + "ethereum": ( + 4.239234, + "ETH", + "#14044d", + "https://cryptologos.cc/logos/ethereum-eth-logo.svg?v=029", + ), +} diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/meta.json b/rio/snippets/snippet-files/project-template-Crypto Dashboard/meta.json new file mode 100644 index 00000000..2435300b --- /dev/null +++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/meta.json @@ -0,0 +1,10 @@ +{ + "level": "intermediate", + "summary": "A simple Crypto Dashboard", + "dependencies": { + "numpy": ">=1.26.4", + "plotly": ">=5.20.0", + "pycoingecko": ">=3.1.0", + "pandas": ">=2.2.1" + } +} diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/pages/__init__.py b/rio/snippets/snippet-files/project-template-Crypto Dashboard/pages/__init__.py new file mode 100644 index 00000000..8adf1690 --- /dev/null +++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/pages/__init__.py @@ -0,0 +1 @@ +from .dashboard_page import DashboardPage as DashboardPage diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/pages/dashboard_page.py b/rio/snippets/snippet-files/project-template-Crypto Dashboard/pages/dashboard_page.py new file mode 100644 index 00000000..a06e37c8 --- /dev/null +++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/pages/dashboard_page.py @@ -0,0 +1,195 @@ +import dataclasses +from pathlib import Path +from typing import * # type:ignore + +import numpy as np + +# +import pandas as pd +from pycoingecko import CoinGeckoAPI + +import rio + +from .. import components as comps +from .. import data_models + +# + + +DIR = Path(__file__).parent.parent +ASSET_DIR = DIR / "assets" + +CRYPTO_LIST: list[str] = ["bitcoin", "litecoin", "ethereum"] + + +FETCH_DATA_FROM_API = True # Set to False to use local data + + +# +class DashboardPage(rio.Component): + """ + The DashboardPage class is a component of a dashboard application, designed to display + a user's cryptocurrency balance and the historical data of three cryptocurrencies. + It uses the rio library to create interactive dashboard components and pandas DataFrame + to store cryptocurrency data. + + The class contains a coin_data attribute that stores the historical data of three + cryptocurrencies: Bitcoin, Litecoin, and Ethereum. The data is fetched from the CoinGecko + API using the fetch_coin_data method. The class also contains an on_populate method that + fetches the data when the component is populated. + + The build method creates a grid layout for the dashboard, containing a balance component, + three cryptocurrency components, and a cryptocurrency chart component. The layout is + structured as follows: + + ################################################### + # BalanceCard | CryptoCard(BTC) # + # Crypto Chart | CryptoCard(LTC) # + # (Crypto Chart) | CryptoCard(ETH) # + ################################################### + + Attributes: + coin_data: A pandas DataFrame that holds the historical data of three cryptocurrencies: + Bitcoin, Litecoin, and Ethereum. + """ + + coin_data: pd.DataFrame = dataclasses.field( + default=pd.DataFrame( + { + "date": np.zeros(5), + **{coin: np.zeros(5) for coin in CRYPTO_LIST}, + } + ) + ) + + @rio.event.on_populate + async def on_populate(self) -> None: + if FETCH_DATA_FROM_API is True: + self.coin_data = self._fetch_coin_data(CRYPTO_LIST) + else: + self.coin_data = self._read_csv(ASSET_DIR / "cryptos.csv") + + def _read_csv(self, path: Path) -> pd.DataFrame: + """ + Reads csv file and returns a pandas DataFrame. + + Args: + path: A string representing the path to the csv file. + + Returns: + pd.DataFrame: A pandas DataFrame containing the data from the csv file. + """ + df = pd.read_csv(path) + df["date"] = pd.to_datetime(df["date"]) + df.set_index("date", inplace=True) + return df + + def _fetch_coin_data( + self, coin_names: list[str], vs_currency: str = "usd", days: str = "30" + ) -> pd.DataFrame: + """ + This method fetches historical data for a list of cryptocurrencies from the + CoinGecko API and returns it as a pandas DataFrame. + + The method creates an instance of the CoinGeckoAPI and iterates over the list + of coin_names. For each coin, it fetches the OHLC (Open, High, Low, Close) data, + converts it into a DataFrame, and processes it by converting the date to a + datetime object, setting the date as the index, and dropping the "open", "high", + and "low" columns. Each processed DataFrame is appended to a list. + + Finally, the method concatenates all the DataFrames in the list into a single + DataFrame along the columns axis, sets the column names to the coin_names, + and returns the merged DataFrame. + + + Args: + coin_names: A list of strings representing the names of the + cryptocurrencies to fetch data for. + vs_currency: A string representing the currency to compare + against. Defaults to "usd". + days: A string representing the number of past days to fetch + data for. Defaults to "30". + + Returns: + pd.DataFrame: A pandas DataFrame containing the historical (OHL)C data for the + specified cryptocurrencies. + """ + + df_list = [] + cg = CoinGeckoAPI() + + for i in range(len(coin_names)): + ohlc = cg.get_coin_ohlc_by_id( + id=coin_names[i], vs_currency=vs_currency, days=days + ) + df = pd.DataFrame(ohlc, columns=["date", "open", "high", "low", "close"]) + df["date"] = pd.to_datetime(df["date"], unit="ms") + df.set_index("date", inplace=True) + df = df.drop(columns=["open", "high", "low"]) + df_list.append(df) + + merged_df = pd.concat(df_list, axis=1) + merged_df.columns = coin_names + + return merged_df + + def build(self) -> rio.Component: + """ + Creates a grid layout for the dashboard. + + This function creates a grid layout for the dashboard, using the rio library. + The grid contains a balance component, three cryptocurrency components, and + a cryptocurrency chart component. The components are added to the grid with + specific row and column positions, widths, and heights. + + Returns: + rio.Grid: A grid layout for the dashboard, containing the balance, cryptocurrency, + and cryptocurrency chart components. See the layout below: + + + ################################################### + # BalanceCard | CryptoCard(BTC) # + # Crypto Chart | CryptoCard(LTC) # + # (Crypto Chart) | CryptoCard(ETH) # + ################################################### + """ + + grid = rio.Grid( + column_spacing=2, + row_spacing=2, + align_y=0.5, + margin_left=20, + margin_right=5, + ) + + grid.add(comps.BalanceCard(data=self.coin_data), row=0, column=0, width=4) + + # use loop to add cards + + for i, coin in enumerate(CRYPTO_LIST): + grid.add( + comps.CryptoCard( + data=self.coin_data, + coin=coin, + coin_amount=data_models.MY_COINS[coin][0], + coin_ticker=data_models.MY_COINS[coin][1], + color=data_models.MY_COINS[coin][2], + logo_url=data_models.MY_COINS[coin][3], + ), + row=i, + column=4, + width=2, + ) + + grid.add( + comps.CryptoChart(data=self.coin_data, coin="bitcoin"), + row=1, + column=0, + width=4, + height=2, + ) + + return grid + + +# diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/thumbnail.svg b/rio/snippets/snippet-files/project-template-Crypto Dashboard/thumbnail.svg new file mode 100644 index 00000000..48a4248e --- /dev/null +++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/thumbnail.svg @@ -0,0 +1,97 @@ + + + + + + + + + + image/svg+xml + + + + + + + TODO: Thumbnail + SimpleDashboard + +