Compare commits

..

3 Commits

Author SHA1 Message Date
kdusek
ed5d126d77 feat: Add JSON output support and auto-download functionality
- Add --json CLI flag for structured JSON output across all commands
- Implement JSON formatting functions for different data types:
  * Single rate lookups with fallback information
  * Unified tax rates (single year and multi-year)
  * Last available rates
  * Error responses with codes and details
- Add auto-download functionality for missing monthly data in tax calculations
- Modify calculate_tax_yearly_average to automatically fetch missing months
- Add rate limiting (1s delay) between API calls to be respectful
- Update CLI argument parsing and output logic for JSON/text modes
- Maintain full backward compatibility - existing commands work unchanged
- Enhance documentation with JSON usage examples and schema
- Update help text to include new --json option

Features:
- JSON output for programmatic consumption
- Automatic data fetching for incomplete years
- Structured error handling
- Comprehensive documentation updates

Breaking changes: None (fully backward compatible)
2026-01-12 22:48:59 +01:00
Kadu
51193ab933 Added explanatory comments to clarify the order of conditional statements, especially the importance of checking --stats before the last available rate functionality 2025-08-20 14:06:14 +02:00
Kadu
ee0f05f583 Fixed --stats functionality to properly handle case when no year is provided, ensuring it prints 'Jednotný kurz' for all years with available data rounded to 2 decimal places 2025-08-20 11:40:02 +02:00
3 changed files with 661 additions and 241 deletions

View File

@@ -17,6 +17,8 @@ Tento projekt je určen pro stahování a správu kurzů cizích měn vůči če
- **Automatické stahování ročních dat**: Pokud jsou požadována data pro rok, který v databázi není, program automaticky stáhne roční data pro daný rok, aktualizuje databázi a teprve poté vrátí výsledek. - **Automatické stahování ročních dat**: Pokud jsou požadována data pro rok, který v databázi není, program automaticky stáhne roční data pro daný rok, aktualizuje databázi a teprve poté vrátí výsledek.
- **Generování reportů**: Lze vygenerovat report kurzů pro zadaný rok, měsíc nebo časové období včetně dopočítaných kurzů pro dny, kdy ve vstupních datech neexistovali. - **Generování reportů**: Lze vygenerovat report kurzů pro zadaný rok, měsíc nebo časové období včetně dopočítaných kurzů pro dny, kdy ve vstupních datech neexistovali.
- **Správné dopočítání kurzů**: Program správně aplikuje pravidla ČNB pro dopočítání kurzů pro víkendy a svátky jak při vyhledávání (`--get-rate`), tak při generování reportů. - **Správné dopočítání kurzů**: Program správně aplikuje pravidla ČNB pro dopočítání kurzů pro víkendy a svátky jak při vyhledávání (`--get-rate`), tak při generování reportů.
- **Výpočet Jednotného kurzu**: Lze vypočítat 'Jednotný kurz' pro daňové účely podle metodiky ČNB jako aritmetický průměr kurzů k posledním dnům každého měsíce v roce.
- **JSON výstup**: Všechny příkazy podporují JSON formát pro programové zpracování pomocí přepínače `--json`.
## Požadavky ## Požadavky
@@ -62,6 +64,8 @@ Při každém spuštění programu:
- `--auto-download`: Povolí automatické stahování denních dat pro dnešní datum, pokud je po 14:30 a kurz není k dispozici. - `--auto-download`: Povolí automatické stahování denních dat pro dnešní datum, pokud je po 14:30 a kurz není k dispozici.
- `--report-year ROK [--report-month MESIC]`: Vygeneruje report kurzů pro zadaný rok (a případně měsíc). Vyžaduje `-c` nebo `--currency`. - `--report-year ROK [--report-month MESIC]`: Vygeneruje report kurzů pro zadaný rok (a případně měsíc). Vyžaduje `-c` nebo `--currency`.
- `--report-period ZACATEK KONEC`: Vygeneruje report kurzů pro zadané časové období. Vyžaduje `-c` nebo `--currency`. - `--report-period ZACATEK KONEC`: Vygeneruje report kurzů pro zadané časové období. Vyžaduje `-c` nebo `--currency`.
- `--stats [ROK]`: Vypočítá 'Jednotný kurz' pro daňové účely podle metodiky ČNB. Pokud je zadán rok, vytvoří kurz pro konkrétní rok. Pokud není rok zadán, vytvoří kurzy pro všechny roky s dostupnými daty. Vyžaduje `-c` nebo `--currency`.
- `--json`: Výstup ve formátu JSON místo prostého textu pro programové zpracování.
### Příklady ### Příklady
@@ -111,6 +115,87 @@ Při každém spuštění programu:
python src/cli.py --report-period 01.07.2020 31.07.2020 -c USD python src/cli.py --report-period 01.07.2020 31.07.2020 -c USD
``` ```
10. **Výpočet Jednotného kurzu pro daňové účely pro USD za rok 2025**:
```bash
python src/cli.py --stats 2025 -c USD
```
11. **Výpočet Jednotného kurzu pro daňové účely pro všechny roky s daty**:
```bash
python src/cli.py --stats -c USD
```
12. **Získání posledního dostupného kurzu USD**:
```bash
python src/cli.py -c USD
```
13. **JSON výstup pro vyhledání kurzu**:
```bash
python src/cli.py --get-rate 01.01.2025 -c USD --json
```
14. **JSON výstup pro výpočet Jednotného kurzu**:
```bash
python src/cli.py --stats 2025 -c USD --json
```
## JSON formát
Při použití přepínače `--json` program vrací strukturovaná data ve formátu JSON:
### Jednotlivý kurz
```json
{
"currency": "USD",
"rate": 24.214,
"date": "01.01.2025",
"timestamp": "2025-01-12T10:30:00Z"
}
```
### Jednotný kurz pro jeden rok
```json
{
"currency": "USD",
"year": 2025,
"unified_rate": 21.84,
"calculation_date": "2025-01-12T10:30:00Z"
}
```
### Jednotný kurz pro více let
```json
{
"currency": "USD",
"results": [
{"year": 2023, "unified_rate": 23.45},
{"year": 2024, "unified_rate": 23.28},
{"year": 2025, "unified_rate": 21.84}
],
"calculation_date": "2025-01-12T10:30:00Z"
}
```
### Poslední dostupný kurz
```json
{
"currency": "USD",
"rate": 20.632,
"date": "31.12.2025",
"timestamp": "2025-01-12T10:30:00Z"
}
```
### Chyba
```json
{
"error": "Kurz nebyl nalezen",
"code": "RATE_NOT_FOUND",
"timestamp": "2025-01-12T10:30:00Z"
}
```
## Chování při různých časech a datumech ## Chování při různých časech a datumech
- **Budoucí datum**: Program vrátí chybu, protože kurzy pro budoucí data ještě nebyly vydány. - **Budoucí datum**: Program vrátí chybu, protože kurzy pro budoucí data ještě nebyly vydány.

View File

@@ -3,6 +3,7 @@
import argparse import argparse
import sys import sys
import os import os
import json
from datetime import datetime from datetime import datetime
# Přidání adresáře src do sys.path, aby bylo možné importovat moduly # Přidání adresáře src do sys.path, aby bylo možné importovat moduly
@@ -17,11 +18,13 @@ import rate_reporter
# Global debug flag # Global debug flag
DEBUG = False DEBUG = False
def debug_print(*args, **kwargs): def debug_print(*args, **kwargs):
"""Print debug messages only if debug mode is enabled.""" """Print debug messages only if debug mode is enabled."""
if DEBUG: if DEBUG:
print(*args, **kwargs) print(*args, **kwargs)
def set_debug_mode(debug): def set_debug_mode(debug):
"""Set the debug mode for this module.""" """Set the debug mode for this module."""
global DEBUG global DEBUG
@@ -34,6 +37,75 @@ def set_debug_mode(debug):
rate_finder.set_debug_mode(DEBUG) rate_finder.set_debug_mode(DEBUG)
rate_reporter.set_debug_mode(DEBUG) rate_reporter.set_debug_mode(DEBUG)
def format_single_rate_json(
currency, rate, requested_date, actual_date=None, fallback=False
):
"""Format single rate lookup as JSON."""
data = {
"currency": currency,
"rate": float(rate) if rate is not None else None,
"date": requested_date,
"timestamp": datetime.now().isoformat() + "Z",
}
if fallback and actual_date:
data["actual_date"] = actual_date
data["fallback"] = True
return data
def format_tax_rate_json(currency, year, rate, monthly_rates=None):
"""Format unified tax rate as JSON."""
data = {
"currency": currency,
"year": year,
"unified_rate": float(rate) if rate is not None else None,
"calculation_date": datetime.now().isoformat() + "Z",
}
if monthly_rates:
data["monthly_rates"] = monthly_rates
return data
def format_multi_year_json(currency, year_results):
"""Format multi-year stats as JSON."""
data = {
"currency": currency,
"results": [
{"year": year, "unified_rate": float(rate) if rate is not None else None}
for year, rate in year_results
],
"calculation_date": datetime.now().isoformat() + "Z",
}
return data
def format_last_rate_json(currency, rate, date):
"""Format last available rate as JSON."""
data = {
"currency": currency,
"rate": float(rate) if rate is not None else None,
"date": date,
"timestamp": datetime.now().isoformat() + "Z",
}
return data
def format_error_json(error_msg, error_code=None, details=None):
"""Format error response as JSON."""
data = {"error": error_msg, "timestamp": datetime.now().isoformat() + "Z"}
if error_code:
data["code"] = error_code
if details:
data["details"] = details
return data
def output_json(data):
"""Output data as formatted JSON."""
print(json.dumps(data, indent=2, ensure_ascii=False))
def check_and_update_yearly_data(): def check_and_update_yearly_data():
""" """
Zkontroluje konzistenci ročních dat pro aktuální rok a případně je aktualizuje. Zkontroluje konzistenci ročních dat pro aktuální rok a případně je aktualizuje.
@@ -42,10 +114,14 @@ def check_and_update_yearly_data():
debug_print(f"Kontroluji konzistenci ročních dat pro rok {current_year}...") debug_print(f"Kontroluji konzistenci ročních dat pro rok {current_year}...")
# Zkontrolujeme konzistenci dat # Zkontrolujeme konzistenci dat
is_consistent = data_fetcher.check_yearly_data_consistency(current_year, output_dir="data") is_consistent = data_fetcher.check_yearly_data_consistency(
current_year, output_dir="data"
)
if not is_consistent: if not is_consistent:
debug_print(f"Roční data pro rok {current_year} nejsou konzistentní. Stahuji aktualizovaná data...") debug_print(
f"Roční data pro rok {current_year} nejsou konzistentní. Stahuji aktualizovaná data..."
)
# Ujistěme se, že adresář data existuje # Ujistěme se, že adresář data existuje
os.makedirs("data", exist_ok=True) os.makedirs("data", exist_ok=True)
# Stáhneme roční data znovu # Stáhneme roční data znovu
@@ -53,6 +129,7 @@ def check_and_update_yearly_data():
else: else:
debug_print(f"Roční data pro rok {current_year} jsou aktuální.") debug_print(f"Roční data pro rok {current_year} jsou aktuální.")
def main(): def main():
global DEBUG global DEBUG
@@ -61,69 +138,70 @@ def main():
parser = argparse.ArgumentParser(description="Stahování a správa kurzů měn z ČNB.") parser = argparse.ArgumentParser(description="Stahování a správa kurzů měn z ČNB.")
parser.add_argument( parser.add_argument(
"--year", "--year", type=int, help="Rok, pro který se mají stáhnout data (např. 2020)."
type=int,
help="Rok, pro který se mají stáhnout data (např. 2020)."
) )
parser.add_argument( parser.add_argument(
"-c", "--currency", "-c",
"--currency",
type=str, type=str,
help="Kód měny (např. USD) pro měsíční stahování, vyhledání kurzu nebo generování reportu." help="Kód měny (např. USD) pro měsíční stahování, vyhledání kurzu nebo generování reportu.",
) )
parser.add_argument( parser.add_argument(
"--start-date", "--start-date",
type=str, type=str,
help="Počáteční datum pro měsíční stahování nebo generování reportu ve formátu DD.MM.YYYY." help="Počáteční datum pro měsíční stahování nebo generování reportu ve formátu DD.MM.YYYY.",
) )
parser.add_argument( parser.add_argument(
"--end-date", "--end-date",
type=str, type=str,
help="Koncové datum pro měsíční stahování nebo generování reportu ve formátu DD.MM.YYYY." help="Koncové datum pro měsíční stahování nebo generování reportu ve formátu DD.MM.YYYY.",
) )
parser.add_argument( parser.add_argument(
"--date", "--date",
type=str, type=str,
help="Datum pro stažení denních kurzů ve formátu DD.MM.YYYY." help="Datum pro stažení denních kurzů ve formátu DD.MM.YYYY.",
) )
parser.add_argument( parser.add_argument(
"--get-rate", "-d", "--get-rate",
"-d",
type=str, type=str,
help="Vyhledá kurz pro zadané datum. Formát: DD.MM.YYYY" help="Vyhledá kurz pro zadané datum. Formát: DD.MM.YYYY",
) )
parser.add_argument( parser.add_argument(
"--auto-download", "--auto-download",
action="store_true", action="store_true",
help="Automaticky stáhne denní data, pokud je po 14:30 a kurz pro dnešní datum není k dispozici." help="Automaticky stáhne denní data, pokud je po 14:30 a kurz pro dnešní datum není k dispozici.",
) )
parser.add_argument( parser.add_argument(
"--report-year", "--report-year", type=int, help="Rok, pro který se má vygenerovat report kurzů."
type=int,
help="Rok, pro který se má vygenerovat report kurzů."
) )
parser.add_argument( parser.add_argument(
"--report-month", "--report-month",
type=int, type=int,
help="Měsíc, pro který se má vygenerovat report kurzů (1-12). Vyžaduje --report-year." help="Měsíc, pro který se má vygenerovat report kurzů (1-12). Vyžaduje --report-year.",
) )
parser.add_argument( parser.add_argument(
"--report-period", "--report-period",
nargs=2, nargs=2,
metavar=('START_DATE', 'END_DATE'), metavar=("START_DATE", "END_DATE"),
help="Období, pro které se má vygenerovat report kurzů. Formát: DD.MM.YYYY DD.MM.YYYY" help="Období, pro které se má vygenerovat report kurzů. Formát: DD.MM.YYYY DD.MM.YYYY",
) )
parser.add_argument( parser.add_argument(
"--stats", "--stats",
nargs='?', nargs="?",
const=True, const=True,
type=int, type=int,
help="Vygeneruje 'Jednotný kurz' pro daňové účely podle metodiky ČNB. " help="Vygeneruje 'Jednotný kurz' pro daňové účely podle metodiky ČNB. "
"Pokud je zadán rok, vytvoří kurz pro konkrétní rok. " "Pokud je zadán rok, vytvoří kurz pro konkrétní rok. "
"Pokud není rok zadán, vytvoří kurzy pro všechny roky s dostupnými daty." "Pokud není rok zadán, vytvoří kurzy pro všechny roky s dostupnými daty.",
) )
parser.add_argument( parser.add_argument(
"--debug", "--debug", action="store_true", help="Zobrazí podrobné ladicí informace."
)
parser.add_argument(
"--json",
action="store_true", action="store_true",
help="Zobrazí podrobné ladicí informace." help="Výstup ve formátu JSON místo prostého textu pro programové zpracování.",
) )
args = parser.parse_args() args = parser.parse_args()
@@ -152,7 +230,16 @@ def main():
check_and_update_yearly_data() check_and_update_yearly_data()
else: else:
# V normálním módu zkontrolujeme pouze při stahování dat # V normálním módu zkontrolujeme pouze při stahování dat
if args.year or args.start_date or args.end_date or args.date or args.get_rate or args.report_year or args.report_period or args.stats: if (
args.year
or args.start_date
or args.end_date
or args.date
or args.get_rate
or args.report_year
or args.report_period
or args.stats
):
current_year = datetime.now().year current_year = datetime.now().year
# Pro jednoduchost v normálním módu nebudeme kontrolovat konzistenci automaticky # Pro jednoduchost v normálním módu nebudeme kontrolovat konzistenci automaticky
pass pass
@@ -166,11 +253,23 @@ def main():
data_fetcher.download_yearly_data(args.year, output_dir="data") data_fetcher.download_yearly_data(args.year, output_dir="data")
elif args.currency and args.start_date and args.end_date and not args.report_period: elif args.currency and args.start_date and args.end_date and not args.report_period:
# Měsíční stahování dat # Měsíční stahování dat
debug_print(f"Stahuji měsíční data pro měnu {args.currency} od {args.start_date} do {args.end_date}...") debug_print(
f"Stahuji měsíční data pro měnu {args.currency} od {args.start_date} do {args.end_date}..."
)
# Ujistěme se, že adresář data existuje # Ujistěme se, že adresář data existuje
os.makedirs("data", exist_ok=True) os.makedirs("data", exist_ok=True)
# Volání funkce pro stažení měsíčních dat # Volání funkce pro stažení měsíčních dat
data_fetcher.download_monthly_data(args.currency, args.start_date, args.end_date, output_dir="data") data_fetcher.download_monthly_data(
args.currency, args.start_date, args.end_date, output_dir="data"
)
elif args.report_period and args.currency:
start_date, end_date = args.report_period
debug_print(
f"Generuji report pro měnu {args.currency} od {start_date} do {end_date}..."
)
rate_reporter.generate_period_report(
start_date, end_date, args.currency, output_dir="data"
)
elif args.date: elif args.date:
debug_print(f"Stahuji denní data pro datum {args.date}...") debug_print(f"Stahuji denní data pro datum {args.date}...")
# Ujistěme se, že adresář data existuje # Ujistěme se, že adresář data existuje
@@ -183,100 +282,49 @@ def main():
debug_print(f"Vyhledávám kurz pro {currency_code} na datum {date_str}...") debug_print(f"Vyhledávám kurz pro {currency_code} na datum {date_str}...")
rate = rate_finder.get_rate_for_date(date_str, currency_code) rate = rate_finder.get_rate_for_date(date_str, currency_code)
if rate: if rate:
if args.json:
json_data = format_single_rate_json(currency_code, rate, date_str)
output_json(json_data)
else:
# Pro --get-rate v normálním režimu zobrazíme pouze kurz # Pro --get-rate v normálním režimu zobrazíme pouze kurz
if not DEBUG: if not DEBUG:
print(rate) print(rate)
else: else:
print(f"Kurz {currency_code} na datum {date_str} (nebo nejbližší pracovní den): {rate}") print(
f"Kurz {currency_code} na datum {date_str} (nebo nejbližší pracovní den): {rate}"
)
else: else:
# Pokud nebyl kurz nalezen a je aktivní přepínač --auto-download, zkusíme stáhnout denní data # Rate not found
if args.auto_download: if args.json:
try: error_data = format_error_json(
requested_date = datetime.strptime(date_str, "%d.%m.%Y") f"Kurz {currency_code} na datum {date_str} nebyl nalezen",
today = datetime.now() "RATE_NOT_FOUND",
)
# Zkontrolujeme, zda je požadované datum dnešní output_json(error_data)
if requested_date.date() == today.date():
# Zkontrolujeme, zda je čas po 14:30
if today.time() >= datetime.strptime("14:30", "%H:%M").time():
debug_print("Automaticky stahuji denní data...")
# Ujistěme se, že adresář data existuje
os.makedirs("data", exist_ok=True)
# Stáhneme denní data pro dnešní datum
today_str = today.strftime("%d.%m.%Y")
data_fetcher.download_daily_data(today_str, output_dir="data")
# Zkusíme znovu vyhledat kurz
rate = rate_finder.get_rate_for_date(date_str, currency_code)
if rate:
if not DEBUG:
print(rate)
else:
print(f"Kurz {currency_code} na datum {date_str} (nebo nejbližší pracovní den): {rate}")
else: else:
if not DEBUG: if not DEBUG:
print("Kurz nenalezen") print("Kurz nenalezen")
else: else:
print(f"Kurz {currency_code} na datum {date_str} (ani v předchozích dnech) nebyl nalezen ani po stažení denních dat.") print(
else: f"Kurz {currency_code} na datum {date_str} (ani v předchozích dnech) nebyl nalezen."
if not DEBUG: )
print("Kurz nenalezen")
else:
print(f"Chyba: Automatické stahování nelze provést, protože čas ještě není po 14:30. Aktuální čas je {today.strftime('%H:%M')}.")
else:
if not DEBUG:
print("Kurz nenalezen")
else:
print("Automatické stahování denních dat je možné pouze pro dnešní datum.")
except ValueError:
if not DEBUG:
print("Kurz nenalezen")
else:
print(f"Neplatný formát data: {date_str}")
else:
if not DEBUG:
print("Kurz nenalezen")
else:
print(f"Kurz {currency_code} na datum {date_str} (ani v předchozích dnech) nebyl nalezen.")
elif args.get_rate is not None and not args.currency: elif args.get_rate is not None and not args.currency:
# Pokud je zadán --get-rate bez data a bez měny # Pokud je zadán --get-rate bez data a bez měny
if DEBUG: if DEBUG:
print("Chyba: Pro použití --get-rate musí být zadána měna pomocí -c/--currency.") print(
"Chyba: Pro použití --get-rate musí být zadána měna pomocí -c/--currency."
)
sys.exit(1) sys.exit(1)
elif args.currency and not args.get_rate: # DŮLEŽITÉ: Pořadí následujících elif podmínek je důležité!
# Pokud je zadána měna, ale není zadán --get-rate, vytiskneme poslední dostupný kurz # Nejprve zpracujeme --stats, pak teprve "poslední dostupný kurz"
currency_code = args.currency
debug_print(f"Vyhledávám poslední dostupný kurz pro {currency_code}...")
rate, date = database.get_last_rate_for_currency(currency_code)
if rate and date:
# Pro normální režim zobrazíme kurz ve formátu "11.11 # dated: dd.mm.yyyy"
if not DEBUG:
print(f"{rate} # dated: {date}")
else:
print(f"Poslední dostupný kurz {currency_code}: {rate} # dated: {date}")
else:
if not DEBUG:
print("Kurz nenalezen")
else:
print(f"Poslední dostupný kurz pro {currency_code} nebyl nalezen.")
elif args.report_year and args.currency:
if args.report_month:
# Generování měsíčního reportu
debug_print(f"Generuji měsíční report pro {args.currency} za {args.report_month}/{args.report_year}...")
rate_reporter.generate_monthly_report(args.report_year, args.report_month, args.currency, output_dir="data")
else:
# Generování ročního reportu
debug_print(f"Generuji roční report pro {args.currency} za rok {args.report_year}...")
rate_reporter.generate_yearly_report(args.report_year, args.currency, output_dir="data")
elif args.report_period and args.currency:
# Generování reportu za období
start_date, end_date = args.report_period
debug_print(f"Generuji report pro {args.currency} za období {start_date} - {end_date}...")
rate_reporter.generate_period_report(start_date, end_date, args.currency, output_dir="data")
elif args.stats is not None and args.currency: elif args.stats is not None and args.currency:
# --stats s nebo bez roku + s měnou
currency_code = args.currency currency_code = args.currency
if args.stats is True: if args.stats is True:
# Pokud je --stats zadán bez roku, vytvoříme kurzy pro všechny roky s dostupnými daty # Pokud je --stats zadán bez roku, vytvoříme kurzy pro všechny roky s dostupnými daty
debug_print(f"Generuji 'Jednotný kurz' pro daňové účely podle metodiky ČNB pro {currency_code} pro všechny roky...") debug_print(
f"Generuji 'Jednotný kurz' pro daňové účely podle metodiky ČNB pro {currency_code} pro všechny roky..."
)
# Získáme seznam všech roků s daty # Získáme seznam všech roků s daty
years = database.get_years_with_data() years = database.get_years_with_data()
@@ -288,58 +336,115 @@ def main():
return return
# Pro každý rok vypočítáme 'Jednotný kurz' # Pro každý rok vypočítáme 'Jednotný kurz'
year_results = []
for year in years: for year in years:
# Zkontrolujeme, zda databáze obsahuje data pro daný rok # Zkontrolujeme, zda databáze obsahuje data pro daný rok
if not rate_finder.check_year_data_in_db(year): if not rate_finder.check_year_data_in_db(year):
debug_print(f"Databáze neobsahuje data pro rok {year}. Stahuji roční data...") debug_print(
f"Databáze neobsahuje data pro rok {year}. Stahuji roční data..."
)
# Ujistěme se, že adresář data existuje # Ujistěme se, že adresář data existuje
os.makedirs("data", exist_ok=True) os.makedirs("data", exist_ok=True)
# Stáhneme roční data s vynuceným stažením # Stáhneme roční data s vynuceným stažením
data_fetcher.download_yearly_data(year, output_dir="data", force=True) data_fetcher.download_yearly_data(
year, output_dir="data", force=True
)
# Vypočítáme 'Jednotný kurz' podle metodiky ČNB # Vypočítáme 'Jednotný kurz' podle metodiky ČNB
tax_rate = rate_reporter.calculate_tax_yearly_average(year, currency_code, output_dir="data") tax_rate = rate_reporter.calculate_tax_yearly_average(
year, currency_code, output_dir="data"
)
year_results.append((year, tax_rate))
if not args.json:
if tax_rate: if tax_rate:
# Pro --stats v normálním režimu zobrazíme pouze 'Jednotný kurz' zaokrouhlený na 2 desetinná místa # Pro --stats v normálním režimu zobrazíme pouze 'Jednotný kurz' zaokrouhlený na 2 desetinná místa
if not DEBUG: if not DEBUG:
print(f"{year}: {tax_rate:.2f}") print(f"{year}: {tax_rate:.2f}")
else: else:
print(f"'Jednotný kurz' pro daňové účely podle metodiky ČNB pro {currency_code} za rok {year}: {tax_rate:.2f}") print(
f"'Jednotný kurz' pro daňové účely podle metodiky ČNB pro {currency_code} za rok {year}: {tax_rate:.2f}"
)
else: else:
if not DEBUG: if not DEBUG:
print(f"{year}: 'Jednotný kurz' nenalezen") print(f"{year}: 'Jednotný kurz' nenalezen")
else: else:
print(f"'Jednotný kurz' pro daňové účely podle metodiky ČNB pro {currency_code} za rok {year} nebyl nalezen.") print(
f"'Jednotný kurz' pro daňové účely podle metodiky ČNB pro {currency_code} za rok {year} nebyl nalezen."
)
# Output JSON for multi-year results
if args.json:
json_data = format_multi_year_json(currency_code, year_results)
output_json(json_data)
else: else:
# Pokud je --stats zadán s konkrétním rokem # Pokud je --stats zadán s konkrétním rokem
year = args.stats year = args.stats
debug_print(f"Generuji 'Jednotný kurz' pro daňové účely podle metodiky ČNB pro {currency_code} za rok {year}...") debug_print(
f"Generuji 'Jednotný kurz' pro daňové účely podle metodiky ČNB pro {currency_code} za rok {year}..."
)
# Zkontrolujeme, zda databáze obsahuje data pro daný rok # Zkontrolujeme, zda databáze obsahuje data pro daný rok
if not rate_finder.check_year_data_in_db(year): if not rate_finder.check_year_data_in_db(year):
debug_print(f"Databáze neobsahuje data pro rok {year}. Stahuji roční data...") debug_print(
f"Databáze neobsahuje data pro rok {year}. Stahuji roční data..."
)
# Ujistěme se, že adresář data existuje # Ujistěme se, že adresář data existuje
os.makedirs("data", exist_ok=True) os.makedirs("data", exist_ok=True)
# Stáhneme roční data s vynuceným stažením # Stáhneme roční data s vynuceným stažením
data_fetcher.download_yearly_data(year, output_dir="data", force=True) data_fetcher.download_yearly_data(year, output_dir="data", force=True)
# Vypočítáme 'Jednotný kurz' podle metodiky ČNB # Vypočítáme 'Jednotný kurz' podle metodiky ČNB
tax_rate = rate_reporter.calculate_tax_yearly_average(year, currency_code, output_dir="data") tax_rate = rate_reporter.calculate_tax_yearly_average(
year, currency_code, output_dir="data"
)
if args.json:
json_data = format_tax_rate_json(currency_code, year, tax_rate)
output_json(json_data)
else:
if tax_rate: if tax_rate:
# Pro --stats v normálním režimu zobrazíme pouze 'Jednotný kurz' zaokrouhlený na 2 desetinná místa # Pro --stats v normálním režimu zobrazíme pouze 'Jednotný kurz' zaokrouhlený na 2 desetinná místa
if not DEBUG: if not DEBUG:
print(f"{tax_rate:.2f}") print(f"{tax_rate:.2f}")
else: else:
print(f"'Jednotný kurz' pro daňové účely podle metodiky ČNB pro {currency_code} za rok {year}: {tax_rate:.2f}") print(
f"'Jednotný kurz' pro daňové účely podle metodiky ČNB pro {currency_code} za rok {year}: {tax_rate:.2f}"
)
else: else:
if not DEBUG: if not DEBUG:
print("'Jednotný kurz' nenalezen") print("'Jednotný kurz' nenalezen")
else: else:
print(f"'Jednotný kurz' pro daňové účely podle metodiky ČNB pro {currency_code} za rok {year} nebyl nalezen.") print(
f"'Jednotný kurz' pro daňové účely podle metodiky ČNB pro {currency_code} za rok {year} nebyl nalezen."
)
elif args.currency and not args.get_rate:
# Pokud je zadána měna, ale není zadán --get-rate, vytiskneme poslední dostupný kurz
# Toto musí být až po --stats, jinak by se --stats nikdy nevykonalo
currency_code = args.currency
debug_print(f"Vyhledávám poslední dostupný kurz pro {currency_code}...")
rate, date = database.get_last_rate_for_currency(currency_code)
if args.json:
json_data = format_last_rate_json(currency_code, rate, date)
output_json(json_data)
else:
if rate and date:
# Pro normální režim zobrazíme kurz ve formátu "11.11 # dated: dd.mm.yyyy"
if not DEBUG:
print(f"{rate} # dated: {date}")
else:
print(
f"Poslední dostupný kurz {currency_code}: {rate} # dated: {date}"
)
else:
if not DEBUG:
print("Kurz nenalezen")
else:
print(f"Poslední dostupný kurz pro {currency_code} nebyl nalezen.")
else: else:
if DEBUG: if DEBUG:
parser.print_help() parser.print_help()
sys.exit(1) sys.exit(1)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -1,5 +1,6 @@
import sys import sys
import os import os
import time
from datetime import datetime, timedelta from datetime import datetime, timedelta
import calendar import calendar
@@ -14,16 +15,53 @@ import rate_finder
# Global debug flag # Global debug flag
DEBUG = False DEBUG = False
def debug_print(*args, **kwargs): def debug_print(*args, **kwargs):
"""Print debug messages only if debug mode is enabled.""" """Print debug messages only if debug mode is enabled."""
if DEBUG: if DEBUG:
print(*args, **kwargs) print(*args, **kwargs)
def set_debug_mode(debug): def set_debug_mode(debug):
"""Set the debug mode for this module.""" """Set the debug mode for this module."""
global DEBUG global DEBUG
DEBUG = debug DEBUG = debug
def get_czech_day_name(date_str):
"""
Vrátí český název dne v týdnu pro zadané datum.
:param date_str: Datum ve formátu DD.MM.YYYY
:return: Český název dne v týdnu
"""
try:
date_obj = datetime.strptime(date_str, "%d.%m.%Y")
# Czech day names
czech_days = [
"pondělí",
"úterý",
"středa",
"čtvrtek",
"pátek",
"sobota",
"neděle",
]
return czech_days[date_obj.weekday()]
except ValueError:
return "neznámý den"
def is_holiday(date_str):
"""
Zkontroluje, zda je zadané datum státní svátek.
:param date_str: Datum ve formátu DD.MM.YYYY
:return: True pokud je svátek, jinak False
"""
return holidays.is_holiday(date_str)
def get_rate_for_date_with_fallback(date_str, currency_code): def get_rate_for_date_with_fallback(date_str, currency_code):
""" """
Vyhledá kurz pro zadané datum a měnu. Pokud kurz pro dané datum neexistuje, Vyhledá kurz pro zadané datum a měnu. Pokud kurz pro dané datum neexistuje,
@@ -50,7 +88,7 @@ def get_rate_for_date_with_fallback(date_str, currency_code):
# a pro případnou následující sobotu, neděli či státní svátek" # a pro případnou následující sobotu, neděli či státní svátek"
# #
# To znamená: # To znamená:
# - Pro víkendy a svátky hledáme kurz zpět v čase podle pravidel ČNB # - Pro víkendy a svátky hledáme kurz zpět v čase
# - Pro běžné dny, které nemají kurz, hledáme kurz z posledního pracovního dne před nimi # - Pro běžné dny, které nemají kurz, hledáme kurz z posledního pracovního dne před nimi
# Zkontrolujeme, zda je datum víkend nebo svátek # Zkontrolujeme, zda je datum víkend nebo svátek
@@ -80,13 +118,206 @@ def get_rate_for_date_with_fallback(date_str, currency_code):
check_date_str = current_date.strftime("%d.%m.%Y") check_date_str = current_date.strftime("%d.%m.%Y")
# Zkontrolujeme, zda je to pracovní den # Zkontrolujeme, zda je to pracovní den
if not holidays.is_weekend(check_date_str) and not holidays.is_holiday(check_date_str): if not holidays.is_weekend(check_date_str) and not holidays.is_holiday(
check_date_str
):
rate = database.get_rate(check_date_str, currency_code) rate = database.get_rate(check_date_str, currency_code)
if rate is not None: if rate is not None:
return rate return rate
return None return None
def get_missing_months_for_tax_calculation(year, currency_code):
"""
Vrátí seznam měsíců, pro které chybí kurzy k posledním dnům pro výpočet 'Jednotného kurzu'.
Zahrnuje pouze měsíce, jejichž poslední den je v minulosti (lze stáhnout).
:param year: Rok k ověření
:param currency_code: Kód měny
:return: Seznam měsíců (1-12) s chybějícími kurzy
"""
import calendar
missing_months = []
for month in range(1, 13):
# Získáme poslední den měsíce
last_day = calendar.monthrange(year, month)[1]
date_str = f"{last_day:02d}.{month:02d}.{year}"
# Pokud je datum v budoucnosti, přeskočíme (nelze stáhnout)
date_obj = datetime.strptime(date_str, "%d.%m.%Y")
current_date = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
if date_obj > current_date:
continue
# Zkusíme najít kurz pro dané datum
rate = database.get_rate(date_str, currency_code)
if rate is None:
# Zkusíme dopočítat
calculated_rate = get_rate_for_date_with_fallback(date_str, currency_code)
if calculated_rate is None:
missing_months.append(month)
return missing_months
def _is_year_complete_for_tax_calculation(year):
"""
Zkontroluje, zda je rok kompletní pro výpočet 'Jednotného kurzu' podle metodiky ČNB.
Rok je kompletní, pokud máme kurzy pro poslední den každého měsíce.
:param year: Rok k ověření
:return: True pokud je rok kompletní, jinak False
"""
import calendar
current_year = datetime.now().year
current_month = datetime.now().month
current_day = datetime.now().day
# Pokud je rok v budoucnosti, není kompletní
if year > current_year:
return False
# Pro všechny roky (včetně minulých) zkontrolujeme, zda máme data pro všechny měsíce
for month in range(1, 13):
# Získáme poslední den měsíce
last_day = calendar.monthrange(year, month)[1]
date_str = f"{last_day:02d}.{month:02d}.{year}"
# Pokud je datum v budoucnosti, nemůže být kompletní
date_obj = datetime.strptime(date_str, "%d.%m.%Y")
current_date = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
if date_obj > current_date:
return False
# Zkusíme najít kurz pro dané datum
rate = database.get_rate(date_str, "USD") # Testujeme na USD jako ukázku
if rate is None:
# Zkusíme dopočítat
calculated_rate = get_rate_for_date_with_fallback(date_str, "USD")
if calculated_rate is None:
return False
return True
# Pro aktuální rok zkontrolujeme, zda máme data pro všechny měsíce
for month in range(1, 13):
# Získáme poslední den měsíce
last_day = calendar.monthrange(year, month)[1]
date_str = f"{last_day:02d}.{month:02d}.{year}"
# Pokud je měsíc v budoucnosti, nemůže být kompletní
if year == current_year and month > current_month:
return False
# Zkusíme najít kurz pro dané datum
rate = database.get_rate(date_str, "USD") # Testujeme na USD jako ukázku
if rate is None:
# Zkusíme dopočítat
calculated_rate = get_rate_for_date_with_fallback(date_str, "USD")
if calculated_rate is None:
return False
return True
def calculate_tax_yearly_average(year, currency_code, output_dir="data"):
"""
Vypočítá 'Jednotný kurz' pro daňové účely podle metodiky ČNB.
Jedná se o aritmetický průměr kurzů k posledním dnům každého kalendářního měsíce v roce.
:param year: Rok
:param currency_code: Kód měny (např. USD)
:param output_dir: Adresář s daty
:return: 'Jednotný kurz' jako desetinné číslo, nebo None pokud není k dispozici
"""
debug_print(
f"Vypočítávám 'Jednotný kurz' pro daňové účely podle metodiky ČNB pro {currency_code} za rok {year}..."
)
# Zkusíme stáhnout chybějící měsíční data
missing_months = get_missing_months_for_tax_calculation(year, currency_code)
if missing_months:
debug_print(
f"Nalezeny chybějící měsíce pro rok {year}: {', '.join(f'{m:02d}' for m in missing_months)}. Stahuji měsíční data..."
)
for month in missing_months:
start_date = f"01.{month:02d}.{year}"
last_day = calendar.monthrange(year, month)[1]
end_date = f"{last_day:02d}.{month:02d}.{year}"
debug_print(
f"Stahuji měsíční data pro {currency_code} za {month:02d}/{year}..."
)
data_fetcher.download_monthly_data(
currency_code, start_date, end_date, output_dir="data"
)
# Přidáme zpoždění, abychom nezatěžovali API
time.sleep(1)
# Zkontrolujeme, zda je rok kompletní po stažení dat
if not _is_year_complete_for_tax_calculation(year):
debug_print(
f"Rok {year} není kompletní pro výpočet 'Jednotného kurzu'. Všechny měsíce musí mít dostupné kurzy k posledním dnům."
)
return None
# Zkontrolujeme, zda databáze obsahuje data pro daný rok
if not rate_finder.check_year_data_in_db(year):
debug_print(f"Databáze neobsahuje data pro rok {year}. Stahuji roční data...")
# Stáhneme roční data s vynuceným stažením
os.makedirs("data", exist_ok=True)
data_fetcher.download_yearly_data(year, output_dir="data", force=True)
# Získáme seznam posledních dní všech měsíců v roce
monthly_rates = []
monthly_dates = []
debug_print(f"Získávám kurzy k posledním dnům všech měsíců v roce {year}...")
for month in range(1, 13):
# Získáme poslední den měsíce
last_day = calendar.monthrange(year, month)[1]
date_str = f"{last_day:02d}.{month:02d}.{year}"
# Získáme kurz pro dané datum
rate = database.get_rate(date_str, currency_code)
if rate is not None:
monthly_rates.append(rate)
monthly_dates.append(date_str)
debug_print(f"Měsíc {month:02d}: {date_str} = {rate}")
else:
# Kurz nebyl nalezen, zkusíme dopočítat
calculated_rate = get_rate_for_date_with_fallback(date_str, currency_code)
if calculated_rate is not None:
monthly_rates.append(calculated_rate)
monthly_dates.append(date_str)
debug_print(
f"Měsíc {month:02d}: {date_str} = {calculated_rate} (dopočítaný kurz)"
)
else:
debug_print(f"Měsíc {month:02d}: {date_str} = kurz nenalezen")
debug_print(f"Počet měsíců s kurzy: {len(monthly_rates)}/12")
# Musíme mít kurzy pro všech 12 měsíců
if len(monthly_rates) != 12:
debug_print(
f"Varování: Nenalezeny kurzy pro všech 12 měsíců ({len(monthly_rates)}/12)"
)
return None
# Výpočet aritmetického průměru
average = sum(monthly_rates) / len(monthly_rates)
debug_print(f"Součet kurzů: {sum(monthly_rates):.6f}")
debug_print(f"Počet měsíců: {len(monthly_rates)}")
debug_print(f"'Jednotný kurz' pro daňové účely: {average:.6f}")
return average
def generate_yearly_report(year, currency_code, output_dir="data"): def generate_yearly_report(year, currency_code, output_dir="data"):
""" """
Vygeneruje report kurzů pro zadaný rok a měnu. Vygeneruje report kurzů pro zadaný rok a měnu.
@@ -125,7 +356,7 @@ def generate_yearly_report(year, currency_code, output_dir="data"):
filepath = os.path.join(output_dir, filename) filepath = os.path.join(output_dir, filename)
try: try:
with open(filepath, 'w', newline='', encoding='utf-8') as csvfile: with open(filepath, "w", newline="", encoding="utf-8") as csvfile:
# Nové pořadí sloupců: Datum, Kurz, Den v týdnu, Svátek, Poznámka # Nové pořadí sloupců: Datum, Kurz, Den v týdnu, Svátek, Poznámka
csvfile.write("Datum,Kurz,Den v týdnu,Svátek,Poznámka\n") csvfile.write("Datum,Kurz,Den v týdnu,Svátek,Poznámka\n")
@@ -149,7 +380,9 @@ def generate_yearly_report(year, currency_code, output_dir="data"):
pass pass
else: else:
# Kurz nebyl nalezen, zkusíme dopočítat # Kurz nebyl nalezen, zkusíme dopočítat
calculated_rate = get_rate_for_date_with_fallback(date_str, currency_code) calculated_rate = get_rate_for_date_with_fallback(
date_str, currency_code
)
if calculated_rate is not None: if calculated_rate is not None:
rate = calculated_rate rate = calculated_rate
note = "Dopočítaný kurz" note = "Dopočítaný kurz"
@@ -160,7 +393,9 @@ def generate_yearly_report(year, currency_code, output_dir="data"):
note = "Kurz není k dispozici" note = "Kurz není k dispozici"
# Zapišeme řádek do CSV # Zapišeme řádek do CSV
csvfile.write(f"{date_str},{rate if rate is not None else ''},{day_name},{holiday_text},{note}\n") csvfile.write(
f"{date_str},{rate if rate is not None else ''},{day_name},{holiday_text},{note}\n"
)
# Přejdeme na další den # Přejdeme na další den
current_date += timedelta(days=1) current_date += timedelta(days=1)
@@ -171,6 +406,7 @@ def generate_yearly_report(year, currency_code, output_dir="data"):
debug_print(f"Chyba při zápisu do souboru: {e}") debug_print(f"Chyba při zápisu do souboru: {e}")
return None return None
def generate_monthly_report(year, month, currency_code, output_dir="data"): def generate_monthly_report(year, month, currency_code, output_dir="data"):
""" """
Vygeneruje report kurzů pro zadaný měsíc, rok a měnu. Vygeneruje report kurzů pro zadaný měsíc, rok a měnu.
@@ -212,7 +448,7 @@ def generate_monthly_report(year, month, currency_code, output_dir="data"):
filepath = os.path.join(output_dir, filename) filepath = os.path.join(output_dir, filename)
try: try:
with open(filepath, 'w', newline='', encoding='utf-8') as csvfile: with open(filepath, "w", newline="", encoding="utf-8") as csvfile:
# Nové pořadí sloupců: Datum, Kurz, Den v týdnu, Svátek, Poznámka # Nové pořadí sloupců: Datum, Kurz, Den v týdnu, Svátek, Poznámka
csvfile.write("Datum,Kurz,Den v týdnu,Svátek,Poznámka\n") csvfile.write("Datum,Kurz,Den v týdnu,Svátek,Poznámka\n")
@@ -236,7 +472,9 @@ def generate_monthly_report(year, month, currency_code, output_dir="data"):
pass pass
else: else:
# Kurz nebyl nalezen, zkusíme dopočítat # Kurz nebyl nalezen, zkusíme dopočítat
calculated_rate = get_rate_for_date_with_fallback(date_str, currency_code) calculated_rate = get_rate_for_date_with_fallback(
date_str, currency_code
)
if calculated_rate is not None: if calculated_rate is not None:
rate = calculated_rate rate = calculated_rate
note = "Dopočítaný kurz" note = "Dopočítaný kurz"
@@ -247,7 +485,9 @@ def generate_monthly_report(year, month, currency_code, output_dir="data"):
note = "Kurz není k dispozici" note = "Kurz není k dispozici"
# Zapišeme řádek do CSV # Zapišeme řádek do CSV
csvfile.write(f"{date_str},{rate if rate is not None else ''},{day_name},{holiday_text},{note}\n") csvfile.write(
f"{date_str},{rate if rate is not None else ''},{day_name},{holiday_text},{note}\n"
)
# Přejdeme na další den # Přejdeme na další den
current_date += timedelta(days=1) current_date += timedelta(days=1)
@@ -258,7 +498,10 @@ def generate_monthly_report(year, month, currency_code, output_dir="data"):
debug_print(f"Chyba při zápisu do souboru: {e}") debug_print(f"Chyba při zápisu do souboru: {e}")
return None return None
def generate_period_report(start_date_str, end_date_str, currency_code, output_dir="data"):
def generate_period_report(
start_date_str, end_date_str, currency_code, output_dir="data"
):
""" """
Vygeneruje report kurzů pro zadané období a měnu. Vygeneruje report kurzů pro zadané období a měnu.
@@ -268,7 +511,9 @@ def generate_period_report(start_date_str, end_date_str, currency_code, output_d
:param output_dir: Adresář, kam se má CSV soubor s reportem uložit. :param output_dir: Adresář, kam se má CSV soubor s reportem uložit.
:return: Chta k vytvořenému CSV souboru. :return: Chta k vytvořenému CSV souboru.
""" """
debug_print(f"Generuji report kurzů pro {currency_code} za období {start_date_str} - {end_date_str}...") debug_print(
f"Generuji report kurzů pro {currency_code} za období {start_date_str} - {end_date_str}..."
)
try: try:
start_date = datetime.strptime(start_date_str, "%d.%m.%Y") start_date = datetime.strptime(start_date_str, "%d.%m.%Y")
@@ -281,12 +526,16 @@ def generate_period_report(start_date_str, end_date_str, currency_code, output_d
today = datetime.now() today = datetime.now()
# Pokud je požadované období v budoucnosti, nepokračujeme # Pokud je požadované období v budoucnosti, nepokračujeme
if start_date.date() > today.date(): if start_date.date() > today.date():
debug_print(f"Chyba: Nelze generovat report pro období v budoucnosti (od {start_date_str}).") debug_print(
f"Chyba: Nelze generovat report pro období v budoucnosti (od {start_date_str})."
)
return None return None
# Pokud je konec období v budoucnosti, omezíme ho na dnešní datum # Pokud je konec období v budoucnosti, omezíme ho na dnešní datum
if end_date.date() > today.date(): if end_date.date() > today.date():
debug_print(f"Upozornění: Konec období byl omezen na dnešní datum ({today.strftime('%d.%m.%Y')}), protože zbytek je v budoucnosti.") debug_print(
f"Upozornění: Konec období byl omezen na dnešní datum ({today.strftime('%d.%m.%Y')}), protože zbytek je v budoucnosti."
)
end_date = today end_date = today
# Zkontrolujeme, zda databáze obsahuje data pro roky v rozmezí # Zkontrolujeme, zda databáze obsahuje data pro roky v rozmezí
@@ -298,14 +547,14 @@ def generate_period_report(start_date_str, end_date_str, currency_code, output_d
debug_print(f"Stahuji roční data pro rok {year}...") debug_print(f"Stahuji roční data pro rok {year}...")
# Stáhneme roční data s vynuceným stažením # Stáhneme roční data s vynuceným stažením
os.makedirs("data", exist_ok=True) os.makedirs("data", exist_ok=True)
data_fetcher.download_yearly_data(year, output_dir="data", force=True) data_fetcher.download_yearly_data(year, output_dir="data")
# Otevřeme CSV soubor pro zápis # Otevřeme CSV soubor pro zápis
filename = f"report_{currency_code}_{start_date_str.replace('.', '_')}_to_{end_date_str.replace('.', '_')}.csv" filename = f"report_{currency_code}_{start_date_str.replace('.', '_')}_to_{end_date_str.replace('.', '_')}.csv"
filepath = os.path.join(output_dir, filename) filepath = os.path.join(output_dir, filename)
try: try:
with open(filepath, 'w', newline='', encoding='utf-8') as csvfile: with open(filepath, "w", newline="", encoding="utf-8") as csvfile:
# Nové pořadí sloupců: Datum, Kurz, Den v týdnu, Svátek, Poznámka # Nové pořadí sloupců: Datum, Kurz, Den v týdnu, Svátek, Poznámka
csvfile.write("Datum,Kurz,Den v týdnu,Svátek,Poznámka\n") csvfile.write("Datum,Kurz,Den v týdnu,Svátek,Poznámka\n")
@@ -329,7 +578,9 @@ def generate_period_report(start_date_str, end_date_str, currency_code, output_d
pass pass
else: else:
# Kurz nebyl nalezen, zkusíme dopočítat # Kurz nebyl nalezen, zkusíme dopočítat
calculated_rate = get_rate_for_date_with_fallback(date_str, currency_code) calculated_rate = get_rate_for_date_with_fallback(
date_str, currency_code
)
if calculated_rate is not None: if calculated_rate is not None:
rate = calculated_rate rate = calculated_rate
note = "Dopočítaný kurz" note = "Dopočítaný kurz"
@@ -340,7 +591,9 @@ def generate_period_report(start_date_str, end_date_str, currency_code, output_d
note = "Kurz není k dispozici" note = "Kurz není k dispozici"
# Zapišeme řádek do CSV # Zapišeme řádek do CSV
csvfile.write(f"{date_str},{rate if rate is not None else ''},{day_name},{holiday_text},{note}\n") csvfile.write(
f"{date_str},{rate if rate is not None else ''},{day_name},{holiday_text},{note}\n"
)
# Přejdeme na další den # Přejdeme na další den
current_date += timedelta(days=1) current_date += timedelta(days=1)
@@ -351,29 +604,6 @@ def generate_period_report(start_date_str, end_date_str, currency_code, output_d
debug_print(f"Chyba při zápisu do souboru: {e}") debug_print(f"Chyba při zápisu do souboru: {e}")
return None return None
def get_czech_day_name(date_str):
"""
Vrátí český název dne v týdnu pro zadané datum.
:param date_str: Datum ve formátu DD.MM.YYYY
:return: Český název dne v týdnu
"""
try:
date_obj = datetime.strptime(date_str, "%d.%m.%Y")
# Czech day names
czech_days = ["pondělí", "úterý", "středa", "čtvrtek", "pátek", "sobota", "neděle"]
return czech_days[date_obj.weekday()]
except ValueError:
return "neznámý den"
def is_holiday(date_str):
"""
Zkontroluje, zda je zadané datum státní svátek.
:param date_str: Datum ve formátu DD.MM.YYYY
:return: True pokud je svátek, jinak False
"""
return holidays.is_holiday(date_str)
# Příklad použití # Příklad použití
if __name__ == "__main__": if __name__ == "__main__":