diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7cb6c89..c60d464 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,9 @@ # This workflow ensures that the code is tested and meets quality standards on every push and pull request name: Tests +permissions: + contents: read + on: push: pull_request: diff --git a/README.md b/README.md index 2914fa1..44d51a9 100644 --- a/README.md +++ b/README.md @@ -336,14 +336,15 @@ Current documentation: ## Status -ARGUS is under active development. +ARGUS has completed its first foundation phase. -The project is currently transitioning from a small FX converter into a broader market analytics platform. +The project now has a runnable local Python application, a Tkinter GUI prototype, basic analytics, tests, documentation, CI checks and open-source readiness files. Current focus: -- finish Sprint 1 foundation -- prepare first public release -- improve README and project documentation -- keep the application runnable and testable -- prepare the next analytics and data-source expansion +- start Sprint 2 — Market Analytics & Data Source Expansion +- improve historical exchange-rate data support +- add stronger market metrics +- expand pandas-based analytics workflows +- improve dashboard usefulness without adding unnecessary chart noise +- document metric definitions, assumptions and data-source behavior diff --git a/docs/research-data-sources b/docs/research-data-sources new file mode 100644 index 0000000..3298d76 --- /dev/null +++ b/docs/research-data-sources @@ -0,0 +1,419 @@ +# ARGUS Data Source Research + +## Goal + +Research and choose a real data source for ARGUS analytics. + +ARGUS should evolve from a simple FX converter into a broader market analysis tool. + +The first real data source should replace the mock time-series client and provide useful historical data for beginner-friendly analytics. + +The selected source should be: + +* free or usable without relevant cost +* easy to integrate in Python +* pandas-friendly +* useful for historical analysis +* suitable for future market analytics +* not limited to pure experimentation if the project becomes more serious later + +--- + +## Why + +ARGUS currently uses mock time-series data for early analytics development. + +Sprint 2 needs a real data client so market metrics can be calculated from real data. + +The existing ExchangeRate API client was useful for the first live currency conversion feature, but ARGUS now needs stronger historical data support. + +The project should not become a pure FX trading tool. + +FX data is a useful entry point because it is simpler than stock analysis: + +* exchange-rate data is easier to model +* fewer external fundamentals are needed +* one clean time series is enough for first analytics +* beginner-friendly metrics can be implemented quickly + +However, the long-term direction is broader market analysis: + +* stocks +* ETFs +* indices +* currencies +* simple trading-oriented insights +* paper trading +* later possible commercial usage if data licensing allows it + +--- + +## Compared Data Sources + +| Source | Best Use Case | Free / Cost | API Key | Python / pandas Fit | Commercial Suitability | Fit for ARGUS | +|---|---|---:|---:|---|---|---| +| Frankfurter | Historical FX data | Free | No | Good | Better than unofficial sources, open-source/self-hostable | Very good first client | +| yfinance | Broad market data for research | Free | No | Excellent | Limited / check Yahoo terms | Very good later client | +| ExchangeRate API | Live currency conversion | Free tier / paid tiers | Yes / depends on endpoint | Okay | Depends on plan and terms | Legacy / live conversion only | +| FRED API | Macroeconomic context data | Free | Yes | Good | Possible, but series-specific rights must be checked | Good later macro context client | +| Alpha Vantage | Stocks, FX, crypto, indicators | Free tier + paid | Yes | Good | More API-like, paid plans available | Possible later | +| Twelve Data | Stocks, FX, ETFs, crypto, indices | Free tier + paid | Yes | Good | More suitable for serious API usage | Possible later | + +--- + +## Current ExchangeRate API + +### What it is + +The current ExchangeRate API client is useful for live currency conversion. + +It fits the original project stage: + +* enter an amount +* select source currency +* select target currency +* receive converted result + +### Strengths + +* Already integrated +* Simple for live conversion +* Good for the original FX converter use case +* Easy to understand + +### Limitations + +* Not ideal as the main historical analytics source +* Historical data support is not the strongest free path for this project +* Less useful once ARGUS moves toward market analytics +* Adds less long-term value if Frankfurter covers FX better + +### ARGUS Fit + +The ExchangeRate API should not be the strategic core client anymore. + +It can remain temporarily as: + +* legacy live conversion client +* fallback client +* comparison point during migration + +Long term, it can probably be removed if Frankfurter covers all required FX use cases. + +--- + +## Frankfurter + +### What it is + +Frankfurter is a free and open-source exchange-rate API. + +It provides current and historical exchange-rate data and does not require an API key. + +### Strengths + +* Free +* No API key required +* Simple REST API +* Historical FX data +* Time-series data +* JSON response format +* Easy to transform into pandas DataFrames +* Good fit for beginner-friendly analytics +* Can replace mock FX time-series data directly +* Can also replace parts of the current ExchangeRate API later + +### Limitations + +* Focused on exchange rates +* Not a broad market-data API +* Does not cover stocks, ETFs or company fundamentals +* Not enough for long-term stock or portfolio analysis alone + +### ARGUS Fit + +Frankfurter is the best first real data client for Sprint 2. + +It solves the immediate problem: + +* replace mock time-series data +* fetch real historical exchange-rate data +* calculate the first real analytics metrics +* keep the implementation beginner-friendly +* avoid unnecessary complexity from stock-market data too early + +FX data is a useful entry point because it is simpler than stock analysis. + +However, ARGUS should not stay limited to FX trading. + +The long-term direction is broader market analysis for normal users: + +* stocks +* ETFs +* indices +* currencies +* watchlists +* basic market insights +* paper trading +* later possible commercial usage if data licensing allows it + +--- + +## yfinance + +### What it is + +yfinance is a Python library for downloading market data from Yahoo Finance. + +It is useful for stocks, ETFs, indices and some FX symbols. + +### Strengths + +* Very easy to use in Python +* Excellent pandas compatibility +* Supports broader market data than Frankfurter +* Good for stocks, ETFs and indices +* Useful for future market analysis +* Useful for later signal generation and ML experiments + +### Limitations + +* Not an official Yahoo Finance API +* Intended mainly for research and educational use +* Commercial usage requires checking Yahoo terms +* Data availability depends on Yahoo symbols +* Less predictable than a dedicated paid market-data API +* Not ideal as the first serious commercial data backbone + +### ARGUS Fit + +yfinance is a very good later client. + +It should be introduced when ARGUS moves from FX-based analytics toward broader market analysis. + +It is especially useful for: + +* stocks +* ETFs +* indices +* market comparisons +* watchlists +* signal experiments +* ML features +* paper trading prototypes + +However, yfinance should not be the first mock-client replacement if the immediate Sprint 2 goal is clean historical FX analytics. + +--- + +## FRED API + +### What it is + +FRED is the Federal Reserve Economic Data API from the Federal Reserve Bank of St. Louis. + +It provides access to economic and macroeconomic time series such as: + +* interest rates +* inflation indicators +* unemployment data +* GDP-related data +* yield curves +* recession indicators +* money supply +* economic sentiment and macro context data + +FRED is not mainly a market-price API. + +It should not replace Frankfurter or yfinance as the main historical price-data client. + +Instead, FRED is useful as a macroeconomic context source for ARGUS. + +### Strengths + +* Very relevant for macroeconomic analysis +* Strong fit for market context +* Useful for explaining why markets may move +* Provides historical economic time series +* Good for dashboards, reports and later signal context +* Can support more serious market analysis than pure price charts + +### Limitations + +* Requires an API key +* Not a stock, ETF or FX price-data provider +* Some series may be owned by third parties +* Copyrighted series may require additional permission for non-personal or commercial use +* Commercial usage needs careful checking per data series +* Not ideal as the first replacement for the mock time-series client + +### ARGUS Fit + +FRED is a strong future addition for ARGUS. + +It becomes valuable when ARGUS starts connecting market movements with economic conditions. + +FRED can help ARGUS answer questions like: + +* Are interest rates rising or falling? +* Is inflation cooling down? +* Is the yield curve inverted? +* Is the macro environment risky? +* Could market weakness be connected to macro conditions? + +This makes FRED useful for analysis and reporting, but not as the first Sprint 2 data client. + +--- + +## Alpha Vantage + +### What it is + +Alpha Vantage is a financial data API for stocks, FX, crypto, commodities, indicators and more. + +### Strengths + +* Official API-style access +* Supports many market data types +* Has documentation for many endpoints +* More suitable than yfinance if the project later needs a more formal API provider +* Could support more serious long-term data workflows + +### Limitations + +* Requires API key +* Free tier is limited +* Request limits can become annoying for analytics workflows +* More setup than Frankfurter or yfinance +* Some advanced or real-time features require paid plans + +### ARGUS Fit + +Alpha Vantage is not recommended as the first Sprint 2 client. + +It is worth revisiting later if ARGUS needs: + +* more formal market-data API access +* indicators +* broader asset coverage +* a clearer path toward commercial or semi-commercial use + +--- + +## Twelve Data + +### What it is + +Twelve Data is a financial market data API covering stocks, forex, ETFs, indices, crypto and more. + +### Strengths + +* Broad market coverage +* API-first design +* Supports JSON and other formats +* Has SDK support +* Better long-term candidate than unofficial data sources +* More suitable for serious market-data workflows + +### Limitations + +* Requires API key +* Free tier limitations apply +* More platform dependency +* More setup than Frankfurter +* Probably too much for the first Sprint 2 step + +### ARGUS Fit + +Twelve Data is interesting for later. + +It may become relevant when ARGUS moves toward: + +* broader market analysis +* paper trading +* more reliable stock/ETF data +* possible commercial use +* serious dashboard or monitoring workflows + +It should not be the first implementation choice because Frankfurter is simpler for the current phase. + +--- + +## Commercial Usage Consideration + +ARGUS should avoid locking itself into data sources that are only useful for experiments if the project may later become more serious. + +Current interpretation: + +| Source | Commercial Direction | +|---|---| +| Frankfurter | Best first choice; open-source/self-hostable, but source and data terms should still be checked before commercial use | +| yfinance | Great for learning and research, but not ideal for commercial use without checking Yahoo terms | +| ExchangeRate API | Commercial use depends on API plan and terms | +| FRED API | Useful for macro context, but series-specific rights must be checked | +| Alpha Vantage | More realistic commercial path through paid plans | +| Twelve Data | More realistic commercial path through paid plans | + +--- + +## Final Portfolio Decision + +Frankfurter should still be selected as the first real data source for Sprint 2. + +Reason: + +* It directly replaces the mock time-series client. +* It provides clean historical exchange-rate data. +* It keeps the first analytics implementation manageable. +* It is simple, free and beginner-friendly. + +yfinance should be added later when ARGUS moves toward broader market analysis: + +* stocks +* ETFs +* indices +* watchlists +* simple signals +* paper trading prototypes + +FRED should be added as a macro context client, not as the first historical price-data client. + +It becomes valuable when ARGUS starts connecting market movements with economic conditions. + +Alpha Vantage or Twelve Data should be evaluated later if ARGUS needs a more serious or commercial-friendly market-data provider. + +```text +Sprint 2: +Frankfurter +→ real historical FX data +→ replaces mock time-series data + +Later: +yfinance +→ broader market data +→ stocks, ETFs, indices, paper trading prototypes + +FRED +→ macroeconomic context +→ inflation, rates, unemployment, GDP, yield curve + +Future: +Alpha Vantage or Twelve Data +→ more serious market-data provider +→ better candidate for commercial or production-like use +``` + +--- + +## Out of Scope for This Ticket + +This research focuses on data sources for analytics. + +The following topics should be handled in later research tickets: + +| Topic | Reason | +|---|---| +| Alpaca | Broker, paper trading and execution API; relevant later, but not first data-source replacement | +| CCXT exchanges | Crypto exchange connector layer; useful later for crypto workflows | +| Broker integrations | Belong to paper trading / execution research | + diff --git a/docs/roadmap.md b/docs/roadmap.md index 4df584c..85706af 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -5,7 +5,7 @@ The roadmap is intentionally iterative: each sprint should leave the project in ## Sprint 1 — Product Foundation & First Public Release -**Status:** In progress +**Status:** Completed Build a small but usable Python application with a clear structure, tests, documentation and first release readiness. @@ -25,11 +25,11 @@ Scope: - Collaboration documentation through `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md` and `LICENSE` Outcome: -ARGUS can be run locally, tested, understood by other developers and used as a small desktop analytics prototype. +Sprint 1 established the local ARGUS foundation with package structure, GUI prototype, analytics prototype, tests, documentation, CI, Dependabot and governance files. ### Sprint 2 — Market Analytics & Data Source Expansion -**Status:** Planned +**Status:** In progress Move from simple FX conversion toward broader market analytics. diff --git a/src/argus/analytics/charts/trend_chart.py b/src/argus/analytics/charts/trend_chart.py index 57c5193..d475bc7 100644 --- a/src/argus/analytics/charts/trend_chart.py +++ b/src/argus/analytics/charts/trend_chart.py @@ -4,6 +4,33 @@ def create_trendchart(curr: str, dates: pd.DataFrame): + """ + Create a trend chart for exchange-rate analysis. + + Builds a Matplotlib figure showing the exchange rate, its rolling + average, and the daily percentage change for a selected currency. + The minimum and maximum exchange-rate values are highlighted in the + chart. + + Args: + curr (str): Currency code or currency pair identifier used for + the trend analysis. + dates (pd.DataFrame): DataFrame containing the date information + used to prepare the time-series analysis. + + Returns: + matplotlib.figure.Figure: Matplotlib figure containing the trend + chart. + + Notes: + The chart uses two y-axes: + + - The left y-axis displays the exchange rate and rolling average. + - The right y-axis displays the daily percentage change. + + Minimum and maximum exchange-rate values are marked with scatter + points and annotations. + """ df = pd.DataFrame() df, min_max_rates = prepare_trend_analysis(curr, dates) min_date = min_max_rates["min_date"][0] diff --git a/src/argus/analytics/metrics/trend_metrics.py b/src/argus/analytics/metrics/trend_metrics.py index 9493ff3..5622acb 100644 --- a/src/argus/analytics/metrics/trend_metrics.py +++ b/src/argus/analytics/metrics/trend_metrics.py @@ -2,18 +2,69 @@ def add_daily_percentage_change(df: pd.DataFrame) -> pd.DataFrame: + """ + Add the daily percentage change of the exchange rate. + + Calculates the percentage change between each rate value and the + previous rate value. The result is added as a new column named + ``daily_pct_change``. + + Args: + df (pd.DataFrame): DataFrame containing at least a ``rate`` column. + + Returns: + pd.DataFrame: A copy of the input DataFrame with an added + ``daily_pct_change`` column. + + Notes: + The first row will contain ``NaN`` because there is no previous + rate value to compare against. + """ result = df.copy() result["daily_pct_change"] = result["rate"].pct_change() * 100 return result def add_rolling_average(df: pd.DataFrame) -> pd.DataFrame: + """ + Add a rolling average of the exchange rate. + + Calculates a rolling mean over the ``rate`` column using a fixed + window size of 3 rows. The result is added as a new column named + ``roll_avg``. + + Args: + df (pd.DataFrame): DataFrame containing at least a ``rate`` column. + + Returns: + pd.DataFrame: A copy of the input DataFrame with an added + ``roll_avg`` column. + """ result = df.copy() result["roll_avg"] = result["rate"].rolling(window=3, min_periods=1).mean() return result def get_min_max_rates(df: pd.DataFrame) -> dict: + """ + Get the minimum and maximum exchange-rate values. + + Finds the rows with the lowest and highest values in the ``rate`` + column and returns their dates and rates in a dictionary. + + Args: + df (pd.DataFrame): DataFrame containing at least ``date`` and + ``rate`` columns. + + Returns: + dict: Dictionary containing the minimum and maximum rate data with + the following keys: + + - ``min_date``: Date of the lowest exchange rate. + - ``min_rate``: Lowest exchange-rate value. + - ``max_date``: Date of the highest exchange rate. + - ``max_rate``: Highest exchange-rate value. + """ min_max = {"min_date": [], "min_rate": [], "max_date": [], "max_rate": []} min_id = df["rate"].idxmin() max_id = df["rate"].idxmax() diff --git a/src/argus/clients/exchangerate_client.py b/src/argus/clients/exchangerate_client.py index 71e9207..8ea5e89 100644 --- a/src/argus/clients/exchangerate_client.py +++ b/src/argus/clients/exchangerate_client.py @@ -6,7 +6,16 @@ ) -def get_rates(curr1, curr2): +def get_rates(curr1: str, curr2: str): + """ + Get the exchange rate between two currencies using the ExchangeRate-API. + + Args: + curr1 (str): The base currency code (e.g., "USD"). + curr2 (str): The target currency code (e.g., "EUR"). + + Returns: A dictionary containing the result status, error type (if any), and conversion rate (if successful). + """ url = f"{EXCHANGE_RATE_BASE_URL}/{EXCHANGE_RATE_API_KEY}/pair/{curr1}/{curr2}" data = {"result": "", "error_type": "", "conversion_rate": None} @@ -43,23 +52,24 @@ def get_rates(curr1, curr2): return None -def check_error(err_type): +def check_error(err_type: str) -> None: + """ + Check the error type returned by the API and print an appropriate message. + + Args: err_type (str): The error type returned by the API. + + Returns: None + """ match err_type: case "unsupported-code" | "malformed-request": - print("Ungültige Anfrage! Bitter versuchen Sie es später erneut.") + print("Invalid request! Please try again later.") case "invalid-key": - print( - "Ungültiger API-Key! Checken Sie Ihren API-Key und versuchen Sie es erneut." - ) + print("Invalid API key! Please check your API key and try again.") case "inactive-account": print( - "Inaktives Konto! Bitte auf exchangerate-api.com gehen und Konto aktivieren." + "Inactive account! Please go to exchangerate-api.com and activate your account." ) case "quota-reached": print( - "Anfrage-Limit erreicht! Bitte später erneut versuchen oder auf exchangerate-api.com upgraden." + "Request limit reached! Please try again later or upgrade to exchangerate-api.com." ) - - -# Testen, ob die API funktioniert -# data = get_rates("EUR", "USD") diff --git a/src/argus/domain/validation.py b/src/argus/domain/validation.py index 8e08d16..16879a7 100644 --- a/src/argus/domain/validation.py +++ b/src/argus/domain/validation.py @@ -167,10 +167,24 @@ def normalize_input_string(input: str) -> str: + """ + Normalizes the input string by stripping leading and trailing whitespace and converting it to uppercase. + + Arg1: input: str - the input string to be normalized + + Return: str - the normalized input string + """ return input.strip().upper() def parse_amount(value: str) -> float | None: + """ + Parses the input string to a float. If the input is not a valid number, it returns None. + + Arg1: value: str - the input string to be parsed as a float + + Return: float or None - the parsed float value if valid, otherwise None + """ try: return float(value) except ValueError: @@ -178,8 +192,22 @@ def parse_amount(value: str) -> float | None: def is_valid_curr_code(code: str) -> bool: + """ + Checks if the given currency code is valid. + + Arg1: code: str - the currency code to be checked + + Return: bool - True if the currency code is valid, otherwise False + """ return code in VALID_CURRENCY_CODES def is_valid_op(op: str) -> bool: + """ + Checks if the given operation is valid. + + Arg1: op: str - the operation to be checked + + Return: bool - True if the operation is valid, otherwise False + """ return op in VALID_OPS diff --git a/src/argus/gui/app.py b/src/argus/gui/app.py index 141fec5..829a905 100644 --- a/src/argus/gui/app.py +++ b/src/argus/gui/app.py @@ -8,6 +8,10 @@ def on_close() -> None: + """ + Handles the closing of the application window. It ensures that any open trend chart is destroyed and + the application is properly closed. + """ if trend_chart_widget is not None: trend_chart_widget.destroy() @@ -16,11 +20,17 @@ def on_close() -> None: def hide_trend_chart() -> None: + """ + Hides the trend chart from the GUI if it is currently displayed. + """ if trend_chart_widget is not None: trend_chart_widget.pack_forget() def show_menu() -> None: + """ + Displays the main menu in the application. It updates the GUI to show the menu interface. + """ app_frame.pack_forget() calc_frame.pack_forget() conv_frame.pack_forget() @@ -30,6 +40,9 @@ def show_menu() -> None: def show_calc() -> None: + """ + Displays the calculator in the application. It updates the GUI to show the calculator interface. + """ conv_frame.pack_forget() hide_trend_chart() menu_frame.pack_forget() @@ -42,6 +55,9 @@ def show_calc() -> None: def show_conv() -> None: + """ + Displays the currency converter in the application. It updates the GUI to show the converter interface. + """ calc_frame.pack_forget() hide_trend_chart() menu_frame.pack_forget() @@ -54,6 +70,10 @@ def show_conv() -> None: def show_trend() -> None: + """ + Displays the trend chart in the application. It prepares the data for trend analysis, + creates the trend chart, and updates the GUI to show the chart. + """ global trend_canvas global trend_chart_widget @@ -101,6 +121,11 @@ def show_trend() -> None: def act_calculate() -> None: + """ + Handles the calculation action when the "Calculate" button is clicked. + It checks the validity of the input numbers and operator, performs the calculation, + and updates the result label accordingly. + """ resp1 = num1.get() resp1 = parse_amount(resp1) @@ -131,6 +156,11 @@ def act_calculate() -> None: def act_convert() -> None: + """ + Handles the conversion action when the "Convert" button is clicked. + It checks the validity of the input currencies and amount, performs the conversion, + and updates the result label accordingly. + """ resp1 = check_currency(curr1.get()) resp2 = check_currency(curr2.get()) amount = parse_amount(amount_e.get()) @@ -162,6 +192,9 @@ def act_convert() -> None: def app() -> None: + """ + The main function that initializes and starts the GUI application. It sets up the main window, frames, labels, entries, and buttons, and defines the layout of the application. + """ root.mainloop() diff --git a/src/argus/main.py b/src/argus/main.py index a6fd534..7130dbf 100644 --- a/src/argus/main.py +++ b/src/argus/main.py @@ -2,6 +2,9 @@ def main() -> None: + """ + The main function that starts the application. + """ app() diff --git a/src/argus/services/calculator_service.py b/src/argus/services/calculator_service.py index 40c1022..8686af1 100644 --- a/src/argus/services/calculator_service.py +++ b/src/argus/services/calculator_service.py @@ -2,7 +2,13 @@ def check_op(op: str) -> bool: - # Tipp: Liste verwenden, wenn mehr als 2 Optionen für etwas besteht + """ + Checks if the input operator is valid. + + Arg1: op: str - the operator to be checked for validity + + Return: bool - True if the operator is valid, otherwise False + """ if is_valid_op(op): return True else: @@ -10,7 +16,15 @@ def check_op(op: str) -> bool: def calc(num1: float, num2: float, op: str) -> float | None: - # Tipp: Auf Ifelse verzichten, wenn davon mehr als 3 Stück entstehen + """ + Performs a calculation based on the provided operator. + + Arg1: num1: float - the first number + Arg2: num2: float - the second number + Arg3: op: str - the operator to be used for the calculation + + Return: float or None - the result of the calculation if valid, otherwise None + """ match op: case "+": return num1 + num2 diff --git a/src/argus/services/conversion_service.py b/src/argus/services/conversion_service.py index 7d16d76..0dc3cf0 100644 --- a/src/argus/services/conversion_service.py +++ b/src/argus/services/conversion_service.py @@ -2,8 +2,14 @@ from argus.domain.validation import normalize_input_string, is_valid_curr_code -# This function has to be moved to dmoain def check_currency(question: str) -> str | None: + """ + Checks if the input question contains a valid currency code. + + Arg1: question: str - the question to be checked for a valid currency code + + Return: str or None - the valid currency code if found, otherwise None + """ resp = normalize_input_string(question) if is_valid_curr_code(resp): @@ -13,6 +19,15 @@ def check_currency(question: str) -> str | None: def get_conv_rate(resp1: str, resp2: str) -> float | None: + """ + Gets the conversion rate between two currencies. + + Arg1: resp1: str - the first currency code + Arg2: resp2: str - the second currency code + + Return: float or None - the conversion rate if found, otherwise None + """ + data = ex_client.get_rates(resp1, resp2) if data is None: @@ -22,6 +37,16 @@ def get_conv_rate(resp1: str, resp2: str) -> float | None: def convert(amount: float, resp1: str, resp2: str) -> float | None: + """ + Converts an amount from one currency to another using the conversion rate. + + Arg1: amount: float - the amount to be converted + Arg2: resp1: str - the first currency code + Arg3: resp2: str - the second currency code + + Return: float or None - the converted amount if conversion rate is found, otherwise None + """ + data = get_conv_rate(resp1, resp2) if data is not None: return amount * data diff --git a/src/argus/services/timeseries_service.py b/src/argus/services/timeseries_service.py index 6ef34ae..3646200 100644 --- a/src/argus/services/timeseries_service.py +++ b/src/argus/services/timeseries_service.py @@ -10,6 +10,17 @@ def prepare_trend_analysis( mock_curr: str, df: pd.DataFrame ) -> tuple[pd.DataFrame, dict]: + """ + Prepares the data for trend analysis by adding conversion rates, daily percentage change, and rolling average. + + Arg1: mock_curr: str - the currency code for which the trend analysis is to + be performed + Arg2: df: pd.DataFrame - the DataFrame containing the dates for which the + conversion rates are to be added + + Return: tuple[pd.DataFrame, dict] - a tuple containing the updated DataFrame with conversion rates, + daily percentage change, and rolling average, and a dictionary with the minimum and maximum rates + """ df["rate"] = 0.0 # For each date one API call to get the rate for i in range(len(df)): diff --git a/src/legacy/cli/interface.py b/src/legacy/cli/interface.py index fa9931a..727adaf 100644 --- a/src/legacy/cli/interface.py +++ b/src/legacy/cli/interface.py @@ -4,6 +4,18 @@ def display_convert() -> None: + """ + Display the currency conversion workflow in the developer interface. + + Prompts the user for an amount, validates both currency inputs, sends + the conversion request, and prints the converted result. + + Args: + None + + Returns: + None + """ while True: amount = parse_amount(input("Amount: ")) if amount is None: @@ -39,6 +51,18 @@ def display_convert() -> None: def display_calc() -> None: + """ + Display the calculator workflow in the developer interface. + + Prompts the user for two numeric values and an arithmetic operation, + validates the input, calculates the result, and prints it to the console. + + Args: + None + + Returns: + None + """ while True: num1 = parse_amount(input("First number: ")) if num1 is None: @@ -66,6 +90,17 @@ def display_calc() -> None: def return_to_menu() -> str: + """ + Ask the user whether they want to return to the menu. + + Repeats the prompt until the user enters either ``y`` or ``n``. + + Args: + None + + Returns: + str: ``y`` if the user wants to return to the menu, otherwise ``n``. + """ while True: repeat = input("Would you like to return to the menu? (y/n) ") match repeat: @@ -78,6 +113,18 @@ def return_to_menu() -> str: def dev_interface() -> None: + """ + Run the developer command-line interface. + + Displays a simple menu that allows the user to choose between the + calculator, currency conversion, or exiting the program. + + Args: + None + + Returns: + None + """ initConv = "Welcome to the Calculator with Currency Conversion!" print(initConv) while True: diff --git a/src/legacy/debug_main.py b/src/legacy/debug_main.py index 8d82035..5ce0c56 100644 --- a/src/legacy/debug_main.py +++ b/src/legacy/debug_main.py @@ -2,6 +2,9 @@ def start_cli() -> None: + """ + Starts the command-line interface for development purposes. + """ dev_interface() diff --git a/tests/test_exchangerate_client.py b/tests/test_exchangerate_client.py index 51e8174..faf0864 100644 --- a/tests/test_exchangerate_client.py +++ b/tests/test_exchangerate_client.py @@ -104,25 +104,22 @@ def test_get_resp(url, timeout): def test_check_error(capsys): check_error("unsupported-code") captured = capsys.readouterr() - assert captured.out == "Ungültige Anfrage! Bitter versuchen Sie es später erneut.\n" + assert captured.out == "Invalid request! Please try again later.\n" check_error("invalid-key") captured = capsys.readouterr() - assert ( - captured.out - == "Ungültiger API-Key! Checken Sie Ihren API-Key und versuchen Sie es erneut.\n" - ) + assert captured.out == "Invalid API key! Please check your API key and try again.\n" check_error("inactive-account") captured = capsys.readouterr() assert ( captured.out - == "Inaktives Konto! Bitte auf exchangerate-api.com gehen und Konto aktivieren.\n" + == "Inactive account! Please go to exchangerate-api.com and activate your account.\n" ) check_error("quota-reached") captured = capsys.readouterr() assert ( captured.out - == "Anfrage-Limit erreicht! Bitte später erneut versuchen oder auf exchangerate-api.com upgraden.\n" + == "Request limit reached! Please try again later or upgrade to exchangerate-api.com.\n" )