#!/usr/bin/env python3 """ PGZ Financijski import — Bilanca + PR-RAS Stream-filter: samo PGZ sport OIBs, ključne kolone """ import psycopg2, psycopg2.extras, os, time from datetime import datetime DSN = f"host=10.10.0.2 port=6432 dbname=rinet_v3 user=rinet password={os.environ['DB_PASSWORD']}" DIR = "/opt/pgz-sport/_downloads/CSV" LOG = "/opt/pgz-sport/logs/fin_import.log" TG_TOKEN = "8535797835:AAFItT-92jzZ9NWFafLxn0dLa1_n2s-JE5Y" TG_CHAT = "7969491558" os.makedirs("/opt/pgz-sport/logs", exist_ok=True) def log(msg): ts = datetime.now().strftime("%H:%M:%S") line = f"[{ts}] {msg}" print(line, flush=True) with open(LOG, "a") as f: f.write(line+"\n") def tg(msg): try: import urllib.request, urllib.parse data = urllib.parse.urlencode({"chat_id": TG_CHAT, "text": f"💰 RNO Fin: {msg}"}).encode() urllib.request.urlopen(f"https://api.telegram.org/bot{TG_TOKEN}/sendMessage", data, timeout=10) except: pass def parse_decimal(s): """Croatian decimal: 1.234.567,89 → 1234567.89""" if not s or not s.strip(): return None try: return float(s.replace(".","").replace(",",".").strip()) except: return None conn = psycopg2.connect(DSN) conn.autocommit = True cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) # Get PGZ sport OIBs cur.execute("SELECT oib, rno_broj, naziv FROM pgz_sport.rno_organizacije WHERE oib IS NOT NULL AND oib != ''") pgz_oibs = {row["oib"]: {"rno_broj": row["rno_broj"], "naziv": row["naziv"]} for row in cur.fetchall()} log(f"PGZ sport OIBs: {len(pgz_oibs)}") # ---- BILANCA ---- log("=== BILANCA ===") cur.execute(""" CREATE TABLE IF NOT EXISTS pgz_sport.rno_bilanca ( id BIGSERIAL PRIMARY KEY, oib TEXT NOT NULL, rno_broj TEXT, naziv TEXT, godina INTEGER, imovina_ukupno NUMERIC(15,2), imovina_dugotrajna NUMERIC(15,2), imovina_kratkotrajna NUMERIC(15,2), obveze_ukupno NUMERIC(15,2), kapital NUMERIC(15,2), raw_json JSONB, created_at TIMESTAMPTZ DEFAULT now(), UNIQUE(oib, godina) ) """) # Key columns to extract (names from header) BIL_KEYS = { "BILImovinaA": "imovina_dugotrajna", "BILImovinaB": "imovina_kratkotrajna", "BIL0a": "imovina_ukupno_a", "BIL0b": "imovina_ukupno_b", "BILObveze0a": "obveze_a", "BILObveze0b": "obveze_b", "BILFondA": "kapital_a", "BILFondB": "kapital_b", } bil_rows = [] total_read = 0 pgz_found = 0 with open(f"{DIR}/Bilanca.txt", encoding="utf-16-le", errors="replace") as f: raw_header = f.readline().strip("\ufeff").strip() headers = raw_header.split("$") h = {v: i for i, v in enumerate(headers)} oib_idx = h.get("OIB", 1) naziv_idx = h.get("Naziv neprofitne organizacije", 0) maticni_idx = h.get("Matični broj", 2) god_idx = h.get("Godina", 3) for line in f: total_read += 1 if total_read % 100000 == 0: log(f" Bilanca: read {total_read:,}, found {pgz_found} PGZ rows") cols = line.strip().split("$") if len(cols) < 5: continue oib = cols[oib_idx].strip() if oib_idx < len(cols) else "" if oib not in pgz_oibs: continue pgz_found += 1 info = pgz_oibs[oib] try: godina = int(cols[god_idx].strip()) if god_idx < len(cols) else 0 except: continue # Extract key financial values raw = {} imovina_a = None imovina_b = None obveze = None kapital = None for col_name, field in BIL_KEYS.items(): idx = h.get(col_name) if idx and idx < len(cols): val = parse_decimal(cols[idx]) raw[col_name] = val if col_name == "BILImovinaA": imovina_a = val elif col_name == "BILImovinaB": imovina_b = val elif col_name in ("BILObveze0a","BILObveze0b"): obveze = val if "a" in col_name else obveze elif col_name in ("BILFondA","BILFondB"): kapital = val if "A" in col_name else kapital imovina_ukupno = None if imovina_a is not None and imovina_b is not None: imovina_ukupno = (imovina_a or 0) + (imovina_b or 0) bil_rows.append(( oib, info["rno_broj"], info["naziv"], godina, imovina_ukupno, imovina_a, imovina_b, obveze, kapital, psycopg2.extras.Json(raw) )) if len(bil_rows) >= 500: psycopg2.extras.execute_batch(cur, """ INSERT INTO pgz_sport.rno_bilanca (oib, rno_broj, naziv, godina, imovina_ukupno, imovina_dugotrajna, imovina_kratkotrajna, obveze_ukupno, kapital, raw_json) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) ON CONFLICT (oib, godina) DO UPDATE SET imovina_ukupno=EXCLUDED.imovina_ukupno """, bil_rows, page_size=200) bil_rows = [] if bil_rows: psycopg2.extras.execute_batch(cur, """ INSERT INTO pgz_sport.rno_bilanca (oib, rno_broj, naziv, godina, imovina_ukupno, imovina_dugotrajna, imovina_kratkotrajna, obveze_ukupno, kapital, raw_json) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) ON CONFLICT (oib, godina) DO UPDATE SET imovina_ukupno=EXCLUDED.imovina_ukupno """, bil_rows, page_size=200) cur.execute("SELECT COUNT(*) as n FROM pgz_sport.rno_bilanca") n_bil = cur.fetchone()["n"] log(f"✅ Bilanca done: {n_bil} redaka za {pgz_found} PGZ sport org ({total_read:,} rows scanned)") # ---- PR-RAS ---- log("=== PR-RAS ===") cur.execute(""" CREATE TABLE IF NOT EXISTS pgz_sport.rno_prras ( id BIGSERIAL PRIMARY KEY, oib TEXT NOT NULL, rno_broj TEXT, naziv TEXT, godina INTEGER, prihodi_ukupno NUMERIC(15,2), prihodi_javni NUMERIC(15,2), prihodi_clanski NUMERIC(15,2), rashodi_ukupno NUMERIC(15,2), rezultat NUMERIC(15,2), raw_json JSONB, created_at TIMESTAMPTZ DEFAULT now(), UNIQUE(oib, godina) ) """) PR_KEYS = { "PR3a": "prihodi_a", "PR31a": "prihodi_javni_a", "PR3b": "prihodi_b", "PR31b": "prihodi_javni_b", "PR5a": "rashodi_a", "PR5b": "rashodi_b", "PR7a": "rezultat_a", "PR7b": "rezultat_b", "PR32a": "prihodi_clanski_a", } pr_rows = [] total_read2 = 0 pgz_found2 = 0 with open(f"{DIR}/PR-RAS.txt", encoding="utf-16-le", errors="replace") as f: raw_header = f.readline().strip("\ufeff").strip() headers = raw_header.split("$") h = {v: i for i, v in enumerate(headers)} oib_idx = h.get("OIB", 1) god_idx = h.get("Godina", 3) for line in f: total_read2 += 1 if total_read2 % 100000 == 0: log(f" PR-RAS: read {total_read2:,}, found {pgz_found2} PGZ rows") cols = line.strip().split("$") if len(cols) < 5: continue oib = cols[oib_idx].strip() if oib_idx < len(cols) else "" if oib not in pgz_oibs: continue pgz_found2 += 1 info = pgz_oibs[oib] try: godina = int(cols[god_idx].strip()) if god_idx < len(cols) else 0 except: continue raw = {} prihodi = rashodi = rezultat = prihodi_javni = prihodi_clan = None for col_name, field in PR_KEYS.items(): idx = h.get(col_name) if idx and idx < len(cols): val = parse_decimal(cols[idx]) raw[col_name] = val if col_name == "PR3a": prihodi = val elif col_name == "PR5a": rashodi = val elif col_name == "PR7a": rezultat = val elif col_name == "PR31a": prihodi_javni = val elif col_name == "PR32a": prihodi_clan = val pr_rows.append(( oib, info["rno_broj"], info["naziv"], godina, prihodi, prihodi_javni, prihodi_clan, rashodi, rezultat, psycopg2.extras.Json(raw) )) if len(pr_rows) >= 500: psycopg2.extras.execute_batch(cur, """ INSERT INTO pgz_sport.rno_prras (oib, rno_broj, naziv, godina, prihodi_ukupno, prihodi_javni, prihodi_clanski, rashodi_ukupno, rezultat, raw_json) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) ON CONFLICT (oib, godina) DO UPDATE SET prihodi_ukupno=EXCLUDED.prihodi_ukupno """, pr_rows, page_size=200) pr_rows = [] if pr_rows: psycopg2.extras.execute_batch(cur, """ INSERT INTO pgz_sport.rno_prras (oib, rno_broj, naziv, godina, prihodi_ukupno, prihodi_javni, prihodi_clanski, rashodi_ukupno, rezultat, raw_json) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) ON CONFLICT (oib, godina) DO UPDATE SET prihodi_ukupno=EXCLUDED.prihodi_ukupno """, pr_rows, page_size=200) cur.execute("SELECT COUNT(*) as n FROM pgz_sport.rno_prras") n_pr = cur.fetchone()["n"] log(f"✅ PR-RAS done: {n_pr} redaka") msg = f"✅ Financijski import: Bilanca {n_bil} + PR-RAS {n_pr} redaka za PGZ sport org" log(msg) tg(msg) conn.close()