corally 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- corally/__init__.py +36 -0
- corally/api/__init__.py +8 -0
- corally/api/free_server.py +259 -0
- corally/api/server.py +190 -0
- corally/cli/__init__.py +9 -0
- corally/cli/calculator.py +59 -0
- corally/cli/currency.py +65 -0
- corally/cli/main.py +208 -0
- corally/core/__init__.py +7 -0
- corally/core/calculator.py +392 -0
- corally/gui/__init__.py +8 -0
- corally/gui/launcher.py +138 -0
- corally/gui/main.py +886 -0
- corally-1.0.0.dist-info/METADATA +192 -0
- corally-1.0.0.dist-info/RECORD +19 -0
- corally-1.0.0.dist-info/WHEEL +5 -0
- corally-1.0.0.dist-info/entry_points.txt +5 -0
- corally-1.0.0.dist-info/licenses/LICENSE +21 -0
- corally-1.0.0.dist-info/top_level.txt +1 -0
corally/cli/main.py
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
"""
|
2
|
+
Main CLI entry point for Corally calculator suite.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import sys
|
6
|
+
from typing import Optional
|
7
|
+
|
8
|
+
from ..core import CalculatorCore, CurrencyConverter, InterestCalculator
|
9
|
+
from ..gui.launcher import launch_gui
|
10
|
+
from ..api.server import start_server
|
11
|
+
from ..api.free_server import start_free_server
|
12
|
+
|
13
|
+
|
14
|
+
def main_cli() -> None:
|
15
|
+
"""Main CLI interface for Corally calculator suite."""
|
16
|
+
print("๐งฎ Corally Calculator Suite")
|
17
|
+
print("=" * 40)
|
18
|
+
|
19
|
+
while True:
|
20
|
+
print("\nSelect an option:")
|
21
|
+
print("1: Basic Calculator")
|
22
|
+
print("2: Currency Converter")
|
23
|
+
print("3: Interest Calculator")
|
24
|
+
print("4: Launch GUI")
|
25
|
+
print("5: Start API Server (Free)")
|
26
|
+
print("6: Start API Server (Paid)")
|
27
|
+
print("7: Exit")
|
28
|
+
|
29
|
+
try:
|
30
|
+
choice = int(input("\nEnter your choice (1-7): "))
|
31
|
+
except ValueError:
|
32
|
+
print("โ Invalid input. Please enter a number between 1-7.")
|
33
|
+
continue
|
34
|
+
|
35
|
+
if choice == 7:
|
36
|
+
print("๐ Goodbye!")
|
37
|
+
break
|
38
|
+
elif choice == 1:
|
39
|
+
calculator_cli()
|
40
|
+
elif choice == 2:
|
41
|
+
currency_cli()
|
42
|
+
elif choice == 3:
|
43
|
+
interest_cli()
|
44
|
+
elif choice == 4:
|
45
|
+
print("๐ Launching GUI...")
|
46
|
+
launch_gui()
|
47
|
+
elif choice == 5:
|
48
|
+
print("๐ Starting Free API Server...")
|
49
|
+
start_free_server()
|
50
|
+
elif choice == 6:
|
51
|
+
print("๐ Starting Paid API Server...")
|
52
|
+
start_server()
|
53
|
+
else:
|
54
|
+
print("โ Invalid choice. Please select 1-7.")
|
55
|
+
|
56
|
+
|
57
|
+
def calculator_cli() -> None:
|
58
|
+
"""Basic calculator CLI interface."""
|
59
|
+
calc = CalculatorCore()
|
60
|
+
print("\n๐งฎ Basic Calculator")
|
61
|
+
print("-" * 20)
|
62
|
+
|
63
|
+
while True:
|
64
|
+
print("\nOperations:")
|
65
|
+
print("1: Addition")
|
66
|
+
print("2: Subtraction")
|
67
|
+
print("3: Multiplication")
|
68
|
+
print("4: Division")
|
69
|
+
print("5: Back to main menu")
|
70
|
+
|
71
|
+
try:
|
72
|
+
choice = int(input("\nSelect operation (1-5): "))
|
73
|
+
except ValueError:
|
74
|
+
print("โ Invalid input. Please enter a number.")
|
75
|
+
continue
|
76
|
+
|
77
|
+
if choice == 5:
|
78
|
+
break
|
79
|
+
elif choice not in [1, 2, 3, 4]:
|
80
|
+
print("โ Invalid choice. Please select 1-5.")
|
81
|
+
continue
|
82
|
+
|
83
|
+
try:
|
84
|
+
a = float(input("Enter first number: "))
|
85
|
+
b = float(input("Enter second number: "))
|
86
|
+
except ValueError:
|
87
|
+
print("โ Invalid number input.")
|
88
|
+
continue
|
89
|
+
|
90
|
+
result = None
|
91
|
+
if choice == 1:
|
92
|
+
result = calc.add(a, b)
|
93
|
+
print(f"โ
{a} + {b} = {result}")
|
94
|
+
elif choice == 2:
|
95
|
+
result = calc.subtract(a, b)
|
96
|
+
print(f"โ
{a} - {b} = {result}")
|
97
|
+
elif choice == 3:
|
98
|
+
result = calc.multiply(a, b)
|
99
|
+
print(f"โ
{a} ร {b} = {result}")
|
100
|
+
elif choice == 4:
|
101
|
+
result = calc.divide(a, b)
|
102
|
+
if result is not None:
|
103
|
+
print(f"โ
{a} รท {b} = {result}")
|
104
|
+
|
105
|
+
|
106
|
+
def currency_cli() -> None:
|
107
|
+
"""Currency converter CLI interface."""
|
108
|
+
converter = CurrencyConverter()
|
109
|
+
print("\n๐ฑ Currency Converter")
|
110
|
+
print("-" * 22)
|
111
|
+
|
112
|
+
while True:
|
113
|
+
print("\nAvailable conversions:")
|
114
|
+
print("1: EUR to USD")
|
115
|
+
print("2: USD to EUR")
|
116
|
+
print("3: EUR to GBP")
|
117
|
+
print("4: GBP to EUR")
|
118
|
+
print("5: EUR to JPY")
|
119
|
+
print("6: JPY to EUR")
|
120
|
+
print("7: Back to main menu")
|
121
|
+
|
122
|
+
try:
|
123
|
+
choice = int(input("\nSelect conversion (1-7): "))
|
124
|
+
except ValueError:
|
125
|
+
print("โ Invalid input. Please enter a number.")
|
126
|
+
continue
|
127
|
+
|
128
|
+
if choice == 7:
|
129
|
+
break
|
130
|
+
elif choice not in range(1, 7):
|
131
|
+
print("โ Invalid choice. Please select 1-7.")
|
132
|
+
continue
|
133
|
+
|
134
|
+
try:
|
135
|
+
amount = float(input("Enter amount: "))
|
136
|
+
except ValueError:
|
137
|
+
print("โ Invalid amount.")
|
138
|
+
continue
|
139
|
+
|
140
|
+
result = None
|
141
|
+
if choice == 1:
|
142
|
+
result = converter.eur_to_usd(amount)
|
143
|
+
print(f"โ
{amount} EUR = {result} USD")
|
144
|
+
elif choice == 2:
|
145
|
+
result = converter.usd_to_eur(amount)
|
146
|
+
print(f"โ
{amount} USD = {result} EUR")
|
147
|
+
elif choice == 3:
|
148
|
+
result = converter.eur_to_gbp(amount)
|
149
|
+
print(f"โ
{amount} EUR = {result} GBP")
|
150
|
+
elif choice == 4:
|
151
|
+
result = converter.gbp_to_eur(amount)
|
152
|
+
print(f"โ
{amount} GBP = {result} EUR")
|
153
|
+
elif choice == 5:
|
154
|
+
result = converter.eur_to_jpy(amount)
|
155
|
+
print(f"โ
{amount} EUR = {result} JPY")
|
156
|
+
elif choice == 6:
|
157
|
+
result = converter.jpy_to_eur(amount)
|
158
|
+
print(f"โ
{amount} JPY = {result} EUR")
|
159
|
+
|
160
|
+
|
161
|
+
def interest_cli() -> None:
|
162
|
+
"""Interest calculator CLI interface."""
|
163
|
+
print("\n๐ Interest Calculator")
|
164
|
+
print("-" * 22)
|
165
|
+
|
166
|
+
while True:
|
167
|
+
print("\nCalculation methods:")
|
168
|
+
print("1: 30/360 method")
|
169
|
+
print("2: act/360 method")
|
170
|
+
print("3: act/365 method")
|
171
|
+
print("4: act/act method")
|
172
|
+
print("5: Back to main menu")
|
173
|
+
|
174
|
+
try:
|
175
|
+
choice = int(input("\nSelect method (1-5): "))
|
176
|
+
except ValueError:
|
177
|
+
print("โ Invalid input. Please enter a number.")
|
178
|
+
continue
|
179
|
+
|
180
|
+
if choice == 5:
|
181
|
+
break
|
182
|
+
elif choice not in range(1, 5):
|
183
|
+
print("โ Invalid choice. Please select 1-5.")
|
184
|
+
continue
|
185
|
+
|
186
|
+
try:
|
187
|
+
principal = float(input("Enter principal amount: "))
|
188
|
+
rate = float(input("Enter interest rate (%): "))
|
189
|
+
start_date = input("Enter start date (DD.MM.YYYY): ")
|
190
|
+
end_date = input("Enter end date (DD.MM.YYYY): ")
|
191
|
+
except ValueError:
|
192
|
+
print("โ Invalid input.")
|
193
|
+
continue
|
194
|
+
|
195
|
+
methods = ["30/360", "act/360", "act/365", "act/act"]
|
196
|
+
method = methods[choice - 1]
|
197
|
+
|
198
|
+
try:
|
199
|
+
result = InterestCalculator.calculate_interest(
|
200
|
+
principal, rate, start_date, end_date, method
|
201
|
+
)
|
202
|
+
print(f"โ
Interest ({method}): {result}")
|
203
|
+
except Exception as e:
|
204
|
+
print(f"โ Error calculating interest: {e}")
|
205
|
+
|
206
|
+
|
207
|
+
if __name__ == "__main__":
|
208
|
+
main_cli()
|
corally/core/__init__.py
ADDED
@@ -0,0 +1,392 @@
|
|
1
|
+
"""
|
2
|
+
Calculator Suite Core Library
|
3
|
+
|
4
|
+
A unified library combining all calculator functionality:
|
5
|
+
- Basic arithmetic operations with logging
|
6
|
+
- Currency conversion (static rates)
|
7
|
+
- Interest calculations with multiple methods
|
8
|
+
|
9
|
+
This module replaces the separate Maxim.py and zinsen.py files.
|
10
|
+
"""
|
11
|
+
|
12
|
+
import csv
|
13
|
+
import os
|
14
|
+
from datetime import datetime
|
15
|
+
from typing import Optional, Union
|
16
|
+
from pathlib import Path
|
17
|
+
|
18
|
+
|
19
|
+
class CalculatorCore:
|
20
|
+
"""
|
21
|
+
Main calculator class providing basic arithmetic operations with automatic logging.
|
22
|
+
|
23
|
+
Features:
|
24
|
+
- Four basic operations: add, subtract, multiply, divide
|
25
|
+
- Automatic CSV logging of all operations
|
26
|
+
- Robust error handling and input validation
|
27
|
+
"""
|
28
|
+
|
29
|
+
def __init__(self, csv_file: Optional[str] = None):
|
30
|
+
"""
|
31
|
+
Initialize calculator with CSV logging.
|
32
|
+
|
33
|
+
Args:
|
34
|
+
csv_file (str): Path to CSV file for logging operations.
|
35
|
+
If None, uses 'data/rechner_log.csv' in current directory.
|
36
|
+
"""
|
37
|
+
if csv_file is None:
|
38
|
+
# Create data directory if it doesn't exist
|
39
|
+
data_dir = Path("data")
|
40
|
+
data_dir.mkdir(exist_ok=True)
|
41
|
+
self.csv_file = str(data_dir / "rechner_log.csv")
|
42
|
+
else:
|
43
|
+
self.csv_file = csv_file
|
44
|
+
self._initialize_csv()
|
45
|
+
|
46
|
+
def _initialize_csv(self) -> None:
|
47
|
+
"""Initialize CSV file with headers if it doesn't exist or is empty."""
|
48
|
+
try:
|
49
|
+
with open(self.csv_file, mode="a", newline="", encoding="utf-8") as file:
|
50
|
+
writer = csv.writer(file)
|
51
|
+
if file.tell() == 0:
|
52
|
+
writer.writerow(["Zahl 1", "Operator", "Zahl 2", "Ergebnis", "Zeitstempel"])
|
53
|
+
except Exception as e:
|
54
|
+
print(f"Warning: Could not initialize CSV file: {e}")
|
55
|
+
|
56
|
+
def _log_operation(self, a: float, operator: str, b: float, result: float) -> None:
|
57
|
+
"""
|
58
|
+
Log calculation to CSV file.
|
59
|
+
|
60
|
+
Args:
|
61
|
+
a (float): First number
|
62
|
+
operator (str): Operation symbol
|
63
|
+
b (float): Second number
|
64
|
+
result (float): Calculation result
|
65
|
+
"""
|
66
|
+
try:
|
67
|
+
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
68
|
+
with open(self.csv_file, mode="a", newline="", encoding="utf-8") as file:
|
69
|
+
writer = csv.writer(file)
|
70
|
+
writer.writerow([a, operator, b, result, timestamp])
|
71
|
+
except Exception as e:
|
72
|
+
print(f"Warning: Could not log operation: {e}")
|
73
|
+
|
74
|
+
def _validate_numbers(self, a: Union[str, int, float], b: Union[str, int, float]) -> tuple[float, float]:
|
75
|
+
"""
|
76
|
+
Validate and convert input numbers to float.
|
77
|
+
|
78
|
+
Args:
|
79
|
+
a: First number (any numeric type or string)
|
80
|
+
b: Second number (any numeric type or string)
|
81
|
+
|
82
|
+
Returns:
|
83
|
+
tuple[float, float]: Validated numbers as floats
|
84
|
+
|
85
|
+
Raises:
|
86
|
+
ValueError: If inputs cannot be converted to float
|
87
|
+
"""
|
88
|
+
try:
|
89
|
+
return float(a), float(b)
|
90
|
+
except (TypeError, ValueError) as e:
|
91
|
+
raise ValueError(f"Invalid number input: {e}")
|
92
|
+
|
93
|
+
def add(self, a: Union[str, int, float], b: Union[str, int, float]) -> Optional[float]:
|
94
|
+
"""
|
95
|
+
Add two numbers.
|
96
|
+
|
97
|
+
Args:
|
98
|
+
a: First number
|
99
|
+
b: Second number
|
100
|
+
|
101
|
+
Returns:
|
102
|
+
float: Sum of a and b, or None if error
|
103
|
+
"""
|
104
|
+
try:
|
105
|
+
num_a, num_b = self._validate_numbers(a, b)
|
106
|
+
result = num_a + num_b
|
107
|
+
self._log_operation(num_a, "+", num_b, result)
|
108
|
+
return result
|
109
|
+
except ValueError as e:
|
110
|
+
print(f"Addition error: {e}")
|
111
|
+
return None
|
112
|
+
|
113
|
+
def subtract(self, a: Union[str, int, float], b: Union[str, int, float]) -> Optional[float]:
|
114
|
+
"""
|
115
|
+
Subtract two numbers.
|
116
|
+
|
117
|
+
Args:
|
118
|
+
a: Minuend (first number)
|
119
|
+
b: Subtrahend (second number)
|
120
|
+
|
121
|
+
Returns:
|
122
|
+
float: Difference of a and b, or None if error
|
123
|
+
"""
|
124
|
+
try:
|
125
|
+
num_a, num_b = self._validate_numbers(a, b)
|
126
|
+
result = num_a - num_b
|
127
|
+
self._log_operation(num_a, "-", num_b, result)
|
128
|
+
return result
|
129
|
+
except ValueError as e:
|
130
|
+
print(f"Subtraction error: {e}")
|
131
|
+
return None
|
132
|
+
|
133
|
+
def multiply(self, a: Union[str, int, float], b: Union[str, int, float]) -> Optional[float]:
|
134
|
+
"""
|
135
|
+
Multiply two numbers.
|
136
|
+
|
137
|
+
Args:
|
138
|
+
a: First number
|
139
|
+
b: Second number
|
140
|
+
|
141
|
+
Returns:
|
142
|
+
float: Product of a and b, or None if error
|
143
|
+
"""
|
144
|
+
try:
|
145
|
+
num_a, num_b = self._validate_numbers(a, b)
|
146
|
+
result = num_a * num_b
|
147
|
+
self._log_operation(num_a, "*", num_b, result)
|
148
|
+
return result
|
149
|
+
except ValueError as e:
|
150
|
+
print(f"Multiplication error: {e}")
|
151
|
+
return None
|
152
|
+
|
153
|
+
def divide(self, a: Union[str, int, float], b: Union[str, int, float]) -> Optional[float]:
|
154
|
+
"""
|
155
|
+
Divide two numbers.
|
156
|
+
|
157
|
+
Args:
|
158
|
+
a: Dividend (first number)
|
159
|
+
b: Divisor (second number)
|
160
|
+
|
161
|
+
Returns:
|
162
|
+
float: Quotient of a and b, or None if error
|
163
|
+
"""
|
164
|
+
try:
|
165
|
+
num_a, num_b = self._validate_numbers(a, b)
|
166
|
+
if num_b == 0:
|
167
|
+
print("Division error: Division by zero is not allowed!")
|
168
|
+
return None
|
169
|
+
result = num_a / num_b
|
170
|
+
self._log_operation(num_a, "/", num_b, result)
|
171
|
+
return result
|
172
|
+
except ValueError as e:
|
173
|
+
print(f"Division error: {e}")
|
174
|
+
return None
|
175
|
+
|
176
|
+
|
177
|
+
class CurrencyConverter:
|
178
|
+
"""
|
179
|
+
Currency converter with predefined exchange rates.
|
180
|
+
|
181
|
+
Supported conversions:
|
182
|
+
- EUR โ USD
|
183
|
+
- EUR โ GBP
|
184
|
+
- EUR โ JPY
|
185
|
+
"""
|
186
|
+
|
187
|
+
# Exchange rates (static)
|
188
|
+
RATES = {
|
189
|
+
'EUR_TO_USD': 1.17,
|
190
|
+
'EUR_TO_GBP': 0.87,
|
191
|
+
'EUR_TO_JPY': 173.84
|
192
|
+
}
|
193
|
+
|
194
|
+
@classmethod
|
195
|
+
def _validate_amount(cls, amount: Union[str, int, float]) -> float:
|
196
|
+
"""
|
197
|
+
Validate and convert amount to float.
|
198
|
+
|
199
|
+
Args:
|
200
|
+
amount: Amount to convert
|
201
|
+
|
202
|
+
Returns:
|
203
|
+
float: Validated amount
|
204
|
+
|
205
|
+
Raises:
|
206
|
+
ValueError: If amount cannot be converted to float
|
207
|
+
"""
|
208
|
+
try:
|
209
|
+
return float(amount)
|
210
|
+
except (TypeError, ValueError) as e:
|
211
|
+
raise ValueError(f"Invalid amount: {e}")
|
212
|
+
|
213
|
+
@classmethod
|
214
|
+
def eur_to_usd(cls, eur: Union[str, int, float]) -> Optional[float]:
|
215
|
+
"""Convert EUR to USD."""
|
216
|
+
try:
|
217
|
+
amount = cls._validate_amount(eur)
|
218
|
+
result = amount * cls.RATES['EUR_TO_USD']
|
219
|
+
return round(result, 2)
|
220
|
+
except ValueError as e:
|
221
|
+
print(f"EUR to USD conversion error: {e}")
|
222
|
+
return None
|
223
|
+
|
224
|
+
@classmethod
|
225
|
+
def usd_to_eur(cls, usd: Union[str, int, float]) -> Optional[float]:
|
226
|
+
"""Convert USD to EUR."""
|
227
|
+
try:
|
228
|
+
amount = cls._validate_amount(usd)
|
229
|
+
result = amount / cls.RATES['EUR_TO_USD']
|
230
|
+
return round(result, 2)
|
231
|
+
except ValueError as e:
|
232
|
+
print(f"USD to EUR conversion error: {e}")
|
233
|
+
return None
|
234
|
+
|
235
|
+
@classmethod
|
236
|
+
def eur_to_gbp(cls, eur: Union[str, int, float]) -> Optional[float]:
|
237
|
+
"""Convert EUR to GBP."""
|
238
|
+
try:
|
239
|
+
amount = cls._validate_amount(eur)
|
240
|
+
result = amount * cls.RATES['EUR_TO_GBP']
|
241
|
+
return round(result, 2)
|
242
|
+
except ValueError as e:
|
243
|
+
print(f"EUR to GBP conversion error: {e}")
|
244
|
+
return None
|
245
|
+
|
246
|
+
@classmethod
|
247
|
+
def gbp_to_eur(cls, gbp: Union[str, int, float]) -> Optional[float]:
|
248
|
+
"""Convert GBP to EUR."""
|
249
|
+
try:
|
250
|
+
amount = cls._validate_amount(gbp)
|
251
|
+
result = amount / cls.RATES['EUR_TO_GBP']
|
252
|
+
return round(result, 2)
|
253
|
+
except ValueError as e:
|
254
|
+
print(f"GBP to EUR conversion error: {e}")
|
255
|
+
return None
|
256
|
+
|
257
|
+
@classmethod
|
258
|
+
def eur_to_jpy(cls, eur: Union[str, int, float]) -> Optional[float]:
|
259
|
+
"""Convert EUR to JPY."""
|
260
|
+
try:
|
261
|
+
amount = cls._validate_amount(eur)
|
262
|
+
result = amount * cls.RATES['EUR_TO_JPY']
|
263
|
+
return round(result, 2)
|
264
|
+
except ValueError as e:
|
265
|
+
print(f"EUR to JPY conversion error: {e}")
|
266
|
+
return None
|
267
|
+
|
268
|
+
@classmethod
|
269
|
+
def jpy_to_eur(cls, jpy: Union[str, int, float]) -> Optional[float]:
|
270
|
+
"""Convert JPY to EUR."""
|
271
|
+
try:
|
272
|
+
amount = cls._validate_amount(jpy)
|
273
|
+
result = amount / cls.RATES['EUR_TO_JPY']
|
274
|
+
return round(result, 2)
|
275
|
+
except ValueError as e:
|
276
|
+
print(f"JPY to EUR conversion error: {e}")
|
277
|
+
return None
|
278
|
+
|
279
|
+
|
280
|
+
class InterestCalculator:
|
281
|
+
"""
|
282
|
+
Interest calculator supporting multiple calculation methods.
|
283
|
+
|
284
|
+
Supported methods:
|
285
|
+
- 30/360: 30-day months, 360-day years
|
286
|
+
- act/360: Actual days, 360-day years
|
287
|
+
- act/365: Actual days, 365-day years
|
288
|
+
- act/act: Actual days, actual years
|
289
|
+
"""
|
290
|
+
|
291
|
+
VALID_METHODS = ["30/360", "act/360", "act/365", "act/act"]
|
292
|
+
|
293
|
+
@classmethod
|
294
|
+
def calculate_interest(
|
295
|
+
cls,
|
296
|
+
capital: Union[str, int, float],
|
297
|
+
interest_rate: Union[str, int, float],
|
298
|
+
start_date: str,
|
299
|
+
end_date: str,
|
300
|
+
method: str = "act/365"
|
301
|
+
) -> Optional[float]:
|
302
|
+
"""
|
303
|
+
Calculate interest between two dates using specified method.
|
304
|
+
|
305
|
+
Args:
|
306
|
+
capital: Principal amount
|
307
|
+
interest_rate: Annual interest rate (percentage)
|
308
|
+
start_date: Start date in DD.MM.YYYY format
|
309
|
+
end_date: End date in DD.MM.YYYY format
|
310
|
+
method: Calculation method (30/360, act/360, act/365, act/act)
|
311
|
+
|
312
|
+
Returns:
|
313
|
+
float: Calculated interest amount, or None if error
|
314
|
+
"""
|
315
|
+
try:
|
316
|
+
# Validate inputs
|
317
|
+
cap = float(capital)
|
318
|
+
rate = float(interest_rate) / 100 # Convert percentage to decimal
|
319
|
+
|
320
|
+
if method not in cls.VALID_METHODS:
|
321
|
+
raise ValueError(f"Invalid method. Allowed: {', '.join(cls.VALID_METHODS)}")
|
322
|
+
|
323
|
+
# Parse dates
|
324
|
+
d1 = datetime.strptime(start_date, "%d.%m.%Y")
|
325
|
+
d2 = datetime.strptime(end_date, "%d.%m.%Y")
|
326
|
+
|
327
|
+
# Calculate days and year basis
|
328
|
+
if method == "30/360":
|
329
|
+
days = (d2.year - d1.year) * 360 + (d2.month - d1.month) * 30 + (d2.day - d1.day)
|
330
|
+
year_basis = 360
|
331
|
+
else:
|
332
|
+
days = (d2 - d1).days
|
333
|
+
if method == "act/360":
|
334
|
+
year_basis = 360
|
335
|
+
elif method in ("act/365", "act/act"):
|
336
|
+
year_basis = 365
|
337
|
+
|
338
|
+
# Calculate interest
|
339
|
+
interest = cap * rate * (days / year_basis)
|
340
|
+
return round(interest, 2)
|
341
|
+
|
342
|
+
except (ValueError, TypeError) as e:
|
343
|
+
print(f"Interest calculation error: {e}")
|
344
|
+
return None
|
345
|
+
except Exception as e:
|
346
|
+
print(f"Unexpected error in interest calculation: {e}")
|
347
|
+
return None
|
348
|
+
|
349
|
+
|
350
|
+
# Backward compatibility aliases
|
351
|
+
Rechner = CalculatorCore
|
352
|
+
Waerungsrechner = CurrencyConverter
|
353
|
+
tageszins = InterestCalculator.calculate_interest
|
354
|
+
|
355
|
+
# Legacy method aliases for Waerungsrechner
|
356
|
+
# CurrencyConverter.eur_in_usd = CurrencyConverter.eur_to_usd
|
357
|
+
# CurrencyConverter.usd_in_eur = CurrencyConverter.usd_to_eur
|
358
|
+
# CurrencyConverter.eur_in_bp = CurrencyConverter.eur_to_gbp
|
359
|
+
# CurrencyConverter.bp_in_eur = CurrencyConverter.gbp_to_eur
|
360
|
+
# CurrencyConverter.eur_in_yen = CurrencyConverter.eur_to_jpy
|
361
|
+
# CurrencyConverter.yen_in_euro = CurrencyConverter.jpy_to_eur
|
362
|
+
|
363
|
+
|
364
|
+
def main():
|
365
|
+
"""Interactive console interface for interest calculation."""
|
366
|
+
print("๐ Interest Calculator\n")
|
367
|
+
|
368
|
+
try:
|
369
|
+
capital = float(input("Capital (โฌ): "))
|
370
|
+
rate = float(input("Interest rate (% per year): "))
|
371
|
+
start_date = input("Start date (DD.MM.YYYY): ")
|
372
|
+
end_date = input("End date (DD.MM.YYYY): ")
|
373
|
+
method = input(f"Method ({', '.join(InterestCalculator.VALID_METHODS)}): ")
|
374
|
+
|
375
|
+
interest = InterestCalculator.calculate_interest(capital, rate, start_date, end_date, method)
|
376
|
+
|
377
|
+
if interest is not None:
|
378
|
+
print("\n๐ฐ Result:")
|
379
|
+
print(f"Capital: {capital:,.2f} โฌ")
|
380
|
+
print(f"Period: {start_date} to {end_date}")
|
381
|
+
print(f"Method: {method}")
|
382
|
+
print(f"Interest: {interest:,.2f} โฌ")
|
383
|
+
print(f"Total: {capital + interest:,.2f} โฌ")
|
384
|
+
else:
|
385
|
+
print("โ Calculation failed. Please check your inputs.")
|
386
|
+
|
387
|
+
except (ValueError, KeyboardInterrupt):
|
388
|
+
print("\nโ Invalid input or operation cancelled.")
|
389
|
+
|
390
|
+
|
391
|
+
if __name__ == "__main__":
|
392
|
+
main()
|