Пример использования Microsoft Flow или Как подарить жене цветы +10


Здесь я продемонстрирую не совсем простой пример использования Microsoft Flow для решения одной практической задачи.

Постановка задачи


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

В таких требованиях нет ничего особенного. Достаточно легко написать программу, которая бы напоминала мне об этих событиях. Но desktop-приложение обладает существенным недостатком. Оно будет работать только на одной машине, в то время, как у меня их несколько (дома, на работе, ...). И еще есть смартфон… Не было бы здорово получать уведомления на любом из моих устройств?

Ну, можно сохранять информацию о событиях в каком-нибудь Интернет-хранилище (например Mlab). В этом случае все экземпляры моего приложения будут работать с одной и той же информацией, что позволит им быть синхронизированными между собой. Тем не менее, мне потребуется установить эти приложения на все мои компьютеры. Кроме того, я использую Windows на компьютерах и Android на смартфоне, так что мне придется писать несколько различных приложений, если я действительно хочу получать свои уведомления везде. Как преодолеть эту проблему? С помощью Web, конечно же.

Если я создам Web-приложение, я смогу использовать его практически на любом устройстве. Замечательно! Но все же не достаточно замечательно. Позвольте мне объяснить мою точку зрения. Хотя не так трудно написать Web-приложение, решающее мою проблему, тем не менее в этом случае мне придется заботиться о множестве вещей. Нужно думать о хостинге, о хранилище данных, о хранении кода, … И зачем? Практически вся требуемая функциональность уже реализована в современных Web-календарях, таких как Google Calendar. Он может создавать события, делать их повторяющимися, посылать мне уведомления об их наступлении, и т.п. Единственно чего там нет, так это требуемой мне рандомизации. Не было бы здорово, если бы я мог просто добавить эту возможность, и использовать все остальные уже имеющиеся возможности?

Когда я думал об этой проблеме, я наткнулся на сайт IFTTT.com. Идея этого сайта проста, но продуктивна. Если что-то происходит, он что-то делает. Я знаю, это звучит странно, поэтому позвольте привести ряд примеров. Если я получил электронное письмо от определенного человека, пошли мне SMS. Если мой любимый автор опубликовал новую статью в своем блоге, сообщи мне об этом в Slack. Или если пришло время определенного события в Google Calendar, пошли мне письмо. Надеюсь, теперь понятно, к чему я веду. Этот сервис может следить за определенными событиями (они называются триггерами) и выполнять некоторые действия, когда эти события произошли. IFTTT поддерживает огромное количество возможных триггеров и действий. Я мог бы отслеживать мои события в Google Calendar, и когда определенное событие происходит, я бы посылал себе письмо, удалял старое событие и добавлял новое на более позднее время. Здорово! Именно то, что требуется! Но не совсем.

Прежде всего, IFTTT допускает только одно действие на триггер. Это не такая уж большая проблема, т.к. можно создать несколько апплетов с идентичными триггерами (апплетом называется комбинация триггера и действия в IFTTT). Один будет слать мне письмо, другой — удалять старое событие, третий — создавать новое событие. Но есть и более серьезное препятствие. Для нового события мне нужно создавать время срабатывания с рандомизацией. И я не нашел, как это сделать в IFTTT. Это означает, что данный сервис не может решить мою задачу. Но, может быть в Интернет есть другие подобные сервисы? Да, есть.

Следующим сайтом, который я рассматривал, был Zapier. Здесь мы можем использовать несколько действий на один триггер, что хорошо. Но это доступно только за деньги, что не так хорошо. Я не играл с Zapier достаточно долго, но у меня сложилось впечатление, что он также не дает возможности для требуемой мне рандомизации. Хотя тут я могу и ошибаться. В любом случае, я перешел к следующему кандидату.

Это был Microsoft Flow. Данный сервис позволяет мне отслеживать 750 срабатываний триггера в месяц бесплатно. Это более чем достаточно для моих нужд. Более того, он имеет поддержку выражений, среди которых есть и функция rand()! Это именно то, что нужно. Теперь позвольте мне показать, как решить описанную задачу с помощью Microsoft Flow.

Решение


Сначала нужно создать новый поток (flow). Потоком называется комбинация триггера и действий. Чтобы сделать это, зарегистрируйтесь на сайте и в главном меню щелкните на My flows, а затем на Create from blank:



Щелкните на кнопке Create from blank:



Вас попросят выбрать триггер. Введите «calendar» в поле поиска и выберите Google Calendar:



В списке доступных триггеров для Google Calendar выберите When an event starts:



Здесь вас могут попросить разрешить доступ со стороны Microsoft Flow к Google Calendar от вашего лица.

Теперь триггер готов. Его единственным параметром является календарь:



Да, в Google Calendar вы можете создать несколько календарей. Каждое событие принадлежит к одному из календарей, и вам нужно выбрать календарь, чьи события будут запускать ваши действия.

Нет ничего плохого в том, что создать новый календарь и класть в него все события, по которым должны срабатывать ваши действия. Но если не все события в календаре должны обрабатываться, вы можете использовать фильтрацию. Нажмите New step и затем Add a condition:



Это создаст для вас фильтр. Теперь нужно решить, как именно мы хотим фильтровать события. К примеру, я хочу обрабатывать только те события, которые содержат текст "[RANDOM]" в поле “местоположение”. Чтобы выполнить это, щелкните на поле ввода Choose value. Microsoft Flow покажет вам список возможных значений, с которыми вы можете работать:



Щелкните на Event List Event Location. И заполните остальные поля соответствующими значениями:



Обратите внимание на ссылку Edit in advanced mode. Она очень полезна. Если щелкнуть на нее, вы получите представление того же условия в виде текстового выражения:



Это очень поможет, когда придет время писать ваши собственные выражения.

Теперь можно добавлять действия для наших событий. В ветке If yes вашего фильтра щелкните на ссылку Add an action. Здесь я создам действие, которое будет посылать мне уведомление о событии на Gmail:



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

Теперь у нас есть уведомления. Пришло время удалить старое событие в календаре и создать новое на более позднее время. С помощью ссылки Add an action создайте действие Delete an event для Google Calendar:



Следует сказать, что вполне возможно просто изменить событие в календаре вместо того, чтобы удалять старое и создавать новое. Но здесь я буду все же придерживаться последнего варианта, чтобы показать вам еще одну возможность Microsoft Flow. Пусть я хочу, чтобы создание нового события в календаре происходило не после удаления старого, а параллельно с этим. Наведите мышку на стрелку между действиями Send email и Delete an event. На экране появится знак «плюс». Щелкните на нем и выберите Add a parallel branch > Add an action в контекстном меню:



Теперь мы можем добавить параллельное действие Create an event из Google Calendar. Для этого действия мы возьмем заголовок, описание и местоположение из нашего исходного события. Единственная вещь, которую осталось сделать — это установить время начала и окончания нового события:



Теперь мы подходим к действительно интересной части. Нам нужно как-то добавить случайное число дней ко времени старта текущего события. Эту новую дату мы будем использовать в качестве начала создаваемого события. Например, я хочу добавить 30 дней плюс/минус 2 дня. Документацию по доступным функциям можно найти здесь. Признаюсь, это не очень простое чтение. У меня было много вопросов, особенно о том, как извлечь время начала из исходного события, чтобы его можно было использовать в функциях. Определенную помощь можно получить от нашего фильтра. Помните ссылку Edit in advanced mode:



Щелчок на ней показывает соответствующее выражение:



Это дало мне некоторую подсказку в написании моих выражений. Теперь щелкните на поле Start time события Create an event и выберите вкладку Expression:



В поле ввода запишите следующее выражение:

addDays(triggerBody()?['start'], add(30, rand(mul(2, -1), add(2, 1))))

и нажмите кнопку Ok. Данное выражение в точности решает нашу задачу: добавляет 30 дней плюс/минус 2 дня ко времени начала исходного события. Но что, если мне нужна некоторая гибкость? Что, если я хочу иметь несколько типов событий? Для событий первого типа я буду увеличивать время начала на 30 дней плюс/минус 2 дня, для событий второго типа — на 14 дней плюс/минус 3 дня, и т.д. Каким образом возможно достичь этого?

Вот один из способов. Помните, что мы храним строку "[RANDOM]" в поле “местоположение” наших событий? Теперь запишем в это поле дополнительную информацию. Его содержимым будет текст в формате "[RANDOM],NN,MM", где NN — две цифры и MM — также две цифры. Будем увеличивать время наступления события на NN дней плюс/минус MM дней. При использовании этого формата, я могу быть уверен, что символы в позициях 9 и 10 (начиная с 0) строки будут представлять NN, а символы в позициях 12 и 13 — MM. А вот выражение, использующее этот новый формат хранения информации для увеличения времени начала события:

addDays(triggerBody()?['start'], add(int(substring(triggerBody()?['location'], 9, 2)), rand(mul(int(substring(triggerBody()?['location'], 12, 2)), -1), add(int(substring(triggerBody()?['location'], 12, 2)), 1))))

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

Здорово! Мы почти достигли нашей цели. Осталось только установить время окончания нового события. И здесь мы встречаем наше последнее препятствие. Нужно, чтобы время окончания события равнялось времени его начала плюс 15 минут. Microsoft Flow поддерживает функцию addMinutes, и можно было бы написать выражение типа:

addMinutes(<предыдущее выражение>, 15)

Однако по природе функции rand, в результате мы получим значение, никак не связанное со значением для времени начала события. Вместо этого было бы хорошо иметь переменную 'nextStart', которая бы хранила значение нашего выражения. Тогда можно было бы использовать ее значение для времени начала события, и использовать

addMinutes(<значение переменной 'nextStart'>, 15)

для времени его окончания. И знаете что? Microsoft Flow поддерживает переменные. Сперва мы должны инициализировать ее. Наведите мышь на стрелку между триггером и фильтром. Щелкните на знаке “плюс” и выберите Add action:



Введите в поле поиска текст «Variables» и выберите Initialize variable:



Задайте имя переменной 'nextStart' и тип «String». Microsoft Flow не имеет отдельного типа для дат и времени, он везде использует строки.



Теперь нужно установить значение для этой переменной. Этого нельзя сделать прямо здесь, поскольку еще не известно, правильное ли это событие. Только после фильтра мы можем быть в этом уверены. Поэтому добавим другое действие типа Set variable после отправки почтового уведомления:



Здесь установим значение созданной переменной в наше длинное выражение. Остается только переиспользовать эту переменную в выражениях для времени начала и окончания события. Ссылаться на переменную в выражении можно так:

variables('nextStart')

Поэтому выражение для времени окончания события будет иметь вид:

addMinutes(variables('nextStart'), 15)



Заключение


Вот и конец истории. Нужно только сохранить поток, и Microsoft Flow запустит его для нас.

Надеюсь, эта статья будет полезна для вас. Для меня Microsoft Flow оказался замечательным инструментом автоматизации задач.




К сожалению, не доступен сервер mySQL