(Inteligentne) Raporty na żądanie

Czytanie zajmie Ci około 10 minut

Dzisiaj przygotujemy narzędzia do automatycznego generowania raportów, które będzie (quasi) inteligentnie odpowiadać na pytania zadane przez email.

Czyli jak pozbyć się upierdliwej pracy (i zająć się czymś innym)

Uwaga: wraz z aktualizacją pakietu gmailr do wersji 1.0 prezentowany poniżej kod związany z logowaniem do konta może nie działać. Przeczytaj proszę informacje o tym jak to zmienić Kod na GitHubie powinien być ok.

Problem z jakim można się często spotkać w korporacjach to wysyłanie tych samych raportów mailem. Wyobraźmy sobie taki scenariusz:

  • są trzy biura sprzedaży, każde jest odpowiedzialne za inny region
  • jesteś specem od generowania raportów ze sprzedaży dla tych biur
  • wszystkie raporty są takie same, różnią się tylko regionem
  • co chwilę ktoś pyta: podeślij mi aktualny raport dla mojego regionu
  • więc generujesz ten raport i odsyłasz mailem w odpowiedzi

Znasz to? Ja znam. Upierdliwe jak cholera. Można zrobić jakiś dashboard (w Shiny czy innym ustrojstwie), ale ludzie zapominają gdzie można go znaleźć (jak zrobiłem kilka takich, to teraz dzwonią z pytaniem o link do tych dashboardów…).

A gdyby tak samo się robiło? Z aktualnych danych? Dzisiaj coś takiego napiszemy!

Założenie jest takie, że pytanie należy zadać poprzez wysłanie maila o odpowiednim tytule na odpowiednią skrzynkę mailową.

Zaczniemy od algorytmu:

  • sprawdź czy są nowe pytania na skrzynce
  • sprawdź czego dotyczy pytanie (jaki temat ma mail)
  • przygotuj odpowiedni raport
  • odeślij do pytającego

W pierwszej kolejności trzeba dostać się do skrzynki i sprawdzić czy są nowe maile. Już prawie rok temu o tym pisałem, wykorzystałem wówczas pakiet gmailr. Tym razem będzie tak samo, też wykorzystamy konto na Gmailu.

W przypadku korporacyjnej poczty trzeba użyć innych rozwiązań, ale na pewno się da :) Osobiście tego nie badałem, ale na StackOverflow jest jakiś kod z użyciem Pythona (osadzony przez rJython – można przepisać na reticulate) oraz jest pakiet imapr który robi coś ze skrzynkami IMAP.

Wróćmy jednak do Gmaila. We wspomnianym wpisie znajdziecie minimalne informacje jak w konsoli dla developerów wyklikać odpowiednią aplikację pozwalającą na dostęp do skrzynki poprzez Gmail API – potrzebujemy odczytywać, wysyłać i modyfikować (przenosić do kosza) maile. Odpowiednie uprawnienia dostaniemy w pliku JSON (w kodzie niżej jest to plik gmail_api_key.json).

Żeby cała maszyneria działała potrzebujemy:

  • dostępu do skrzynki Gmail po API
  • serwera z Linuxem (z takiego założenia wychodzę) albo czegoś co pozwoli nam uruchomić w zadanych odstępach czasu skrypty R – potrzebny będzie więc cron albo inny task scheduler (może być w Windowsie)
  • skonfigurowanego dla R renderera PDFów z plików markdown (przy instalacji RStudio to się chyba samo instaluje w Linuxie, na Windows zdaje się trzeba doinstalować ręcznie pandoc)

Teraz czas na kodowanie. Najpierw autoryzujemy się w Gmailu i pobieramy najnowsze maile (skrypt process_inbox.R):

Mając identyfikatory maili możemy pobrać interesujące nas informacje – wystarczy nadawca i temat. Odpowiednia funkcja zrobi to dla podanego ID:

Teraz możemy przepuścić wszystkie identyfikatory maili przez tę funkcję:

Jeśli nie dbamy o inbox:0 to chwilę to może potrwać (chyba że odpowiednio skonstruowaliśmy filtr)… ale po to był warunek wyszukiwania ograniczający zapytanie do jedynie dzisiejszych maili co wystarczy, bo sprawdzanie skrzynki będzie odbywało się często (co kilka minut).

Zatem mamy listę maili ze skrzynki. W założeniu przyjęliśmy, że pytania zawarte są w tytule maila. Zanim jednak będziemy odpowiadać na maile – przygotujmy odpowiednie odpowiedzi.

Chcę pokazać Wam dwa rozwiązania – jedno odpowiedź w treści maila, a drugie – jako załącznik z pełnym raportem.

Zacznijmy od odpowiedzi w treści. Zakładać można sytuację, w której raport nie musi być wygenerowany online, bo dane są wolno zmienne. Taki wariant przyjmiemy. Odpowiedzią na pytanie daj wykop będzie lista wykopalisk nie starszych niż 12 godzin z głównej strony Wykop.pl. Przygotujmy taką dość statyczną odpowiedź (to nowy, oddzielny skrypt – make_wykop_mail.R) przy okazji poznając odrobinę API Wykopu:

Uruchomienie powyższego skryptu (w założeniu co jakiś czas) przygotuje statyczny plik z HTMLem gotowym do wklejenia w treść maila.

Drugie zadanie to raport z danych online (tutaj też trochę wolno zmiennych, ale nie ma to znaczenia). Tym razem ma to być załącznik w PDFie, więc przygotujemy odpowiedni dokument RMarkdown, który po skompilowaniu da PDFa.

Jak pewnie wiecie RMarkdown pozwala na mieszanie treści z kodem (w R, ale też np. w Pythonie przy pomocy pakietu reticulate), co więcej – wynik działania kodu pojawia się w treści dokumentu. Cały dokument bitcoin.Rmd poniżej:

Mam nadzieję, że kod jest wystarczająco skomentowany i poradzicie sobie ze zrozumieniem :) Proszę zwrócić uwagę na początek:

i gdzieś dalej r params$odbiorca_raportu. To zapewni nam, że każdy raport będzie generowany inaczej, w zależności od podanego parametru odbiorca_raportu. Oczywiście parametry mogą być różne: może to być region, może to być zakres dat albo właśnie zamawiający (i na tej podstawie określamy region czy jakiś inny poziom dostępu do danych i uprawnień). Parametrów tych będziemy używać za chwilę przy renderowaniu raportu.

Nasz przykładowy raport pobiera dane z API serwisu nomics.com dotyczący notować bitcoinów (przed sprawdzeniem czy to działa potrzebujecie pliku nomics_apikey.rda z zapisaną zmienną zawierającą klucz API). Przy każdym renderowaniu dane są pobierane, nieco przetwarzane i rysowane. Wynik trafia do PDFa. O tym, że raport jest wygenerowany online w tym przypadku świadczy informacja w treści samego raportu (czas aktualizacji). Po zapisaniu tego dokumentu na dysku możecie go knit-nąć – powinien powstać gotowy dokument.

Oczywiście mogą to być inne dane – z bazy SQL, z jakiegoś Hadoopa czy Kafki. Może to być wynik działania jakiegoś modelu na zebranych danych – do wyboru, do koloru.

Krótkie podsumowanie tego co mamy w tym momencie:

  • pobieramy dzisiejsze maile z inboxa skrzynki na Gmailu
  • generujemy statyczne kawałki HTMLa z informacjami z Wykopu
  • mamy pełny raport (do wyrenderowania) z danych online generowany w locie do PDF

Jesteśmy w więcej niż połowie drogi. Pozostało sprawdzenie co wysłać (na podstawie tematu otrzymanego maila), przygotowanie odpowiedzi i wysyłka. Zacznijmy od dwóch funkcji, które przygotują i wyślą nam odpowiednią treść. Piszemy więc ciąg dalszy skryptu process_inbox.R.

Najpierw to co prostsze – Wykop:

Druga wersja wymaga wygenerowania dedykowanego (pamiętajcie o parametrach!) PDFa:

Wszystko gotowe, tylko nie działa. Dlaczego? Ano dlatego, że jeszcze nie sprawdzamy czy mamy odpowiednie pytanie w tytule maila oraz nie reagujemy na nie. Prosta pętla, która przejdzie przez wszystkie maile na koniec skryptu process_inbox.R:

Już za chwileczkę, już za momencik zacznie się kręcić! Właściwie już działa, o ile poczynimy ręcznie następujące kroki (w tej właśnie kolejności):

  • uruchomimy make_wykop_mail.R żeby przygotować treść maila dotyczącego Wykopu
  • wyślemy sobie (na skrzynkę do której mamy dostęp przez API Gmaila) wiadomość z tytułem “daj wykop”
  • wyślemy sobie maila z tytułem “daj bitcoin”
  • uruchomimy process_inbox.R

W odpowiedzi powinniśmy dostać dwa maile – z newsletterem Wykopu oraz drugi z raportem w PDFie o cenie bitcoinów. No chyba, że coś nie będzie działało ;)

Ale przecież nie po to robi się automaty, żeby je ręcznie uruchamiać! Dodajemy więc do crona (albo innego task schedulera) dwa zadania: jedno to uruchomienie make_wykop_mail.R na przykład co 15 minut, drugie – uruchomienie process_inbox.R na przykład co 3 minuty (to określi jak często będziemy sprawdzać inbox i odpowiadać). U mnie te wpisy wyglądają tak:

Cała maszyna działa na moim serwerze (na przykład tu możesz sobie taki kupić i tak skonfigurować) i odpowiada na maile wysłane na adres prokulski@gmail.com z tytułem (bez dodatkowych znaków, spacji na końcu itd.) daj wykop lub daj bitcoin – możecie sprawdzić. Uwaga – nie wiem jak długo w cronie to będzie wisiało, pewnie kiedyś wyłączę.

Gotowe pliki znajdziecie w repozytorium automatic_reports na moim GitHubie. Jeśli budujecie to rozwiązanie u siebie to warto w odpowiednich skryptach zadbać o to, aby pliki zapisywały i pobierały się z odpowiednich folderów (zwykłe i niezbyt koszerne setwd() na początek skryptów wystarczy). Oczywiście trzeba dostosować odpowiednio też wpisy w cronie.

Można przygotować kolejne rozwiązania:

  • sprawdzać treść maila, a nie tylko jego tytuł. A w treści, w kolejnych liniach mogą być na przykład określone parametry
  • można pobierać dane z załączników przychodzących maili i przetwarzać te załączniki: wyobrażam sobie, że wysyłamy plik Excela, w treści maila piszemy które kolumny nas interesują, a w odpowiedzi dostajemy stosowny wykres z danych zawartych w Excelu
  • można zaprząc sztuczną inteligencję czy jakieś metody machine learningowe do analizowania treści maila i na podstawie tej analizy odpowiadać – automatyczne, inteligentne odpowiedzi w biurach obsługi klienta to byłoby coś!

Inspiracją do tego wpisu był tekst Automated Report Generation with Papermill oraz jego druga część gdzie podobny problem rozwiązany jest inaczej (według mnie nieco gorzej).

Jeśli Ci się podobało albo przyda się do czegoś to podziel się wpisem ze światem (odpowiednie guziczki poniżej). Wpadnij też na Dane i Analizy na Facebooku – tam więcej takich smaczków (szczególnie dla praktyków). Nieco więcej smaczków znajdziesz też w nie-tak-bardzo cyklicznym newsletterze, którego archiwum tutaj.

Możesz też rzucić piniądz ;-) autorowi, czy tam postawić witrualną kawę – czy Wykop daje Ci newsletter? No właśnie. A serwerki same się nie opłacą…

3 komentarze do wpisu „(Inteligentne) Raporty na żądanie”

  1. Cześć,
    w „korporacji” robimy wlasnie taki interfejs mailowy. do tej pory było to VBA sprawdzające skrzynkę, obsługiwało załączniki, odpowiadało itp. jako że narzędzie się rozrasta powoli przechodzimy na pythona z użyciem win32com na outlooku.
    nic nie stoi na przeszkodzie by potem odpalic aplikację jako serwis i zapomnieć ze to wogóle tam jest :)

    • Problem jest tylko z tym, że po VBA to komputer musi być włączony. Oczywiście może to być wirtualna maszyna, więc da się obejść.

      Ale jak użytkownicy (odbiorcy) reagują? Pamiętają „komendy” wysyłane mailem? Bo chyba to jest najważniejsze wyzwanie (jak zwykle) – zmiana ludzkich przyzwyczajeń.

      • na chwilę obecną mamy 4literowe kody

        mało kto używa więcej niż 3 więc nie spotkaliśmy się z sytuacją gdzie ktoś zapomniał ale wraz z rozwojem przewidziany jest słownik akcji

        co do potrzeby włączonego komputera (firma pracuje na windowsie, korzysta z Office), łatwiej było przekonać decydentów o potrebie 1dodatkowego komputera do którego fizyczny dostęp mają 3-4 osoby niż do wydzielenia zasobów z firmowego serwera. zarówno jest bezpieczniej dla firmy jak i wygodniej dla nas. VBA+python+selenium (bo ktoś wpadł na genialny pmysł żeby udostępnić nam tylko serwis webowy do ściągania danych, więc sobie je automatycznie ściągamy :) )

Dodaj komentarz