This commit is contained in:
2025-11-30 19:37:24 +01:00
parent 1347f7dcd7
commit ab2f4256aa
15 changed files with 1554 additions and 29 deletions

313
30 Report.py Normal file
View File

@@ -0,0 +1,313 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
FIO EXPORT SCRIPT — FULLY COMMENTED VERSION
-------------------------------------------
This script connects to your MySQL "fio" database,
reads all transactions, and exports them into a highly formatted
Excel workbook.
Excel file includes:
• First sheet: "ALL" → contains ALL transactions
• Additional sheets: one for each account from accounts.json
• First 5 CZK sheets appear first in custom order
• All formatting exactly preserved (colors, borders, widths, formulas)
Everything is generated automatically.
"""
import mysql.connector
from mysql.connector import Error
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from datetime import datetime
import os
import glob
import json
# ======================================================
# CONFIGURATION
# ======================================================
# MySQL server parameters
DB_HOST = "192.168.1.76"
DB_PORT = 3307
DB_USER = "root"
DB_PASS = "Vlado9674+"
DB_NAME = "fio"
# Where to save Excel files
OUTPUT_DIR = r"Z:\Dropbox\!!!Days\Downloads Z230"
# JSON file with list of accounts (name + account_number)
ACCOUNTS_JSON = r"C:\Users\vlado\PycharmProjects\FIO\accounts.json"
# Columns that MUST be written as TEXT in Excel using ="value"
# to avoid Excel stripping zeros or changing formatting
TEXT_COLUMNS = ["cislo_uctu", "protiucet", "kod_banky", "ks", "vs", "ss"]
# ======================================================
# REMOVE OLD EXPORT FILES
# ======================================================
def cleanup_old_exports():
"""
Deletes older versions of exported XLSX files that match
specific filename patterns. This keeps your folder clean,
ensuring you only have the most recent export.
"""
patterns = [
os.path.join(OUTPUT_DIR, "*FIO*transaction*.xlsx"),
os.path.join(OUTPUT_DIR, "*FIO*transactions*.xlsx"),
os.path.join(OUTPUT_DIR, "*FIO_transactions*.xlsx"),
]
# Check each pattern
for pattern in patterns:
for file in glob.glob(pattern):
try:
os.remove(file)
print(f"🗑 Deleted old export: {file}")
except:
# If file cannot be deleted (locked or permission denied),
# simply skip it.
pass
# ======================================================
# CORE EXCEL FORMATTING FUNCTION
# ======================================================
def format_sheet(ws, rows, headers):
"""
Applies ALL formatting rules to a worksheet:
- Writes headers
- Writes all rows
- Converts selected columns to Excel text formulas
- Colors rows based on "objem" (red=negative, green=positive)
- Sets fixed column widths
- Adds borders to every cell
- Center-aligns first 10 columns
- Freezes header row and enables filtering
"""
# -------------------------------
# 1) Format HEADER row
# -------------------------------
for col_idx in range(1, len(headers) + 1):
cell = ws.cell(row=1, column=col_idx)
cell.font = Font(bold=True) # bold text
cell.fill = PatternFill(start_color="FFFF00", fill_type="solid") # yellow background
# -------------------------------
# 2) Write DATA rows
# -------------------------------
for row in rows:
excel_row = []
for h in headers:
val = row[h]
# For text-sensitive columns, write ="value"
# This prevents Excel from stripping zeros or treating them as numbers.
if h in TEXT_COLUMNS and val is not None:
excel_row.append(f'="{val}"')
else:
excel_row.append(val)
ws.append(excel_row)
# -------------------------------
# 3) Background coloring by "objem"
# -------------------------------
# Light red (ARGB) = negative
fill_red = PatternFill(start_color="FFFFDDDD", end_color="FFFFDDDD", fill_type="solid")
# Light green (ARGB) = positive or zero
fill_green = PatternFill(start_color="FFEEFFEE", end_color="FFEEFFEE", fill_type="solid")
# Find column index where "objem" is located
objem_col_index = headers.index("objem") + 1
# Apply row coloring
for row_idx in range(2, len(rows) + 2): # Start at row 2 (row 1 = header)
cell_objem = ws.cell(row=row_idx, column=objem_col_index)
# Convert objem to float
try:
value = float(cell_objem.value)
except:
value = 0
# Choose correct color
fill = fill_red if value < 0 else fill_green
# Apply fill to entire row
for col_idx in range(1, len(headers) + 1):
ws.cell(row=row_idx, column=col_idx).fill = fill
# -------------------------------
# 4) Fixed column widths
# -------------------------------
fixed_widths = [
6, 11, 11, 5, 14, 14, 8, 6, 13, 13,
50, 53, 12, 12, 5, 49, 29, 5, 29, 16,
15, 12, 49, 20
]
# Apply width using A, B, C... column names
for i, width in enumerate(fixed_widths, start=1):
col_letter = chr(64 + i) # convert 1 → 'A', 2 → 'B', ...
ws.column_dimensions[col_letter].width = width
# -------------------------------
# 5) Add borders + alignment
# -------------------------------
thin = Side(border_style="thin", color="000000")
border = Border(left=thin, right=thin, top=thin, bottom=thin)
align_center = Alignment(horizontal="center")
total_rows = len(rows) + 1
total_cols = len(headers)
for row_idx in range(1, total_rows + 1):
for col_idx in range(1, total_cols + 1):
cell = ws.cell(row=row_idx, column=col_idx)
cell.border = border # add border
# Center-align ONLY first 10 columns
if col_idx <= 10:
cell.alignment = align_center
# Freeze header row so it stays visible while scrolling
ws.freeze_panes = "A2"
# Enable auto filter on top row
ws.auto_filter.ref = ws.dimensions
# ======================================================
# MAIN EXPORT PROCESS
# ======================================================
def export_fio():
print("Connecting to MySQL...")
# Connect to MySQL database
try:
conn = mysql.connector.connect(
host=DB_HOST,
port=DB_PORT,
user=DB_USER,
password=DB_PASS,
database=DB_NAME
)
except Error as e:
print("❌ Failed to connect:", e)
return
cur = conn.cursor(dictionary=True)
# -------------------------------
# Load accounts.json
# -------------------------------
with open(ACCOUNTS_JSON, "r", encoding="utf-8") as f:
accounts = json.load(f)
# -------------------------------
# Define priority first sheets
# -------------------------------
preferred_order = [
"CZK rodina",
"CZK ordinace",
"CZK na jídlo",
"CZK TrialHelp",
"CZK maminka svojě věci"
]
accounts_sorted = []
# Step 1: add priority accounts first
for pref in preferred_order:
for acc in accounts:
if acc["name"] == pref:
accounts_sorted.append(acc)
# Step 2: add remaining accounts afterward
for acc in accounts:
if acc not in accounts_sorted:
accounts_sorted.append(acc)
# -------------------------------
# Create a new Excel workbook
# -------------------------------
wb = Workbook()
wb.remove(wb.active) # remove default empty sheet
# -------------------------------
# FIRST SHEET: ALL TRANSACTIONS
# -------------------------------
cur.execute("SELECT * FROM transactions ORDER BY datum DESC")
all_rows = cur.fetchall()
if all_rows:
headers = list(all_rows[0].keys())
ws_all = wb.create_sheet(title="ALL")
ws_all.append(headers)
format_sheet(ws_all, all_rows, headers)
# -------------------------------
# INDIVIDUAL SHEETS PER ACCOUNT
# -------------------------------
for acc in accounts_sorted:
acc_num = acc["account_number"]
sheet_name = acc["name"][:31] # Excel sheet name limit
print(f"➡ Creating sheet: {sheet_name}")
query = f"""
SELECT *
FROM transactions
WHERE cislo_uctu = '{acc_num}'
ORDER BY datum DESC
"""
cur.execute(query)
rows = cur.fetchall()
if not rows:
print(f"⚠ No data for {sheet_name}")
continue
headers = list(rows[0].keys())
ws = wb.create_sheet(title=sheet_name)
ws.append(headers)
format_sheet(ws, rows, headers)
conn.close()
# -------------------------------
# Save Excel file
# -------------------------------
cleanup_old_exports()
# File name includes timestamp
timestamp = datetime.now().strftime("%Y-%m-%d %H-%M-%S")
filename = f"{timestamp} FIO transactions.xlsx"
output_file = os.path.join(OUTPUT_DIR, filename)
wb.save(output_file)
print(f"✅ Export complete:\n{output_file}")
# ======================================================
# MAIN ENTRY POINT
# ======================================================
if __name__ == "__main__":
export_fio()