z230
This commit is contained in:
172
40 fio 01.py
Normal file
172
40 fio 01.py
Normal file
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Import Fio banka CSV export (UTF-8, ; separated, quoted)
|
||||
into MySQL database `fio.transactions`.
|
||||
|
||||
- Datum je vždy ve formátu dd.mm.yyyy.
|
||||
- Přidává diagnostický fingerprint do sloupce `uniq_fp` (indexovaný).
|
||||
- Zatím NEblokuje duplicity; jen je snadno zobrazí pro ladění.
|
||||
"""
|
||||
|
||||
import csv
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
import pymysql
|
||||
from pymysql.cursors import DictCursor
|
||||
|
||||
# ======== CONFIG ========
|
||||
CSV_PATH = Path(r"u:\Dropbox\!!!Days\Downloads Z230\Vyhledane pohyby (1).csv")
|
||||
TABLE_NAME = "transactions"
|
||||
|
||||
MYSQL_CONFIG = {
|
||||
"host": "192.168.1.76",
|
||||
"port": 3307,
|
||||
"user": "root",
|
||||
"password": "Vlado9674+",
|
||||
"database": "fio",
|
||||
"charset": "utf8mb4",
|
||||
"cursorclass": DictCursor,
|
||||
"autocommit": True,
|
||||
}
|
||||
|
||||
# ---------- helpers ----------
|
||||
def clean(s: str):
|
||||
"""Normalize text for consistent comparison (trim + lowercase)."""
|
||||
if not s:
|
||||
return None
|
||||
s = s.strip().lower()
|
||||
return s or None
|
||||
|
||||
def build_fingerprint(data: dict) -> str:
|
||||
"""Sestaví diagnostický fingerprint z normalizovaných polí."""
|
||||
parts = [
|
||||
data["datum"].strftime("%Y-%m-%d") if data["datum"] else "",
|
||||
f"{data['objem']:.2f}" if data["objem"] is not None else "",
|
||||
data.get("cislo_uctu") or "",
|
||||
data.get("protiucet") or "",
|
||||
data.get("kod_banky") or "",
|
||||
data.get("vs") or "",
|
||||
(data.get("zprava") or "")[:100],
|
||||
(data.get("poznamka") or "")[:100],
|
||||
]
|
||||
return "|".join(parts)
|
||||
|
||||
# ======== DB SETUP ========
|
||||
def get_mysql_connection():
|
||||
return pymysql.connect(**MYSQL_CONFIG)
|
||||
|
||||
def ensure_table_exists(conn):
|
||||
sql_create = f"""
|
||||
CREATE TABLE IF NOT EXISTS `{TABLE_NAME}` (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
datum DATE,
|
||||
objem DECIMAL(12,2),
|
||||
mena CHAR(3),
|
||||
cislo_uctu VARCHAR(40),
|
||||
protiucet VARCHAR(40),
|
||||
kod_banky VARCHAR(20),
|
||||
ks VARCHAR(20),
|
||||
vs VARCHAR(20),
|
||||
ss VARCHAR(20),
|
||||
zprava VARCHAR(500),
|
||||
poznamka VARCHAR(500),
|
||||
uniq_fp VARCHAR(512),
|
||||
imported_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
"""
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(sql_create)
|
||||
|
||||
# ✅ přidej sloupec pouze pokud chybí
|
||||
cur.execute(f"SHOW COLUMNS FROM `{TABLE_NAME}` LIKE 'uniq_fp'")
|
||||
if not cur.fetchone():
|
||||
cur.execute(f"ALTER TABLE `{TABLE_NAME}` ADD COLUMN uniq_fp VARCHAR(512) NULL")
|
||||
print("🆕 Sloupec uniq_fp přidán.")
|
||||
|
||||
# ✅ vytvoř index pokud ještě není
|
||||
cur.execute(f"SHOW INDEX FROM `{TABLE_NAME}` WHERE Key_name = 'idx_uniq_fp'")
|
||||
if not cur.fetchone():
|
||||
cur.execute(f"CREATE INDEX idx_uniq_fp ON `{TABLE_NAME}` (uniq_fp)")
|
||||
print("🆕 Index idx_uniq_fp vytvořen.")
|
||||
|
||||
print("✅ Tabulka a index zkontrolovány.")
|
||||
|
||||
# ======== IMPORT ========
|
||||
def import_fio_csv():
|
||||
with open(CSV_PATH, "r", encoding="utf-8-sig", newline="") as f:
|
||||
reader = csv.DictReader(f, delimiter=";", quotechar='"')
|
||||
rows = list(reader)
|
||||
|
||||
with get_mysql_connection() as conn:
|
||||
ensure_table_exists(conn)
|
||||
inserted = 0
|
||||
|
||||
for row in rows:
|
||||
# --- DATUM ---
|
||||
raw_date = (row.get("Datum") or "").strip()
|
||||
try:
|
||||
datum = datetime.strptime(raw_date, "%d.%m.%Y").date() if raw_date else None
|
||||
except ValueError:
|
||||
datum = None # případně continue, pokud chceš řádek zahodit
|
||||
|
||||
# --- OBJEM ---
|
||||
objem_str = (row.get("Objem") or "").replace(" ", "").replace(",", ".")
|
||||
try:
|
||||
objem = float(objem_str)
|
||||
except ValueError:
|
||||
objem = None
|
||||
|
||||
# --- normalizace textů ---
|
||||
data = {
|
||||
"datum": datum,
|
||||
"objem": objem,
|
||||
"mena": clean(row.get("Měna")),
|
||||
"cislo_uctu": clean(row.get("Číslo účtu")),
|
||||
"protiucet": clean(row.get("Protiúčet")),
|
||||
"kod_banky": clean(row.get("Kód banky")),
|
||||
"ks": clean(row.get("KS")),
|
||||
"vs": clean(row.get("VS")),
|
||||
"ss": clean(row.get("SS")),
|
||||
"zprava": clean(row.get("Zpráva pro příjemce")),
|
||||
"poznamka": clean(row.get("Poznámka")),
|
||||
}
|
||||
|
||||
# --- fingerprint pro ladění duplicit ---
|
||||
data["uniq_fp"] = build_fingerprint(data)
|
||||
|
||||
# --- INSERT (záměrně bez IGNORE, ať duplicitní řádky opravdu uvidíme) ---
|
||||
placeholders = ", ".join(["%s"] * len(data))
|
||||
sql = f"INSERT INTO `{TABLE_NAME}` ({', '.join(data.keys())}) VALUES ({placeholders})"
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(sql, list(data.values()))
|
||||
inserted += 1
|
||||
|
||||
print(f"✅ Import hotový: vloženo {inserted} řádků.")
|
||||
|
||||
# ----- rychlý report duplicit podle fingerprintu -----
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(f"""
|
||||
SELECT uniq_fp, COUNT(*) AS c
|
||||
FROM `{TABLE_NAME}`
|
||||
GROUP BY uniq_fp
|
||||
HAVING c > 1
|
||||
ORDER BY c DESC
|
||||
LIMIT 10;
|
||||
""")
|
||||
dups = cur.fetchall()
|
||||
|
||||
if dups:
|
||||
print("\n⚠️ TOP 10 duplicitních fingerprintů (uniq_fp, count):")
|
||||
for r in dups:
|
||||
print(f" {r['uniq_fp']} -> {r['c']}")
|
||||
print("\n💡 Tip: SELECT * FROM `transactions` WHERE uniq_fp = '...';")
|
||||
else:
|
||||
print("✅ Žádné duplicity podle uniq_fp nenalezeny.")
|
||||
|
||||
# ======== MAIN ========
|
||||
if __name__ == "__main__":
|
||||
if not CSV_PATH.exists():
|
||||
raise SystemExit(f"❌ Soubor {CSV_PATH} nenalezen.")
|
||||
import_fio_csv()
|
||||
47
40 fio 02 diagnostika.py
Normal file
47
40 fio 02 diagnostika.py
Normal file
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Diagnostický test: načti Fio CSV a ověř parsování datumu.
|
||||
Nenačítá se do MySQL – pouze vypíše výsledek.
|
||||
"""
|
||||
|
||||
import csv
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
# ✅ Tvoje cesta k souboru
|
||||
CSV_PATH = Path(r"u:\Dropbox\!!!Days\Downloads Z230\Vyhledane pohyby (1).csv")
|
||||
|
||||
|
||||
def parse_czech_date(s: str):
|
||||
"""Očistí řetězec a zkusí dd.mm.yyyy."""
|
||||
if not s:
|
||||
return None
|
||||
s = s.strip().replace("\u00A0", "").replace("\ufeff", "")
|
||||
try:
|
||||
return datetime.strptime(s, "%d.%m.%Y").date()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
with open(CSV_PATH, "r", encoding="utf-8-sig", newline="") as f:
|
||||
reader = csv.DictReader(f, delimiter=";", quotechar='"')
|
||||
rows = list(reader)
|
||||
|
||||
print(f"Načteno {len(rows)} řádků.\n")
|
||||
print("Ukázka prvních 10 řádků s hodnotou Datum:\n")
|
||||
|
||||
for i, row in enumerate(rows[:10], start=1):
|
||||
raw = row.get("Datum")
|
||||
parsed = parse_czech_date(raw)
|
||||
print(f"{i:02d}. raw={repr(raw)} -> parsed={parsed}")
|
||||
|
||||
input("\n🔸 Stiskni Enter pro pokračování nebo ukončení... ")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if not CSV_PATH.exists():
|
||||
raise SystemExit(f"❌ Soubor {CSV_PATH} nenalezen.")
|
||||
main()
|
||||
45
40 fio struktura gpc.txt
Normal file
45
40 fio struktura gpc.txt
Normal file
@@ -0,0 +1,45 @@
|
||||
Struktura GPC formátu (pro elektronické výpisy)
|
||||
Výstupní soubor obsahuje záznamy "Data - výpis v Kč", "Data - obratová položka", které mají pevnou délku
|
||||
130 znaků. Mezi jednotlivými údaji v záznamu není žádný oddělovač. Do pevné délky jsou údaje doplňovány
|
||||
zleva příslušným počtem znaků nula.
|
||||
Struktura záznamu "Data - výpis v Kč" :
|
||||
byty Obsah
|
||||
1 - 3 "074" = označení typu záznamu "Data - výpis v Kč"
|
||||
4 - 19 přidělené č. účtu s vodícími nulami
|
||||
20 - 39 20 alfanumerických znaků zkráceného názvu účtu, doplněných případně mezerami zprava
|
||||
40 - 45 datum starého zůstatku ve formátu DDMMRR
|
||||
46 - 59 starý zůstatek v haléřích 14 numerických znaků s vodícími nulami
|
||||
60 znaménko starého zůstatku, 1 znak "+" či "-"
|
||||
61 - 74 nový zůstatek v haléřích 14 numerických znaků s vodícími nulami
|
||||
75 znaménko nového zůstatku, 1 znak "+" či "-"
|
||||
76 - 89 obraty debet (MD) v haléřích 14 numerických znaků s vodícími nulami
|
||||
90 znaménko obratů debet (MD), 1 znak "0" či "-"
|
||||
91 -104obraty kredit (D) v haléřích 14 numerických znaků s vodícími nulami
|
||||
105 znaménko obratů kredit (D), 1 znak "0" či "-"
|
||||
106-108 pořadové číslo výpisu
|
||||
109-114 datum účtování ve formátu DDMMRR
|
||||
115-128 (vyplněno 14 znaky mezera z důvodu sjednocení délky záznamů)
|
||||
129-130 ukončovací znaky CR a LF
|
||||
Struktura záznamu "Data - obratová položka v Kč" :
|
||||
byty obsah
|
||||
1 - 3 "075" = označení typu záznamu "Data - obratová položka"
|
||||
4 - 19 přidělené číslo účtu 16 numerických znaků s vodícími nulami
|
||||
20 – 35číslo účtu 16 numerických znaků s vodícími nulami (případně v pořadí předčíslí + číslo účtu)
|
||||
36 – 48číslo dokladu 13 numerických znaků
|
||||
49 – 60částka v haléřích 12 numerických znaků s vodícími nulami
|
||||
61 kód účtování vztažený k číslu účtu:
|
||||
1 = položka debet
|
||||
2 = položka kredit
|
||||
4 = storno položky debet
|
||||
5 = storno položky kredit
|
||||
62 – 71variabilní symbol 10 numerických znaků s vodícími nulami
|
||||
72 – 81konstantní symbol 10 numerických znaků s vodícími nulami ve tvaru BBBBKSYM, kde:
|
||||
BBBB = kód banky,
|
||||
KSYM = konstantní symbol
|
||||
82 – 91specifický symbol 10 numerických znaků s vodícími nulami
|
||||
92 – 97"000000" = valuta
|
||||
98 –117 20 alfanumerických znaků zkráceného názvu klienta, doplněno případně mezerami zprava
|
||||
118 "0"
|
||||
119-122 "0203" = kód měny pro Kč
|
||||
123-128 datum splatnosti ve formátu DDMMRR
|
||||
129-130 ukončovací znaky CR a LF
|
||||
Reference in New Issue
Block a user