Загрузка данных
-- coding: utf-8 --
""" Сбор итогов по ФИО из всех .xlsm в папке с датами. Лист: ЕРП Столбцы: A - заявка, K - ФИО, L - балл (50/20/10/5/0) Инкрементально: читает только новые/изменённые файлы, итог пересобирается каждый раз. """
import os import json from pathlib import Path from collections import defaultdict import openpyxl
==== НАСТРОЙКИ ====
ROOT = r"\server\share\папка_с_датами" # корневая папка с подпапками по датам SHEET_NAME = "ЕРП" COL_FIO = 11 # K COL_SCORE = 12 # L HEADER_ROWS = 1 # сколько строк шапки пропускать
CACHE_FILE = "cache.json" CACHE_VERSION = 2 OUT_FILE = "итоги.xlsx"
===================
---------- Кэш ----------
def load_cache(): if os.path.exists(CACHE_FILE): try: with open(CACHE_FILE, "r", encoding="utf-8") as f: data = json.load(f) if data.get("version") == CACHE_VERSION: return data print("[i] Кэш устарел — перечитываю все файлы") except (json.JSONDecodeError, OSError) as e: print(f"[!] Не удалось прочитать кэш: {e}") return {"version": CACHE_VERSION}
def save_cache(cache): cache["version"] = CACHE_VERSION with open(CACHE_FILE, "w", encoding="utf-8") as f: json.dump(cache, f, ensure_ascii=False, indent=1)
---------- Парсинг балла ----------
def parse_score(v): """ Возвращает (score, valid). 0 — валидный балл. Пусто/нечисло — 0, но valid=False. """ if v is None: return 0.0, False s = str(v).strip().replace(",", ".") if s == "": return 0.0, False try: return float(s), True except ValueError: return 0.0, False
---------- Чтение одного файла ----------
def read_file(path): rows = [] wb = openpyxl.load_workbook(path, data_only=True, read_only=True) try: if SHEET_NAME not in wb.sheetnames: return rows ws = wb[SHEET_NAME] for row in ws.iter_rows(min_row=HEADER_ROWS + 1, values_only=True): fio = row[COL_FIO - 1] if len(row) >= COL_FIO else None if not fio or not str(fio).strip(): continue raw = row[COL_SCORE - 1] if len(row) >= COL_SCORE else None score, valid = parse_score(raw) rows.append({ "fio": str(fio).strip(), "score": score, "valid": valid, }) return rows finally: wb.close()
---------- 1. Сканирование папки ----------
cache = load_cache() found = {} for path in Path(ROOT).rglob("*.xlsm"): if path.name.startswith("~$"): continue try: found[str(path)] = os.path.getmtime(path) except OSError: pass
---------- 2. Чтение новых/изменённых ----------
new_files = 0 for fpath, mtime in found.items(): entry = cache.get(fpath) if entry and entry.get("mtime") == mtime: continue try: rows = read_file(fpath) cache[fpath] = {"mtime": mtime, "rows": rows} new_files += 1 print(f"[+] {fpath} ({len(rows)} строк)") except Exception as e: print(f"[!] Ошибка {fpath}: {e}")
---------- 3. Удаление выпавших файлов ----------
for fpath in list(cache.keys()): if fpath == "version": continue if fpath not in found: del cache[fpath] print(f"[-] Удалён из кэша: {fpath}")
save_cache(cache)
---------- 4. Агрегация ----------
agg = defaultdict(lambda: { "score": 0.0, "count": 0, "zero_count": 0, "invalid_count": 0, "files": set(), })
for fpath, entry in cache.items(): if fpath == "version": continue fname = os.path.basename(fpath) for r in entry["rows"]: fio = r["fio"] agg[fio]["score"] += r["score"] agg[fio]["count"] += 1 agg[fio]["files"].add(fname) if not r["valid"]: agg[fio]["invalid_count"] += 1 elif r["score"] == 0: agg[fio]["zero_count"] += 1
---------- 5. Выгрузка ----------
out_wb = openpyxl.Workbook() ws = out_wb.active ws.title = "Итоги" ws.append([ "ФИО", "Сумма баллов", "Всего заявок", "Из них 0 баллов", "Пустой/нечисловой балл", "Файлов", ])
for fio in sorted(agg): d = agg[fio] total = int(d["score"]) if d["score"] == int(d["score"]) else round(d["score"], 2) ws.append([ fio, total, d["count"], d["zero_count"], d["invalid_count"], len(d["files"]), ])
Автоширина колонок
for col in ws.columns: width = max((len(str(c.value)) for c in col if c.value is not None), default=10) ws.column_dimensions[col[0].column_letter].width = min(width + 2, 60)
out_wb.save(OUT_FILE)
print(f"\nГотово.") print(f"Новых/обновлённых файлов: {new_files}") print(f"Всего файлов в кэше: {sum(1 for k in cache if k != 'version')}") print(f"Уникальных ФИО: {len(agg)}") print(f"Результат: {OUT_FILE}")