Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 38 additions & 2 deletions Currency Script/main.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,38 @@
# TODO IMPORT API / CONVERTER / CURRENCY MODULES
# TODO UPDATE / POPULATE MAIN TO ENSURE THERE IS A INTUITIVE SIMPLE UI
"""
main.py - Simple CLI interface for Currency Converter

Prompts user for input currencies and amount, and displays the converted value.
"""

from converter import CurrencyConverter

def main():
"""
Run the interactive currency converter CLI loop.
Prompts user for source and target currencies and amount to convert.
Continues until the user types 'no' to exit.
"""
print("Welcome to the Currency Converter\n")

converter = CurrencyConverter()

while True:
try:
from_currency = input("Enter source currency code (e.g., AUD): ").strip().upper()
to_currency = input("Enter target currency code (e.g., USD): ").strip().upper()
amount = float(input(f"Enter amount in {from_currency}: "))

converted = converter.convert(from_currency, to_currency, amount)
print(f"\n✅ {amount} {from_currency} = {converted} {to_currency}\n")

except ValueError as e:
print(f"❌ Error: {e}\n")

# Ask to convert another or exit
again = input("Do you want to convert another amount? (yes/no): ").strip().lower()
if again != "yes":
print("Thank you for using the Currency Converter. Goodbye!")
break

if __name__ == "__main__":
main()
57 changes: 57 additions & 0 deletions Currency Script/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
Currency Converter
==================

A simple Python CLI application that converts an amount from one currency to another
using live exchange rates fetched from an open API.

Modules:
--------

1. **api_handler.py**
- Handles fetching exchange rate data from the online API.

2. **currencies.py**
- Provides utility functions to list supported currencies and validate currency codes.

3. **converter.py**
- Contains the core logic to convert between currencies using the exchange rates.

4. **main.py**
- CLI interface where users input the currencies and amount to convert.

5. **test_*.py**
- Unit tests for each module to ensure functionality.

Requirements:
-------------
- Python 3.7+
- `requests` library

Install dependencies:
---------------------
Run the following command to install dependencies:
- pip install -r requirements.txt

How to Use:
-----------
Run the main script from the terminal:

Follow the prompts to enter:
- Source currency code (e.g., USD)
- Target currency code (e.g., AUD)
- Amount to convert

The app will display the converted amount and allow you to repeat or exit.

Testing:
--------
To run tests:
- python -m unittest discover

Note:
-----
The conversion is based on live exchange rates retrieved from:
https://open.er-api.com/v6/latest

Make sure you are connected to the internet when running the app.

1 change: 0 additions & 1 deletion Currency Script/readme.txt

This file was deleted.

2 changes: 1 addition & 1 deletion Currency Script/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
# TODO - UPDATE REQUIREMENTS TO NOTE INSTALLS ARE NOTED
requests
10 changes: 9 additions & 1 deletion Currency Script/setup.py
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
# UPDATE SETUP TO NOTE SET UP / PACKAGES
from setuptools import setup

setup(
name="currency_converter",
version="0.1",
install_requires=[
"requests"
],
)
16 changes: 12 additions & 4 deletions Currency Script/src/api_handler.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import requests

def get_exchange_data(api_url: str = "https://open.er-api.com/v6/latest") -> dict:
"""Fetch latest exchange data from the API."""
"""
Retrieve the latest foreign exchange rates from the specified API.

Args:
api_url (str): Endpoint to fetch exchange data from. Defaults to the open.er-api URL.

Returns:
dict: A dictionary containing the base currency, timestamp, and exchange rates.

Raises:
Exception: If the API request fails or returns a non-200 status.
"""
response = requests.get(api_url)
if response.status_code != 200:
raise Exception(f"API request failed with status {response.status_code}")

data = response.json()
# Ensure response was successful
if data.get("result") != "success":
raise Exception(f"API returned error: {data.get('error-type', 'Unknown error')}")

return data # Includes 'base_code', 'time_last_update_utc', and 'rates'

Expand Down
7 changes: 2 additions & 5 deletions Currency Script/src/converter.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
"""
CurrencyConverter: Converts an amount from one currency to another using live exchange rates.
"""

from api_handler import get_exchange_data

class CurrencyConverter:
Expand Down Expand Up @@ -35,7 +31,8 @@ def convert(self, from_currency: str, to_currency: str, amount: float) -> float:
converted_amount = round(amount_in_base * self.rates[to_currency], 2)
return converted_amount

# --- DEBUG / MANUAL TEST ---
# --- DEBUG / MANUAL TEST SECTION ---
# This section runs only when you run this file directly (not when imported elsewhere)
if __name__ == "__main__":
print("Running manual test for CurrencyConverter...\n")

Expand Down
21 changes: 19 additions & 2 deletions Currency Script/src/currencies.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
from api_handler import get_exchange_data

# Returns a list of all supported currency codes.
def get_supported_currencies(rates):
"""
Return a list of supported currency codes from the rates dictionary.

Args:
rates (dict): Dictionary of currency codes and their exchange rates.

Returns:
list: List of supported currency codes (e.g., ['USD', 'EUR']).
"""
return list(rates.keys())

# Checks if a currency code is supported.
def is_valid_currency(currency_code, rates):
"""
Check if the given currency code is valid and supported.

Args:
currency_code (str): Currency code to validate (e.g., 'USD').
rates (dict): Dictionary of available exchange rates.

Returns:
bool: True if the currency code exists in the rates, False otherwise.
"""
return currency_code.upper() in rates

# --- DEBUG / MANUAL TEST SECTION ---
Expand Down
Empty file.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
37 changes: 37 additions & 0 deletions Currency Script/tests/test_api_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import sys
import os
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../src')))

import unittest
from unittest.mock import patch
from src.api_handler import get_exchange_data

class TestAPIHandler(unittest.TestCase):
"""
Unit tests for the get_exchange_data function in api_handler.py.
"""
@patch("api_handler.requests.get")
def test_get_exchange_data_success(self, mock_get):
mock_get.return_value.status_code = 200
mock_get.return_value.json.return_value = {
"base": "EUR",
"date": "2025-06-02",
"rates": {"USD": 1.14, "GBP": 0.84}
}

data = get_exchange_data()
self.assertEqual(data["base"], "EUR")
self.assertIn("USD", data["rates"])

@patch("api_handler.requests.get")
def test_get_exchange_data_failure(self, mock_get):
"""
Test that get_exchange_data raises an exception when API response fails.
"""
mock_get.return_value.status_code = 404
with self.assertRaises(Exception):
get_exchange_data()

if __name__ == "__main__":
unittest.main()

33 changes: 33 additions & 0 deletions Currency Script/tests/test_converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import unittest
from converter import CurrencyConverter

class TestCurrencyConverter(unittest.TestCase):
"""Unit tests for CurrencyConverter class."""

@classmethod
def setUpClass(cls):
"""Initialize converter once for all tests."""
cls.converter = CurrencyConverter()

def test_valid_conversion(self):
"""Test converting from USD to EUR returns a float value."""
result = self.converter.convert("USD", "EUR", 100)
self.assertIsInstance(result, float)
self.assertGreater(result, 0)

def test_same_currency(self):
"""Test converting a currency to itself returns the same amount."""
amount = 50
result = self.converter.convert("USD", "USD", amount)
self.assertAlmostEqual(result, amount, places=2)

def test_invalid_currency(self):
"""Test invalid currency codes raise ValueError."""
with self.assertRaises(ValueError):
self.converter.convert("XXX", "EUR", 100)

with self.assertRaises(ValueError):
self.converter.convert("USD", "YYY", 100)

if __name__ == "__main__":
unittest.main()
33 changes: 33 additions & 0 deletions Currency Script/tests/test_currencies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import unittest
from currencies import get_supported_currencies, is_valid_currency

class TestCurrencies(unittest.TestCase):
"""Unit tests for currency helper functions."""

def setUp(self):
"""Set up sample rates dictionary for use in tests."""
self.sample_rates = {
"USD": 1.1,
"EUR": 1.0,
"GBP": 0.85,
"JPY": 150.0
}

def test_get_supported_currencies(self):
"""Test that all currency codes are returned correctly."""
expected = ["USD", "EUR", "GBP", "JPY"]
result = get_supported_currencies(self.sample_rates)
self.assertListEqual(sorted(result), sorted(expected))

def test_is_valid_currency_true(self):
"""Test that valid currency codes return True."""
self.assertTrue(is_valid_currency("usd", self.sample_rates))
self.assertTrue(is_valid_currency("EUR", self.sample_rates))

def test_is_valid_currency_false(self):
"""Test that unsupported currency codes return False."""
self.assertFalse(is_valid_currency("ABC", self.sample_rates))
self.assertFalse(is_valid_currency("xyz", self.sample_rates))

if __name__ == "__main__":
unittest.main()