/
YakuninAV
/
Convertors
Обзор
Документация
Войти
/
YakuninAV
/
Convertors
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
python/pir_document.py
286 строк
13 KB
YakuninAV
begin with gitflick
31 июл 2026, 15:21
31 июл 2026, 15:21
1dc0fc6
Код
Авторство
О чём код?
# модуль для работы с документами ПИР import docx import re import json import openpyxl as xl nameCorrections = 'corrections' namePricing = 'pricings' nameDistributions = 'distibutions' nameDistribution = 'distibution' nameCol = 'col' # адрес колонки nameCod1 = 'cod1' # код разблюдовки из документа nameCod2 = 'cod2' # код разблюдовки пользовательский (может быть расширен для уникальности) nameCod3 = 'cod3' # код разблюдовки пользовательский (должен быть в базе) nameDistrs = 'distrs' nameDistrSets = 'distr_sets' nameSeqNum = 'seq' # последовательный номер набора разблюдовок (deprecated) nameDocCypher = 'doc' # шифр документа (deprecated) nameNumber = 'ord' # номер набора разблюдовок, используется при пересечении диапазонов nameDistrName = 'nam' # наименование набора разблюдовки nameBand = 'bnd' # диапазон применения набора nameStages = 'stg' # стадии(разблюдовки), связанные с набором nameStage = 'st' # стадия nameQuota = 'qt' # доля стадии в стоимости расценки (в процентах) nameValues = 'vals' # список процентных значений разблюдовок (буква колонки, значение в ячейке) nameItem = 'item' def read_document(filename): """ процедура выгружает из документа оглавление и таблицы \n filename - имя файла с документом ворд \n """ doc = docx.Document(filename) i = 0 title = 0 p = re.compile(r"^Title$|^Heading ([1-9]+)$", re.I | re.U) dc = dict() dc.update({"fn": filename}) dc.update({nameItem: list()}) dc = {"lvl": 0, "fn": filename, nameItem: list()} iter_paragraph = iter(enumerate(doc.paragraphs)) while True: try: elem = next(iter_paragraph) paragraph = elem[1] m = p.fullmatch(paragraph.style.name) if m: if m.string == 'Title': if title == 0 or title == i + 1: s = dc.setdefault("nm", "") + paragraph.text dc["nm"] = s title = i else: item = {"lvl": int(m.groups()[0]), "i": elem[0], "nm": paragraph.text, nameItem: list()} dc[nameItem].append(item) read_outline(iter_paragraph, dc) except StopIteration: break print("собрано оглавление") f = open(filename + ".json", "wt", encoding="utf-8") print(json.dumps(dc, ensure_ascii=False, indent=4), file=f) f.close() # that's all folks! def read_outline(iter_paragraph, node): """ функция считывает информацию на текущем уровне заголовков iter - итератор списка содержащего абзацы документа node - узел иерархии заголовков - dict: i, lvl, nm, items """ p = re.compile(r"^Heading ([1-9]+)$", re.I | re.U) item = node lvl = node["lvl"] + 1 # currentlevel while True: try: elem = next(iter_paragraph) paragraph = elem[1] m = p.fullmatch(paragraph.style.name) if m: l = int(m.groups()[0]) if l == lvl: item = {"lvl": l, "i": elem[0], "nm": paragraph.text, nameItem: list()} node[nameItem].append(item) elif l > lvl: subitem = {"lvl": l, "i": elem[0], "nm": paragraph.text, nameItem: list()} item[nameItem].append(subitem) item = read_outline(iter_paragraph, item) if item["lvl"] < lvl: return item else: item = {"lvl": l, "i": elem[0], "nm": paragraph.text, nameItem: list()} return item except StopIteration: return {"lvl": 0} # that's all folks! def read_tables(doc, node): """ функция считывает все таблицы из документа ворд doc - документ """ for table in doc.tables: print(table) for row in table.rows: for cell in row.cells: print(cell.text) pass # that's all folks! def read_addenum(filename): """ функция считывает дополнительную информацию о документе ПИР из MS Excel filename - имя файла Excel (*.xlsx) """ def read_corrections(ws): """ функция считывает лист "Поправки" ws - лист Excel """ rows = ws.max_row ls = list() for i in range(3, ws.max_row + 1): if ws.cell(i, 2).value == "finish": break item = {"doc": ws.cell(i, 2).value, "ref": ws.cell(i, 3).value, "knd": ws.cell(i, 4).value, "stg": ws.cell(i, 5).value, "apl": int(ws.cell(i, 6).value), "fml": ws.cell(i, 7).value, "min": ws.cell(i, 8).value, "max": ws.cell(i, 9).value, "prc": ws.cell(i, 10).value, "bdc": ws.cell(i, 11).value, "btb": ws.cell(i, 12).value, "nam": ws.cell(i, 13).value, } ls.append(item) return ls def read_stages(ws): """ функция считывает лист "Стадийность"\n ws - лист Excel\n Функция возвращает результат в виде структуры\n distribution:\n name - наименование раздела\n col - адрес колонки\n col - адрес колонки\n cod1 - код разблюдовки (объявлен в документе)\n cod2 - код разблюдовки (заявлен пользователем и, в случае если найден в БД и наименования не совпадают, к коду добавляется расширение "_ххх" для обеспечения уникальности при добавлении в БД)\n cod3 - код разблюдовки (заявлен пользователем в качестве обязательного, наименование игнорируется, должен присутствовать в БД, если не найден то используется также как и cod2)\n distribution_set():\n row - номер строки\n document - шифр документа\n seqnum - последовательный номер\n name - наименование набора\n ordn - порядковый номер (для наборов с пересекающимися диапазонами)\n band - диапазон шифров расценок\n stage - стадия\n rate - доля стадии (в процентах)\n data -\n xl - адрес колонки, содержащей значение доли разблюдовкиn\n value - процент\n """ distr = list() lastcol = ws.max_column for i in range(9, ws.max_column+1): if ws.cell(1, i).value == "finish": lastcol = i break if ws.cell(1, i).value == None: continue col = xl.utils.get_column_letter(i) name = ws.cell(1, i).value cod1 = ws.cell(3, i).value cod2 = ws.cell(4, i).value cod3 = ws.cell(5, i).value distr.append({"distr": {"col": col, "name": name, "cod1": cod1, "cod2": cod2, "cod3": cod3}}) dset = list() stages = list() sts = list() sq = 1 seq = 0 nsd = '0' nam = bnd = doc = '' stage = dict() for i in range(7, ws.max_row + 1): s = ws.cell(i, 2).value if s == "finish": break if s != None: doc = s st = ws.cell(i, 6).value qt = ws.cell(i, 7).value vals = list() for j in range(9, lastcol): s = ws.cell(i, j).value if s != None: vals.append((xl.utils.get_column_letter(j), s)) stage = dict({nameStage: st, nameQuota: qt, nameValues: vals}) if st in sts: #переход к новому набору sts = list(st) dset.append({nameSeqNum: seq, nameDocCypher: doc, nameNumber: nsd, nameDistrName: nam, nameBand: bnd, nameStages: stages}) else: sts.append(st) stages.append(stage) s = ws.cell(i, 1).value if s != None: seq = int(s) s = ws.cell(i, 3).value if s != None: nsd = s s = ws.cell(i, 4).value if s != None: nam = s s = ws.cell(i, 5).value if s != None: bnd = s return {nameDistrs: distr, nameDistrSets: dset} def read_pricing(ws): """ функция считывает лист "Расценки по ТЧ" ws - лист Excel """ rows = ws.max_row ls = list() for i in range(3, ws.max_row + 1): if ws.cell(i, 2).value == "finish": break item = { "doc": ws.cell(i, 2).value, "ref": ws.cell(i, 3).value, "nam": ws.cell(i, 4).value, "stg": ws.cell(i, 5).value, "unm": ws.cell(i, 6).value, "pra": ws.cell(i, 7).value, "prb": ws.cell(i, 8).value, "cor": ws.cell(i, 9).value, "adp": ws.cell(i, 10).value, "cyp": ws.cell(i, 11).value } ls.append(item) return ls dc = dict({"fn": filename}) wb = xl.open(filename) for ws in wb.worksheets: if ws.title == 'Поправки': dc.update({nameCorrections: read_corrections(ws)}) elif ws.title == 'Расценки по ТЧ': dc.update({namePricing: read_pricing(ws)}) elif ws.title == 'Стадийность': dc.update({nameDistributions: read_stages(ws)}) elif ws.title == 'Сооружения': pass elif ws.title == 'Спецификация': pass else: pass f = open(filename + ".json", "wt", encoding="utf-8") print(json.dumps(dc, ensure_ascii=False, indent=4), file=f) f.close() print('файл ' + filename + ' прочитан') return dc # that's all folks! def apply_code(distributions, elm, code): for d in distributions: if elm == d: d.update({'code': code}) return True return False def apply_code_to_col(distributions, col, code): """ Функция заменяет в distributions коды разблюдовок.\n Структура distributions имеет следующий вид:\n distribution:\n name - наименование разблюдовки\n col - адрес колонки\n cod1 - код разблюдовки (объявлен в документе)\n cod2 - код разблюдовки (заявлен пользователем и, в случае если найден в БД и наименования не совпадают, к коду добавляется расширение "_ххх" для обеспечения уникальности при добавлении в БД)\n cod3 - код разблюдовки (заявлен пользователем в качестве обязательного, наименование игнорируется, должен присутствовать в БД, если не найден то используется также как и cod2)\n При замене в distribution с "col": col добавляется значение "code":new_code """ for d in distributions: if col == d[nameCol]: d.update({'code': code}) break