# gemini generated import xlrd from coord import Coord, Merged EMPTY_CTYPES = [xlrd.XL_CELL_EMPTY, xlrd.XL_CELL_BLANK] def border(sh, coord): cell = sh.cell(coord.row, coord.col) xf_style: "xlrd.formatting.XF" = sh.book.xf_list[cell.xf_index] return xf_style.border def border_right(sh, cell): return border(sh, cell).right_line_style def border_left(sh, cell): return border(sh, cell).left_line_style def border_bottom(sh, cell): return border(sh, cell).bottom_line_style def border_top(sh, cell): return border(sh, cell).top_line_style def parse_all_dirt(sh, min_pos, right, down): RET = set() row = min_pos.row while row < min_pos.row + down: col = min_pos.col while col < min_pos.col + right: #print(excel_coordinate(row, col)) value = str(sh.cell(row, col).value) if value is not None and len(value) > 0: RET.add(value) col += 1 row += 1 return RET import re # GEMINI def normalize_name(raw_name): """ Приводит разнородные записи ФИО к единому структурированному виду. """ # Шаг 1: Очистка name = re.sub(r'\s+', ' ', raw_name).strip() # Шаг 2: Извлечение звания known_titles = ['проф.', 'доц.', 'акад.', 'к.т.н.', 'д.м.н.'] title = None for t in known_titles: if name.lower().startswith(t): title = t # Удаляем звание из строки, убираем лишние пробелы name = name[len(t):].strip() break # Шаг 3 и 4: Разделение и идентификация parts = name.split(' ') last_name = None initials = None # Простой эвристический анализ # Ищем инициалы (содержат точку или состоят из 1-2 заглавных букв) initials_parts = [] name_parts = [] for part in parts: if '.' in part or (1 <= len(part) <= 2 and part.isupper()): initials_parts.append(part) else: # Считаем все остальное частью фамилии (для двойных фамилий) name_parts.append(part) if name_parts: last_name = " ".join(name_parts) if initials_parts: initials = "".join(initials_parts) # Сливаем "А." и "Н." в "А.Н." # Если фамилия не найдена (например, только инициалы), # но есть части, считаем первую часть фамилией if not last_name and name_parts: last_name = name_parts[0] return { "last_name": last_name, "initials": initials, "title": title } def excel_coordinate(row, col): """ Преобразует координаты строки и столбца (начиная с 0) в эквивалент Excel (например, A7, CB34). Args: row: Индекс строки (начиная с 0). col: Индекс столбца (начиная с 0). Returns: Строка, представляющая координату ячейки в стиле Excel. ~ Google Gemini, tested """ col_str = '' while col >= 0: col_str = chr(ord('A') + col % 26) + col_str # Преобразуем в буквы, начиная с A col = col // 26 - 1 # Уменьшаем номер столбца и учитываем переход к следующему разряду (как в 26-ричной системе) return col_str + str(row + 1) # Добавляем номер строки (Excel начинается с 1) def get_merged(sh, rowx, colx): """Даём ей координаты ячейки таблицы а она выдаёт её границы если переданные координаты находятся 'внутри' объединённой ячейки""" for crange in sh.merged_cells: rlo, rhi, clo, chi = crange chi -= 1 rhi -= 1 if rlo <= rowx <= rhi and chi >= colx >= clo: return rlo, clo, rhi, chi # если ячейка не часть объединённых то начала и концы у неё равны. return rowx, colx, rowx, colx def get_merged_coord(sh, coord): merged = get_merged(sh, coord.row, coord.col) return Merged(coord1=Coord(merged[0], merged[1]), coord2=Coord(merged[2], merged[3])) def merged_humanize(crange): """Получить из 4 цифр границ AA:BB координаты как в Excel""" row_low, col_low, row_high, col_high = crange # see order! return excel_coordinate(row_low, col_low) + ":" + excel_coordinate(row_high, col_high) def unspace(s: str): """Убрать пробелы из текста""" return s.strip().replace(" ", "").replace("\t", "") def find(sh, query = None): for rx in range(sh.nrows): i = 0 for x in sh.row(rx): if x.value == query: return rx, i i += 1 return None def weekday_to_num(st: str): if st.upper().strip() == "ПОНЕДЕЛЬНИК": return 1 if st.upper().strip() == "ВТОРНИК": return 2 if st.upper().strip() == "СРЕДА": return 3 if st.upper().strip() == "ЧЕТВЕРГ": return 4 if st.upper().strip() == "ПЯТНИЦА": return 5 if st.upper().strip() == "СУББОТА": return 6 if st.upper().strip() == "ВОСКРЕСЕНЬЕ": return 7 return -1