import json import xlrd import aigenerated from coord import Coord, Merged from translations import ExcelSheetReader import utils LOGGING = True def pprint(*args, **kwargs): if LOGGING: print(*args, **kwargs) class Parser: def __init__(self, reader: ExcelSheetReader): self.reader = reader self.groups = {} self.teachers = set() self.places = set() pprint("Parser created for '{0}'".format(reader.info())) def parse(self): monday = self.reader.find("ПОНЕДЕЛЬНИК") if monday is None: print(" -- Failed parse! -- ") print("ПОНЕДЕЛЬНИК НЕ НАЙДЕН!") return head_rx = monday.row - 1 # выше первого понидельника if head_rx < 0: raise Exception("head_rx < 0: Программа пыталась найти 'ПОНЕДЕЛЬНИК', но по всей видимости не нашла.") head = self.reader.get_row_values(head_rx) # get all ROW (months, groups) pprint(f"head={head}") self.groups = parse_groups(self.reader, head, monday, head_rx) # parse groups to self.groups pprint(f'self.groups={json.dumps(self.groups, indent=2, ensure_ascii=False)}') pprint("\n\n\n") for group in self.groups.values(): pprint("\nSTART OF PROCESS GROUP\n") self.process_group(group, monday) pprint("\nEND OF PROCESS GROUP\n") pprint(self.teachers) def parse_potokoviy(self, merged: Merged): speaker = None location = None # speaker low = merged.low speaker_pos = low.shift(down=merged.height()) speaker = speaker_pos.cell(self.reader).value # location location = merged.high.shift(down=1).cell(self.reader).value return {"loc": str(location), "leader": str(speaker), "name": str(merged.cell(self.reader).value)} def process_group(self, group, monday): """ Обработать группы, выполняется для каждой группы, после того как они распарены (parse_groups) group = {'name': 'ИВТ-260', 'position': [5, 6], 'position_human': 'G6:J6'} """ pprint(f"process_group group={group}") group_name = group['name'] pprint(group_name) row = group['position'][0] + 1 # counter for while, +1 for shift down; также номер строки в таблице (вроде с нуля) weeknum = 1 # номер недели, щёлкнет +1 при каком-то условии. while row < self.reader.get_row_count(): # maybe условие чтобы не уйти ниже чем есть строк pos = Coord(row, group['position'][1]) # текущая позиция, верхний левый угол (=low) pos_right = pos.shift(right=3) pair_pos = pos.replace(col=5) weekday_pos = pos.replace(col=4) merged = self.reader.get_merged_coord(pos) right_cell = pos_right.cell(self.reader) merged_cell = merged.cell(self.reader) cv = merged_cell.value # В конце (12 пара:>) название группы, можно использовать как якорь if utils.unspace(cv) == group_name: pprint("Lesson == group name; ending group loop.") break weekday_mr = self.reader.get_merged_coord(weekday_pos) weekday = utils.unspace(weekday_mr.cell(self.reader).value) pair_mr = self.reader.get_merged_coord(pair_pos) pair = utils.unspace(pair_mr.cell(self.reader).value) skip = 0 if weekday == "": if weeknum == 1: weeknum += 1 pprint("------") skip = 1 row += 1 else: break if not skip: next = 3 # на сколько пыгнуть для следующего шага? is_empty_lesson = right_cell.is_empty() and merged_cell.is_empty() dispname = "" parsed_discipline_name = None parsed_location = None parsed_leader = None is_2pair = False is_solid = pos_right in merged parsed_uncotigorized = [] is_wide_maybe_potokoviy = merged.width() > 4 # потоковая ли лекция (занимает несколько групп.) if is_empty_lesson: dispname = "" if not is_empty_lesson: may_prepod = merged.low.shift(down=2) if utils.has_no_bottom_border(self.reader, may_prepod): next = 6 is_2pair = True if is_wide_maybe_potokoviy: ret = self.parse_potokoviy(merged) parsed_location = ret['loc'] parsed_leader = ret['leader'] parsed_discipline_name = ret['name'] parsed_uncotigorized = list(utils.parse_all_dirt(self.reader, merged.low, merged.width(), next)) else: if (is_solid): parsed_discipline_name = cv dispname = cv dispname += (" SOLD" if is_solid else " SPLIT") dispname += (" [ДВУПАРНЫЙ]" if is_2pair else "") parsed_uncotigorized = list(utils.parse_all_dirt(self.reader, merged.low, 4, next)) if parsed_leader: dispname += f" [{parsed_leader}]" if parsed_location: dispname += f" [{parsed_location}]" dispname = dispname.replace("\n", "\\n") pprint(f"[{group_name}] row={row}; {pos} {pos_right} {pair} {weekday}: {'[ПОТОКОВЫЙ] ' if is_wide_maybe_potokoviy else ''}{dispname} {parsed_uncotigorized}") # пытаемся из некотегорезированных данных выцепить место и лидера (препода) prepods = set() if parsed_leader is not None: prepods.add(aigenerated.extract_last_name(parsed_leader)) locations = set() if parsed_location is not None: locations.add(parsed_location.replace(" ", "").replace("-", "")) for x in list(parsed_uncotigorized): if aigenerated.is_surname_string(x): prepods.add(aigenerated.extract_last_name(x)) if aigenerated.is_room_number(x): locations.add(x.replace(" ", "").replace("-", "") if x is not None else None) # оставшееся в дисциплину (костыль) if parsed_discipline_name is None: parsed_discipline_name = " ".join(parsed_uncotigorized) prepods.discard(None) prepods.discard("") locations.discard(None) locations.discard("") if not is_empty_lesson: slots = group['slots'] w = weekday + ("_1" if weeknum == 1 else "_2") if w not in slots.keys(): slots[w] = {} today = slots[w] today[pair] = { "pos": str(pos), "discipline": parsed_discipline_name, "locations": list(locations), "leads": list(prepods), "is_solid": is_solid, "is_2pair": is_2pair, "is_flow": is_wide_maybe_potokoviy, "raw": parsed_uncotigorized, "weeday": utils.weekday_to_num(weekday), "weeknum": weeknum } self.teachers.add(aigenerated.extract_last_name(parsed_leader)) # INCREMENT на next и конец цикла. row += next def parse_groups(reader: "ExcelSheetReader", head, monday: Coord, head_rx): """Распознать список групп и метаданные к ним, по сути получить список названий группы и координат её верхнего header-а (AQ6:AT6)""" groups = {} i = 0 while i < len(head): x = head[i] pprint(f"while i={i} head[i]={x}") merged = reader.get_merged_coord(Coord(head_rx, i)) if i > monday.col + 1: if merged is None or x == "": break if merged.width() != 4: pprint(f"WARNING: group header witdh !=4 (found: {merged.width()}); blocks !=4 not supported by parser.") break name = utils.unspace(x) groups[name] = { "name": name, "position": [head_rx, i], "position_human": utils.merged_humanize(merged.as_numbers()), "slots": {} } if merged is None: i += 1 else: i += merged.width() return groups