Уже полгода хожу в фитнес клуб World Class или как пишут в самом клубе - являюсь его резидентом. Выбрал этот клуб в основном из-за наличия в нём бассейна. Потом заинтересовался групповыми программами и частенько хожу на сайкл тренировки - это тренировки на специальном велотренажере.

И единственное меня раздражает, что для просмотра расписания занятий на следующую неделю каждый раз приходилось идти на сайт сети Ворд Класс, переходить в мой клуб, отбиваться от нескольких назойливых баннеров которые предлагают перезвонить через 3 секунды для того чтобы стать их клиентом. И только после этого найти в огромном расписании несколько строчек которые я ищу. На неделе таких занятий может проходить несколько и я вносил их в свой календарь, чтобы сходить хотя бы на одно в неделю.
Вся это операция с поиском и занесением в календарь занимала минут 15. И так каждую неделю.

В какой-то момент мне это надоело и я подумал, а не парсить ли расписание и добавлять в свой календарь занятия на сайкле автоматически?

Некоторым препятствием стало то, что сайт World Class динамически загружает контент с помощью JavaScript после начальной загрузки страницы. Зато нашлась конечная точка API и теперь занятия по сайклу каждый понедельник ночью добавляются в мой календарь за 3 секунды работы скрипта вместо 15 минут моей жизни каждую неделю.

Автоматически созданное при помощи скрипта событие в моём гугл календаре
Автоматически созданное при помощи скрипта событие в моём гугл календаре

Веб-сайт с общедоступным расписанием клуба World Class

Обычно расписание приветствует вот так
Обычно расписание приветствует вот так

На сайте используется динамическая загрузка контента с помощью JavaScript. Это значит что сайт, который динамически загружает контент с помощью JavaScript после первоначальной загрузки страницы, - это тот, на котором не вся информация сразу доступна при первом появлении страницы. Вместо этого дополнительные данные или контент извлекаются с сервера и отображаются по мере взаимодействия со страницей. Этот метод обычно используется в современных веб-приложениях. Это устраняет необходимость перезагружать всю страницу и делает процесс более быстрым и плавным для пользователя.

Конечная точка API - это определенный URL-адрес, который позволяет внешним приложениям взаимодействовать с ним. API позволяют различным программным системам взаимодействовать друг с другом, отправляя запросы и получая ответы.

Когда взаимодействую с сайтом, который динамически загружает контент, то сайт часто запрашивает данные со своего сервера с помощью API. Сервер обрабатывает запрос и возвращает необходимые данные, которые затем отображаются на веб-странице. В контексте фитнес-клуба World Class конечной точкой API будет определенный URL-адрес, который предоставляет данные о расписании занятий. Отправив запрос на эту конечную точку, можно получить данные о расписании напрямую, минуя необходимость вручную перемещаться по сайту.

Поворотный момент и техническая проблема

В какой-то момент я наткнулся на чистую ссылку, которая ведёт только на само расписание фитнес-клуба World Class без любой рекламы. Ссылка выглядит следующим образом:

https://my.worldclass.ru/scheduling?clubs=d35ba5fc-1f7e-11ec-b664-50e548298f06

Где вместо d35ba5fc-1f7e-11ec-b664-50e548298f06 должен стоять идентификатор вашего клуба. Выше это идентификатор клуба в городе Перми.

Все доступные идентификаторы других клубов можно посмотреть прямо в браузере в режиме просмотра страницы:

Список доступных клубов
Список доступных клубов

Поскольку World Class динамически загружает расписание с помощью JavaScript после начальной загрузки страницы, то стало некоторой проблемой найти конечную точка API и обратиться к ней за этим общедоступным и свободно опубликованным в интернете расписанием.

ICG Color Cycle (сайкл)

В пермском клубе World Class не обязательно записываться на занятия по сайклу заранее - достаточно прийти примерно за полчаса до занятия и взять на входе черную карточку для участия - места есть до тех пор пока карточки не закончатся. Правда бывают приходят и те кто записались онлайн, но карточку не взяли - просто не знали про это и мест не хватает. Это некоторая путаница.

Примерно так выглядят занятия по сайклу
Примерно так выглядят занятия по сайклу

Создание решения

Поскольку я использую Гугл календарь, то написал Google Apps скрипт. Apps Script - платформа на основе JavaScript для быстрой и простой разработки решений.

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

Скрипт работает следующим образом:

  1. Получение расписания: скрипт отправляет запрос в API World Class с идентификатором определенного спортзала. Далее извлекает расписание на следующие семь дней.

  2. Фильтрация соответствующих занятий: после извлечения расписания скрипт отфильтровывает занятия «ICG Color Cycle» и «CORE», которые я хочу посещать. Все дальнейшие действия происходят только для отобранной выборки.

  3. Создание событий календаря: для каждой отфильтрованной тренировки скрипт автоматически создает событие в Google календарь по умолчанию (можно задать любой календарь, я уже писал ранее про работу с календарями). Каждое событие включает:

  • Название: название тренировки (в моём случае либо «ICG Color Cycle», либо «CORE»).

  • Продолжительность: каждое событие запланировано на один час.

  • Описание: подробное объяснение зачем нужна эта тренировка.

  • Расположение: конкретный адрес клуба World Class, где проходит занятие.

  • Ведение журнала и обработка ошибок: скрипт регистрирует свой ход выполнения, указывая, когда он начинается и заканчивается, и предоставляя подробную информацию о каждом созданном событии. Если запрос API не выполняется или возвращает ошибку, скрипт регистрирует сообщение об ошибке для устранения неполадок.

Файл где можно посмотреть токен, который понадобится для обращения к API
Файл где можно посмотреть токен, который понадобится для обращения к API

По сути, этот скрипт - инструмент экономии времени для резидентов клуба World Class, которые могут быть уверены, что всегда будут знать когда проходят их любимые занятия:

function WorldclassAPISchedulingPerm() {
    console.log(`Функция WorldclassAPISchedulingPerm начала работу в ${(new Date()).toLocaleString("ru-RU")}`);
  
    //https://habr.com/ru/articles/838322/

    const url = 'https://my.worldclass.ru/api/v1/clubs/scheduling';
    const currentDate = new Date();
    const endDate = new Date(currentDate);
    endDate.setDate(currentDate.getDate() + 7);

    const payload = {
        "gymList": [
            "d35ba5fc-1f7e-11ec-b664-50e548298f06" // это World Class в Перми
        ],
        "startDate": currentDate.toISOString().split('.')[0],
        "endDate": endDate.toISOString().split('.')[0],
        "chain": 1,
        "token": "70a69ca6-XXXX-XXXX-XXXX-005056b1372d" // здесь надо указать токен, узнать можно в DevTools Chrome на скриншоте выше 
    };

    const options = {
        method: 'post',
        contentType: 'application/json',
        payload: JSON.stringify(payload),
        muteHttpExceptions: true,
        headers: {
            'brand': 'WorldClass',
            'language': 'Ru',
            'chain': '1',
            'Accept': '*/*',
            'Origin': 'https://my.worldclass.ru',
            'Referer': 'https://my.worldclass.ru/scheduling?clubs='
        },
        timeoutSeconds: 30
    };

    try {
        const response = UrlFetchApp.fetch(url, options);
        const responseCode = response.getResponseCode();
        console.log(`Код ответа: ${responseCode}`);

        const responseText = response.getContentText();
        const responseData = JSON.parse(responseText); // Разбираем JSON-строку

        if (responseCode === 200) {
            // Фильтрация для "ICG Color Cycle (сайкл)" и "CORE"
            const filteredDates = responseData.data
                .filter(item => item.service.name === "ICG Color Cycle (сайкл)" || item.service.name === "CORE")
                .map(item => ({
                    name: item.service.name,
                    startDate: item.startDate
                }));

            console.log('Отфильтрованные даты:', filteredDates);

            // Создаем события в календаре для каждой отфильтрованной даты
            filteredDates.forEach(eventData => {
                const startTime = new Date(eventData.startDate);
                const endTime = new Date(startTime);
                endTime.setHours(startTime.getHours() + 1); // Устанавливаем продолжительность события на 1 час

                let eventTitle = eventData.name;
                let eventDescription = '';

                if (eventData.name === "ICG Color Cycle (сайкл)") {
                    eventDescription = 'ICG Color Cycle (сайкл тренировка с индивидуальными мониторами с мультимедийной системой ICG для визуализации данных и создания увлекательных программ. В зависимости от ваших данных мощности и сердечного ритма, которые фиксируются в режиме реального времени, для вас формируется оптимальная нагрузка в пяти цветовых зонах (белой, синей, зеленой, желтой и красной). Эти цвета зажигаются на передней панели – и отлично видны тренеру, который контролирует группу.';
                } else if (eventData.name === "CORE") {
                    eventDescription = 'CORE тренировка фокусируется на укреплении центральной части тела, включая мышцы брюшного пресса и поясницы. Занятие направлено на повышение стабильности и силы всего тела.';
                }

                CalendarApp.getDefaultCalendar().createEvent(
                    eventTitle,
                    startTime,
                    endTime, {
                        description: eventDescription,
                        location: 'Фитнес клуб World Class, Пермская ул., 33, Пермь, Пермский край, Россия, 614045'
                    }
                );

                console.log(`Событие создано для: ${startTime} - ${eventTitle}`);
            });

        } else {
            console.error(`Ошибка: получен статус ${responseCode} с ответом: ${responseText}`);
        }

    } catch (error) {
        console.error('Ошибка запроса:', error.message);
    }

    console.log(`Функция WorldclassAPISchedulingPerm закончила работу в ${(new Date()).toLocaleString("ru-RU")}`);
}
Результат выполнения скрипта
Результат выполнения скрипта

Я не хожу на каждое занятие, а просто смотрю в календарь и когда намечается окно в моей деятельности - иду на ближайшее.

Как Вы можете воспользоваться этим скриптом?

Если вы новичок и хотите тоже автоматизировать получение определенных занятий, то скрипт Google Apps это мощный инструмент, который может вам помочь:

Шаг 1: Доступ к скрипту Google Apps

  1. Откройте Google Drive: начните с перехода на Google Drive.

  2. Создайте новый скрипт:

  • Нажмите кнопку Создать в верхнем левом углу.

  • Выберите Еще в раскрывающемся меню.

  • Нажмите Скрипт Google Apps. Откроется редактор скриптов Google Apps на новой вкладке.

Шаг 2: Назовите свой проект

  1. Назовите свой скрипт: как только откроется редактор скриптов приложений, вы увидите проект без названия. Нажмите на заголовок (обычно это «Проект без названия») и дайте ему осмысленное имя, например «Расписание World Class».

Шаг 3: Скопируйте код

  1. Удалите код по умолчанию: в редакторе вы увидите код по умолчанию (function myFunction() {}). Вы можете удалить его, чтобы освободить место для нового скрипта.

  2. Скопируйте и вставьте код:

  • Перейдите к статье с кодом скрипта Google Apps.

  • Скопируйте весь скрипт, предоставленный в статье.

  • Вернитесь в редактор скриптов приложений и вставьте код в пустое место.

Шаг 4: Сохраните свой скрипт

  1. Сохраните свою работу: Щелкните значок дискеты или нажмите Ctrl + S (или Cmd + S на Mac), чтобы сохранить свой скрипт.

Шаг 5: Запустите скрипт

  1. Запустите свой скрипт: Чтобы протестировать скрипт, щелкните значок воспроизведения (▶) в верхней части редактора. Если вы запускаете скрипт впервые, Google запросит авторизацию.

  2. Авторизуйте свой скрипт: Следуйте инструкциям, чтобы разрешить скрипту доступ к вашему Google Calendar и другим необходимым службам.

Шаг 6: Проверьте вывод

  1. Проверьте журналы: После запуска скрипта вы можете проверить сообщения журнала, чтобы убедиться, что он работает правильно. Перейдите в Просмотр > Журналы в редакторе скриптов приложений, чтобы увидеть вывод.

Шаг 7: Автоматизируйте его

  1. Установите триггер: если вы хотите, чтобы этот скрипт запускался автоматически через регулярные интервалы, вы можете настроить триггер:

  • Нажмите на значок часов на панели инструментов (Триггеры).

  • Нажмите + Добавить триггер в правом нижнем углу.

  • Установите запуск скрипта еженедельно, например на ночь между понедельником и вторником.

И это все! Вы успешно создали скрипт Google Apps, который автоматизирует добавление расписания в ваш календарь Google.

Результаты

При использовании гугл календаря этот скрипт представляет удобный инструмент который ищет интересующие групповые события и добавляет их автоматически в Ваш собственный Гугл Календарь.

Автор: Михаил Шардин,

2 сентября 2024 г.

Комментарии (2)


  1. fdsvptr
    02.09.2024 16:54
    +1

    Все хорошо кроме того, что все строится на парсинге по названию тренировки. Но расписание клубов динамическое.

    И тут интересные сценарии возникают:

    1. Предположим название переименуют и план сеансов один, тогда вы поймете что-то не так по пустому Гугл календарю

    2. Но что если часть сеансов переименуют в условный Black Cycle, тогда часть сеансов будет в вашем календаре, а часть исчезнет

    3. Самый интересный: вы привыкли что сайкл три раза в неделю, но плюсом добавляют Black Cycle в более удобные слоты и вуаля, их не будет опять

    Но возможно в WC все статично, тогда решение будет рабочим)


    1. empenoso Автор
      02.09.2024 16:54

      Да, вы абсолютно правы.

      Но названия у них вряд ли изменятся - эти названия по фирме производителю тренажёров.

      Если увижу что в моём календаре что-то придёт не так - всегда могу свериться с их расписанием на сайте.