11 min read
Od problemu do aplikacji - prezentacja na Wroc.py

UWAGA Post archiwalny, przeniesiony ze starego bloga. Linki mogą nie działać, dane mogą być przeterminowane, autor może się wstydzić tego co napisał.

Logo Wroc.py

Dnia 5 marca dumnie prowadziłem prelekcje ‘Od problemu do aplikacji czyli jak automatyzować zadania’ na Wroc.py 54.

Poniżej umieszczam link do prezentacji, a już niedługo zamieszczę tekstową transkrypcję do niej, tak aby osoby które nie uczestniczyły w Wroc.py miały także okazje wyciągnąć coś dla siebie ze slajdów.

Link do prezentacji w serwisie slides.com

Transkrypcja

O mnie

Pozwolę sobie ominąć ten wstęp pozostawiając wyłącznie najistotniejszy element tej sekcji:

Wyznaję zasadę: Problem, System, Automation

Polega ona na 3 etapach:

  • Problem

Odnalezienie w życiu powtarzalnego problemu lub takiego na który często napotykamy.

  • System

Stworzenie systemu, flowchartu, TODO-listy tego w jaki sposób dany problem rozwiązujemy. Dzięki temu zdołamy ogarnąć chaos który występuje w danym problemie, zamknąć go w odpowiednich ramach i zaoszczędzić czas - właśnie przekształciliśmy nasz problem w zamknięty schemat postępowania, koniec z rozwiązywaniem tego problemu za każdym razem od nowa.

  • Automatyzacja

Próba zautomatyzowania całego systemu bądź elementów łatwo automatyzowalnych aby zaoszczędzić nasz czas.

To, że istnieją takie trzy etapy wcale nie oznacza, że każdy z moich problemów je przechodzi. Są problemy słabo automatyzowalne ale też problemy słabo systematyzowalne i musimy sobie z tego zdawać sprawę. Jednakże w tym przypadku omówimy problem łatwy do usystematyzowania oraz automatyzacji.

Problem

Problem który potrzebowałem rozwiązać pojawił się naturalnie. Lubię muzykę, i lubię mieć ją dobrze pokatalogowaną. Z podziałem na rok i na miesiąc, tak abym mógł w dowolnym momencie cofnąć się do muzyki i wspomnień z nią związanych. Lubię też zbierać, czego dowodem jest ogromny zbiór memów na moim dysku.

Te dwie rzeczy łączą się w mój problem: problem zbierania muzyki oraz odpowiedniego katalogowania jej (wraz z odpowiednim tagowaniem).

System

Sposób w jaki rozwiązaywałem ten problem do tej pory można rozbić na listę odpowiednich kroków:

  • Sprawdź każdy z linków w przeglądarce (Google Chrome) czy działa
  • Pobierz piosenki za pomocą Free YouTube to MP3 Converter
  • Użyj Mp3tag:
    • Usuń wszelkie “[Radio Edit]”, “[Official Video]” itp. z nazwy
    • Wygeneruj artystę oraz tytuł piosenki na podstawie poprawionej nazwy
    • Uzupełnij rok oraz gatunek muzyki ręcznie

Automatyzacja

Najprostszym rozwiązaniem mojego problemu było połączenie możliwości pobierania muzyki z YouTube za pomocą biblioteki YouTubeDL oraz parsowania tagów artystów z LastFM w narzędzie które nazwałem LlameDL

Tak, jeżeli logo przypomina Ci Winamp to wiesz skąd brałem inspiracje.

Minimum Viable Solution

Pierwszym etapem, gdy wiemy jak rozwiązujemy problem manualnie, to stworzenie MVS - Minimum Viable Solution. Będzie to najprostsze rozwiązanie naszego problemu bądź jego elementu. W tym przypadku chcemy zastąpić cały proces jednym skryptem.

Warto w tym miejscu oprzeć się na wiedzy którą posiadamy bez szukania tysiąca bibliotek i technologii które możemy wykorzystać - tworzymy minimalne działające rozwiązanie.

W moim przypadku było to użycie biblioteki YouTubeDL do pobierania muzyki oraz parsowanie LastFM z pomocą requests.

W tym momencie następuje natychmiastowa gratyfikacja. Udało nam się zautomatyzować nasz problem. YEY!

Readme

Działający kod to nie wszystko. Powinniśmy także zadbać o prostą dokumentacje naszego skryptu. Plik README może stać się nie tylko pomocny w sytuacji gdy zechcemy podzielić się z kimś naszym skryptem ale także w sytuacji gdy sami będziemy chcieli po czasie skorzystać ze skryptu.

Najprostszy plik który na tym etapie warto już mieć będzie zawierać informacje o tym, do czego jest dany skrypt, jak go uruchomić oraz jakie ma wymagania środowiskowe (system, biblioteki itp.).

Gratification

README

Gratification_1

First issue

Chwalenie się kodem na tym etapie może być ryzykowne. W moim przypadku po pochwaleniu się skryptem na reddit dostałem pierwszy problem na githubie. Mój skrypt działał tak jak chciałem, ale tylko u mnie. Tylko na mojej maszynie.

Issue

WOW

Improvements

W tym momencie powinniśmy zadbać o możliwość rozbudowy naszego skryptu oraz jego reużywalność. Udostępnić więcej możliwości użycia danego skryptu w zależności od potrzeb i środowiska.

Existing libraries

W Pythonie jest niemalże pewne, że kod który napisałeś można zastąpić już istniejącą biblioteką.

Rule 34 of Python - 'If there is a need, there is a Python library for it'

Bardzo przydatnym narzędziem w tym miejscu staje się PyPi gdzie możemy wyszukać spośród istniejących bibliotek czy któraś z nich może zrobić pewną pracę za nas. Dzięki temu dostajemy fajny interfejs, rozwijany przez innego developera który prawdopodobnie wziął pod uwagę wiele problemów związanych z danym problemem które my byśmy odnajdywali i implementowali na nowo. W ten sposób w moim skrypcie parsowanie lastFM zastąpiłem biblioteką musicbrainzngs. Nie dość, że uprościło to moje rozwiązanie, to dało dostęp do dużo większej biblioteki informacji o artystach!

Może się tu pojawić pytanie: Jak wybierac biblioteki z gąszcza innych dostępnych na PyPi ?

Gdy chcę wykorzystaćjakiś interfejs, zasada jest prosta: wybieram biblioteke która najszybciej pozwoli mi zaimplementować rozwiązanie. To powoduje, że często wygrywają u mnie biblioteki których przykład sposobu użycia jest wystarczająco krótki i prosty do zrozumienia i wykorzystania.

Unit tests

Test Pyramid

Test Pyramid

Aby zapewnić, że zmiany które wprowadzimy w bibliotece nie zmienią logiki działania programu warto napisać unit testy do naszego kodu. Uprości to dalszy proces związany z wprowadzaniem zmian w kodzie, a także zabezpieczy nas przed możliwymi problemami związanymi ze zmianami w zależnościach czy interfejsach.

Refactoring

Na refactoring zawsze jest dobry moment. Mając napisane odpowiednio unit testy robienie refactoringu jest łatwiejsze i bezpieczniejsze. Po każdych zmianach możemy na nowo uruchomić testy i zweryfikować czy nasze zmiany nie wpłynęły na działanie aplikacji.

Więcej informacji o metodach refactoryzacji możecie poczytać na https://refactoring.guru/

Static Code Analysis

To analizatory które za nas upewnią się, że nasz kod jest miły ładny i przyjemny. Dzięki temu, możemy się skupić na implementowaniu nowych rzeczy, a weryfikacje czy wszystko implementowane jest poprawnie pozostawimy analizatorom.

Dzielimy je na:

  • Logiczne - weryfikujące statyczne typowanie, wykrywają problemy bądź luki bezpieczeństwa
    • MyPy, Bandit, PyFlakes, Pyre
  • Stylistyczne - weryfikujące zachowanie stylu w kodzie oraz dokumentacji
    • pycodestyle, pydocstyle
  • Analityczne - weryfikujące złożoność kodu, ilość linii
    • Radon, Mccabe

Continuous Integration

Aby to wszystko miało sens, dobrze jest aby wszystkie testy były uruchamiane przy każdorazowych zmianach w kodzie. Uruchamianie wszystkich narzędzi po kolei po każdej zmianie mija się z celem. I tutaj pojawia się hasło CI - Continuous Integration. Ciągła integracja naszego kodu

Najpopularniejsze narzędzia do CI z którymi mam doświadczenie to:

  • TravisCI
  • Gitlab CI
  • Jenkins

W moim projekcie wykorzystałem TravisCI, choć teraz skorzystałbym zapewne z GitlabCI (który wspiera także repozytoria GitHubowe). Dzięki temu wszelkie analizatory czy unit testy będą uruchamiane na serwerach z pomocą zdefiniowanych przez nas pipeline’ów, gdzie sami ustalamy które analizatory i w jakiej konfiguracji mają być uruchamiane, a także uruchamiać nasz kod dla różnych wersji Pythona aby zapewnić kompatybilność.

Gratyfikacja

Mam aplikacje ze skonfigurowanym CI oraz z badgami!!

README

Gratification

Interface

Aby zapewnić jak najlepszą użyteczność z naszego narzędzia powinno ono mieć przyjazne i dopasowany User Interface. Rozróżniamy kilka typów UI:

CLI

To interface oparty o terminal. W tym przypadku mamy wiele ciekawych bibliotek pythonowych które pozwolą nam na proste stworzenie CLI dla naszej aplikacji. Jednymi z nich są:

  • Argparse
  • Google Fire
  • Click
  • Docopt

Aby stworzyć jak najlepsze narzędzie CLI dobrze jest spojrzeć na inne narzędzia tego typu dostępne w chociażby Linuxie. Jest pewna lista argumentów / flag wykorzystywanych przez te narzędzia, dlatego też tworząc CLI dobrą praktyką jest wykorzystanie istniejących patternów - nasze narzędzie będzie wtedy łatwiejsze do użycia.

http://www.catb.org/esr/writings/taoup/html/ch10s05.html

entry_points

W przypadku aplikacji typowo desktopowych, dobrą praktyką jest dodanie skryptu do łatwego uruchamiania go poprzez użycie nazwy w terminalu zamiast odnoszenia się do jego ścieżki. entry_points - to parametr który możemy zdefiniować w pliku setup.py, a który zapewnia nam dodanie narzędzia do uruchamiania wszędzie na naszej maszynie.

GUI

To interfejs graficzny. Nie mam dużego doświadczenia z nimi, jednak najpopularniejszymi są:

  • tkinter
  • PyQT5

Ten pierwszy jest wbudowaną biblioteką Pythonową która pozwala nam tworzyć proste i lekkie GUI. Druga natomiast jest dużą biblioteką pozwalającą wyklikać nam GUI do aplikacji, jednakże minusem jest rozmiar jaki taka aplikacja będzie ostatecznie ważyć.

Web Framework

Frameworki pozwalające stworzyć witrynę możliwą do hostowania lokalnie bądź na zewnętrznym serwerze. Rzadko wykorzystywana metoda przy udostepnianiu skryptów które rozwiązują nasz problem chyba, że myślimy o udostępnianiu na szeroką skalę lub o monetyzacji. Zaliczamy do nich:

  • Flask
  • Django

Gratyfikacja

Po dodaniu odpowiedniego CLI nasza aplikacja otrzymała ładną komendę do wywołania w terminalu

llamedl --bookmark_name music_to_download --directory_path /home/jarek/Music

Gratification

Extensibility

Aplikacja już działa, ma świetne wsparcie dla użytkownika ale czegoś jej brakuje. Mimo, że daliśmy możliwość wyboru katalogów skąd pobierane będą linki, a także gdzie utwory będą zapisane nasza aplikacja wciąż jest ograniczona. Jej implementacja zakłada tylko pobieranie tresci z jednego źródła, dla linków z jednego źródła i tagowanie z pomocą danych z jednego źródła. Ważnym jest teraz taka zmiana implementacji, aby można było wprowadzać obsługę nowych interfejsów. Na tym poziomie skupiłem się na dodaniu możliwości przekazywania linków na kilka różnych sposobów.

Design Patterns

Design Patterny zostały stworzone aby ułatwić implementacje różnych potwarzalnych w aplikacjach metod za pomocą odpowiednich szablonów. Dzięki temu programista znający design patterny widząc kod łatwiej zrozumie jak dana metoda jest implementowana w naszej aplikacji, oraz jak rozszerzyć jej możliwości.

Więcej o design patternach można przeczytać tutaj https://refactoring.guru/design-patterns, a o tym, jak używać ich z Pythonem oraz które z nich są aplikowalne w nim tutaj https://python-patterns.guide.

Composition over inheritance

Inheritance is when you design your types around what they are, and composition is when you design types around what they do.

Gratyfikacja

Cleaning Up

Gdy mamy już wszystko gotowe można zrobić wielkie sprzątanie w naszej aplikacji.

Documentation

Jeżeli chcemy aby nasza aplikacja się dalej rozrastała, powinna ona posiadać odpowiednią dokumentacje. Dzięki temu użytkownikom łatwiej będzie zrozumieć jej działanie, a deweloperom rozpocząć implementacje nowych rozwiązań w niej. Najpopularniejszymi systemami do generowania takich dokumentacji są:

  • Sphinx
  • Read The Docs

Refactoring

Tak jak wspominałem wcześniej, zawsze jest dobry moment na refactoring. Po przejściu tylu etapów oraz pisząc dokumentacje jest szansa, że zauważysz miejsca gdzie można było coś zrobić lepiej, coś poprawić, dopisać coś. Staraj się zawsze zostawiać kod lepszym niż go zastałeś, nawet jeżeli nie zajmujesz się aktualnie tą funkcją, to jeżeli możesz coś w niej poprawić by była lepsza - zrób to. Pamiętaj, że statystycznie więcej kodu czytamy niż piszemy, więc niech ten kod będzie jak najbardziej czytelny być może.

Autoformatting

Ja tu piszę kod, nie maluje. Nie muszę wiedzieć jak coś ma dobrze wyglądać ale chcę by wyglądało dobrze zawsze. Dlatego też aby nie spierać się o to jakiego wcięcia użyć korzystajmy z możliwości technologii. Autoformattery pozwalają na automatyczne formatowanie kodu według konfiguracji ustalonej przez dany formatter. DEP8 ustalił black jako typowy formater do kodu w aplikacjach Django, i także go polecam (notatka: nie wspiera Pythona 2).

  • autopep8
  • yapf
  • black

WOW

Co dalej?

W moim przypadku projekt to metoda nauki nowych rzeczy, ale także poznawanie różnych bibliotek i interfejsów. Dlatego też dalsze plany na aplikacje to:

  • Docker
  • Wsparcie dla innych przeglądarek ( Firefox, Opera )
  • Wsparcie dla innych źródeł ( Soundcloud, Spotify?! )

Oh no

Podsumowanie

Dużo osób gdy chce nauczyć się programowania, zastanawia się co napisać, jak napisać. Najważniejsze jest bowiem zacząć i pisać. LlameDL traktowałem od samego początku jako możliwość nauki oraz rozwijania wiedzy z programowania. Z czasem ucząc się oraz poznając nowe możliwości starałem się je wprowadzić do swojego projektu - takie połączenie nauka “just in time learning” z nauką poprzez praktykę.

Jeżeli jest coś, co chciałbym abyś zapamiętał z tej prezentacji to dwie frazy:

Problem, System, Automation

Zdefiniuj problem, stwórz system jego rozwiązywania i spróbuj go zautomatyzować

Solution, Interface, Extensibility

Twórz działające rozwiązanie, następnie ułatwiaj jego ponowne użycie a ostatecznie pozwól na obsługę większej ilości przypadków użycia

Dziękuję :)