Python в помощь инженеру ПТО

от автора

Всем привет.
В статье приведу немного скриптов на Python для решения вопросов с которыми иногда пересекается инженер ПТО строительной организации. Склёпал сам по мотивам информации из инета. Профи будет скучно), уровень «без диплома программиста».

На рабочем ноуте используется: Win10, Python 3.11, Office2019. Програмлю в PyScripter (нравится мне он))

Пример 1. Надо архивировать (не в смысле запаковать)

Проводятся испытания отдельных участков трубопровода, штук 50-60. На каждое заводится папка. Так как ни заказчик, ни технадзор, ни еще кто-нибудь не могут определиться, как лучше оформить это дело, то возникает ситуация когда куча документов имеет 2-3-4 варианта оформления. Значит надо скрипт, который текущий вариант закинет в архивную папку. (Ну да, «мой комьютер» или «тотал коммандер» никак с этим не справляются)):

##------------ЗАДАЧА--------------------------- ## Создать папку Архив, в ней создать подпапку ## Переместить все файлы ворд/Эксель в подпапку архива  import glob, os, shutil  source_dir = os.getcwd() #Path where your files are at the moment dst = source_dir+'/Архив/Вар2' #Path you want to move your files to  if not os.path.isdir(dst):           #создаем архив     os.makedirs(dst)  #либо выбираем типы файлов - типа оффис format_move_files = ['*.xls*', '*.doc*'] #либо все. ##format_move_files = ['*.*']  for fmf in format_move_files:     files = glob.iglob(os.path.join(source_dir, fmf))     for file in files:         if os.path.isfile(file):             shutil.move(file, dst)

Пример 2. Создаем папки

Собралось как-то много файлов в одной папке. И тут супостаты опять пристали со своими хотелками — каждый файл в свою папку. Нууу, лааадно. Лучше день потерять….потом за минуту само сделается. Идея такова, тем же тоталом получаю список файлов, его сохраняю в файле «папки.txt». Вот теперь его скрипт должен открыть и наштамповать папок:

import os  codirovk = 'utf-8'  #------------вар2 работает------------------ with open('папки.txt', "r", encoding=codirovk) as file1:     # итерация по строкам     for line in file1:         print(line.strip())         if not os.path.isdir(line.strip()): ##делаем папки если их нет             os.makedirs(line.strip())   #------------вар1 работает------------------ ##file1 = open('папки.txt', "r") ## ### считываем все строки ##lines = file1.readlines() ## ### итерация по строкам ##for line in lines: ####    print(line.strip()) ##    if not os.path.isdir(line.strip()): делаем папки если их нет ##        os.makedirs(line.strip()) ## ### закрываем файл ##file1.close

Пример 3. Достаем файлы

И вот присылают как-то папку со сметами, а они каждый файл в отдельной папке, да еще вперемешку с ведомостями всякими, да еще названия файлов пляшут, ну и их 392шт. Шо делать, шо делать — расчехляем питон. Надо достать все файлы смет с попутным переименованием их в одну папку:

# задача - достать из множества папок все файлы в одну папку (где скрипт) # допзадача - переименовать добавляя имена подпапок  import os, shutil import glob import re  dst = os.getcwd() print(dst)  # допзадача переименовать добавляя имена подпапок for name in sorted(glob.glob(dst+'/**/*локал*.xls', recursive=True)): # имеем D:\1 На почту\КС-6\Глава 1\01-02-01Р-01\Л0326174_Локальная смета.xls # вырезаем эту часть D:\1 На почту\КС-6\   и заменяем косые черты на тире     newname = name[len(dst)+1:]     newname = re.sub(r'\\', '-', newname)     os.rename(name, newname)  # пройтись по всем папкам каталога запуска скрипта и переместить # файлы *локал*.xls в папку скрипта for name in sorted(glob.glob(dst+'/**/*локал*.xls', recursive=True)):     try:         shutil.move(name, dst)     except:         pass

Пример 4. Из ворда в пдф)

Есть пару десятков файлов ворда в формате docx, надо из них pdf. Ну конечно, сча я буду сидеть каждый ручками открывать и делать сохранить как. Хотя есть еще и пдф-принтеры…..Так, ладно, питон погнали:

##*******************работает только docx  #pip install docx2pdf  from docx2pdf import convert   import os  convert(os.getcwd()) # конвертация текущего каталога

Пример 5 с подпримером 6. Работаем с Excel. Замена текста во множестве файлов.

Мы строили, строили и наконец — построили. Но….да твою ж дивизию: ну почему когда 85% исполнительной документации уже сделано и подписано (а это примерно 1200-1500 актов скрытых работ, сделанных в эксель) вдруг какой-то заразе приходит в голову поменять подписанта из-за игр генподрядчик-субподрядчик-субсубподрядчик. И, шо, эти люди думают, радостно потирая ручки, шо я буду мучиться. Ну держитесь — где там мой удав, пора придушить воооон того….А ладно, смотрим шо есть в интернете для питона. А там много чего есть, те что приглянулись вот:

  • openpyxl — работает только с xlsx Для его использования возникла подзадача сконвертировать из xls в xlsx. Смотрим вопрос в инете. Находим pyexcel, xls2xlsx — но рушат форматирование. Находим pywin32. Делаем:

# Задача: конвертация эксель из xls в xlsx # Все остальные библиотеки работают вкось # Эксель должен быть установлен на компе!!!  # pip bloks ##     python -m pip install --upgrade pywin32  # import bloks import os  import win32com.client as win32  # config bloks path =os.getcwd() format_files = ('.xls')  pred_prefiks_file_name = ''  # relise bloks for root, dirs, files in os.walk(path):     for file in files:         if(file.endswith(format_files, 0, len(file))): ##            print(os.path.join(file))## - этот вариант выводит только имя файла             print(file) ##        print(os.path.join(root, file))## - этот вариант выводит полный путь и имя файла             # -------делаем через COM объект -----------------------------             excel = win32.gencache.EnsureDispatch('Excel.Application')             wb = excel.Workbooks.Open(os.path.join(root, file))             wb.SaveAs(pred_prefiks_file_name+os.path.join(root, file)+'x', FileFormat = 51)    #FileFormat = 51 is for .xlsx extension             wb.Close()                               #FileFormat = 56 is for .xls extension             excel.Application.Quit()  input("Работа завершена. Тисни ентер.")

Далее обрабатываем openpyxl, но он тоже нарушает форматирование. Ищем дальше и находим:

  • aspose-cells -добавляет лист ‘Evaluation Warning’ и может еще какое ограничение содержит, так как он платный.

Практически он работает как надо. Делаю через него скрипт, а лишний лист удаляю с помощью pywin32.

## худо бедно работает как надо мне, но компонент платный  ##pip install aspose-cells ##pip install aspose-cells-python ##python -m pip install --upgrade pywin32  import os from aspose.cells import Workbook, ReplaceOptions import win32com.client as win32  path =os.getcwd() format_files = ('.xlsx')##'.pdf','.doc', '.docx', '.dwg', '.py' ## - закомментировать если нужны все файлы  find_str = 'Руководитель проекта ООО "Капуста" ' removent_str = 'Заместитель генерального директора по объектам ООО "Рога и копыта"'  # ВНИМАНИЕ - замена осуществляет по полным данным в ячейке. Т.е. меняется ячейка на ячейку  pred_prefiks_file_name = ''  for root, dirs, files in os.walk(path):     for file in files:         if(file.endswith(format_files, 0, len(file))):## - закомментировать если нужны все файлы             print(file)             # Load Excel file             workbook = Workbook(file)   ##Workbook("Workbook.xlsx")             # Create replace options             replace = ReplaceOptions()             # Set case sensitivity and text matching options - раздел выдает ошибку ##            replace.setCaseSensitive(False) ##            replace.setMatchEntireCellContents(False)             # Replace text             workbook.replace(find_str, removent_str, replace)             # Save as Excel XLSX file             workbook.save(pred_prefiks_file_name+file);  ## этот блок удаляет последний добавляемый лист 'Evaluation Warning' - по факту просто последний             excel = win32.gencache.EnsureDispatch('Excel.Application')             wb = excel.Workbooks.Open(os.path.join(root, pred_prefiks_file_name+file))             ws = wb.ActiveSheet             try:                 excel.DisplayAlerts=False                 wb.Worksheets(wb.Sheets.Count).Delete()             finally:                 pass             wb.Save()             wb.Close()             excel.Application.Quit()  input("Работа завершена. Тисни ентер.")

Да шо ж я упускаю что-то. Как то все топорно. Стоп, эту работу лучше экселя никто не сделает, а это значит вот же решение — pywin32. И конвертировать ничего не надо было. Ищем материалы в инете, просматриваем, пробуем раз 50 с попутным улучшением, готово:

##python -m pip install --upgrade pywin32  import os import win32com.client as win32 import re  path = os.getcwd() format_files = ('.xlsx', '.xls')##'.pdf','.doc', '.docx', '.dwg', '.py' ## - закомментировать если что-то не надо  # ищем строку find_str = 'Сидоров Ю.М.' # строка для замены найденной строки removent_str = 'Сидоров Ю.А.'  # делаем 4 замены по порядку, а можно было и через список сделать  #Сидоров Ю.М.                                     Сидоров Ю.А. #Руководитель проекта ООО "Капуста"               Заместитель генерального директора по объектам ООО "Рога и копыта" #Сидоров Ю.М. Приказ №15 29.03.2023               Сидоров Ю.А. Приказ № П100  от  31.09.2022 #Иванов А.А.                                      Сидоров Ю.А.  #количество строк в которых надо делать поиск row_find = 125 #количество столбцов в которых надо делать поиск col_find = 40  #если надо спереди имени файла добавить префикс pred_prefiks_file_name = ''  excel = win32.gencache.EnsureDispatch('Excel.Application')  for root, dirs, files in os.walk(path):     for file in files:         if ((file.endswith(format_files, 0, len(file))) and (not file.endswith('~$', 0, 2))):             # not file.endswith('~$', 0, 2) - защита от временных файлов эксель которые имена начинаются на ~$             print('файл: ',file)             #print(os.path.join(file))## - этот вариант выводит только имя файла             #print(os.path.join(root, file))## - этот вариант выводит полный путь и имя файла ##            excel = win32.gencache.EnsureDispatch('Excel.Application')             wb = excel.Workbooks.Open(os.path.join(root, file))             print('листов: ', wb.Worksheets.Count)             for sh_i in range(1, wb.Worksheets.Count+1):  #перебираем листы книги                 ws = wb.Worksheets(sh_i)    # идем на лист и активируем его                 ws.Activate()                 print('лист: ', sh_i)                 fp = 0                 cii2 = 0                 for row_i in range(1, row_find): #ws.Columns.Count - это вообще все строки                     for col_i in range(1, col_find): #ws.Rows.Count - это вообще все колонки                         if ws.Cells(row_i,col_i).Value:                             cii2 = cii2 + 1                             #вариант с re - заменяет внутри                             data_from_cell = str(ws.Cells(row_i,col_i).Value) #получаем строку из данных                             result = re.findall(find_str, data_from_cell)                             if len(result) > 0:                                 print('в ячейке: ',row_i,',',col_i)                                 print('данные в ячейке: '+data_from_cell)                                 fp = 1                                 data_from_cell2 = re.sub(find_str, removent_str, data_from_cell, count=0)                                 print('измененные данные: '+data_from_cell2)                                 ws.Cells(row_i,col_i).Value = data_from_cell2                                 print('замена выполнена')                              #вариант через данные эксель - совпадение по ячейке полностью - плохой вариант     ##                        if ws.Cells(row_i,col_i).Value == find_str:     ##                            print('найдено на листе: ', sh_i)     ##                            print('в ячейке: ',row_i,', ',col_i)     ##                            fp = 1     ##                            ws.Cells(109,1).Value = removent_str     ##                            print('замена выполнена')                  if fp == 0:                     print('не найдено на листе: ', sh_i)                 print('пройдено ячеек: ', cii2)             wb.Save(os.path.join(root, pred_prefiks_file_name+file))             wb.Close() ##            excel.Application.Quit()  excel.Application.Quit()  print('') print('----------------------------------------------') input("Работа завершена. Тисни ентер.")

В общем как поиск/замену сделать через pywin32 не нашел. Перебираю заданную область по ячейково. Сначала всю ячейку заменял — но это отдельный текст внутри ячейки не заменишь, а только целиком. Потом через модуль re уже стало работать, как я и хотел. Работает медленнее, чем aspose, но мне как-то спокойнее)).

P.S.: Так и живём в ПТО 🙂


ссылка на оригинал статьи https://habr.com/ru/articles/761484/