170 lines
5.7 KiB
Python
170 lines
5.7 KiB
Python
|
||
# 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
|
||
|