import os import sys import traceback import pandas as pd from datetime import datetime # === Вспомогательное === def get_script_dir() -> str: try: return os.path.dirname(os.path.abspath(__file__)) except NameError: # на всякий случай для интерактивных сред return os.getcwd() def log(msg: str): ts = datetime.now().strftime('%Y-%m-%d %H:%M:%S') line = f'[{ts}] {msg}' print(line) try: with open(log_file, 'a', encoding='utf-8') as f: f.write(line + '\n') except Exception: pass def is_temp_or_hidden(name: str) -> bool: # временные Excel-файлы (~$...) и скрытые (.xxx) return name.startswith('~$') or name.startswith('.') # === Пути === script_dir = get_script_dir() folder_path = os.path.join(script_dir, 'Files-todo/manifest') # имя результата: All-todo-YYYYMMDD-HHMM.xlsx timestamp = datetime.now().strftime('%Y%m%d-%H%M') output_filename = f'All-todo-{timestamp}.xlsx' output_file = os.path.join(folder_path, output_filename) # лог в той же папке log_file = os.path.join(folder_path, 'merge_log.txt') def main(): log('=== Старт объединения Excel-файлов ===') log(f'Папка: {folder_path}') if not os.path.isdir(folder_path): log(f'ОШИБКА: Папка не найдена: {folder_path}') sys.exit(1) # собираем только .xlsx, исключая временные/скрытые и любые All-todo-*.xlsx names = sorted(os.listdir(folder_path)) files = [] for name in names: if is_temp_or_hidden(name): continue if not name.lower().endswith('.xlsx'): continue if name.startswith('All-todo-'): continue full = os.path.join(folder_path, name) if os.path.isfile(full): files.append(full) if not files: log('Нет входных .xlsx файлов для слияния. Завершение.') return log(f'Найдено файлов: {len(files)}') dfs = [] all_columns = [] seen_cols = set() processed = 0 errors = 0 for i, path in enumerate(files, start=1): fname = os.path.basename(path) try: df = pd.read_excel(path, engine='openpyxl') if df is None or df.empty: log(f'ПРЕДУПРЕЖДЕНИЕ: Пустой файл — пропуск: {fname}') continue if not all_columns: for c in df.columns: if c not in seen_cols: all_columns.append(c) seen_cols.add(c) df['SourceFile'] = fname dfs.append(df) processed += 1 log(f'[{i}/{len(files)}] OK: {fname} — строк: {len(df)}') except Exception as e: errors += 1 log(f'[{i}/{len(files)}] ОШИБКА: {fname}: {e}') log(traceback.format_exc()) if not dfs: log('Все файлы пустые или не прочитаны — нечего сохранять.') return combined = pd.concat(dfs, ignore_index=True) # порядок колонок: сначала из первого удачного файла, потом прочие по алфавиту, затем SourceFile (если нужно) extra_cols = [c for c in combined.columns if c not in all_columns and c != 'SourceFile'] extra_cols.sort() final_cols = all_columns + extra_cols if 'SourceFile' not in final_cols: final_cols.append('SourceFile') combined = combined.reindex(columns=final_cols) try: with pd.ExcelWriter(output_file, engine='openpyxl') as writer: combined.to_excel(writer, index=False, sheet_name='Sheet') log(f'Готово: {output_file}') log(f'Итоговых строк: {len(combined)}') log(f'Успешно обработано файлов: {processed}, ошибок: {errors}') except Exception as e: log(f'ОШИБКА сохранения {output_file}: {e}') log(traceback.format_exc()) log('=== Готово ===') if __name__ == '__main__': main()