first commit
This commit is contained in:
330
src/data_fetcher.py
Normal file
330
src/data_fetcher.py
Normal file
@@ -0,0 +1,330 @@
|
||||
import requests
|
||||
import os
|
||||
import csv
|
||||
from datetime import datetime, timedelta
|
||||
import sys
|
||||
|
||||
# Přidání adresáře src do sys.path, aby bylo možné importovat moduly
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__)))
|
||||
|
||||
import database
|
||||
import holidays
|
||||
|
||||
# Definice URL pro stahování dat
|
||||
YEARLY_DATA_URL = "https://www.cnb.cz/cs/financni-trhy/devizovy-trh/kurzy-devizoveho-trhu/kurzy-devizoveho-trhu/rok.txt"
|
||||
MONTHLY_DATA_URL = "https://www.cnb.cz/cs/financni-trhy/devizovy-trh/kurzy-devizoveho-trhu/kurzy-devizoveho-trhu/vybrane.txt"
|
||||
DAILY_DATA_URL = "https://www.cnb.cz/cs/financni-trhy/devizovy-trh/kurzy-devizoveho-trhu/kurzy-devizoveho-trhu/denni_kurz.txt"
|
||||
|
||||
# Export cesty k databázovému souboru
|
||||
DB_PATH = database.DB_PATH
|
||||
|
||||
def check_yearly_data_consistency(year, output_dir="data"):
|
||||
"""
|
||||
Zkontroluje, zda roční data pro zadaný rok obsahují záznamy za poslední 3 pracovní dny.
|
||||
|
||||
:param year: Rok, pro který se má zkontrolovat konzistence dat.
|
||||
:param output_dir: Adresář, kde se nachází CSV soubor s ročními daty.
|
||||
:return: True pokud jsou data konzistentní, jinak False.
|
||||
"""
|
||||
filename = f"{year}.csv"
|
||||
filepath = os.path.join(output_dir, filename)
|
||||
|
||||
# Zkontrolujeme, zda soubor existuje
|
||||
if not os.path.exists(filepath):
|
||||
print(f"Soubor {filepath} neexistuje.")
|
||||
return False
|
||||
|
||||
# Načteme data z CSV souboru
|
||||
try:
|
||||
with open(filepath, 'r', encoding='utf-8') as csvfile:
|
||||
reader = csv.reader(csvfile)
|
||||
lines = list(reader)
|
||||
except IOError as e:
|
||||
print(f"Chyba při čtení souboru {filepath}: {e}")
|
||||
return False
|
||||
|
||||
if len(lines) < 2:
|
||||
print(f"Soubor {filepath} je prázdný nebo obsahuje pouze hlavičku.")
|
||||
return False
|
||||
|
||||
# Získáme seznam dat z CSV (první sloupec)
|
||||
dates_in_file = []
|
||||
for line in lines[1:]: # Přeskočíme hlavičku
|
||||
if line and line[0]: # Zkontrolujeme, že řádek a první sloupec nejsou prázdné
|
||||
dates_in_file.append(line[0])
|
||||
|
||||
if not dates_in_file:
|
||||
print(f"Soubor {filepath} neobsahuje žádná data.")
|
||||
return False
|
||||
|
||||
# Zkontrolujeme poslední 3 pracovní dny
|
||||
today = datetime.now()
|
||||
checked_dates = []
|
||||
|
||||
# Projdeme posledních 7 dní (pro jistotu) a vybereme 3 pracovní dny
|
||||
current_date = today - timedelta(days=1) # Začneme včerejškem
|
||||
days_checked = 0
|
||||
|
||||
while len(checked_dates) < 3 and days_checked < 7:
|
||||
date_str = current_date.strftime("%d.%m.%Y")
|
||||
|
||||
# Zkontrolujeme, zda je to pracovní den (není víkend ani svátek)
|
||||
if not holidays.is_weekend(date_str) and not holidays.is_holiday(date_str):
|
||||
# Zkontrolujeme, zda je rok správný
|
||||
try:
|
||||
date_obj = datetime.strptime(date_str, "%d.%m.%Y")
|
||||
if date_obj.year == year:
|
||||
checked_dates.append(date_str)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
current_date -= timedelta(days=1)
|
||||
days_checked += 1
|
||||
|
||||
# Pokud nemáme žádné pracovní dny k ověření, vrátíme True (nemáme co kontrolovat)
|
||||
if not checked_dates:
|
||||
print(f"Pro rok {year} nebyly nalezeny žádné pracovní dny k ověření v posledním týdnu.")
|
||||
return True
|
||||
|
||||
# Zkontrolujeme, zda všechny pracovní dny jsou v souboru
|
||||
missing_dates = []
|
||||
for date_str in checked_dates:
|
||||
if date_str not in dates_in_file:
|
||||
missing_dates.append(date_str)
|
||||
|
||||
if missing_dates:
|
||||
print(f"V souboru {filepath} chybí záznamy pro následující pracovní dny: {', '.join(missing_dates)}")
|
||||
return False
|
||||
else:
|
||||
print(f"Roční data pro rok {year} jsou konzistentní.")
|
||||
return True
|
||||
|
||||
def download_yearly_data(year, output_dir="data", force=False):
|
||||
"""
|
||||
Stáhne roční data z CNB a uloží je do CSV souboru a databáze.
|
||||
|
||||
:param year: Rok, pro který se mají stáhnout data (např. 2020).
|
||||
:param output_dir: Adresář, kam se má CSV soubor uložit.
|
||||
:param force: Pokud je True, data se stáhnou i v případě, že již existují a jsou konzistentní.
|
||||
:return: Cesta k vytvořenému CSV souboru.
|
||||
"""
|
||||
# Pokud není vynucené stahování, zkontrolujeme konzistenci existujících dat
|
||||
if not force:
|
||||
if check_yearly_data_consistency(year, output_dir):
|
||||
print(f"Roční data pro rok {year} jsou aktuální. Není nutné je stahovat znovu.")
|
||||
return os.path.join(output_dir, f"{year}.csv")
|
||||
|
||||
url = f"{YEARLY_DATA_URL}?rok={year}"
|
||||
print(f"Stahuji data z: {url}")
|
||||
|
||||
try:
|
||||
response = requests.get(url)
|
||||
response.raise_for_status() # Vyvolá výjimku pro chybné HTTP kódy
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Chyba při stahování dat: {e}")
|
||||
return None
|
||||
|
||||
# Vytvoření jména souboru
|
||||
filename = f"{year}.csv"
|
||||
filepath = os.path.join(output_dir, filename)
|
||||
|
||||
# Zpracování dat a uložení do CSV
|
||||
lines = response.text.strip().split('\n')
|
||||
if not lines:
|
||||
print("Stažený soubor je prázdný.")
|
||||
return None
|
||||
|
||||
# První řádek obsahuje hlavičku
|
||||
header = lines[0].split('|')
|
||||
# Převod názvů sloupců do formátu vhodného pro CSV (např. nahrazení mezer podtržítky)
|
||||
header = [col.replace(' ', '_').replace('-', '_') for col in header]
|
||||
|
||||
# Zpracování datových řádků
|
||||
data_rows = []
|
||||
for line in lines[1:]:
|
||||
if not line.strip():
|
||||
continue
|
||||
# Nahrazení českých desetinných čárek za tečky
|
||||
row = [item.replace(',', '.') for item in line.split('|')]
|
||||
data_rows.append(row)
|
||||
|
||||
# Uložení do databáze
|
||||
date_str = row[0] # Datum je vždy v prvním sloupci
|
||||
for i in range(1, len(header)):
|
||||
col_name = header[i]
|
||||
if col_name == "Datum":
|
||||
continue
|
||||
|
||||
# Název sloupce je ve formátu "1_USD", "100_JPY" atd.
|
||||
# Rozdělíme ho na množství a kód měny
|
||||
parts = col_name.split('_')
|
||||
if len(parts) >= 2:
|
||||
try:
|
||||
amount = int(parts[0])
|
||||
currency_code = '_'.join(parts[1:]) # Pro případ měn se složeným názvem
|
||||
rate = float(row[i])
|
||||
database.insert_rate(date_str, currency_code, amount, rate)
|
||||
except (ValueError, IndexError):
|
||||
# Přeskočit sloupce, které nelze parsovat
|
||||
continue
|
||||
|
||||
# Zápis do CSV souboru
|
||||
try:
|
||||
with open(filepath, 'w', newline='', encoding='utf-8') as csvfile:
|
||||
writer = csv.writer(csvfile)
|
||||
writer.writerow(header)
|
||||
writer.writerows(data_rows)
|
||||
print(f"Data byla úspěšně uložena do: {filepath}")
|
||||
return filepath
|
||||
except IOError as e:
|
||||
print(f"Chyba při zápisu do souboru: {e}")
|
||||
return None
|
||||
|
||||
def download_monthly_data(currency_code, start_date, end_date, output_dir="data"):
|
||||
"""
|
||||
Stáhne měsíční data pro zadanou měnu a časové období z CNB a uloží je do CSV souboru a databáze.
|
||||
|
||||
:param currency_code: Kód měny (např. USD).
|
||||
:param start_date: Počáteční datum ve formátu DD.MM.YYYY.
|
||||
:param end_date: Koncové datum ve formátu DD.MM.YYYY.
|
||||
:param output_dir: Adresář, kam se má CSV soubor uložit.
|
||||
:return: Cesta k vytvořenému CSV souboru.
|
||||
"""
|
||||
url = f"{MONTHLY_DATA_URL}?od={start_date}&do={end_date}&mena={currency_code}&format=txt"
|
||||
print(f"Stahuji data z: {url}")
|
||||
|
||||
try:
|
||||
response = requests.get(url)
|
||||
response.raise_for_status()
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Chyba při stahování dat: {e}")
|
||||
return None
|
||||
|
||||
# Vytvoření jména souboru
|
||||
filename = f"{start_date.replace('.', '_')}_to_{end_date.replace('.', '_')}_{currency_code}.csv"
|
||||
filepath = os.path.join(output_dir, filename)
|
||||
|
||||
# Zpracování dat a uložení do CSV
|
||||
lines = response.text.strip().split('\n')
|
||||
if not lines:
|
||||
print("Stažený soubor je prázdný.")
|
||||
return None
|
||||
|
||||
# První řádek obsahuje informace o měně, druhý hlavičku dat
|
||||
currency_info = lines[0]
|
||||
header = lines[1].split('|')
|
||||
header = [col.replace(' ', '_').replace('-', '_') for col in header]
|
||||
|
||||
# Zpracování datových řádků
|
||||
data_rows = []
|
||||
for line in lines[2:]:
|
||||
if not line.strip():
|
||||
continue
|
||||
row = [item.replace(',', '.') for item in line.split('|')]
|
||||
data_rows.append(row)
|
||||
|
||||
# Uložení do databáze
|
||||
# Formát řádku: Datum|Kurz
|
||||
if len(row) >= 2:
|
||||
date_str = row[0]
|
||||
try:
|
||||
# Z currency_info získáme množství
|
||||
# Příklad: "Měna: USD|Množství: 1"
|
||||
parts = currency_info.split('|')
|
||||
amount_part = next((p for p in parts if p.startswith("Množství:")), None)
|
||||
if amount_part:
|
||||
amount = int(amount_part.split(':')[1].strip())
|
||||
rate = float(row[1])
|
||||
database.insert_rate(date_str, currency_code, amount, rate)
|
||||
except (ValueError, IndexError):
|
||||
print(f"Chyba při parsování řádku: {line}")
|
||||
|
||||
# Zápis do CSV souboru
|
||||
try:
|
||||
with open(filepath, 'w', newline='', encoding='utf-8') as csvfile:
|
||||
csvfile.write(f"{currency_info}\n") # Uložení informací o měně
|
||||
writer = csv.writer(csvfile)
|
||||
writer.writerow(header)
|
||||
writer.writerows(data_rows)
|
||||
print(f"Data byla úspěšně uložena do: {filepath}")
|
||||
return filepath
|
||||
except IOError as e:
|
||||
print(f"Chyba při zápisu do souboru: {e}")
|
||||
return None
|
||||
|
||||
def download_daily_data(date, output_dir="data"):
|
||||
"""
|
||||
Stáhne denní data z CNB pro zadané datum a uloží je do CSV souboru a databáze.
|
||||
|
||||
:param date: Datum ve formátu DD.MM.YYYY.
|
||||
:param output_dir: Adresář, kam se má CSV soubor uložit.
|
||||
:return: Cesta k vytvořenému CSV souboru.
|
||||
"""
|
||||
url = f"{DAILY_DATA_URL}?date={date}"
|
||||
print(f"Stahuji data z: {url}")
|
||||
|
||||
try:
|
||||
response = requests.get(url)
|
||||
response.raise_for_status()
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Chyba při stahování dat: {e}")
|
||||
return None
|
||||
|
||||
# Vytvoření jména souboru
|
||||
filename = f"daily_{date.replace('.', '_')}.csv"
|
||||
filepath = os.path.join(output_dir, filename)
|
||||
|
||||
# Zpracování dat a uložení do CSV
|
||||
lines = response.text.strip().split('\n')
|
||||
if not lines:
|
||||
print("Stažený soubor je prázdný.")
|
||||
return None
|
||||
|
||||
# První řádek obsahuje datum a číslo, druhý hlavičku
|
||||
date_info = lines[0]
|
||||
header = lines[1].split('|')
|
||||
header = [col.replace(' ', '_').replace('-', '_') for col in header]
|
||||
|
||||
# Zpracování datových řádků
|
||||
data_rows = []
|
||||
for line in lines[2:]:
|
||||
if not line.strip():
|
||||
continue
|
||||
row = [item.replace(',', '.') for item in line.split('|')]
|
||||
data_rows.append(row)
|
||||
|
||||
# Uložení do databáze
|
||||
# Formát řádku: země|měna|množství|kód|kurz
|
||||
if len(row) >= 5:
|
||||
try:
|
||||
# country = row[0]
|
||||
# currency_name = row[1]
|
||||
amount = int(row[2])
|
||||
currency_code = row[3]
|
||||
rate = float(row[4])
|
||||
database.insert_rate(date, currency_code, amount, rate)
|
||||
except (ValueError, IndexError):
|
||||
print(f"Chyba při parsování řádku: {line}")
|
||||
|
||||
# Zápis do CSV souboru
|
||||
try:
|
||||
with open(filepath, 'w', newline='', encoding='utf-8') as csvfile:
|
||||
csvfile.write(f"{date_info}\n") # Uložení informací o datu
|
||||
writer = csv.writer(csvfile)
|
||||
writer.writerow(header)
|
||||
writer.writerows(data_rows)
|
||||
print(f"Data byla úspěšně uložena do: {filepath}")
|
||||
return filepath
|
||||
except IOError as e:
|
||||
print(f"Chyba při zápisu do souboru: {e}")
|
||||
return None
|
||||
|
||||
# Příklad použití
|
||||
if __name__ == "__main__":
|
||||
# Pro testování lze spustit tento skript samostatně
|
||||
database.init_db()
|
||||
downloaded_file = download_yearly_data(2020)
|
||||
if downloaded_file:
|
||||
print(f"Stažený soubor: {downloaded_file}")
|
||||
else:
|
||||
print("Stažení se nezdařilo.")
|
||||
Reference in New Issue
Block a user