z230
This commit is contained in:
200
Mazání Library/10 První skript.py
Normal file
200
Mazání Library/10 První skript.py
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import pymysql
|
||||||
|
import pymysql.cursors
|
||||||
|
|
||||||
|
# ================= KONFIGURACE =================
|
||||||
|
|
||||||
|
# --- BEZPEČNOSTNÍ POJISTKA ---
|
||||||
|
# True = POUZE VÝPIS (nic se nesmaže, databáze se nezmění)
|
||||||
|
# False = OSTRÝ REŽIM (maže soubory i záznamy v DB!)
|
||||||
|
DRY_MODE = True
|
||||||
|
|
||||||
|
# 1. Přístup k MySQL
|
||||||
|
DB_CONFIG = {
|
||||||
|
'host': '192.168.1.76',
|
||||||
|
'port': 3307,
|
||||||
|
'user': 'root',
|
||||||
|
'password': 'Vlado9674+',
|
||||||
|
'db': 'torrents',
|
||||||
|
'charset': 'utf8mb4',
|
||||||
|
'autocommit': True
|
||||||
|
}
|
||||||
|
|
||||||
|
TABULKA = "file_md5_index"
|
||||||
|
|
||||||
|
# 2. Mapování cest
|
||||||
|
SERVER_PREFIX = "/mnt/user/Library"
|
||||||
|
# Používáme 'r' pro raw string, aby se zpětná lomítka chápala správně
|
||||||
|
LOCAL_PREFIX = r"\\tower1\#library"
|
||||||
|
|
||||||
|
|
||||||
|
# ===============================================
|
||||||
|
|
||||||
|
def get_connection():
|
||||||
|
return pymysql.connect(
|
||||||
|
cursorclass=pymysql.cursors.DictCursor,
|
||||||
|
**DB_CONFIG
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def convert_path(db_path):
|
||||||
|
"""Převede cestu z Linux serveru na lokální cestu Windows."""
|
||||||
|
if db_path.startswith(SERVER_PREFIX):
|
||||||
|
relative_path = db_path[len(SERVER_PREFIX):]
|
||||||
|
# Ořízneme počáteční lomítka z relativní cesty, aby fungoval join
|
||||||
|
relative_path = relative_path.lstrip("/").lstrip("\\")
|
||||||
|
# Spojí cesty a opraví lomítka
|
||||||
|
local_path = os.path.join(LOCAL_PREFIX, relative_path)
|
||||||
|
return os.path.normpath(local_path)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def step_1_mark_duplicates():
|
||||||
|
print(f"\n--- KROK 1: Hledání duplicit v DB (DRY_MODE={DRY_MODE}) ---")
|
||||||
|
|
||||||
|
try:
|
||||||
|
conn = get_connection()
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
if DRY_MODE:
|
||||||
|
# V DRY_MODE jen počítáme, co bychom označili (neprovádíme UPDATE)
|
||||||
|
sql = f"""
|
||||||
|
SELECT COUNT(*) as pocet
|
||||||
|
FROM {TABULKA} t1
|
||||||
|
JOIN {TABULKA} t2 ON t1.blake3 = t2.blake3
|
||||||
|
WHERE t1.host_name = 'TOWER1'
|
||||||
|
AND t2.host_name = 'SYNOLOGY'
|
||||||
|
AND (t1.to_delete IS NULL OR t1.to_delete = 0);
|
||||||
|
"""
|
||||||
|
cursor.execute(sql)
|
||||||
|
result = cursor.fetchone()
|
||||||
|
affected = result['pocet']
|
||||||
|
print(f"[DRY-RUN] Našel jsem {affected} shodných záznamů (DB nebude změněna).")
|
||||||
|
else:
|
||||||
|
# V OSTRÉM režimu provádíme UPDATE
|
||||||
|
sql = f"""
|
||||||
|
UPDATE {TABULKA} t1
|
||||||
|
JOIN {TABULKA} t2 ON t1.blake3 = t2.blake3
|
||||||
|
SET t1.to_delete = 1
|
||||||
|
WHERE t1.host_name = 'TOWER'
|
||||||
|
AND t2.host_name = 'SYNOLOGY'
|
||||||
|
AND (t1.to_delete IS NULL OR t1.to_delete = 0);
|
||||||
|
"""
|
||||||
|
print("Provádím UPDATE záznamů v databázi...")
|
||||||
|
cursor.execute(sql)
|
||||||
|
affected = cursor.rowcount
|
||||||
|
conn.commit()
|
||||||
|
print(f"Hotovo. Označeno {affected} záznamů ke smazání.")
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
return affected
|
||||||
|
|
||||||
|
except pymysql.MySQLError as e:
|
||||||
|
print(f"Chyba MySQL při označování: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def step_2_delete_files():
|
||||||
|
print(f"\n--- KROK 2: Mazání souborů (DRY_MODE={DRY_MODE}) ---")
|
||||||
|
|
||||||
|
try:
|
||||||
|
conn = get_connection()
|
||||||
|
files_to_process = []
|
||||||
|
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
print("Stahuji seznam souborů...")
|
||||||
|
|
||||||
|
if DRY_MODE:
|
||||||
|
# V DRY_MODE nemůžeme hledat podle 'to_delete=1' (protože jsme nic neoznačili),
|
||||||
|
# takže musíme použít JOIN dotaz přímo pro simulaci výpisu.
|
||||||
|
sql = f"""
|
||||||
|
SELECT t1.id, t1.full_path
|
||||||
|
FROM {TABULKA} t1
|
||||||
|
JOIN {TABULKA} t2 ON t1.blake3 = t2.blake3
|
||||||
|
WHERE t1.host_name = 'TOWER'
|
||||||
|
AND t2.host_name = 'SYNOLOGY'
|
||||||
|
AND (t1.to_delete IS NULL OR t1.to_delete = 0)
|
||||||
|
"""
|
||||||
|
else:
|
||||||
|
# V OSTRÉM režimu bereme to, co jsme v kroku 1 označili
|
||||||
|
sql = f"SELECT id, full_path FROM {TABULKA} WHERE host_name = 'TOWER' AND to_delete = 1"
|
||||||
|
|
||||||
|
cursor.execute(sql)
|
||||||
|
files_to_process = cursor.fetchall()
|
||||||
|
|
||||||
|
count = len(files_to_process)
|
||||||
|
print(f"Nalezeno {count} souborů.")
|
||||||
|
|
||||||
|
if count == 0:
|
||||||
|
print("Žádné soubory k zpracování. Konec.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# V ostrém režimu se zeptáme na potvrzení
|
||||||
|
if not DRY_MODE:
|
||||||
|
confirm = input(f"-> [POZOR] Opravdu chcete SMAZAT {count} souborů? (napište 'ano'): ")
|
||||||
|
if confirm.lower() != 'ano':
|
||||||
|
print("Operace zrušena.")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
print("-" * 40)
|
||||||
|
print("VÝPIS SOUBORŮ, KTERÉ BY BYLY SMAZÁNY:")
|
||||||
|
print("-" * 40)
|
||||||
|
|
||||||
|
deleted_counter = 0
|
||||||
|
errors = 0
|
||||||
|
|
||||||
|
for row in files_to_process:
|
||||||
|
db_id = row['id']
|
||||||
|
server_path = row['full_path']
|
||||||
|
local_path = convert_path(server_path)
|
||||||
|
|
||||||
|
if not local_path:
|
||||||
|
print(f"[SKIP PATH] Nesedí prefix: {server_path}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# --- LOGIKA DRY RUN vs REAL ---
|
||||||
|
if DRY_MODE:
|
||||||
|
# Pouze výpis
|
||||||
|
print(f"[DRY-RUN] Bylo by smazáno: {local_path}")
|
||||||
|
deleted_counter += 1
|
||||||
|
else:
|
||||||
|
# Ostré mazání
|
||||||
|
try:
|
||||||
|
if os.path.exists(local_path):
|
||||||
|
os.remove(local_path)
|
||||||
|
print(f"[OK SMAZÁNO] {local_path}")
|
||||||
|
|
||||||
|
# Smazání z DB
|
||||||
|
with conn.cursor() as del_cursor:
|
||||||
|
del_sql = f"DELETE FROM {TABULKA} WHERE id = %s"
|
||||||
|
del_cursor.execute(del_sql, (db_id,))
|
||||||
|
conn.commit()
|
||||||
|
deleted_counter += 1
|
||||||
|
else:
|
||||||
|
print(f"[NENÍ NA DISKU] Mažu jen z DB: {local_path}")
|
||||||
|
with conn.cursor() as del_cursor:
|
||||||
|
del_sql = f"DELETE FROM {TABULKA} WHERE id = %s"
|
||||||
|
del_cursor.execute(del_sql, (db_id,))
|
||||||
|
conn.commit()
|
||||||
|
deleted_counter += 1
|
||||||
|
|
||||||
|
except OSError as e:
|
||||||
|
print(f"[CHYBA OS] {local_path}: {e}")
|
||||||
|
errors += 1
|
||||||
|
except pymysql.MySQLError as e:
|
||||||
|
print(f"[CHYBA DB] ID {db_id}: {e}")
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
print("-" * 30)
|
||||||
|
if DRY_MODE:
|
||||||
|
print(f"DRY RUN DOKONČEN. Zobrazena simulace pro {deleted_counter} souborů.")
|
||||||
|
else:
|
||||||
|
print(f"HOTOVO. Úspěšně smazáno: {deleted_counter}, Chyby: {errors}")
|
||||||
|
|
||||||
|
except pymysql.MySQLError as e:
|
||||||
|
print(f"Kritická chyba DB: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
step_1_mark_duplicates()
|
||||||
|
step_2_delete_files()
|
||||||
67
Pandas/10 DuplicateTest.py
Normal file
67
Pandas/10 DuplicateTest.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import pandas as pd
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
import time
|
||||||
|
|
||||||
|
# --- KONFIGURACE ---
|
||||||
|
db_user = 'root'
|
||||||
|
db_pass = 'Vlado9674+'
|
||||||
|
db_host = '192.168.1.76'
|
||||||
|
db_port = '3307'
|
||||||
|
db_name = 'torrents' # <--- ZDE DOPLNIT NÁZEV DATABÁZE
|
||||||
|
|
||||||
|
# --- PŘIPOJENÍ ---
|
||||||
|
connection_string = f'mysql+mysqlconnector://{db_user}:{db_pass}@{db_host}:{db_port}/{db_name}'
|
||||||
|
engine = create_engine(connection_string)
|
||||||
|
|
||||||
|
# SQL dotaz - vybíráme i full_path, abychom mohli v Pandas ukázat příklad cesty
|
||||||
|
# POZOR: Načítání 5.8M textových řetězců (full_path) zabere dost RAM (odhadem 2-4 GB).
|
||||||
|
query = """
|
||||||
|
SELECT id, blake3, file_size, full_path
|
||||||
|
FROM file_md5_index FORCE INDEX (idx_full_path_prefix)
|
||||||
|
WHERE host_name='Tower1' AND full_path LIKE '/mnt/user/#Library%'
|
||||||
|
"""
|
||||||
|
|
||||||
|
print("1. Začínám stahovat data z MySQL do RAM...")
|
||||||
|
start_load = time.time()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Stáhnutí dat
|
||||||
|
df = pd.read_sql(query, engine)
|
||||||
|
end_load = time.time()
|
||||||
|
print(f"-> Data stažena za: {end_load - start_load:.2f} sekund")
|
||||||
|
print(f"-> Počet řádků v paměti: {len(df)}")
|
||||||
|
|
||||||
|
print("\n2. Začínám hledat duplicity (Pandas GroupBy)...")
|
||||||
|
start_process = time.time()
|
||||||
|
|
||||||
|
# Logika hledání duplicit
|
||||||
|
# Najdeme jen ty, co mají duplicitní hash
|
||||||
|
duplicity = df[df.duplicated(subset=['blake3'], keep=False)]
|
||||||
|
|
||||||
|
if not duplicity.empty:
|
||||||
|
# Seskupení
|
||||||
|
vysledek = duplicity.groupby('blake3').agg({
|
||||||
|
'file_size': 'first', # Velikost souboru (předpokládáme stejnou pro stejný hash)
|
||||||
|
'id': 'count', # Počet výskytů
|
||||||
|
'full_path': lambda x: x.iloc[0] # Ukázka první cesty (rychlejší než 'first')
|
||||||
|
}).rename(columns={'id': 'pocet_kopii'})
|
||||||
|
|
||||||
|
# Filtrujeme jen ty, co mají skutečně více kopií a seřadíme podle velikosti * počet kopií
|
||||||
|
# (Chceme vidět, kde plýtváme nejvíc místa)
|
||||||
|
vysledek['celkove_plytvani'] = vysledek['file_size'] * (vysledek['pocet_kopii'] - 1)
|
||||||
|
vysledek = vysledek.sort_values('celkove_plytvani', ascending=False)
|
||||||
|
|
||||||
|
end_process = time.time()
|
||||||
|
print(f"-> Zpracováno za: {end_process - start_process:.4f} sekund")
|
||||||
|
|
||||||
|
print("\n--- TOP 20 NEJVĚTŠÍCH DUPLICIT ---")
|
||||||
|
# Zobrazíme hash, počet kopií, velikost jednoho souboru a ukázku cesty
|
||||||
|
print(vysledek[['pocet_kopii', 'file_size', 'full_path']].head(20))
|
||||||
|
|
||||||
|
print(f"\nCelkem nalezeno {len(vysledek)} unikátních souborů, které mají duplicity.")
|
||||||
|
else:
|
||||||
|
print("Nebyly nalezeny žádné duplicity.")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\nCHYBA: {e}")
|
||||||
|
print("Zkontrolujte prosím název databáze a jestli máte dost paměti RAM.")
|
||||||
Reference in New Issue
Block a user