Все статьи

Инженерный кейс: Превращение сырого PDF колледжа в PWA-экосистему с форумом и Push-уведомлениями

pwacase-studyfastapireactarchitecturereverse-engineeringparsing

Инженерный кейс: Превращение сырого PDF колледжа в PWA-экосистему с форумом

В академической среде студенческий опыт (User Experience) часто разбивается о консервативную IT-инфраструктуру. В Высшем колледже информатики НГУ ключевой источник оперативных данных — расписание занятий — годами публиковался в виде неструктурированных, формируемых вручную многостраничных PDF-документов.

В данном материале представлен технический разбор разработки автономного сервиса: от преодоления фундаментальных проблем Data Extraction и Reverse Engineering до проектирования реактивного PWA-клиента, масштабируемого бэкенда на FastAPI и полноценного студенческого комьюнити-хаба.


1. Проблематика и архитектурный контекст

С точки зрения продуктового анализа исходная схема распространения данных страдала критическими UX-недостатками:

  • Отсутствие машиночитаемого API: Официальный портал не предоставлял открытых эндпоинтов для интеграции.
  • Высокий Friction при доступе: Студентам требовалось ежедневно загружать многостраничные файлы, вручную масштабировать сетку и выискивать номер группы среди сотен ячеек.
  • Нулевая реактивность: При внесении экстренных правок в расписание (замена преподавателя, перенос аудитории) пользователи узнавали об этом постфактум.

Цель проекта: разработать автономный, высокопроизводительный Fullstack-сервис, автоматически преобразующий сырые данные в типизированные API-структуры с мгновенной доставкой до конечных мобильных и десктопных клиентов.


2. Инженерия данных: Реверс-инжиниринг и кастомный парсинг PDF

Парсинг табличных данных из PDF — одна из наиболее нетривиальных задач из-за отсутствия в стандарте PDF семантических понятий «строка», «колонка» или «таблица». Это всего лишь набор координат отрисовки векторов и текстовых глифов.

Архитектурный тупик legacy-решений

На начальном этапе исследовались существующие Open Source решения, включая проект бота VkiHub от разработчика DedMaxTech. Однако очередной семестровый апдейт верстки со стороны учебной части колледжа привел к полному рассинхрону структур данных:

  1. Нестабильные Bounding Boxes: Смещение базовых линий колонок приводило к контаминации ячеек соседних групп.
  2. Динамические Merged Cells: Объединение ячеек на уровне подгрупп, потоковых лекций и числителей/знаменателей ломало детерминированные матрицы.
  3. Гетерогенный формат сущностей: Произвольные написания ФИО (Иванов И.И., Иванов И. И, Иванов-Петров А.), вариативные обозначения корпусов (ГК, КПА, аудитории главного корпуса НГУ) и служебные маркеры («консультация», «дистанционно», «зачет»).

Полагаться на внешние решения без прямого контроля логики было невозможно: малейшее изменение исходного шаблона приводило к cascade failures всего пайплайна.

Проектирование собственного Core-парсера с нуля

В новой версии бэкенда (services/parser.py) внешние зависимости были полностью устранены в пользу собственного алгоритмического пайплайна:

[ PDF с портала ] 

       ▼ (Camelot Lattice/Stream Mode)
[ Сырая табличная матрица ] 

       ▼ (find_header_row & is_group Regex)
[ Нормализация заголовков групп ] 

       ▼ (Heuristic Cell Parsing & Entity Extraction)
[ Типизированный JSON-Payload ] 

       ▼ (Inverted Index Construction)
[ Индексы: Группы / Преподаватели / Аудитории ]

Ключевые инженерные решения, заложенные в ядро парсера:

  • Динамический Header Search: Алгоритм find_header_row сканирует верхние строки матрицы с помощью строгих регулярных выражений (^[А-ЯA-Z]\d{4}[а-яa-z]{1,2}\d$), автоматически определяя динамическую границу между метаданными документа и сеткой расписания.
  • Эвристический анализ ячеек (parse_cell): Комплексный парсер текстового блока производит послойную деструктуризацию:
    • Изоляция сущности преподавателя с нормализацией инициалов: extract_teacher.
    • Извлечение номера аудитории и локации корпуса: extract_room.
    • Классификация типа занятия (isLecture, isPractice, isExam, isConsultation, isCanceled).
    • Очистка от дублирующихся токенов и шума (remove_duplicate_words).
def parse_cell(self, text: str):
    if not text or not text.strip():
        return None

    text = self.clean_text(text)
    teacher = self.extract_teacher(text)
    classroom = self.extract_room(text)
    isLecture = 'лекци' in text.lower() or 'диста' in text.lower()
    lesson_text = self.extract_subject(text)
    lesson = self.normalize_lesson(self.remove_duplicate_words(lesson_text))

    if not lesson:
        return None

    formatted_lesson = (lesson[0].upper() + lesson[1:]) if lesson.lower() == lesson else lesson
    if classroom:
        formatted_lesson = formatted_lesson.replace(classroom, '')

    isIssusing = ((not teacher or not classroom) and (not isLecture or (isLecture and 'нет' in text.lower()))) or 'выставле' in text.lower()

    return {
        'line': self.remove_duplicate_words(text) if isIssusing else text,
        'lesson': formatted_lesson,
        'teacher': teacher,
        'classroom': classroom,
        'isLecture': isLecture,
        'isPractice': 'практик' in text.lower(),
        'isExam': 'экзам' in text.lower(),
        'isIssusing': isIssusing,
        'isConsultation': 'консул' in text.lower(),
        'isCanceled': 'отмен' in text.lower()
    }
  • Построение инвертированных индексов (Inverted Index): На лету создаются вторичные проекции данных — расписание по преподавателям (/schedule/teachers) и по аудиториям (/schedule/classrooms), что позволило за O(1) рассчитывать занятость любого кабинета в заданный временной слот.

3. Архитектура сервиса и стек технологий

Архитектура системы строилась по принципу модульного разделения ответственности и максимальной оптимизации Time-to-Interactive (TTI).

Уровень системы Стек и инструменты Архитектурная роль
Backend Core Python 3.12, FastAPI, Tortoise ORM Асинхронная обработка запросов, фоновый воркер синхронизации, транзакционная целостность
Database PostgreSQL Хранение профилей, сессий, тредов форума, подписок на уведомления
Frontend App React 19, TypeScript, Vite Реактивный UI, Single Page Application, клиентский роутинг и кэширование
PWA & Offline Service Workers, Web App Manifest Фоновое кэширование ассетов, установка в роли нативного приложения, оффлайн-доступ
Styling & UI Модульный SCSS, Custom Design System Легковесная компонентная дизайн-система без оверхеда сторонних UI-библиотек
Push Gateway Web Push Protocol, VAPID, pywebpush Доставка пуш-уведомлений об апдейтах расписания на системном уровне ОС

4. Продуктовое масштабирование: Рождение студенческого форума

Изначально сервис проектировался как утилитарный Schedule Viewer. Однако в ходе совместной работы с дизайнером проекта открылся очевидный потенциал: объединить ежедневный органический трафик студентов вокруг общего коммуникационного хаба.

Проектирование UGC-подсистемы (User Generated Content)

Форум был спроектирован с упором на минимальный time-to-value:

  • Разделение контуров: Авторизация через сессионные токены, роли пользователей и инструменты модерации.
  • Иерархическая структура: Тематические категории, посты с тегами, вложенные ветки комментариев и интерактивные реакции.
  • Оптимизация мобильного UX: Адаптивные диалоги создания тем, оптимистичные UI-апдейты при отправке сообщений, плавные микровзаимодействия.

5. Ключевые возможности платформы

На сегодняшний день платформа представляет собой готовую экосистему для учащихся и преподавателей:

  1. Мониторинг занятости аудиторий в Real-Time: Благодаря инвертированному индексу аудиторий студенты могут в один клик найти свободные кабинеты для самоподготовки или командной работы.

  2. Event-Driven Push-уведомления: Фоновый планировщик (services/request.py) производит периодический polling официального сайта. При обнаружении изменений для целевой группы или преподавателя воркер мгновенно рассылает Web Push уведомления подписанным пользователям.

  3. Offline-First PWA Архитектура: Приложение сохраняет работоспособность при нестабильной сотовой связи в аудиториях колледжа благодаря прогрессивному кэшированию Service Worker.

  4. Интегрированный студенческий форум: Единая точка коммуникации для обмена конспектами, поиска сокомандников и обсуждения учебных вопросов.


6. Инфраструктурные вызовы и технические выводы

В процессе разработки и эксплуатации был решен ряд технических вызовов:

  • Resilience официального источника: Периодические сетевые сбои сайта колледжа потребовали внедрения Fallback-кэширования последних валидных версий расписания.
  • Постоянная эволюция входного формата: Отказ от сторонних библиотек в пользу собственного алгоритма нормализации ячеек снизил процент ошибок парсинга до статистически незначимых величин.

Итоги

Проект стал наглядным примером того, как глубокий технический подход, устранение сторонних зависимостей и фокус на реальных болях пользователей позволяют в связке разработчика и дизайнера создать высоконагруженный студенческий продукт для целого колледжа.