SDJ_05_2010_PL

84

description

Software Developers Jurnal

Transcript of SDJ_05_2010_PL

Page 1: SDJ_05_2010_PL
Page 2: SDJ_05_2010_PL
Page 3: SDJ_05_2010_PL
Page 4: SDJ_05_2010_PL

4

SPIS TREŚCI

32 Plone – Zbuduj firmowy intranetRadosław JankiewiczWybierając narzędzie mające zapewnić bezpieczną i sprawną komunikację wewnątrz-firmową, warto zwrócić uwagę na Plo-ne – otwarty system CMS oferujący szeroki zakres możliwości dysponujący wieloma dodatkowymi produktami, które pozwa-lają dopasować go do własnych potrzeb. W poniższym artyku-le przedstawione zostało zastosowanie Plona w roli firmowe-go intranetu.

40 Python 3...czyli co nowego w trzeciej edycji językaŁukasz LangaŁukasz przeprowadzi Cię przez nowości w najświeższej edycji ję-zyka programowania Python i pokaże, w jaki sposób wpływają na sposób tworzenia programów.

46 Testy wydajnościowe z FunkloademAndrzej Mleczko Masz problemy z wydajnością swojej aplikacji? Nie wiesz gdzie leży problem? A może po prostu chcesz wiedzieć czy twoja konfiguracja wytrzyma prawdziwe obciążenie? W dodatku szukasz rozwiązania OpenSource? Przeczytaj koniecznie ten artykuł...

50 Django – Doświadczenia z pracy z frameworkiemŁukasz LangaO ile dokumentacja Django i blogosfera obfitują w zachęcają-ce tutoriale i przewodniki dla początkujących, dość rzadko zna-leźć można artykuły wykraczające poza przysłowiowy „pierwszy blog w 15 minut”.

BAZY DANYCH54 Krótki wstęp do ZODBMichał WęgrzynekPrawie każda aplikacja, z wyjątkiem najprostszych skryptów, musi w jakiś sposób gromadzić przetwarzane dane. Może je zapisywać bezpośrednio w plikach, w relacyjnych bazach da-nych czy w jednej z modnych ostatnio baz NoSQL, takich jak Co-uchDB czy MongoDB.

58 Sieć dziś i jutro – Rozpowszechnienie Internetu zmieniło na zawsze sposób, w jaki pracujemy, odpoczywamy i komunikujemy się ze sobąPaweł Korzec, Krzysztof WysockiPo pierwszej rewolucji Internetu przyszła jej kolejna faza: Web 2.0 i dzięki powstaniu serwisów społecznościowych wciągnęła do sieci miliony nowych użytkowników. Co przyniesie ze sobą trzecia fala? Zapraszamy do czytania.

17 Opis DVD

BIBLIOTEKA MIESIĄCA6 Przetwarzanie obrazów za pomocą OpenCVIgor KupczyńskiPrzetwarzanie obrazu jest też częstym motywem w filmach scien-cie-fiction. Bohaterowie za pomocą swoich komputerów potrafią wskazać na obrazie niewidoczne gołym okiem detale, wyostrzyć rozmyte napisy; roboty namierzają wrogów na polu bitwy, a sys-tem ochronny wpuszcza do budynku tylko tych, których rozpozna na podstawie obrazu z kamery.

KLUB TECHNICZNY12 Eclipse IDETomasz MaćkowiakDla języka Python istnieje wiele edytorów. Poczynając od konso-lowych, takich jak vim, przez dedykowane, jak IDLE, kończąc na wielo-językowych platformach programistycznych, jak Netbeans lub Eclipse. Niniejszy artykuł prezentuje wykorzystanie popular-nego Eclipse do rozwoju aplikacji w Pythonie.

NARZĘDZIA18 Buildout – Narzędzie automatyzujące budowę i zarządzanie aplikacjami w języku PythonWojciech LichotaW trakcie rozwoju aplikacji ilość zależnych bibliotek stopniowo się zwiększa. Ręczne zarządzanie projektem staje się mało wy-godne, a instalacja oprogramowania na nowej maszynie wy-dłuża się. Użycie narzędzia Buildout pozwoli zautomatyzować większość tych czynności i ułatwi pracę programistom i admi-nistratorom.

PROGRAMOWANIE 22 Django – Szybkie tworzenie serwisów Web 2.0Paweł MandesJeśli cenisz sobie prostotę, elegancką architekturę i zwięzły kod, a jednocześnie potrzebujesz zaawansowanych funkcjonalności i do tego gonią Cię terminy – framework Django jest dla Ciebie. Powstał na potrzeby redakcji on-line, gdzie szybkość reakcji na ciągłe zmiany jest kluczowa.

05/2010 (185)

4 05/2010

Page 5: SDJ_05_2010_PL

www.sdjournal.org 5www.sdjournal.org

APLIKACJE BIZNESOWE62 Wdrożenia SAP – droga przez mękę?Co może się nie udać?Karolina ZmitrowiczWdrażanie systemu SAP jest przedsięwzięciem wymagających ścisłej współpracy ze strony firmy wdrożeniowej i klienta. Roz-wiązania typu SAP posiadają z reguły gotową funkcjonalność podstawową ogólną lub dedykowaną dla danej branży.Warsztaty

70 C++ Qt 4.5 – Podstawy budowy aplikacji przy użyciu biblioteki QtŁukasz KlejnbergW artykule zostanie zaprezentowana budowa bardzo prostej aplikacji okienkowej w oparciu o bibliotekę Qt – będzie to zwy-kły kalkulator z prostymi wyrażeniami arytmetycznymi.

EFEKTYWNOŚC PRACY78 Na kiedy? – Planowanie zadań programistycznychMichał Bartyzel, Mariusz SieraczkiewiczZ opracowywaniem zadań programistycznych wiążą się dwa kluczowe pytania: ile to zajmie? oraz na kiedy? Pierwsze pyta-nie dotyczy szacowania zadań, czyli określania w jednostkach czasu, jak długo potrwają prace. Drugie pytanie dotyczy plano-wania – czyli osadzania oszacowanych czynności w kalendarzu. W artykule zajmujemy się tym drugim pytaniem.

5

Miesięcznik Software Developer’s Journal (12 numerów w roku)jest wydawany przez Software Press Sp. z o.o. SK

Redaktor naczelny: Łukasz Łopuszański [email protected]

Redaktor prowadzący: Maciej Dziergwa

Projekt okładki: Agnieszka Marchocka

Skład i łamanie: Tomasz Kostro www.studiopoligraficzne.com

Kierownik produkcji: Andrzej Kuca [email protected]

Dział produkcji i kolportażu: Alina Stebakow [email protected]

Nakład: 6 000 egz.

Adres korespondencyjny:Software Press Sp. z o.o. SK,

ul. Bokserska 1, 02-682 Warszawa, Polskatel. +48 22 427 36 91, fax +48 22 224 24 59

www.sdjournal.org [email protected]

Dział reklamy: [email protected]

Obsługa prenumeraty: EuroPress Polska [email protected]

Dołączoną do magazynu płytę CD przetestowano programem AntiVirenKit firmy G DATA Software Sp. z o.o.

Redakcja dokłada wszelkich starań, by publikowane w piśmie i na towarzyszących mu nośnikach informacje i programy były poprawne, jednakże nie bierze odpowiedzialności za efekty

wykorzystania ich; nie gwarantuje także poprawnego działania programów shareware, freeware i public domain.

Uszkodzone podczas wysyłki płyty wymienia redakcja.

Wszystkie znaki firmowe zawarte w piśmie są własności odpowiednich firm.

Zostały użyte wyłącznie w celach informacyjnych.

Redakcja używa systemu automatycznego składu

Osoby zainteresowane współpracą prosimy o kontakt:[email protected]

Druk: Artdruk www.artdruk.com

Wysokość nakładu obejmuje również dodruki. Redakcja nie udziela pomocy technicznej w instalowaniu i użytkowaniu

programów zamieszczonych na płycie CD-ROM dostarczonej razem z pismem.

Sprzedaż aktualnych lub archiwalnych numerów pisma po innej cenie niż wydrukowana na okładce – bez zgody wydawcy – jest działaniem na jego szkodę i skutkuje odpowiedzialnością

sądową.

Page 6: SDJ_05_2010_PL

05/20106

Biblioteka miesiącaPrzetwarzanie obrazów za pomocą OpenCV

www.sdjournal.org 7

Z przetwarzaniem i analizą strumie-nia wideo na co dzień ma do czy-nienia każdy z nas. W komputerze

przechowujemy zdjęcia z wakacji, ogląda-my filmy publikowane przez internautów na popularnych serwisach, a komunikatory internetowe umożliwiają wideokonferen-cje między uczestnikami z całego świata.

Jednak motorem napędowym tej dzie-dziny jest przemysł, który potrzebuje au-tomatycznych metod analizy obrazów w procesach kontroli jakości czy wykry-wania defektów materiałowych. Wiele ba-dań finansowanych jest także przez sek-tor usług medycznych – wspomaganie diagnozowania poprzez klasyfikację zdjęć rentgenowskich czy obrazów mikrosko-powych tkanek pozwoliłoby na obniże-nie kosztów leczenia oraz zwiększenie je-go skuteczności.

Przetwarzanie obrazu jest też częstym motywem w filmach sciencie-fiction. Boha-terowie za pomocą swoich komputerów po-trafią wskazać na obrazie niewidoczne go-łym okiem detale, wyostrzyć rozmyte na-

pisy; roboty namierzają wrogów na polu bi-twy, a system ochronny wpuszcza do budyn-ku tylko tych, których rozpozna na podsta-wie obrazu z kamery.

Okazuje się jednak, że do wielu operacji związanych z przetwarzaniem obrazu nie potrzeba ani kosmicznych technologii, ani drogiego oprogramowania. W artykule zo-stanie przedstawiona biblioteka OpenCV, która umożliwia wykonanie wielu operacji na obrazach statycznych oraz na strumie-niu wideo. Biblioteka oferuje interfejs pro-gramistyczny w języku Python, co dodatko-wo ułatwia pisanie programów z jej wyko-rzystaniem. Czytelnik zapozna się z pod-stawowymi koncepcjami programowania z wykorzystaniem tej OpenCV oraz stwo-rzy system wykrywający twarze w obrazie z kamery lub pliku wideo. Artykuł opisu-je krok po kroku, w jaki sposób uzyskać kil-ka ciekawych efektów za pomocą OpenCV ; zawiera tylko niezbędne minimum teo-rii – ma natomiast pozwolić czytelniko-wi rozpocząć przygodę z przetwarzaniem obrazów.

Biblioteka – informacje wstępneOpenCV jest wieloplatformową biblioteką oferującą zbiór funkcji do przetwarzania ob-razów statycznych oraz video. Jej rozwój zo-stał zapoczątkowany w firmie Intel, jednak

obecnie jest dostępna na licencji BSD, co oznacza, że jest darmowa zarówno do użyt-ku naukowego , jak i komercyjnego. Funk-cjonalność biblioteki opisano w ramce „Za-awansowane możliwości OpenCV”.

31 września 2009 została wydana wer-sja 2.0 biblioteki. Oprócz wielu usprawnień i nowych funkcji w kodzie biblioteki dodane zostały także nowy interfejs programistycz-ny w C++ oraz, co bardziej interesujące dla czytelników, nowy interfejs dla języka Py-thon (ramka „OpenCV i Python”).

Skrót CV pochodzi od angielskiego ter-minu Computer Vision, który oznacza dyscyplinę zajmującą się głównie przetwa-rzanie obrazów, ale też rozpoznawaniem obiektów znajdujących się na nich, mode-lowaniem otoczenia oraz interakcją mię-dzy środowiskiem a systemami kompute-rowymi.

Biblioteka jest stabilna i dojrzała oraz ma za sobą wsparcie ważnego gracza na rynku IT, jakim jest Intel, co w połącze-niu z otwartą licencją czyni z niej dosko-nałą kandydatkę do wykorzystania we wła-snym projekcie z zakresu przetwarzania obrazów.

Adresy internetowe oprogramowania wymienionego w tekście można znaleźć w ramce „W Sieci”. Biblioteka OpenCV jest oprogramowaniem typu Open So-urce i niezależnie od wybranej platfor-my można ją zbudować ze źródeł, prost-szym wyjściem jest jednak skorzystanie z przygotowanych paczek binarnych.

Podstawowe wykorzystanie OpenCVNa początek, aby sprawdzić, czy bibliote-ka została zainstalowana poprawnie zade-monstrujemy konwersję obrazu do innego formatu. W bibliotece OpenCV format ob-razu jest rozpoznawany na podstawie roz-

Przetwarzanie obrazów za pomocą OpenCVPrzetwarzanie obrazu jest też częstym motywem w filmach sciencie-fiction. Bohaterowie za pomocą swoich komputerów potrafią wskazać na obrazie niewidoczne gołym okiem detale, wyostrzyć rozmyte napisy; roboty namierzają wrogów na polu bitwy, a system ochronny wpuszcza do budynku tylko tych, których rozpozna na podstawie obrazu z kamery.

Dowiesz się:• W jaki sposób zainstalować i wykorzystać

bibliotekę OpenCV;• Poznasz podstawowe techniki przetwarza-

nia obrazów;• Nauczysz się przetwarzać strumień wideo;• Jak wykorzystać techniki uczenia maszyno-

wego do wykrywania twarzy.

Powinieneś wiedzieć:• Podstawy programowania w języku Python;• Podstawowe informacje o tym, jak kompu-

ter reprezentuje przechowywane obrazy.

Poziom trudności

Page 7: SDJ_05_2010_PL

05/20106

Biblioteka miesiącaPrzetwarzanie obrazów za pomocą OpenCV

www.sdjournal.org 7

szerzenia pliku. Kod przedstawiono na li-stingu 1.

Przetwarzanie jednopunktoweObraz w OpenCV przechowywany jest jako zbiór pikseli; z każdym pikselem skojarzone może być kilka kanałów – w modelu RGB jest to kanał czerwony, zielony oraz niebie-ski. Piksel jest krotką zawierającą wartości ja-sności poszczególnych kanałów, z przedziału <0, 255>. Obiekt obrazu daje też dostęp do różnych jego parametrów, takich jak rozmiar czy ilość kanałów.

Najprostszą formą przetwarzania obra-zu jest przetwarzanie jednopunktowe – za-wartość piksela w obrazie wynikowym za-leży tylko i wyłącznie od jego zawarto-ści w obrazie źródłowym. Oprogramowa-nie przetwarzania obrazu w tym modelu jest dość proste. Należy iterować po kolej-

Listing 1. Wczytywanie modułu OpenCV

# Wczytanie modułu OpenCV

import cv

# Załadowanie obrazka do zmiennej src

src = cv.LoadImage("budynek.bmp")

# Ponowne zapisane, tym razem w innym formacie

cv.SaveImage("budynek.png", src)

Listing 2 . Przetwarzanie jednopunktowe

ON = (255, 255, 255) # Elementy czerwone zaznaczymy na biało

OFF = (0, 0, 0) # reszta pozostanie czarna

src = cv.LoadImage("auto.bmp")

# Przygotowanie obrazu wynikowego

# src.depth pozwala pobrać głębie kolorów obrazu wejściowego, a

# src.nChannels – liczbę jego kanałów

dst = cv.CreateImage(cv.GetSize(src), src.depth, src.nChannels)

# Iteracja po kolejnych pikselach obrazu

for y in xrange(0, src.height):

for x in xrange(0, src.width):

# Warto zwrócić uwagę na odwrotną kolejność kanałów

b, g, r = src[y, x] # Pobranie wartości jasności piksela

# Kiedy piksel jest czerwony? Gdy wartość jasności koloru

# czerwonego jest wyższa od pozostałych o co najmniej

# pewien próg, tutaj dobrany na 30

DELTA = 30

if r > b + DELTA and r > g + DELTA:

dst[y, x] = ON # Ustaw wartość piksela w obrazie

# wynikowym

else:

dst[y, x] = OFF

# Erozja jest efektem pozwalającym wygładzić uzyskany obraz

cv.Erode(dst, dst)

# Wreszcie zapisujemy wyjście do pliku

cv.SaveImage("out_auto_czerwony.bmp", dst)

Listing 3. Wyodrębnienie kanału zielonego

src = cv.LoadImage("budynek.bmp")

# wyodrębnienie poszczególnych kanałów

czerwony = cv.CreateImage(cv.GetSize(src), src.depth, 1)

zielony = cv.CreateImage(cv.GetSize(src), src.depth, 1)

niebieski = cv.CreateImage(cv.GetSize(src), src.depth, 1)

cv.Split(src, czerwony, zielony, niebieski, None)

# wyzerowanie kanałów innych niż zielony

cv.Zero(czerwony)

cv.Zero(niebieski)

# Ponowne połączenie w obraz

dst = cv.CreateImage(cv.GetSize(src), src.depth, src.nChannels)

cv.Merge(czerwony, zielony, niebieski, None, dst)

cv.SaveImage("budynek_zielony.bmp", dst)

Rysunek 1a. Oryginalny obraz

Rysunek 1b. Elementy czerwone

Rysunek 2a. Oryginalny obraz

Rysunek 2b. Kanał zielony

Page 8: SDJ_05_2010_PL

05/20108

Biblioteka miesiącaPrzetwarzanie obrazów za pomocą OpenCV

www.sdjournal.org 9

nych punktach (pikselach) w obrazie. Dla każdego punktu można obliczyć jego ja-sność na poszczególnych kanałach w obra-zie wyjściowym. Nawet taki prosty model jak przetwarzanie jednopunktowe pozwala rozwiązać wiele ciekawych problemów. Na listingu 2 przedstawiono przykład wykry-wania w obrazie elementów czerwonych. Oprócz przetwarzania jednopunktowego

wykorzystano także efekt erozji, który deli-katnie wygładza krawędzie obiektów znaj-dujących się na obrazie.

OpenCV zapamiętuje wartości pikseli w kolejnych komórkach tablicy. Są one prze-chowywane wierszami, dlatego ze wzglę-du na wydajność, warto przeglądać ob-raz w sposób przedstawiony na Listingu 2 – wiersz po wierszu. Na Rysunku 1a, b

przedstawiono efekt wyszukiwania elemen-tów czerwonych.

Przetwarzanie jednopunktowe pozwala na przykład na manipulowanie rozkładem jasności danego koloru czy modyfikowanie kontrastu obrazu, a także wyliczanie pew-nych statystyk, takich jak histogramy.

Oprócz przetwarzania jednopunktowego wykorzystano także efekt erozji, który deli-katnie wygładza krawędzie obiektów znaj-dujących się na obrazie.

Na Rysunku 2a,b przedstawiono efekt działania kodu z Listingu 3

Listing 4. Przetwarzanie wielopunktowe

src = cv.LoadImage(nazwa_zrodla, 1)

# Wykrywanie krawędzi – można do tego celu zastosować operator

# Laplace'a

dst = cv.CreateImage(cv.GetSize(src), cv.IPL_DEPTH_16S, 3)

cv.Laplace(src, dst)

cv.SaveImage("out_budynek_krawedzie.bmp", dst)

# Erozja

dst = cv.CreateImage(cv.GetSize(src), src.depth, src.nChannels)

cv.Erode(src, dst)

cv.SaveImage("out_budynek_erozja.bmp", dst)

# Rozmycie – metoda cv.Smooth

dst = cv.CreateImage(cv.GetSize(src), src.depth, src.nChannels)

# CV_BLUR – rodzaj rozmycia, a kolejne liczby to współczynniki

# im większe tym efekt bardziej intensywny

cv.Smooth(src, dst, cv.CV_BLUR, 9, 7, 3, 1)

cv.SaveImage("out_budynek_rozmycie.bmp", dst)

# Konwersja do skali szarości i normalizacja histogramu

dst = cv.CreateImage(cv.GetSize(src), 8, 1)

dst2 = cv.CreateImage(cv.GetSize(src), 8, 1)

cv.CvtColor(src, dst, cv.CV_BGR2GRAY)

cv.EqualizeHist(dst, dst2)

cv.SaveImage("out_budynek_szary1.bmp", dst) # Skala odcieni szarości

cv.SaveImage("out_budynek_szary2.bmp", dst2)# Dodatkowa normalizacja

# histogramu

# Skalowanie – metoda cv.Resize

dst = cv.CreateImage((128, 128), src.depth, src.nChannels)

# Metoda ta skaluje obraz źródłowy do wymiarów obrazu wynikowego

cv.Resize(src, dst)

cv.SaveImage("out_budynek_miniaturka.bmp", dst)

# Dodanie prostego znaku wodnego

# Obraz, na którym zostanie umieszczony tekst

wmark = cv.CreateImage(cv.GetSize(src), src.depth, src.nChannels)

dst = cv.CreateImage(cv.GetSize(src), src.depth, src.nChannels)

# Wyzerowanie tablicy ze znakiem wodnym

cv.Zero(wmark)

# Inicjalizacja czcionki

font = cv.InitFont(cv.CV_FONT_HERSHEY_DUPLEX,2,2,0,1);

# Umieszczenie tekstu na obrazie

cv.PutText(wmark, "Watermark", (400, 80), font, \

cv.CV_RGB(100, 100, 100))

# Suma ważona obrazu źródłowego i znaku wodnego

cv.AddWeighted(src, 1.0, watermark, 1.0, 0.0, dst)

cv.SaveImage("out_budynek_znak_wodny.bmp", dst)

Rysunek 3a. Oryginalny obraz

Rysunek 3b. Wykryte krawędzie

Rysunek 3c. Erozja

Rysunek 3d. Rozmycie

Page 9: SDJ_05_2010_PL

05/20108

Biblioteka miesiącaPrzetwarzanie obrazów za pomocą OpenCV

www.sdjournal.org 9

Przetwarzanie wielopunktowePrzetwarzanie jednopunktowe jednak jest często niewystarczające i do określenia ja-sności piksela wyjściowego wymagane jest uwzględnienie wartości innych pikseli ob-razu. Tego typu operacji można dokonać ręcznie lub wykorzystać jedną z wielu wbu-dowanych w OpenCV funkcji. Przykła-dem takiej funkcji jest erozja wykorzysta-

Listing 5. Przetwarzanie sekwencji wideo

# Pobranie sekwencji z pliku

# sequence = cv.CaptureFromFile("video.avi")

# Pobranie sekwencji z kamery

sequence = cv.CaptureFromCAM(0) # argument jest numerem kamery

# Gdy coś pójdzie nie tak, to sequence == None

if not sequence:

print u"Nie udało się uruchomić kamery"

sys.exit(1)

while 1:

# Pobierz aktualną ramkę z kamery

frame = cv.QueryFrame(sequence)

# Koniec transmisji

if frame is None:

break

# Kamera daje domyślnie obraz po odbiciu lustrzanym

cv.Flip(frame, None, 1)

# Zrób coś z ramką

# doSomething(frame)

# Oczekiwanie na klawisz

klawisz = cv.WaitKey(10) # Czas w milisekundach

# 'q' = (ASCI) 0x71

if klawisz == 0x71:

print u"Wciśnięto 'q', kończę"

break

Rysunek 3e. Skala szarości

Rysunek 3f. Normalizacja histogramu

Rysunek 3g. Skalowanie

Rysunek 3h. Znak wodny

Instalacja OpenCV 2.0W przypadku korzystania z systemu Linux prostym rozwiązaniem jest skorzystanie z pakie-tów znajdujących się w repozytorium dystrybucji (ta metoda jest specyficzna dla danej dys-trybucji i dlatego nie zostanie tu opisana). W przypadku systemu Windows warto skorzystać z instalatora:

• Przed instalacją OpenCV należy zainstalować najnowszą wersję interpretera Python (OpenCV wspiera gałąź 2.x języka), pod adresem http://www.python.org/download/ do-stępny jest instalator dla Windows.

• Należy pobrać oraz zainstalować OpenCV w wersji 2.0, również korzystając z instalatora dla systemu Windows, url: http://sourceforge.net/projects/opencvlibrary/

• Trzeba także uaktualnić odpowiednie zmienne systemowe (przyjęto, że OpenCV znajdu-je się w katalogu C:\OpenCV2.0), można to zrobić we właściwościach systemu (kliknięcie prawym klawiszem na „Mój komputer”, wybranie opcji właściwości) w zakładce „Zaawan-sowane”, po wybraniu opcji zmienne środowiskowe. Do zmiennej PATH należy po średni-ku dopisać C:\OpenCV2.0\bin. A do zmiennej PYTHONPATH C:\OpenCV2.0\Python2.6\Lib\site-packages

Budowanie ze źródełZbudowanie OpenCV ze źródeł jest dobrym rozwiązaniem wtedy, gdy chcemy mieć dokład-ną kontrolę nad modułami, które wchodzą w jej skład lub wtedy, gdy nasza dystrybucja sys-temu Linux nie oferuje tej biblioteki wcale lub w satysfakcjonującej nas wersji. Biblioteka ma wiele zależności i jej kompilacja może przysporzyć pewnych problemów. Opis kompilacji, jak i skryptu konfiguracyjnego znajdziemy na stronie OpenCV (http://opencv.willowgarage.com/wiki/InstallGuide).

Page 10: SDJ_05_2010_PL

05/201010

Biblioteka miesiącaPrzetwarzanie obrazów za pomocą OpenCV

www.sdjournal.org 11

na w wykrywaniu elementów czerwonych. Na Listingu 4 przedstawionych zostało kil-ka innych efektów.

Rysunek 3 przedstawia efekt działania programu.

Inną przydatną metodą może być cv.Filter2D, która pozwala zastoso-wać filtr o dowolnej masce, przekazy-wanej jako argument. Model w któ-rym do określenia wynikowej jasności pik-sela wykorzystuje się wartości jasności pik-seli znajdujących się w sąsiedztwie został nazwany przetwarzaniem lokalnym. Me-toda cv.Filter2D jako argument przyj-muje maskę zawierającą wagi, określają-ce wpływ sąsiadów na wartość jasności piksela wynikowego. Ta wartość to suma jasności poszczególnych sąsiadów prze-mnożona przez ich wagi w masce, a na-stępnie znormalizowana (poprzez po-dzielenie przez sumę wag wszystkich są-siadów).

OpenCV zawiera też metody pozwalają-ce rysować figury geometryczne na obrazie wynikowym, a także dokonywać przekształ-ceń całego obrazu. Dzięki temu technikę za-stosowaną do stworzenia znaku wodnego można także wykorzystać do wygenerowa-nia captcha. Każdą literkę można obrócić o pewien kąt (na przykład za pomocą metod cv.2DRotationMatrix oraz cv.WarpAffine) czy poddać innym zniekształcającym mo-dyfikacjom; dodatkowo można utrudnić odczytanie obrazu wynikowego przez za-stosowanie takich metod jak cv.Line czy cv.DrawContours, które wytworzą na obra-zie dodatkowe artefakty.

Przedstawione tu przykłady mają zachę-cić czytelnika do własnych eksperymentów z biblioteką, jednak z pewnością nie wy-czerpują możliwości OpenCV, dlatego war-to też zajrzeć do dokumentacji (patrz ramka „W Sieci”). Zobacz też ramkę „Zaawansowa-ne możliwości OpenCV”.

Przetwarzanie sekwencji wideoOpenCV udostępnia strumienie wideo w formie kolejnych klatek (ang. frame). Każda z nich jest takim samym obiektem

Listing 6. Wykrywanie twarzy w obrazie z kamery

# Wczytanie modułów

import cv

import sys

# Lokalizacja pliku z parametrami klasyfikatora

PARAMS = 'C:\OpenCV2.0\data\haarcascades\' + \

'haarcascade_frontalface_alt.xml'

# Funkcja zaznaczająca twarze w obrazie

def detect_faces(src):

# Klasyfikatorowi wystarczy wersja w skali szarości

gray = cv.CreateImage(cv.GetSize(src), 8, 1)

cv.CvtColor(src, gray, cv.CV_BGR2GRAY)

# Poprawienie kontrastu

cv.EqualizeHist(gray, gray)

# Uruchom kaskadę Haara,

# na wejściu należy podać plik z parametrami klasyfikatora,

# znajduje się on w dystrybucji OpenCV.

haarParams = cv.Load(PARAMS)

faces = cv.HaarDetectObjects(gray,

haarParams,

cv.CreateMemStorage(0),

1.2, 3,

cv.CV_HAAR_DO_CANNY_PRUNING)

# Wreszcie narysuj prostokąty dokoła znalezionych twarzy

if faces:

for (x, y, w, h), n in faces:

cv.Rectangle(src, (x, y), (x + w, y + h),

cv.CV_RGB(255, 0, 0), 3)

# Inicjalizacja kamery i okna do wyświetlenia wyniku

print "Naciśnij 'q' aby zakończyć"

# Stwórz okno do wyświetlenia wyniku

cv.NamedWindow('Faces', cv.CV_WINDOW_AUTOSIZE)

cam = cv.CaptureFromCAM(0)

if not cam:

print "Nie można otworzyć kamery"

sys.exit(1)

# Pętla powtarzająca wykrywanie twarzy dla kolejnych ramek

while 1:

frame = cv.QueryFrame(cam)

if frame is None:

break

cv.Flip(frame, None, 1)

# Właściwe wykrywanie twarzy

detect_faces(frame)

# Wyświetl obraz z kamery

cv.ShowImage('Faces', frame)

klawisz = cv.WaitKey(10)

# 'q' = (ASCI) 0x71

if klawisz == 0x71:

print "Wciśnięto 'q', kończę"

break Rysunek 4. Wykrywanie twarzy w obrazie z kamery

Page 11: SDJ_05_2010_PL

05/201010

Biblioteka miesiącaPrzetwarzanie obrazów za pomocą OpenCV

www.sdjournal.org 11

jak obrazy, które przetwarzaliśmy wcze-śniej. Powoduje to, że przetwarzanie se-kwencji wideo nie różni się zasadniczo od przetwarzania obrazów statycznych. Se-kwencja może być pobierana z pliku lub w czasie rzeczywistym z kamery. Listing 5 przedstawia przykład pracy ze strumie-niami wideo.

Ten ogólny schemat zostanie uzupeł-niony w następnym punkcie, tak aby uzy-skać aplikację, która będzie potrafiła zna-leźć twarze w obrazie z kamery. W przed-stawionym powyżej modelu przetwarzanie

kolejnej klatki jest niezależne od poprzed-niej, ale oczywiście nic nie stoi na przeszko-dzie aby pamiętać część wartości lub cech poprzednich klatek. Wtedy w obliczaniu jasności wynikowej danego piksela zosta-nie uwzględniony kolejny wymiar – czas. Poprzez porównywanie z historycznymi klatkami można określić na przykład zmia-ny pojawiające się na obrazie, takie jak po-jawienie się nowych obiektów na obrazie, przemieszczenie się już istniejących obiek-tów, czy zmiana jasności wynikająca na przykład z nadejścia nocy.

Przykład aplikacji – wykrywanie twarzy w obrazie z kameryOstatnim elementem potrzebnym do zbu-dowania systemu wykrywającego twarze jest klasyfikator – funkcja która otrzymaw-szy obraz znajdzie na nim twarze. Tworze-nie klasyfikatorów rozpoznających obiekty znajdujące się na obrazie jest bardzo cieka-wym problemem, któremu poświęcono wie-le prac naukowych; niestety zdecydowanie wykracza on poza zakres zagadnień prezen-towane w tym artykule. Dlatego zostanie tu-taj zostanie wykorzystany istniejący i spraw-dzony już klasyfikator. Doskonale nadaje się do tego kaskada Haara, która dodatkowo jest wbudowana w OpenCV (opis działania – patrz ramka „W Sieci”). Po wczytaniu pli-ku zawierającego parametry pracy kaskady (również dołączonego do OpenCV) zwra-ca ona lokalizację twarzy odnalezionych na obrazie. Kod programu przedstawiono na Listingu 6.

Efekt działania programu można zoba-czyć na Rysunku 4.

PodsumowanieKonieczność przetwarzania obrazów, czy to statycznych, czy sekwencji wideo wy-stępuje w wielu dziedzinach życia. Prawdo-podobnie każdy z czytelników zetknął się z jednym z wielu zastosowań tej dyscypli-ny. W artykule pokazano, że do wykonania wielu operacji nie potrzeba dedykowanych rozwiązań sprzętowych ani specjalistycz-nego oprogramowania. Przedstawiono bi-bliotekę OpenCV, która doskonale spraw-dza się w tej kategorii zastosowań. Pokaza-no kilka częstych przypadków użycia, a tak-że wskazano, gdzie można szukać dalszych informacji. Przetwarzanie obrazów jest za-gadnieniem trudnym – to co człowiekowi wydaje się oczywiste i naturalne jest często niemożliwe do opisania za pomocą kompu-terowych algorytmów. Model przechowy-wania oraz przetwarzania obrazów wyko-rzystywany w systemach komputerowych zdecydowanie różni się od postrzegania środowiska przez człowieka. Autor ma na-dzieję, że biblioteka OpenCV oraz ten ar-tykuł choć trochę ułatwią rozwiązywanie problemów z tego ciekawego, choć skom-plikowanego zagadnienia, a także, że zachę-cił czytelników do własnych eksperymen-tów z przetwarzaniem obrazów.

W Sieci

• http://python.org/ - strona domowa interpretera python;• http://sourceforge.net/projects/opencvlibrary/ - strona projektu OpenCV na sourcefor-

ge.net;• http://opencv.willowgarage.com/wiki/Welcome - dokumentacja OpenCV;• http://opencv.willowgarage.com/documentation/python/index.html - dokumentacja inter-

fejsu OpenCV dla języka Python;• http://tech.groups.yahoo.com/group/OpenCV/messages - archiwum listy mailingowej

użytkowników OpenCV, można tam znaleźć rozwiązanie wielu problemów, jak i przykła-dy ciekawych zastosowań OpenCV;

• http://myopencv.wordpress.com/ - zbiór poradników przedstawiający ciekawe zastosowa-nia OpenCV (autor korzysta z interfejsu dla języka C, nie Python; jednak stworzenie ana-logicznych programów w języku Python nie powinno nastręczyć większych problemów zainteresowanemu czytelnikowi);

• http://opencv.willowgarage.com/documentation/python/object_detection.html?highli-ght=haardetectobjects#haar-feature-based-cascade-classi�er-for-object-detection – opis działania klasyfikatora Haara.

IGOR KUPCZYŃSKIIgor Kupczyński jest pracownikiem STX Next sp. z o.o. gdzie zajmuje się głównie oprogramowa-niem tworzonym w języku Python. Do jego za-interesowań należą statystyka, analiza danych oraz systemy wspomagania decyzji. Kontakt z autorem: [email protected]

OpenCV i PythonBiblioteka OpenCV napisana jest w C, jednak oferuje też API dla innych języków, a w szcze-gólności dla języka Python. W wersji 1.x biblioteki dostępny był wrapper dla Python stwo-rzony za pomocą narzędzia SWIG (funkcje dostępne były w pakietach opencv.cv oraz opencv.highgui). Jest on tutaj wspomniany, ponieważ można go znaleźć w wielu przykła-dach dla wersji 1.1 opublikowanych w Internecie. W wersji 2.0 wprowadzono nowe API dla Python (pakiet cv) . W artykule skupiono się właśnie na tym interfejsie, gdyż jest on bardziej zgodny z „zen” języka Python, a programowanie z jego pomocą jest łatwiejsze przy tej samej oferowanej funkcjonalności. Warto zwrócić uwagę, że poprzednia wersja jest nadal dostęp-na, jeśli ktoś pragnie uruchomić kod napisany dla wersji 1.x. Opis starego wrappera można zaleźć pod adresem http://opencv.willowgarage.com/wiki/SwigPythonInterface, a krótki opis różnic: http://opencv.willowgarage.com/wiki/PythonInterface.

Zaawansowane możliwości OpenCVW artykule przedstawiono tylko niektóre z możliwości biblioteki OpenCV. Biblioteka znajdu-je zastosowanie w wielu obszarach znanych z Computer Vision (polski termin „przetwarzanie obrazów” jest trochę węższy, dlatego autor pozostanie tutaj przy odpowiedniku angielskim) oraz uczeniem maszynowym, takich jak:

• Przetwarzanie obrazów 2D i 3D;• Detekcja obiektów;• Rozpoznawanie twarzy;• Rozpoznawanie ruchu;• Rozpoznawanie gestów;• Przetwarzanie obrazów w czasie rzeczywistym;• Stereowizja;• Robotyka.

Biblioteka zawiera także implementację wielu algorytmów uczenia maszynowego (głównie klasyfikatorów), co umożliwia na przykład nie tylko rozpoznawanie twarzy, ale także przypisa-nie jej do konkretnej osoby z bazy danych.OpenCV oferuje interfejs programistyczny w językach C, C++ oraz C# i Python.

Page 12: SDJ_05_2010_PL

05/201012

Klub technicznyEclipse IDE

www.sdjournal.org 13

Możliwości

• Kreatory;• Podświetlanie składni;• Formatowanie kodu;• Szablony;• Dopełnianie kodu;• Automatyczne importy;• Wyświetlanie dokumentacji;• Zwijanie bloków kodu;• Zamiana tabulacji na spacje i na odwrót;• Usuwanie plików .pyc z projektu;• Statyczna analiza kodu;• Refactoring;• Interaktywna konsola;• Wsparcie dla testów jednostkowych;• Debugger;• Zdalny debugger.

InstalacjaObsługę Pythona można w prosty sposób do-instalować do istniejącej instalacji Eclipsa. W tym celu należy przejść do ekranu insta-lacji oprogramowania (Help –> Install new so-ftware). Następnie należy dodać (Add) nową witrynę z repozytorium Pydev [2]. Ze spisu pakietów należy wybrać Pydev for Eclipse. Na-

leży zatwierdzić wszystkie dalsze okna dialo-gowe i pozwolić na ponowne uruchomienie Eclipse. Po ponownym uruchomieniu, Pydev powinien być zainstalowany.

Alternatywnym sposobem instalacji jest użycie usługi Yoxos OnDemand, która pozwa-la w łatwy sposób przygotować własną dystry-bucję środowiska Eclipse. Na stronie [3] moż-na wybrać, jakie funkcjonalności Eclipse nas interesują, usługa dobierze ich zależności i po-zwoli na pobranie IDE wraz z dodatkami w po-staci jednego archiwum. Użycie Yoxos OnDe-mand jest przyjemniejsze niż ręczna instalacja pakietów z domyślnej instalacji Eclipse, szcze-gólnie jeśli potrzebujemy wielu wtyczek (typo-wy Pythonowiec zainstaluje zapewne Pydev, edytory HTML, CSS oraz JavaScript, wtycz-ki do systemów wersjonowania). Wadą usługi mogą być starsze wersje niektórych pakietów. Usługa posiada wyszukiwarkę pakietów. Aby przygotować podstawową wersję Eclipse dla Pythona, należy wybrać z grupy Programming languages komponent Pydev for Eclipse (opcjo-nalnie także Pydev Extensions), wybrać Add the selected components to the Plan i zaakceptować zależności. Po kliknięciu przycisku Start Do-wnload rozpocznie się pobieranie. Yoxos On-Demand oferuje także funkcjonalność szablo-nów, dzięki czemu można łatwo współdzielić w organizacji przygotowaną wersję IDE.

KonfiguracjaPrzed pierwszym użyciem dodatku Pydev należy go skonfigurować. W tym celu należy

otworzyć okno preferencji (Window –> Prefe-rences) i przejść do sekcji Pydev.

Pydev obsługuje interpretery CPythona, Jythona i IronPythona. Należy otworzyć pre-ferencje wybranego interpretera (np. Inter-preter – Python). Konfiguracji interpretera dokonuje się przez wskazanie jego pliku wy-konywalnego (lub pliku jar w przypadku Jy-thona). Pydev może także spróbować auto-matycznie znaleźć interpreter zainstalowany w systemie (przycisk Auto Config).

Pydev automatycznie wykrywa zainsta-lowane dla danego interpretera biblioteki. W oknie konfiguracji interpretera można ręcznie dodać dodatkowe biblioteki do PY-THONPATH.

Pydev analizuje wykryte biblioteki i zapa-miętuje znalezione w nich symbole, których używa potem m.in. w dopełnianiu składni. Oznacza to, że po instalacji nowych bibliotek należy odświeżyć bazę symboli Pydev. Można tego dokonać, wybierając akcję Apply na ekra-nie konfiguracji interpretera. Zmusza to Py-dev to ponownego przeskanowania bibliotek.

Konfiguracja interpretera wystarcza, aby rozpocząć korzystanie z Pydev.

ProjektAby rozpocząć pracę z Pydev, należy utwo-rzyć nowy projekt. W tym celu wybieramy z menu File –> New –> Project..., następnie wybieramy z grupy Pydev opcję Pydev project. Kolejny ekran pozwala na wybór typu (CPy-thon, Jython, IronPython) i instancji inter-pretera oraz wersji gramatyki języka. Wer-sja gramatyki nie musi być koniecznie zgod-na z wersją interpretera, przykładowo moż-na tworzyć projekt z gramatyką Python 2.4 za pomocą interpretera Python 2.6.

Wybór opcji Create default 'src' folder and add it to the pythonpath? zależy od preferencji programisty. Zaznaczenie tej opcji spowoduje utworzenie w projekcie osobnego podkatalo-

Pydev

Dla języka Python istnieje wiele edytorów. Poczynając od konsolowych, takich jak vim, przez dedykowane, jak IDLE, kończąc na wielo-językowych platformach programistycznych, jak Netbeans lub Eclipse. Niniejszy artykuł prezentuje wykorzystanie popularnego Eclipse do rozwoju aplikacji w Pythonie.

Dowiesz się:• Jak skonfigurować Eclipse do pracy z Pytho-

nem;• Jak podłączyć projekt buildout do Eclipse;• Jakie udogodnienia oferuje Eclipse progra-

mistom Pythona.

Powinieneś wiedzieć:• Znajomość środowiska Eclipse;• Znajomość narzędzia buildout.

Poziom trudności

Python w Eclipse

Page 13: SDJ_05_2010_PL

05/201012

Klub technicznyEclipse IDE

www.sdjournal.org 13

gu na źródła. Dodać katalog ze źródłami moż-na w dowolnym momencie, otwierając okno właściwości projektu. W nim należy przejść do sekcji Pydev – PYTHONPATH. Na za-kładce Source folders można dodać katalogi projektu do ścieżki za pomocą akcji Add so-urce folder. Programiści, którzy preferują trzy-mać moduły Pythona bezpośrednio w katalo-gu głównym projektu, powinni dodać cały ka-talog projektu jako Source folder.

W konfiguracji PYTHONPATH projektu, na zakładce External libraries można zdefi-niować dodatkowe biblioteki używane w pro-jekcie.

Integracja z buildoutKonfiguracji zewnętrznych bibliotek można dokonać automatycznie, jeśli projekt korzy-sta z narzędzia buildout. Dla buildouta istnie-je recepta pb.recipes.pydev, która uaktualnia plik konfiguracyjny projektu (.pydevproject) na podstawie zmiennych buildouta. Recep-ta potrafi tylko uaktualnić istniejącą konfi-gurację, nie potrafi utworzyć jej od zera, stąd przed jej wykorzystaniem projekt Eclipsowy musi już istnieć. Aby użyć recepty w swoim projekcie, należy dodać nową sekcję do pli-ku buildout.cfg. Przykładowy zmodyfikowa-ny plik przedstawia Listing 1.

Parametr eggs w sekcji pydev wskazu-je, które biblioteki należy podłączyć do Py-dev. Parametr pydevproject_path pozwala określić położenie pliku konfiguracyjnego. Dodatkowo można użyć parametru extra_paths, aby wyspecyfikować wiele dodatko-wych ścieżek.

Konfiguracja dodatkowych ścieżek jest szczególnie użyteczna w przypadku bardziej skomplikowanych projektów, w których kod jest rozrzucony w wielu miejscach. Dobrym przykładem jest tu instancja Zope/Plone, która posiada w osobnych miejscach eggi, bi-blioteki Zope oraz wiele produktów współ-egzystujących w przestrzeni nazw Products. Przydatna jest tu recepta przedstawiona w Listingu 2, która oprócz podłączania do Pydev bibliotek Zope, tworzy linki symbo-liczne do wszystkich produktów w jednym katalogu, który można podczepić do Pydev (należy zwrócić uwagę, że recepta wyma-ga, aby katalog prodlinks został utworzony wcześniej).

Po zamknięciu i ponownym otwarciu pro-jektu w Eclipse ścieżki do bibliotek powinny być ustawione, co można sprawdzić w sek-cji Pydev – PYTHONPATH okna właściwo-ści projektu.

Dopełnianie koduPydev potrafi dopełniać kod Pythona. Dopeł-nianie odbywa się automatycznie (m.in. po wpisaniu kropki za wyrażeniem, konfiguro-walne w opcjach Pydev) lub można je wywo-

łać ręcznie przez domyślną kombinację kla-wiszy CTRL+spacja. Dopełnianie jest kon-tekstowe, przykładowo przy dopełnianiu wy-rażenia self. Pydev podpowiada składowe kla-sy oraz jej nadklas.

Pydev posiada mechanizm automatycz-nych importów. Użycie w dopełnianiu sym-bolu z niezaimportowanego modułu spowo-duje automatyczne dodanie wyrażenia im-port do edytowanego pliku.

Pydev wyświetla dokumentację używa-nych symboli. W domyślnej konfiguracji (można zmienić w opcjach) po najechaniu kursorem na symbol w okienku popup jest wyświetlana dokumentacja symbolu.

Pydev pozwala na definiowanie szablonów, umożliwiających przyspieszenie wpisywa-nia powtarzających się kawałków kodu. Sza-blony wykorzystuje się, używając ich nazwy w dopełnianiu kodu. Często używanym sza-

Listing 1. Przykładowa kon�guracja Pydev w buildout.cfg

[buildout]

parts = app pydev

develop = .

[app]

recipe = zc.recipe.egg:scripts

eggs = my.project

[pydev]

recipe = pb.recipes.pydev

eggs = ${app:eggs}

pydevproject_path = ${buildout:directory}/.pydevproject

Listing 2. Dodatkowa kon�guracja Pydev dla instancji Zope w buildout-dev.cfg

[buildout]

extends = buildout.cfg

parts +=

make_pydev_init_files

pydev

[make_pydev_init_files]

recipe = iw.recipe.cmd:py

on_install=true

cmds =

>>> dirs = """${instance:products}""".split("\n")

>>> prodlinks = os.path.join("""${buildout:directory}""".strip() ,

'prodlinks')

>>> Products = os.path.join(prodlinks,'Products')

>>> import os

>>> if not os.path.isdir(prodlinks): os.mkdir(prodlinks)

>>> if not os.path.isdir(Products): os.mkdir(Products)

>>> open(os.path.join(Products , '__init__.py'),'w').write('#')

>>> for dir in dirs:

>>> if dir:

>>> for product in [os.path.join(dir,a) for a in os.listdir(dir)

if os.path.isdir(os.path.join(dir,a))]:

>>> linkname = os.path.join(Products, os.path.basename(produc

t))

>>> if not os.path.islink(linkname): os.symlink(product,linkn

ame)

[pydev]

recipe = pb.recipes.pydev

pydevproject_path = ${buildout:directory}/src/my.product/.pydevproject

eggs =

${instance:eggs}

extra_paths =

${zope2:location}/lib/python

Page 14: SDJ_05_2010_PL

05/201014

Klub technicznyEclipse IDE

www.sdjournal.org 15

blonem jest pd, który wstawia w miejscu kur-sora zatrzymanie PDB: import pdb;pdb.set_trace(). Własne szablony można dodawać w preferencjach Window –> Preferences –> Pydev –> Editor –> Templates.

KomentarzePydev pomaga przy tworzeniu komentarzy. Potrafi zakomentować linie kodu (domyśl-

ny skrót klawiszowy CTRL+/), tworzyć ko-mentarze blokowe (domyślna kombinacja CTRL+4).

Pydev obsługuje wstawianie komentarzy typu docstring. Ustawiając kursor na nazwie funkcji lub klasy w ich definicji, można z me-nu Quick fix, dostępnego pod skrótem kla-wiszowym CTRL+1, wybrać opcję Make do-cstring. Dla funkcji Pydev wstawi komentarz

zawierający już szablon opisu parametrów (li-nie @param name: dla każdego parametru). Edytując docstring i wywołując dopełnianie składni, Pydev podpowiada też inne elemen-ty docstringów, np. @author lub @rtype.

PrzeglądanieZ pomocą dodatku Pydev można w łatwy sposób przeglądać kod. Edytor Pydev'a umoż-liwia zwijanie kodu importów, funkcji lub klas. Na symbolach można wykonać akcję Go to definition (domyślny klawisz F3), która przenosi do definicji symbolu. Na atrybucie, funkcji lub klasie przeniesie do jej definicji, Pydev automatycznie otwiera plik źródłowy. Działa to także dla klas bibliotecznych.

Kolejną funkcją Pydev'a jest zaznacza-nie wystąpień danego tokenu w całym pli-ku. Jest to przydatne, jeśli chcemy się do-wiedzieć, gdzie używana jest dana zmienna, funkcja lub import. Aby podświetlić wszyst-kie wystąpienia tokenu, wystarczy naprowa-dzić kursor na token. Wystąpienia zostaną za-znaczone w kodzie żółtym podświetleniem, dodatkowo na prawej krawędzi okna edyto-ra (obok paska przewijania) zostaną wyświe-tlone znaczniki pozwalające łatwo przejść do wystąpień tokenu.

Pydev posiada wbudowaną przeglądarkę symboli pozwalającą szybko znaleźć klasy, metody, pola lub zmienne globalne na pod-stawie (części) ich nazwy. Przeglądarkę moż-na uruchomić z menu Pydev –> Globals brow-ser lub za pomocą kombinacji CTRL+Shift+T

RefactoringPydev obsługuje skromny zasób przekształ-ceń kodu. Możliwe przekształcenia:

• Rename

• Extract method

• Inline local variable

• Extract local variable

Dodatkowo Pydev wyposażony jest w opcje automatycznej generacji kodu:

• Override/Implement methods

• Generate propeties (tworzenie getterów/setterów z wykorzystaniem property)

• Generate Constructor using Fields

Przekształcenia dostępne są w menu Refac-toring.

Interaktywna konsolaInteraktywną konsolę można otworzyć za po-mocą kombinacji CTRL+ALT+Enter. Jeśli do-stajemy w konsoli błędy o niemożności połą-czenia z konsolą, należy przestawić w opcjach Window –> Preferences –> Pydev –> Interac-tive Console parametr Maximum connection Rysunek 2. Kon�guracja interpretera

Rysunek 1. Instalacja Pydev

Page 15: SDJ_05_2010_PL

05/201014

Klub technicznyEclipse IDE

www.sdjournal.org 15

attempts for initial communication na większą wartość.

Interaktywna konsola oferuje funkcje po-dobne jak edytor, m.in. dopełnianie kodu, wyświetlanie dokumentacji, automatyczne importy, szablony. Interaktywna konsola, gdy uruchomiona w kontekście edytora, ma po-prawnie skonfigurowane zewnętrzne biblio-teki projektu, więc ma dostęp do tych samych symboli co kod w edytorze. Konsola posiada możliwość zapisania treści sesji do pliku tek-stowego, który może być natychmiast wyko-rzystany jako doctest.

Linki do plików pojawiające się w konsoli są klikalne i przenoszą do pliku źródłowego. Można dzięki temu błyskawicznie zobaczyć, w którym miejscu kodu wystąpił wyjątek.

Testy jednostkowePydev w ograniczonym zakresie wspiera testy jednostkowe. Aby szybko utworzyć przypadek testowy, należy w kreatorze nowego modułu Pythona wybrać szablon Module: Unittest lub Module: Unittest with setUp and tearDown.

Uruchomienie testu jednostkowego jest możliwe przez opcję Run as –> Python unit-test. Komunikaty testowania są wypisywa-ne na konsolę. Traceback błędu jest klikalny, można przejść od razu do pliku źródłowego, w którym wystąpił błąd.

DebuggerPydev pozwala na używanie Eclipsowego debuggera do pracy ze skryptami Pythona. Można definiować punkty wstrzymania (bre-akpoint) przez dwukrotne kliknięcie na le-wej linijce edytora. Dodatkowo można zde-finiować warunek, który musi być spełniony, aby wykonanie zostało wstrzymane w danym punkcie. Warunki można dodać, wywołując menu kontekstowe na ikonie punktu wstrzy-mania i wybierając Breakpoint properties. Na-stępnie należy zaznaczyć opcję Enable Con-dition i wpisać warunek (np. a != 0) w po-lu tekstowym.

Aby uruchomić skrypt w trybie debug, na-leży wybrać opcję Debug As –> Python Run. Eclipse przełączy się na perspektywę Debug. Perspektywa ta oprócz paneli edytora, konso-li i Outline posiada dodatkowo panele:

• Debug - wyświetla wątki i ich stos wy-wołań, pozwala manipulować przebie-giem programu (przejście do następnej linii, przejście wgłąb, skok do wyjścia z aktualnej funkcji, wznowienie działa-nia skryptu).

• Variables - wyświetla zmienne i ich war-tości; dla zmiennych złożonych ich war-tość wyświetlana jest w postaci rozwijal-nego drzewa.

• Breakpoints - wyświetla znajdujące się w projekcie punkty wstrzymania.

• Expressions - wyświetla zdefiniowane śledzone wyrażenia i ich wartości.

Aby dodać wyrażenie do śledzonych wy-rażeń, wystarczy zaznaczyć je w edyto-rze i z menu kontekstowego wybrać opcję Watch. Wyrażenie będzie wyświetlane w pa-nelu Expressions.

Wartości zmiennych oprócz panelu Va-riables można podejrzeć także, najeżdżając kursorem na zmienną w edytorze. Wartości zmiennych można edytować w czasie działa-nia programu. W tym celu w panelu Varia-bles należy wywołać menu kontekstowe na

zmiennej, której wartość chcemy zmienić, wybrać Change value i wpisać nową wartość w polu tekstowym.

Klikając na elementach stosu wywołań w pa-nelu Debug, można poruszać się w górę i w dół stosu, dzięki czemu można zobaczyć, skąd zo-stała wywołana funkcja, w której zatrzymał się debugger. Edytor automatycznie przełączy się na odpowiednią pozycję w pliku ze źródłem.

Zdalne debuggowaniePydev umożliwia zdalne debuggowanie progra-mów. Dzięki temu można używać debuggera z Eclipse w skryptach uruchamianych nie z we-

Rysunek 3. Dopełnianie kodu

Rysunek 4. Pydev w akcji

Page 16: SDJ_05_2010_PL

05/201016

Klub techniczny

Opcjonalnym argumentem do settrace jest adres IP maszyny, na której uruchomiony jest serwer debuggera. Jeśli Eclipse i debug-gowany skrypt działają na tej samej maszy-nie, argument można pominąć.

Do poprawnego działania debuggera w zdalnym skrypcie, musi on mieć dostęp do skryptu pydevd. Skrypt ten należy wydo-być z katalogu wtyczek Eclipse. Przykładowa ścieżka do katalogu ze źródłami to

eclipse/plugins/org.python.pydev.debug_

1.5.4.2010011921/pysrc

Katalog ten musi znaleźć się w PYTHON-PATH skryptu, który chcemy debuggować. Przykładowa komenda do uruchomienia skryptu wygląda więc następująco:

PYTHONPATH=.:~/eclipse/plugins/

org.python.pydev.debug_

1.5.4.2010011921/pysrc python -m

my.project.test

Uruchomiony skrypt zostanie wstrzy-many, gdy dojdzie do instrukcji pydevd.settrace(). Skrypt połączy się z ser-werem debuggera, Eclipse pokaże przyłączo-ny proces, dalej z debuggera można korzy-stać w normalny sposób.

PodsumowanieW niniejszym artykule starałem się pokazać możliwości Eclipse jako platformy do pro-gramowania w Pythonie. Pydev jako edytor jest w stanie bardzo ułatwić pracę progra-misty przez dopełnianie składni i wykrywa-nie błędów, jako debugger pozwala na uży-cie zachwalanego debuggera z Eclipse do pracy ze skryptami Python, łatwo integruje się z bardzo popularnym systemem buildo-ut. Wszystko to sprawia, że Eclipse jest kom-pleksowym i wygodnym narzędziem pracy dla programistów Pythona. Prawdą jest, że możliwości narzędzi pythonowych odbiega-ją funkcjonalnością od narzędzi dla Javy, ale wynika to po części ze specyfiki samego ję-zyka. Mam nadzieję, że udało mi się zachę-cić do wypróbowania Pydev na własnej skó-rze tym, którzy jeszcze z niego nie korzysta-li, a starzy wyjadacze być może znaleźli opis nowych funkcji, z których do tej pory nie ko-rzystali.

TOMASZ MAĆKOWIAKPracuje jako Developer w �rmie STX Next. To-mek specjalizuje się w technologiach webowych wykorzystujących Pythona i JavaScript. W swo-jej dotychczasowej pracy zajmował się również tworzeniem aplikacji w GWT/GXT. Posiada dobrą znajomość Javy, w tym J2ME.Kontakt z autorem:[email protected]

wnątrz Eclipse lub w skryptach działających na innych maszynach. Pydev zawiera w sobie ser-wer debuggera, który należy ręcznie urucho-mić. Można zrobić to przez akcję Pydev: start the pydev server na perspektywie Debug. Serwer bę-dzie uruchomiony w tle i czekał na połączenia.

Punkty wstrzymania wstawia się w spo-sób podobny jak dla pdb. Należy w kodzie, w miejscu, gdzie chcemy, aby wykonanie zo-stało przerwane, wstawić

import pydevd;pydevd.settrace('172.16.97.1')

Rysunek 5. Interaktywna konsola

Rysunek 6. Pydev debugger

W Sieci

• [0] Strona pobierania środowiska Eclipse http://www.eclipse.org/downloads/• [1] Strona domowa projektu Pydev http://Pydev.org/• [2] Eclipsowa witryna pobierania Pydev http://Pydev.org/updates• [3] Yoxos OnDemand – tworzenie spersonalizowanych wersji Eclipse http://ondemand.yoxos.com/geteclipse/start

Page 17: SDJ_05_2010_PL

17

Opis DVD

Jeśli nie możesz odczytać zawartości płyty DVD, a nie jest ona uszkodzona mechanicznie, sprawdź ją na co najmniej dwóch napędach DVD. W razie problemów z płytą, prosimy pisać pod adres: [email protected]

Redakcja nie udziela pomocy

technicznej w instalowaniu i użytkowaniuprogramów

zamieszczonych na płytach DVD-ROM dostarczonych razem z pismem.

Programowanie w języku Java

Od Witaj świecie do aplikacji korporacyjnych. Szkieletu aplikacji Struts2

Ósmy odcinek wideo kursu ma charakter warsztatowy. Je-go celem jest rozwój aplikacji do zarządzania konferencjami wprowadzając mechanizmy Struts2.

W poprzednim odcinku został nakreślone podstawowe zasady posługiwania się akcjami, znacznikami na stronach JSP oraz plikiem konfiguracyjnym. W tym odcinku zdoby-te wcześniej umiejętności zostaną poszerzone i pogłębione w trakcie budowy funkcjonalności dodawania nowych konfe-rencji oraz wyświetlania ich listy. W oparciu o Struts2 zosta-nie zbudowany przykładowy przepływ akcji i widoków JSP, przedstawiona zostanie szczegółowo komunikacja między akcjami a widokami JSP.

Podstawową formą konfiguracji w Struts2 jest plik XML. O ile istnieje wielu zwolenników tej formy pracy z aplikacjami internetowymi, to utrzymanie złożonych aplikacji staje się kło-potliwe. Alternatywną formą konfiguracji mogą być adnotacje stosowane na poziomie kodu źródłowego, czyli w tym przypad-ku – akcji, umożliwiające zdefiniowanie informacji, które znaj-dują się plikach struts.xml.

Ponadto zostanie omówiona wtyczka o nazwie Convention, dzięki której programista otrzymuje narzędzie, umożliwiające zminimalizowanie niezbędnej konfiguracji poprzez wprowa-dzenie konwencji. Dzięki temu rozwiązaniu, nie musimy pre-cyzować wielu szczegółów, które wynikają z kontekstu tworzo-nego rozwiązania.

Python Eclipse IDE

Materiały do artykułów

Page 18: SDJ_05_2010_PL

05/201018

NarzędziaBuildout

www.sdjournal.org 19

System Buildout jest podstawowym sposobem na budowanie i dystrybu-cję takich projektów składających się

z setek czy dziesiątek bibliotek – Plone, Zo-pe, Grok. Ułatwia także instalację innych pro-jektów – np.: Django, Pylons, repoze.bfg. Zo-stał napisany w języku Python z myślą o bu-dowaniu aplikacji w tym samym języku, ale nie tylko.

Do zademonstrowania możliwości ofero-wanych przez Buildout posłużę się aplikacją SimpleSite – będącą przykładem strony zbu-dowanej przy użyciu framework-u Pylons. Jej szerszy opis można znaleźć w „Definitive Guide to Pylons” (http://pylonsbook.com).

InstalacjaDo instalacji Buildout wymagany jest jedy-nie interpreter języka Python oraz dostęp do Internetu. W przypadku instalowania pakie-tów binarnych potrzebne jest zainstalowanie Pythona w wersji deweloperskiej. Nie są wy-magane prawa administratora, gdyż wszyst-kie operacje wykonywane są w lokalnym folderze i nie wpływają na pozostałe zain-stalowane programy. Na początek potrzeb-ne są dwie rzeczy – skrypt rozruchowy (bo-

otstrap.py) oraz plik konfiguracyjny (buildo-ut.cfg). Zadaniem skryptu rozruchowego jest stworzenie podstawowego środowiska, dzięki któremu buildout będzie mógł wykonać swo-je zadania. W przypadku gdy na komputerze, na którym jest on uruchamiany, brak jest od-powiednich bibliotek, zostaną one ściągnięte z Internetu.

$ cd simple-site

$ touch buildout.cfg

$ wget http://tinyurl.com/bootstrap-py

$ python bootstrap.py

Po uruchomieniu skryptu następuje ścią-gnięcie i zainstalowanie pakietu zc.buildout, a więc rdzenia systemu. Tworzona jest także struktura folderów:

• bin – zawiera wygenerowane skrypty;• downloads – miejsce, do którego są ścią-

gane paczki;• eggs – miejsce instalacji pakietów i bi-

bliotek Pythona;• parts – inne biblioteki lub oprogramo-

wanie.

Inne często używane foldery, lecz nie będą-ce tworzone podczas pierwszego urucho-mienia, to:

• etc – pliki konfiguracyjne;• src – kod źródłowy;• var – miejsce na dane, np. baza danych,

pliki logów itp.

Generowany jest także skrypt . /bin/buildout – wykonujący cały proces budowy aplikacji.

Pliki konfiguracyjneKonfigurację projektu definiuje się w plikach w formacie ConfigParser (potocznie zwanym INI). Plik taki składa się z sekcji, a każda sek-cja zawiera pary klucz – wartość. Za działanie każdej z sekcji odpowiedzialna jest tak zwana recepta (ang. recipe). Uruchomienie systemu buildout jest zatem w rzeczywistości wykona-niem serii plug-in'ów budujących poszczegól-ne części składowe projektu. Taki mechanizm wtyczek pozwala w łatwy sposób rozszerzyć podstawową funkcjonalność systemu.

Zmienne między sekcjami można pobierać poprzez strukturę ${nazwa-sekcji:klucz}. Podstawowe parametry działania skryptu de-finiuje się w sekcji [buildout]. W parame-trze parts określona jest lista sekcji, które po-winny zostać zbudowane.

Przykładowy projekt składać ma się z pa-kietu SimpleSite. Pakiet ten dostępny jest w repozytorium pakietów Python (PyPI

Buildout

W trakcie rozwoju aplikacji ilość zależnych bibliotek stopniowo się zwiększa. Ręczne zarządzanie projektem staje się mało wygodne, a instalacja oprogramowania na nowej maszynie wydłuża się. Użycie narzędzia Buildout pozwoli zautomatyzować większość tych czynności i ułatwi pracę programistom i administratorom.

Dowiesz się:• Jak tworzyć pliki konfiguracyjne buildout;• Jak instalować aplikacje używając buildout;• Poznasz najciekawsze dodatki do buildout.

Powinieneś wiedzieć:• Podstawy obsługi linii komend Unix;• distutils – mechanizm dystrybucji pakietów.

Poziom trudności

Narzędzie automatyzujące budowę i zarządzanie aplikacjami w języku Python

Listing 1. Wynik wykonania skryptu rozruchowego

$ python bootstrap.py

Creating directory ‘/tmp/simple-site/bin’.

Creating directory ‘/tmp/simple-site/parts’.

Creating directory ‘/tmp/simple-site/eggs’.

Creating directory ‘/tmp/simple-site/develop-eggs’.

Generated script ‘/tmp/simple-site/bin/buildout’.

Page 19: SDJ_05_2010_PL

05/201018

NarzędziaBuildout

www.sdjournal.org 19

– ang. Python Package Index) pod adresem http://pypi.python.org/pypi/SimpleSite. Do uru-chomienia tej aplikacji potrzebny będzie ser-wer HTTP wspierający standard WSGI. Naj-prostszym rozwiązaniem jest użycie zaimple-mentowanego w Pythonie serwera Paste. By móc go łatwo uruchomić wprost z listy pole-ceń, potrzebny jest PasteScript instalowany przez receptę zc.recipe.egg:scripts.

Aby zbudować projekt, należy teraz uru-chomić wygenerowany podczas instalacji skrypt ./bin/buildout. Ściągnie on i zainsta-luje wszystkie zależne pakiety. Zależności te, zgodnie ze standardem przyjętym dla biblio-tek języka Python, a więc distutils, określa-ne są w pliku setup.py każdej z paczek. W oma-wianym przypadku głównymi składnikami, bez których projekt nie może działać, jest Py-lons (framework, na bazie którego zbudowana jest aplikacja) oraz Mako (system szablonów).

Teraz można przystąpić do pierwsze-go uruchomienia aplikacji. W tym celu ko-nieczne jest wygenerowanie konfiguracji oraz utworzenie bazy danych.

$ ./bin/paster make-config SimpleSite

simplesite.ini

$ ./bin/paster setup-app simplesite.ini

Niestety ostatnia komenda zakończy się niepowodzeniem, ponieważ nie została

zainstalowana żadna biblioteka do komu-nikacji z bazą SQL. Do czasu wdrożenia aplikacji na serwer produkcyjny wystarczy prosta implementacja – SQLite. Koniecz-ne jest zatem dodanie pakietu pysqlite do projektu. Jest to pakiet binarny, a więc do instalacji konieczny jest kompilator GCC, a także pliki nagłówkowe Pytho-na. Te elementy muszą być zainstalowane w systemie. Wymagana jest także bibliote-ka SQLite. Ponieważ w systemie mogą wy-stępować inne programy używające SQLi-te w innych wersjach, wolimy, aby biblio-teka była zainstalowana w lokalnym fol-derze. W tym celu użyta zostanie recep-ta zc.recipe.cmmi (skrót od configure-make-make-install).

Po ponownym przebudowaniu (./bin/buildout) możliwe jest już uruchomienie serwera:

$ ./bin/paster serve simplesite.ini

Listing 2. Zawartość pliku buildout.cfg

[buildout]

parts = paster

eggs = SimpleSite

[paster]

recipe = zc.recipe.egg:scripts

eggs =

PasteScript

${buildout:eggs}

Listing 3. Wynik wykonania skryptu budującego

Getting distribution for ‘zc.recipe.egg’.

Got zc.recipe.egg 1.2.2.

Installing paster.

Getting distribution for ‘PasteScript’.

Searching for Paste>=1.3

Reading http://pypi.python.org/simple/Paste/

Reading http://pythonpaste.org

Best match: Paste 1.7.2

Downloading http://pypi.python.org/packages/source/P/Paste/

Paste-1.7.2.tar.gz#md5=a6a58d08dc4bff

91d5d1c519d2277f8a

Processing Paste-1.7.2.tar.gz

Got PasteScript 1.7.3.

Getting distribution for ‘SimpleSite’.

Searching for PasteDeploy

Reading http://pypi.python.org/simple/PasteDeploy/

Reading http://pythonpaste.org/deploy/

Reading http://pythonpaste.org/deploy/paste-deploy.html

Best match: PasteDeploy 1.3.3

Downloading http://pylons.cachefly.net/download/0.9.7/

PasteDeploy-1.3.3.tar.gz

Processing PasteDeploy-1.3.3.tar.gz

Searching for Paste>=1.3

Reading http://pypi.python.org/simple/Paste/

Reading http://pythonpaste.org

Best match: Paste 1.7.2

Downloading http://pylons.cachefly.net/download/0.9.7/Paste-

1.7.2.tar.gz

Processing Paste-1.7.2.tar.gz

Got SimpleSite 0.3.0.

Getting distribution for ‘AuthKit>=0.4.3,<=0.4.99’.

Got AuthKit 0.4.5.

Getting distribution for ‘FormBuild>=2.0.1,<=2.0.99’.

Got FormBuild 2.0.1.

Getting distribution for ‘Mako>=0.2.2,<=0.2.99’.

Got Mako 0.2.5.

Getting distribution for ‘SQLAlchemy>=0.5,<=0.5.99’.

Got SQLAlchemy 0.5.8.

Getting distribution for ‘Pylons>=0.9.7,<=0.9.7.99’.

Got Pylons 0.9.7.

Getting distribution for ‘PasteDeploy’.

Got PasteDeploy 1.3.3.

Getting distribution for ‘Paste>=1.3’.

Got Paste 1.7.2.

Getting distribution for ‘WebOb>=0.9.3’.

Got WebOb 0.9.8.

Getting distribution for ‘decorator>=2.1.0’.

Got decorator 3.1.2.

Getting distribution for ‘elementtree>=1.2,<=1.3’.

Got elementtree 1.2.7-20070827-preview.

Getting distribution for ‘python-openid>=2.1.1’.

Got python-openid 2.2.4.

Getting distribution for ‘Beaker>=1.1’.

Got Beaker 1.5.1.

Getting distribution for ‘nose>=0.9.2’.

Got nose 0.11.1.

Getting distribution for ‘WebHelpers>=0.6.1,<0.6.99’.

Got WebHelpers 0.6.4.

Getting distribution for ‘Tempita>=0.2’.

Got Tempita 0.4.

Getting distribution for ‘WebTest>=1.1’.

Got WebTest 1.2.

Getting distribution for ‘WebError>=0.10.1’.

Got WebError 0.10.2.

Getting distribution for ‘simplejson>=2.0.8’.

Got simplejson 2.0.9.

Getting distribution for ‘FormEncode>=1.2.1’.

Got FormEncode 1.2.2.

Getting distribution for ‘Routes>=1.10.3’.

Got Routes 1.11.

Getting distribution for ‘Pygments’.

Got Pygments 1.2.2.

Generated script ‘/tmp/simple-site/bin/paster’.

Page 20: SDJ_05_2010_PL

05/201020

NarzędziaBuildout

www.sdjournal.org 21

i obejrzenie rezultatu pod adresem: http://localhost:5000.

Konfiguracja deweloperskaObecna konfiguracja pozwala na budowanie aplikacji na podstawie najnowszych pakietów pochodzących z repozytorium Pythona – Py-PI. Podczas rozwijania aplikacji programista powinien mieć możliwości pracowania na ko-dzie jeszcze nie opublikowanym. Najczęściej pochodzącym wprost z systemu kontroli wer-sji. W tej sytuacji bardzo użyteczne jest roz-szerzenie mr.developer, które ułatwia pobra-nie i dołączenie do projektu pakietu wprost

z systemu kontroli wersji. Wspierane proto-koły to: Subversion, Mercurial, Git i CVS.

Deweloperska konfiguracja może instalo-wać także inne przydatne narzędzia. Przykła-dem może być nosetests – skrypt automa-tycznie znajdujący i uruchamiający testy jed-nostkowe i funkcjonalne.

Konfiguracja dla osób rozwijających apli-kację powinna zostać umieszczona w od-dzielnym pliku, ponieważ zawiera elementy, które nie powinny znaleźć się w wersji osta-tecznej. Buildout ułatwia taki podział dzię-ki możliwości rozszerzania plików konfigu-racyjnych. W tym celu wykorzystany jest pa-

rametr extends sekcji buildout. W plikach, które rozszerzają w ten sposób bazowe pliki, możliwe jest użycie operatora +=, dzięki któ-remu odziedziczona wartość parametru zo-stanie rozszerzona, a nie nadpisana.

Wtyczki, które ingerują w znaczny spo-sób w proces budowania aplikacji, dołączane są poprzez parametr extensions. W omawia-nym przykładzie mr.developer zmienia dzia-łanie systemu buildout w ten sposób, że pacz-ki określone w parametrze auto-checkout zamiast być ściągnięte z repozytorium pakie-tów zostaną zbudowane ze źródeł. W sekcji sources określa się adresy, pod którymi znaj-duje się kod źródłowy.

Należy przebudować aplikację, urucha-miając skrypt buildout z opcją -c wskazują-cą na nowy plik:

$ ./bin/buildout -c buildout-devel.cfg

Po zakończeniu działania możliwe jest uru-chomienie testów dołączonych do SimpleSi-te. Służy do tego wygenerowany skrypt:

$ ./bin/nose SimpleSite

Dodatkowym ułatwieniem oferowanym przez mr.developer jest skrypt pozwalający na aktualizację wszystkich pakietów źródłowych w jednym momencie. Nie ma tu znaczenia ro-dzaj systemu kontroli wersji odpowiedzialny za przechowywanie zmian. Po prostu w celu aktualizacji wszystkich pakietów źródłowych wystarczy uruchomić skrypt:

./bin/develop update

Inne często wykorzystywane recepty w kon-figuracji dla programistów to:

• buildout.dumppickedversions – wy-świetla wersje zainstalowanych pakie-tów, przydatne przy tworzeniu konfigu-racji produkcyjnej;

• pb.recipes.pydev – generuje konfigu-rację dla środowiska Eclipse z rozszerze-niem PyDev;

• collective.recipe.omelette – połą-czenie wielu pakietów w jednym drze-wie folderów,

• hexagonit.recipe.download – pobiera i rozpakowuje pliki (np.: przykładową bazę danych);

• collective.recipe.seleniumrc – in-stalacja Selenium RC (testy funkcjonal-ne w przeglądarce internetowej);

• collective.recipe.funkload – instala-cja FunkLoad (testy obciążeniowe apli-kacji WWW);

• plone.recipe.command – uruchomienie dowolnego zestawu komend linii pole-ceń lub kodu Pythona.

Listing 4. Plik buildout.cfg po dodaniu SQLite

[buildout]

parts =

sqlite

pysqlite

paster

eggs =

SimpleSite

pysqlite

[paster]

recipe = zc.recipe.egg:scripts

eggs =

PasteScript

${buildout:eggs}

[sqlite]

# kompilacja bibliotek binarnych SQLite (lekka baza danych SQL)

recipe = zc.recipe.cmmi

url = http://www.sqlite.org/sqlite-amalgamation-3.6.22.tar.gz

[pysqlite]

# kompilacja biblioteki pozwalającej na używanie

# bazy SQLite przez programy napisane w Pythonie

recipe = zc.recipe.egg:custom

egg = pysqlite

include-dirs = ${sqlite:location}/include

library-dirs = ${sqlite:location}/lib

rpath = ${sqlite:location}/lib

PasteScriptDzięki tej bibliotece możliwe jest łatwe uruchomienie skryptów Pythona wprost z linii ko-mend. Pakiety podczas instalacji określają w pliku setup.py nazwy funkcji, wykonanie których uruchomi skrypt, program lub zainicjalizuje komponent – jest to tak zwany entrypoint. Paste-Script wyszukuje wszystkie dostępne skrypty i pozwala je uruchomić bez znajomości pełnej ścieżki do pliku, w którym znajduje się kod źródłowy.

WSGISkrót od Python Web Server Gateway Interface. Specyfikacja ta określa interfejs na styku apli-kacji internetowych napisanych w języku Python i serwerów mającymi za zadanie obsługi-wać przychodzące żądanie HTTP. Określa także sposób współdzielenia i wymiany danych pomiędzy elementami wchodzącymi w skład aplikacji webowej. Jako serwery dla aplikacji WSGI najczęściej stosuje się Paster oraz Apache wraz z mod _ wsgi. Standard WSGI wspiera zdecydowana większość aplikacji webowych napisanych w języku Python (m.in: django, Py-lons, repoze.bfg, CherryPy, zope).

Page 21: SDJ_05_2010_PL

05/201020

NarzędziaBuildout

www.sdjournal.org 21

Konfiguracja produkcyjnaProjekt przystosowany do instalacji na serwe-rze produkcyjnym także posiada inne skład-niki. I w tym przypadku różnicę konfiguruje się w osobnym pliku.

W celu zabezpieczenia przed niespodzie-wanymi błędami wynikającymi z różnic mię-dzy kolejnymi wersjami zależnych bibliotek często po przeprowadzeniu wszystkich te-stów na instancji testowej zapisuje się w pliku versions.cfg wersje wszystkich użytych bi-bliotek. Proces ten automatyzowany jest przez rozszerzenie buildout.dumppickedversions.

Wydanie wersji stabilnej – nadającej się do instalacji na serwerach produkcyjnych wiąże się z opublikowaniem nowych wersji wszyst-kich pakietów, w których były zmiany. Jeże-li są to biblioteki OpenSource, nowe wydania znajdą się w ogólnym repozytorium – PyPI. Pozostałą część należy umieścić na prywat-nym serwerze z ograniczonym dostępem. Nie jest konieczne instalowanie specjalistycz-

nego oprogramowania – wystarczy dowolny serwer HTTP z włączoną opcją wyświetla-nia zawartości folderów. Link do tak przygo-towanego zasobu należy podać w parametrze find-links.

Aplikacje webowe zgodne ze standardem WSGI można uruchamiać przy użyciu dowol-nego serwera przystosowanego do tego. W wer-sji podstawowej użyty był Paster. W wersji pro-dukcyjnej natomiast najczęściej spotykane jest użycie rozszerzenia mod_wsgi do serwera Apa-che. W tym celu aplikacja musi udostępnić plik rozpoczynający (ang. entry point). Plik ta-ki generowany jest przez receptę

Podczas budowania aplikacji wygenerowa-ny zostanie plik wsgi w folderze parts/wsgiapp. Pełną ścieżkę do tego pliku należy ustawić w parametrze WSGIScriptAlias konfigura-cji Apache.

Konfiguracja produkcyjna często zawiera także dodatkowe elementy. Najbardziej przy-datne z nich to:

• plone.recipe.apache – instalacja i kon-figuracja serwer Apache;

• plone.recipe.varnish – instalacja ser-wera cache dla aplikacji internetowych;

• zest.recipe.mysql – instalacja MySQL wraz z MySQL-python;

• zc.recipe.cmmi – instalacja dodatkowe-go oprogramowania (np.: PostgreSQL, memcached);

• collective.recipe.supervisor – kon-trola wielu procesów;

• z3c.recipe.usercrontab – ułatwia konfigurację operacji cyklicznych (cron-tab);

• iw.recipe.backup – kopie zapasowe wy-branych plików.

Wady i zaletyBuildout nie jest systemem pozbawionym wad. Najbardziej irytującym z nich jest po-wolne działanie w przypadku problemów z siecią. Pomaga wtedy opcja -t do skryptu buildout, pozwalająca na ustawienie limi-tu czasu odpowiedzi. W przypadku gdy nie-osiągalne jest repozytorium PyPI, nawet pro-ste przebudowanie aplikacji nie jest w ogóle możliwe. W takim przypadku można użyć opcji -N blokującej sprawdzanie, czy dostęp-na jest nowsza wersja pakietu lub opcji -o włączająca tryb offline.

Utrudnione jest także szukanie przyczyn błędów budowania. Buildout niestety poka-zuje mało przydatne informacje. Uruchomie-nie wraz z opcją -v lub -vv zwiększa ilość ko-munikatów pomocniczych, ale w większości przypadków są one mało użyteczne.

Według mnie podstawową zaletą jest to, że przy użyciu paru komend można zbudować lub zaktualizować całą aplikację. Jest to tym bardziej pomocne, gdy nad projektem pra-cuje kilka osób i co pewien czas każda z nich musi dostosować się do najnowszych zmian.

Buildout pozwala na szybkie zbudowanie aplikacji w identycznej konfiguracji na czy-stym serwerze. Jest to przydatne szczególnie w sytuacjach awaryjnych.

Sądzę, że nakład pracy potrzebny na stwo-rzenie konfiguracji Buildout zwraca się dość szybko, a dodatkowo oszczędza się sporo stre-su podczas uaktualnień, migracji i sytuacji nieoczekiwanych.

WOJCIECH LICHOTAAbsolwent Uniwersytetu im. A. Mickiewicza w Po-znaniu, kierunek Informatyka Stosowana. Obec-nie pracuje jako analityk i programista w �rmie STX Next (http://stxnext.pl). Zawodowo zajmu-je się aplikacjami internetowymi, począwszy od małych portali społecznościowych, po duże roz-wiązania dla sektora bankowego. Języka Python oraz systemu Linux używa od 8 lat.Kontakt z autorem: [email protected]

Listing 5. Plik buildout-devel.cfg

[buildout]

extensions =

mr.developer

extends =

buildout.cfg

parts +=

nose

auto-checkout =

SimpleSite

[sources]

SimpleSite = hg http://bitbucket.org/meatballhat/simplesite/

[nose]

recipe = pbp.recipe.noserunner

eggs =

${paster:eggs}

working-directory = ${buildout:directory}/src

defaults = -v --nocapture --with-doctest --doctest-tests

--with-pylons=${buildout:directory}/src/SimpleSite/test.ini

Listing 6. Plik buildout-production.cfg

[buildout]

extends =

buildout.cfg

versions.cfg

parts +=

wsgiapp

versions = versions

find-links =

http://login:[email protected]

[wsgiapp]

recipe = collective.recipe.modwsgi

eggs = ${buildout:eggs}

config-file = ${buildout:directory}/production.ini

Page 22: SDJ_05_2010_PL

05/201022

ProgramowanieDjango – szybkie tworzenie serwisów Web 2.0

www.sdjournal.org 23

Django pozwala Ci tworzyć apli-kacje webowe o wysokiej wydaj-ności szybko, a co najważniej-

sze, struktura Twojego projektu nie bę-dzie przypominała pozlepianych ze sobą brył ciągle powtarzającego się kodu. Dzięki temu, jego dalszy rozwój i utrzymanie nie będzie już udręką. Tak przynajmniej twier-dzą twórcy Django.

My spróbujemy stworzyć szybko społecz-nościowy serwis mikroblogowy, korzysta-jąc ze standardowych funkcjonalności do-starczanych przez framework. Idea mikro-blogów jest prosta, o czym świadczy olbrzy-mia na świecie popularność Twittera czy, wśród polskich użytkowników, naszego ro-dzimego Blipa.

Nasz projekt będzie oferował podstawo-wą funkcjonalność tego typu serwisów: umieszczamy swoje krótkie wiadomości (statusy), które widzą śledzący nas znajo-mi (followers), jak również na nasz 'pulpit' trafiają statusy obserwowanych przez nas przyjaciół.

Zobaczymy jak Django nam to ułatwi, dostarczając przy okazji sporo zadowolenia z tworzonego kodu.

Krok 1. Instalacja Django oraz utworzenie nowego projektuNajnowsze Django 1.2 wymaga zainstalo-wanego w systemie języka Python w wersji co najmniej 2.4. Z powodu braku wstecznej zgodności, uruchomienie z wersją 3.0 nie jest obecnie możliwe. Powinniśmy dysponować też jakimś silnikiem relacyjnej bazy danych (framework oficjalnie wspiera MySQL, Post-greSQL oraz Oracle), choć na czas fazy two-rzenia aplikacji wystarczy prosty SQLite.

Użyjemy najnowszej, deweloperskiej wersji Django (w chwili powstania artyku-łu oznaczona jako 1.2 beta). Jak zapewnia-ją twórcy, jest stabilna, oferuje więcej funk-cjonalności oraz mniej błędów niż wydanie „oficjalne”.

Sprawdzamy, gdzie w systemie mamy za-instalowane biblioteki dla Pythona:

$ python -c "from distutils.sysconfig

import get_python_

lib; print get_

python_lib()"

W katalogu, w którym chcemy mieć źró-dła najnowszego Django, wykonujemy po-lecenie:

$ svn co http://code.djangoproject.com/

svn/django/trunk/ django-trunk

Zostanie utworzony katalog django-trunk, w którym znajdzie się najnowszy kod z re-pozytorium SVN.

Jeśli w systemie nie posiadamy klienta Subversion, ze strony projektu możemy po-brać paczkę zip ze źródłami i rozpakować ją tak, aby źródła znalazły się w katalogu djan-go-trunk.

Stwórzmy dowiązanie do tego katalogu w katalogu z bibliotekami.

$ ln -s `pwd`/django-trunk/django SITE-

PACKAGES-DIR/django

Oczywiście zamiast SITE-PACKAGES-DIR wpisujemy katalog, gdzie znajdują się bi-blioteki Pythona.

Utwórzmy jeszcze dowiązanie do skryp-tu administracyjnego w katalogu, gdzie system składuje lokalne pliki wykonywal-ne, abyśmy mieli do niego dostęp z każde-go miejsca:

$ ln -s `pwd`/django-trunk/django/bin/

django-admin.py

/usr/local/bin

Django

Jeśli cenisz sobie prostotę, elegancką architekturę i zwięzły kod, a jednocześnie potrzebujesz zaawansowanych funkcjonalności i do tego gonią Cię terminy – framework Django jest dla Ciebie. Powstał na potrzeby redakcji on-line, gdzie szybkość reakcji na ciągłe zmiany jest kluczowa.

Dowiesz się:• jak zainstalować i zacząć pracę z najnowszą

wersją frameworka Django• jak uwierzytelniać i autoryzować użytkow-

ników aplikacji internetowej przy pomocy standardowych mechanizmów Django

Powinieneś wiedzieć:• powinieneś znać podstawy programowania

w języku Python• powinieneś znać (X)HTML / CSS i podsta-

wowe zagadnienia związane z tworzeniem stron internetowych

Poziom trudności

Szybkie tworzenie serwisów Web 2.0

DjangoNazwa frameworka pochodzi od pseudonimu genialnego gitarzysty jazzowego Jean'a „Djan-go” Reinhardt'a. Django był Belgiem cygańskiego pochodzenia. W wyniku wypadku doznał rozległych oparzeń, przez co miał sparaliżowane dwa palce. Opracował jednak własną, uni-kalną technikę tak, aby mógł grać swoje solówki gitarowe pozostałymi zdrowymi palcami. Jego muzykę możemy usłyszeć m.in. w ścieżce dźwiękowej do gry Bioshock. Prawidłowa polska wymowa nazwy frameworka brzmi po prostu „dżango”. Angielską może-my usłyszeć w pliku MP3 na stronie: http://docs.djangoproject.com/en/dev/faq/general/#what-does-django-mean-and-how-do-you-pronounce-it

Page 23: SDJ_05_2010_PL

05/201022

ProgramowanieDjango – szybkie tworzenie serwisów Web 2.0

www.sdjournal.org 23

Spróbujmy teraz utworzyć nowy projekt:

$ django-admin startproject ublog

Zostanie utworzony katalog ublog zawiera-jący następujące pliki:

• __init.py__ - pusty plik mówiący Py-thonowi że katalog jest package'em

• manage.py - skrypt do wykonywania różnych zadań administracyjnych

• settings.py - ustawienia / konfiguracja projektu

• urls.py - deklaracje URLi dla projektu

W ramach projektu, Django pozwala na tworzenie wielu aplikacji. Aplikacje takie są swego rodzaju modułami, które można włączać lub wyłączać w razie potrzeby lub wykorzystywać w innych projektach. Apli-kacja jest po prostu swego rodzaju opako-waniem niezależnej funkcjonalności.

Tworzymy więc aplikację do tworzenia i wyświetlania postów.

$ cd ublog

$ python manage.py startapp posts

W katalogu z projektem zostanie utworzony podkatalog posts, a w nim następujące pliki:

• __init.py__• models.py - klasy modeli• tests.py - testy• views.py - definicje widoków

Teraz do pliku settings.py należy dopi-sać aplikację posts do INSTALLED _ APS

(patrz listing 1). Modelami i widokami zaj-miemy się później.

Pozostałe używane aplikacje przydadzą się w naszym projekcie m.in. do wygenerowania panelu administracyjnego, autoryzacji i uwie-rzytelniania czy obsługi sesji. Wszystkie te funkcjonalności mamy w Django gotowe. Jeśli którejś z nich nie potrzebujemy, usuwamy jej wpis. Uruchamiamy wbudowany serwer:

$ python manage.py runserver

Krok 2. Baza danych i modeleZakładam, że MySQL jest już zainstalowany i skonfigurowany w naszym systemie. Aby Python mógł używać MySQL'a, należy zain-stalować moduł MySQLdb. Pozostaje utwo-rzyć bazę danych dla naszej aplikacji oraz użytkownika, dla którego Django będzie łą-czyć się z tą bazą.

$ mysql --user=root mysql --password

W konsoli mysql'a wydajemy następujące komendy SQL:

CREATE DATABASE ublog CHARACTER SET uft8;

CREATE USER 'ublog'@'localhost'

IDENTIFIED BY 'mypassword';

GRANT ALL PRIVILEGES ON *.* TO

'ublog'@'localhost'

WITH GRANT OPTION;

Listing 1. Fragmenty pliku settings.py

# Konfiguracja bazy danych

DATABASES = {

'default': {

'ENGINE': 'django.db.backends.mysql',

'NAME': 'ublog',

'USER': 'ublog',

'PASSWORD': 'mypassword',

'HOST': '' // standardowo localhost

'PORT': '' // standardowy port

}

}

# Bezwzględna ścieżka do katalogu przechowującego media:

MEDIA_ROOT = '/home/pmandes/Devel/ublog/static/'

# URL do plikół przechowywanych w MEDIA_ROOT.

MEDIA_URL = '/static/'

# Plik ze zworcami URLi dla projektu

ROOT_URLCONF = 'ublog.urls'

# Bezwzględna ścieżka do katalogu z szablonami

TEMPLATE_DIRS = (

"/home/pmandes/Devel/ublog/templates"

)

# Aplikacje użyte w projekcie

INSTALLED_APPS = (

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.sites',

'django.contrib.messages',

'ublog.posts'

)

Rysunek 1. Ekran powitalny Django

Page 24: SDJ_05_2010_PL

05/201024

ProgramowanieDjango – szybkie tworzenie serwisów Web 2.0

www.sdjournal.org 25

CREATE USER 'ublog'@'%' IDENTIFIED BY

'mypassword';

GRANT ALL PRIVILEGES ON *.* TO

'ublog'@'%' WITH

GRANT OPTION;

Należy pamiętać, aby w docelowym środo-wisku produkcyjnym użytkownik root był zabezpieczony hasłem oraz aby dostęp do bazy dostępny był tylko z konkretnej ma-szyny produkcyjnej.

W pliku settings.py definiujemy połącze-nie z utworzoną bazą danych wypełniając pola tablicy DATABASES jak na Listingu 1.

W przypadku używania do celów dewelo-perskich bazy SQLite wystarczy wpis:

DATABASES = {

'default': {

'ENGINE': 'django.db.backends.sq

lite3',

'NAME': 'ublog.db' // nazwa pliku

do którego sqlite

zapisuje dane

}

}

Mamy połączenie z bazą danych. Teraz na-leży zdefiniować modele, czyli klasy repre-zentujące zapisywane dane. Wbudowany w Django ORM zajmie się resztą.

W pliku posts/models.py zdefiniujemy dwie klasy dziedziczące po klasie Model:

• Profile – klasa reprezentująca dane użytkownika, czyli autora postów;

• Post – klasa reprezentująca pojedyn-czy post w naszym serwisie mikroblo-gowym.

Plik ten powinien wyglądać jak na Listin-gu 2.

Zauważmy, że w klasie Profile pole nick (czyli pseudonim użytkownika) ma ustawioną flagę uniqe. Znaczy to, że w sys-temie może istnieć tylko jeden użytkow-nik o danym pseudonimie. Póki co profil nie ma nic wspólnego z reprezentacją za-logowanego w systemie użytkownika, zaj-miemy się tym później. Pozostałe pola de-finiują takie właściwości profilu użytkow-nika jak: jego pełna nazwa, płeć, położenie, URL do strony domowej, ścieżka do pliku z awatarem oraz krótka notka informacyj-na o sobie.

Klasa Post posiada pole author zdefinio-wany jako klucz obcy. Jeden autor może umieścić wiele postów, a dany post ma tyl-ko jednego autora. Mamy więc tu związek je-den-do-wielu. ORM wygeneruje odpowied-nie relacje.

Warto zauważyć jeszcze zdefiniowane opcje wyboru dla płci użytkownika w krot-

Listing 2. Plik posts/models.py – klasy modeli

from django.db import models

GENDER_CHOICES = (

('M', 'Male'),

('F', 'Female'),

)

class Profile(models.Model):

nick = models.CharField(max_length=20, unique=True)

full_name = models.CharField(max_length=100)

gender = models.CharField(max_length=1, choices=GENDER_CHOICES)

location = models.CharField(max_length=255)

url = models.URLField(max_length=255)

avatar = models.CharField(max_length=255)

info = models.CharField(max_length=255)

def __unicode__(self):

return self.full_name

class Post(models.Model):

content = models.CharField(max_length=160)

author = models.ForeignKey(Profile)

pub_date = models.DateTimeField('date published')

def __unicode__(self):

return self.content

Listing 3. Plik posts/admin.py

from ublog.posts.models import Profile

from ublog.posts.models import Post

from django.contrib import admin

admin.site.register(Profile)

admin.site.register(Post)

Rysunek 2. Panel administracyjny po zalogowaniu

Page 25: SDJ_05_2010_PL

05/201024

ProgramowanieDjango – szybkie tworzenie serwisów Web 2.0

www.sdjournal.org 25

ce (ang. tuple) GENDER_CHOICES przekazanej jako parametr dla pola gender.

Dodatkowo każda klasa ma zdefiniowa-ną metodę __unicode__ zwracającą repre-zentację obiektu w postaci łańcucha zna-ków.

Sprawdźmy teraz, jakie tabele zostaną utworzone dla aplikacji posts:

$ python manage.py sqlall posts

Jeśli nie popełniliśmy nigdzie jakiegoś błędu, dostaniemy w wyniku ciąg komend SQL ge-nerujących odpowiednie tabele.

Widzimy więc, że zostanie utworzony klucz obcy oraz indeks na odpowiednich polach. Poleceniem syncdb zatwierdzamy wszelkie zmiany do bazy danych.

$ python manage.py syncdb

Oprócz naszych tabel zostaną też utworzo-ne tabele dla pozostałych dołączonych do projektu aplikacji. Przy okazji skrypt zapy-ta nas, czy utworzyć dla aplikacji auth su-perużytkownika oraz poprosi o zdefinio-wanie jego nazwy i hasła:

You just installed Django's auth system,

which means you don't have any superusers

defined.

Would you like to create one now? (yes/no):

yes

Username (Leave blank to use 'jasio'):

admin

E-mail address: [email protected]

Password:

Password (again):

Superuser created successfully.

W tym momencie mamy już gotową bazę danych dla naszego projektu oraz zdefinio-wane podstawowe modele.

Krok 3. Panel administracyjnyPrzyszła pora na to, aby wprowadzić jakieś przykładowe dane oraz mieć możliwość za-rządzania nimi z panelu administracyjne-go. Z pomocą przyjdzie nam aplikacja ad-min, która odciąży nas od żmudnego two-rzenia formularzy edycyjnych dla każdej tabeli z bazy danych. Automatyczne pane-le administracyjne to jedna z najprzyjem-niejszych funkcjonalności, jakie oferuje nam Django. Jedyne, co my musimy zrobić, to odkomentować odpowiednie linijki w pli-ku urls.py oraz określić, którymi modelami chcemy zarządzać.

W pliku ulrs.py powinna znajdować się następujący kod:

from django.contrib import admin

admin.autodiscover()

urlpatterns = patterns('ublog.posts.vi

ews',

(r'^admin/', include(admin.site.ur

ls)),

)

Teraz w katalogu z aplikacją posts two-rzymy plik admin.py którego cała zawar-tość znajduje się na listingu 3. Rejestruje-

my w nim klasy Profile i Post aby aplikacja admin mogła z nich korzystać.

Zalogujmy się do panelu administracyjne-go utworzonymi wcześniej danymi superu-żytkownika – powinniśmy zobaczyć nasze zarejestrowane klasy jak na rysunku 2.

Dodajmy jakiś przykładowy profil użyt-kownika oraz kilka postów. Widzimy, że ma-my wszystko, czego potrzeba: walidację pól

Listing 4. Plik urls.py – mapowanie wzorców URL na metody widoków

from django.conf.urls.defaults import *

from django.conf import settings

from django.contrib import admin

admin.autodiscover()

urlpatterns = patterns('ublog.posts.views',

(r'^$', 'index'),

(r'^dashboard/$', 'dashboard'),

(r'^profile/$', 'profile'),

(r'^post/$', 'add_post'),

(r'^post/(?P<post_id>\d+)/$', 'get_post'),

(r'^login/$', 'login'),

(r'^logout/$', 'logout'),

(r'^admin/', include(admin.site.urls)),

)

if settings.DEBUG:

urlpatterns += patterns("django.views",

url(r"^static/(?P<path>.*)", "static.serve", {

"document_root": '/home/pmandes/Devel/ublog/static/',

})

)

Listing 5. Plik ublog/views.py – de�nicje funkcji widoków

from django.http import HttpResponseRedirect, HttpResponse

from django.core.urlresolvers import reverse

from django.http import HttpResponse

def index(request):

return HttpResponse("To jest widok główny")

def dashboard(request)

return HttpResponse("To jest widok pulpitu użytkownika")

def get_post(request, post_id):

return HttpResponse("Widok dla pojedynczego statusu: %s" % post_id)

def add_post(request):

return HttpResponseRedirect(reverse('ublog.posts.views.dashboard'))

MVC czy MTV?W klasycznym podejściu do wzorca MVC (Model-View-Controller) za prezentację danych z modelu dostarczonych przez którąś z metod - akcji kontrolera (Controller) odpowiada wi-dok (View). Jednakże wg twórców Django naturalne jest rozdzielenie tego „co chcę wyświe-tlić” od tego „jak chcę to wyświetlić”. Dlatego też to w widokach (będącymi zwykłymi funk-cjami) pobieramy z modeli potrzebne dla danej prezentacji dane („co”) i renderujemy je przy pomocy szablonów („jak”). Stąd też akronim MTV (Model-Template-View).

Page 26: SDJ_05_2010_PL

05/201026

ProgramowanieDjango – szybkie tworzenie serwisów Web 2.0

www.sdjournal.org 27

formularza, komponenty takie jak wybór daty czy listy rozwijane oraz komunikaty o błędach. Oczywiście panel administracyj-ny można łatwo dostosować do własnych po-trzeb, łącznie ze zmianą całego wyglądu, ale wykracza to poza temat tego artykułu.

Krok 4. Widoki i szablonySama baza danych oraz panel administra-cyjny to jeszcze nie wszystko. Brakuje naj-ważniejszego, czyli interfejsu użytkownika - strony WWW, na której użytkownik bę-dzie widział statusy swoich znajomych, oraz

własne posty, oczywiście z formularzem do ich umieszczania. Zajmiemy się teraz war-stwą widoku.

Zacznijmy od zdefiniowania URLi do po-szczególnych widoków i zasobów (w sty-lu REST):

• GET / - strona główna, czyli wyświetla-nie najnowszych statusów umieszcza-nych przez wszystkich użytkowników;

• GET /dashboard - pulpit zalogowanego użytkownika, tu wyświetlają się posty tylko jego znajomych;

• GET /post/123 - wyświetlenie pojedyn-czego statusu o podanym identyfikato-rze

• POST /post - zasób, na który zostaną wysłane dane z formularza do umiesz-czania statusów.

Mapowanie URLi na definicje widoków do-konuje się przy pomocy wyrażeń regular-nych w zmiennej urlpatterns znajdującej się w pliku urls.py (Listing 4).

Django zaczyna dopasowywać otrzyma-ny URL z powyższymi wyrażeniami po ko-lei i jeśli to się uda, to wywołuje podaną funkcję zwrotną. Jako pierwszy argument do tej funkcji zostanie przekazany obiekt HttpRequest. Kolejnymi argumentami bę-dą przechwycone z wyrażenia regularne-go wartości (jak w powyższym przykładzie post_id). Na koniec przekazane zostaną ar-gumenty wraz z ich wartościami zdefinio-wane w opcjonalnym słowniku. Pełna skład-nia krotek występujących w urlpatterns wygląda następująco:

(regular expression, Python callback

function

[, optional

dictionary])

Funkcje zwrotne definiujemy w pliku ublog/views.py (Listing 5).

O funkcjach zwrotnych możemy my-śleć jak o akcjach w kontrolerach we wzor-cu MVC (model-view-controller). Jednakże Django stosuje nieco inne podejście do te-go wzorca niż takie frameworki jak np. Ru-by On Rails czy popularny dla środowiska Java – Spring. Nie definiujemy kontrole-rów, gdyż kontrolerem i zawiadowcą (ang. dispatcher) jest samo Django. Zamiast te-go definiujemy funkcje zwrotne będące lo-giką widoków w ramach aplikacji. Pobiera-ją one dane z modeli, renderują je przy po-mocy szablonów i zwracają w postaci obiek-tu HttpResponse. Podejście takie nazywa się MTV.

Oczywiście funkcje te nie muszą zwra-cać wyrenderowanego widoku. Jak widzimy w definicji add_post, zwracany jest obiekt

Listing 6. Widok główny

from django.template import Context, loader

from ublog.posts.models import Post

from django.http import HttpResponse

def index(request):

# modelu Post obieramy 20 ostatnich obiektów posortowanych po dacie

latest_post_list = Post.objects.all().order_by('-pub_date')[:20]

# ładujemy szablon z pliku

template = loader.get_template('posts/index.html')

# tworzymy kontekst dla szablonu zawierający dane do wyświetlenia

context = Context({

'latest_post_list': latest_post_list,

})

# renderujemy szablon i przekazujemy go do obiektu odpowiedzi HttpRespose

return HttpResponse(templete.render(context))

Listing 7. Plik templates/posts/index.html – szablon widoku postów

{% extends "base.html" %}

{% block content %}

{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}

Ustaw swój status:

<form action="/post/" method="post">

{% csrf_token %}

<textarea name="content" rows="4" cols="60"></textarea>

<input type="submit" value="Post" />

</form>

<br/><br/>

{% if latest_post_list %}

<div class="ublog_posts">

{% for post in latest_post_list %}

<div class="ublog_post">

<div class="ublog_post_content">{{ post.content }}</div>

<div class="ublog_post_date">{{ post.pub_date }}</div>

<div class="ublog_post_author">{{ post.author }}</div>

</div>

<hr/>

{% endfor %}

</div>

{% else %}

<p>No posts are available.</p>

{% endif %}

{% endblock%}

Page 27: SDJ_05_2010_PL

05/201026

ProgramowanieDjango – szybkie tworzenie serwisów Web 2.0

www.sdjournal.org 27

HttpResponseRedirect powodujący prze-kierowanie na inny URL. Zamianę nazwy funkcji na odpowiadający jej URL dokonu-je funkcja reverse.

Pobierzmy teraz dane dla widoków i wy-renderujmy je przy pomocy szablonów.Przy-kładowo definicja funkcji index mogłaby wyglądać jak na Listingu 6.

Kod ten pobiera 20 ostatnich postów, ła-duje szablon zdefiniowany w pliku posts/index.html , a następnie umieszcza pobrane dane w kontekście. Kontekst jest obiektem mapującym nazwy zmiennych występujące w szablonie na konkretne obiekty Pythona. Oczywiście wywołanie teraz strony zakoń-czy się komunikatem TemplateDoesNotE-xist. Musimy wskazać Django w konfigura-cji, gdzie ma szukać szablonów oraz utwo-rzyć w tej lokalizacji sam szablon.

W pliku settings.py definiujemy pełną ścieżkę do szablonów naszego projektu, np.

TEMPLATE_DIRS = (

"/home/jasio/projects/ublog/

templates"

)

W katalogu z naszym projektem tworzy-my katalog templates, a w nim posts. Tu umieścimy wszystkie szablony dla aplikacji posts. Plik templates/posts/index.html po-winien wyglądać ja na Listingu 7.

Widzimy, że w szablonie możemy uży-wać takich konstrukcji sterujących, jak if-else czy iterować po liście z obiekta-mi pętla for. Pusta lista daje wartość lo-giczną false.

W tym momencie, odświeżając stronę, powinniśmy zobaczyć nasze posty, które wcześniej dodaliśmy testowo do panelu ad-ministracyjnego.

Django oferuje nam jeszcze tzw. skróty dla najczęściej tworzonego w widokach ko-du. Wszystko po to, żeby zgodnie z zasadą DRY (don't repeat yourself) było jak najmniej powtarzających się fragmentów kodu. Tym-czasem niemalże we wszystkich widokach będziemy robić to samo.

Zmieńmy teraz funkcje index, tak aby ko-rzystała ze skrótu (Listing 8).

Skrót render_to_response pobiera ścież-kę do plik szablonu, kontekst z obiektami z danymi do wyrenderowania oraz dodatko-wo kontekst żądania, który przyda nam się nieco później.

Pozostaje jeszcze obsługa sytuacji, kiedy post o podanym id nie istnieje i w takim wypadku należałoby zgłosić stronę błędu Http 404, zamiast komunikatu o błędzie aplikacji.

Można to zrobić na kilka sposobów. Pierwszym z nich jest rzucenie wyjątku Http404.

Język szablonów DjangoNajczęściej używane tagi w szablonach:

dziedziczenie po szablonie nadrzędnym:{% extends „base.html” %}

wczytanie innego szablonu i wyrenderowanie go w miejscu:{% include „szablon.html” %}

definicja bloku:{% block nazwa_bloku %}{% endblock %}iterowanie po liście:{% for artykul in lista_artykulow %}

{{ artykul.tytul}}

{% endfor %}

iterowanie po liście z obsługą listy pustej:{% for artykul in lista_artykulow %} {{ artykul.tytul }}

{% empty %}

brak artykulow{% endfor %}

konstrukcja warunkowa if:{% if lista_arykulow %}

liczba artykulow: {{ lista_artykulow|lenght }}{% else %}

brak artykulow{% endif %}

porównanie, czy wartości są równe:{% ifequal artykul.tytul artykul_glowny.tytul %}

{% endifequal %}

porównanie, czy wartości są różne: {% ifnotequal uzytkownik.nazwa „józek” %}

{% endifnotequal %}

więcej o języku szablonów i wbudowanych tagach na stronie:http://docs.djangoproject.com/en/dev/ref/templates/builtins/

Listing 8. Poprawina funkcja index korzystająca ze skrótu

from django.shortcuts import render_to_response

def index(request):

latest_post_list = Post.objects.all().order_by('-pub_date')[:10]

return render_to_response('posts/index.html', {'latest_post_list': latest_

post_list}, context_instance=RequestContext(request))

Listing 9. Funkcja get_post z obsługą wyjątku 404

from django.http import Http404

def get_post(request, post_id):

try:

post = Post.objects.get(id=post_id)

except Post.DoesNotExist:

raise Http404

return render_to_response('posts/post.html', {'post': post})

Page 28: SDJ_05_2010_PL

05/201028

ProgramowanieDjango – szybkie tworzenie serwisów Web 2.0

www.sdjournal.org 29

Kod funkcji get_post wyglądałby jak na Listingu 9.

Drugim sposobem jest oczywiście skorzy-stanie ze skrótu get_object_or_404, który robi dokładnie to samo (Listing 10).

W przypadku kiedy post nie zostanie zna-leziony, Django wygeneruje standardowy widok django.views.defaults.page_not_found() . Możemy też zdefiniować własny szablon dla błędu 404. Wystarczy, że utwo-rzymy plik 404.html w katalogu templates. Django skorzysta z niego, ale tylko wtedy, gdy w ustawieniach tryb DEBUG przełączy-my na false.

Częstym problemem dla początkujących programistów Django jest serwowanie statycz-nych plików, takich jak np. grafiki czy szablo-ny css. Wynika to z tego, że w produkcyjnym środowisku pliki takie najczęściej serwowane są z osobnego miejsca, będące wewnątrz kata-logu DOCUMENT_ROOT serwera WWW. Natomiast projekt Django znajduje się poza nim, tak aby nie było dostępu do jego plików z sieci. W środowisku deweloperskim, kiedy używamy wbudowanego serwera, można po-radzić sobie z tym w następujący sposób:

W pliku settings.py definiujemy katalogi, w których znajdują się statyczne media (peł-

na ścieżka do plików na dysku oraz ścieżka z URL'a):

MEDIA_ROOT = '/home/jasio/project/ublog/

static'

MEDIA_URL = '/static/'

Teraz w pliku urls.py definiujemy odpowied-ni wzorzec dla statycznych plików. Wzorce te powinny działać tylko wtedy, kiedy będzie-my w trybie DEBUG. Definicja tego wzorca została umieszczona na Listingu 4.

Należy jednak pamiętać, że rozwiązanie tego typu w środowisku produkcyjnym by-łoby niezbyt bezpieczne i niewydajne.

W warstwie widoku naszej aplikacji bra-kuje jeszcze dwóch rzeczy: głównego sza-blonu dla strony WWW oraz formularza do umieszczania na mikroblogu swoich statusów.

W katalogu templates tworzymy plik ba-se.html. Powinien on zawierać definicję DO-CTYPE oraz wszystkie niezbędne nagłówki HTML, a także miejsce, w które będą ren-derowane bloki z szablonów dziedziczących po nim. np.:

<div id="content">

{% block content %}{% endblock %}

</div>

Ze względu na wielkość, pełen kod tego szablonu został umieszczony na naszej pły-cie CD.

W szablonie tym widzimy użycie stałej MEDIA_URL, która prowadzi do pliku ze sty-lami css, oraz znaczniki bloków, w które ren-derowane będą szablony stworzone dla po-szczególnych funkcji zwrotnych w views.py.

Utwórzmy katalog static w głównym kata-logu projektu – umieścimy w nim nasze de-finicje stylów css - plik ublog.css, obrazki oraz skrypty js.

Przeróbmy teraz szablon templates/posts/index.html tak, aby dziedziczył po głównym szablonie base.html oraz wypełnił odpowied-nie bloki wyrenderowanymi danymi. Przy oka-zji dodajmy do niego formularz (Listing 7).

Teraz uzupełnijmy plik views.py uzupełnio-ny o obsługę formularza w funkcji add_post. Pełny kod tego pliku widzimy na listingu 11.

W tym momencie mamy już działający zrąb aplikacji. Można wysyłać formularzem statusy, które dodają się do bazy danych i wy-świetlają na stronie.

Oczywiście pozostało jeszcze wiele do zrobienia, jak np. edycja profilu użytkowni-ka, upload i wyświetlanie awatarów czy wy-świetlanie obserwowanych przyjaciół, nie mówiąc już o stworzeniu nieco mniej asce-tycznego wyglądu. Mamy już jednak zrąb aplikacji. Na koniec zajmijmy się jeszcze jed-nym bardzo ważnym aspektem.

Listing 10. Funkcja get_post używająca skrótu

from django.shortcuts import render_to_response, get_object_or_404

def get_post(request, post_id):

post = get_object_or_404(Post, id=post_id)

return render_to_response('posts/post.html', {'post': post})

Listing 11. Plik post/views.py.

import datetime

from ublog.posts.models import Post, Profile

from django.template import RequestContext

from django.http import HttpResponseRedirect, HttpResponse

from django.shortcuts import render_to_response, get_object_or_404

from django.core.urlresolvers import reverse

def index(request):

latest_post_list = Post.objects.all().order_by('-pub_date')[:20]

return render_to_response('posts/index.html', {'latest_post_list': latest_post_

list}, context_instance=RequestContext(request))

def dashboard(request):

latest_post_list = Post.objects.all().order_by('-pub_date')[:20]

return render_to_response('posts/index.html', {'latest_post_list': latest_post_

list}, context_instance=RequestContext(request))

def get_post(request, poll_id):

post = get_object_or_404(Post, id=post_id)

return render_to_response('posts/post.html', {'post': post}, context_instance=

RequestContext(request))

def add_post(request):

content = request.POST['content']

author = Profile.objects.get(id=1)

post = Post(author=author, content=content, pub_date=datetime.datetime.now())

post.save()

return HttpResponseRedirect(reverse('ublog.posts.views.index'))

Page 29: SDJ_05_2010_PL

05/201028

ProgramowanieDjango – szybkie tworzenie serwisów Web 2.0

www.sdjournal.org 29

Krok 5. Uwierzytelnianie i autoryzacjaDjango dostarcza standardowo mechanizmy do uwierzytelnienia i autoryzacji użytkow-nika. Należy rozróżnić dwa te dwa etapy:

• Uwierzytelnianie (ang. authentication) - sprawdzenie, czy użytkownik jest fak-tycznie tym, za kogo się podaje. Zazwy-czaj polega to na sprawdzeniu, czy po-dana przez niego nazwa użytkownika i hasło zgadza się z tym, co mamy w ba-zie danych użytkowników.

• Autoryzacja (ang. authorization) – sprawdzenie, czy użytkownik jest uprawniony do wykonania określo-nych operacji w systemie. Zwykle pole-ga to na sprawdzeniu, czy operacja jest dla niego zdefiniowana jako dozwolona w tabeli uprawnień.

Procesem tym (zwanym w skrócie A&A) zajmuje się aplikacja auth. System A&A w Django składa się z następujących części:

• Users – użytkownicy zarejestrowani w naszym serwisie;

• Permissions – uprawnienia, czyli flagi binarne (tak/nie) określające, czy dany użytkownik może wykonać pewną ope-rację;

• Groups – użytkownicy mający przy-znane w ramach danej grupy te same uprawnienia;

• Messages – komunikaty, prosty sposób na kolejkowanie i wyświetlanie komu-nikatów systemowych użytkownikom.

Na początku projektu dołączyliśmy od-powiednie składniki (Authentication-Middleware oraz django.contrib.auth), a odpowiednie tabele zostały potworzo-ne w bazie danych. Dopóki nie mamy me-chanizmu pozwalającego na rejestrowa-nie się użytkowników w systemie, stwórz-my jednego testowego w panelu admi-nistracyjnym. Przyznajmy mu prawa do dodawania/usuwania posta oraz tworzenia i edycji profilu.

To, czy użytkownik został uwierzytelnio-ny, możemy sprawdzić w funkcjach wido-ków następującym kodem:

if request.user.is_authenticated():

# zrób coś dla uwierzytelnionego

użytkownika

else:

# zrób coś dla użytkownika

anonimowego

Jeśli użytkownik jest niezalogowany, obiektem request.user będzie instancja AnonymousUser.

Dodajmy teraz do posts/views.py funkcję login.

Pobiera ona z formularza logowania na-zwę użytkownika oraz hasło, a następ-

nie próbuje go uwierzytelnić metodą auth.authenticate. Jeśli podane dane się zgadzają oraz użytkownik jest oznaczony w systemie jako aktywny, następuje jego za-

Rysunek 3. Pulpit użytkownika z formularzem

Rysunek 4. Przyznawanie praw użytkownikowi w panelu administracjnym

Page 30: SDJ_05_2010_PL

05/201030

Programowanie

logowanie w systemie metodą auth.login, po czym następuje przekierowanie na stronę /posts/dashboard. W przypadku niepowodze-nia zostaniemy przekierowani z powrotem na stronę główną.

Aby użytkownik mógł się wylogować, po-trzebuje oczywiście też funkcji logout:

def logout(request):

auth.logout(request)

return HttpResponseRedirect

("/posts/index/")

Wylogowanego użytkownika przekierowu-jemy na stronę główną. Pozostaje jeszcze

stworzyć formularz do logowania oraz pod-łączyć funkcje login/logout w urls.py :

(r'^login/$', 'login'),

(r'^logout/$', 'logout'),

Dla formularza stworzymy szablon lo-gin.html (Listing 13).

Teraz dla przykładu zabezpieczmy for-mularz do umieszczania statusów tak, aby nie wyświetlał się niezalogowanemu użyt-kownikowi.

def dashboard(request):

if not request.user.is_

authenticated():

return HttpResponseRedirect('/

posts/index')

# ...

W szablonach również możemy korzy-stać z obiektu User, jeśli przekażemy RequestContext:

{% if user.is_authenticated %}

<p>Witaj, {{ user.username }}. Jessteś

zalogowany.</p>

{% else %}

<p>Witaj. Proszę się zalogować.</p>

{% endif %}

I to by było na tyle. Zostało jeszcze do za-implementowania jeszcze kilka funkcjonal-ności, jak chociażby rejestracja użytkowni-ka z wysłaniem wiadomości email z prośbą o potwierdzenie, czy edycja własnego profi-lu. Temat jest na tyle obszerny, że można by mu poświęcić cały osobny artykuł.

PodsumowanieDjango oferuje nam olbrzymie możliwości przy tworzeniu aplikacji webowych. Auto-matyczne generowanie kodu, paneli admi-nistracyjnych oraz wiele gotowych do uży-cia aplikacji powoduje, że projekt powstaje szybko, a standardowe problemy rozwiązuje sam framework. W końcu, nasz serwis posia-da zwięzły, łatwy do utrzymania kod, a dal-szy rozwój nie oznacza konieczności przepi-sywania wszystkiego od zera.

Cały kod źródłowy niniejszego projektu dostępny jest w repozytorium GIT pod ad-resem: http://github.com/pmandes/ublog.

W Sieci

• http://www.djangoproject.com – oficjalna strona projektu Django;• http://code.djangoproject.com/wiki – wiki projektu, prawdziwa kopalnia wiedzy;• http://www.django.pl – polska strona społeczności Django;• http://www.djangobook.com – online'owa wersja książki „The Django Book”;• http://djangoadvent.com – blog z artykułami o nadchodzącej wersji 1.2 frameworka.

Listing 12. Funkcja login

from django.contrib import auth

def login(request):

username = request.POST.get('username', '')

password = request.POST.get('password', '')

user = auth.authenticate(username=username, password=password)

if user is not None and user.is_active:

# Hasło prawidłowe oraz użytkonik jest oznaczony jako "active"

auth.login(request, user)

# Przekierowanie na pulpit użytkownika

return HttpResponseRedirect("/posts/dashboard/")

else:

# Przekierowanie na stronę główna

return HttpResponseRedirect("/posts/index/")

Listing 13. Formularz logowania.

{% block content %}

{% if form.errors %}

<p class="error">Nieprawidłowy login lub hasło.</p>

{% endif %}

<form action="" method="post">

{% csrf_token %}

<label for="username">Nazwa użytkownika:</label>

<input type="text" name="username" value="" id="username">

<label for="password">Hasło:</label>

<input type="password" name="password" value="" id="password">

<input type="submit" value="login" />

</form>

{% endblock %}

PAWEŁ MANDES Autor jest współpracownikiem GG Network S.A., gdzie m.in. współtworzy i rozwija Pocztę Ga-du-Gadu. Jako programista interesuje się wie-loma technologiami i językami (jak np. Python/Django, czy Ruby on Rails). Pasjonują go również algorytmy genetyczne oraz programowanie gier. Kontakt z autorem: [email protected]

Page 31: SDJ_05_2010_PL
Page 32: SDJ_05_2010_PL

05/201032

ProgramowaniePlone – zbuduj firmowy intranet

www.sdjournal.org 33

Podstawowe informacjePlone jest systemem CMS, który pozwala na zarządzanie treścią w formie elektronicznej (dokumentami, plikami czy obrazami) za po-mocą przeglądarki internetowej, umożliwia-jąc publikowanie wybranych fragmentów tre-ści w Internecie lub lokalnym intranecie. Plo-ne jest zbudowany w oparciu o serwer aplika-cji Zope napisany w języku Python. Projekt został zapoczątkowany pod koniec 1999 ro-ku, a jego pierwsze wydanie nastąpiło w roku 2001, szybko zyskując sobie zwolenników na całym świecie. Z racji tego, że Plone jest pro-jektem otwartym – jego fundamentem jest duża i aktywna społeczność, która zapewnia ciągły rozwój i stabilność projektu. Wśród użytkowników Plona znajdują się takie organi-zacje jak: NASA, Oxfam, Amnesty Internatio-nal, eBay czy Novell.

Pierwsze wydanie systemu CMS Plone miało miejsce w 2001 roku i doczekało się po-nad 15 000 pobrań, zanim w roku 2002 wy-dana została wersja 1.0. Wersja 2.0 wydana w 2004 roku wniosła wiele zmian, wprowa-dzając m.in. framework do budowania wła-snych typów treści w postaci produktu Ar-

chetypes. Od momentu wydania Plona w wer-sji 2.1 (działającej z pythonem 2.3) w 2005 roku, systematycznie postępował rozwój sys-temu. W 2006 roku została wydana wersja 2.5, która niosła ze sobą przejście na pythona 2.4. Wprowadziła również, dzięki produkto-wi Five, możliwość wykorzystania technologii z zope 3 – komponentowej architektury (Zo-pe Component Architecture). Kolejnym du-żym krokiem była wersja 3.0, której premie-ra nastąpiła w 2007 roku. Wydanie to miało na celu uczynienie pracy z Plone bardziej wy-dajnej oraz wniosło wiele istotnych funkcjonal-ności, takich jak wersjonowanie treści, mecha-nizm blokowania treści poddawanej edycji czy nowy mechanizm portletów, umożliwiający kontekstowe zarządzanie nimi. Kolejne wersje – 3.1, 3.2 oraz 3.3 – wydawane na przestrze-

ni lat 2008 – 2009 niosły ze sobą dalsze roz-szerzenia w funkcjonalności oraz przejście na dystrybucje w postaci pakietów (python eggs). W chwili wydawania niniejszego artykułu najnowszą wydaną stabilną wersją Plona jest wersja 3.3.4. Działa ona na pythonie 2.4.3+, w oparciu o zope 2.10.4+.

W najbliższej przyszłości zostanie wyda-na wersja 4.0 (obecnie istniejąca jako beta), działająca na zope 2.12 (w pełni dystrybu-owanym w postaci pakietów) i pythonie 2.6 – szybszym i cechującym się lepszym zarzą-dzaniem pamięcią. W Plonie 4 dane w posta-ci plików binarnych są domyślnie przechowy-wane w systemie plików. Jednak najważniej-sze zmiany tego wydania dotyczą wzrostu wy-dajności działania systemu. Na Rysunku 1 [2] przedstawione zostało porównanie wydajno-ści mierzonej w liczbie obsługiwanych żądań w czasie 1 sekundy, uwzględniając różne sytu-acje – wyświetlanie strony dla niezalogowane-go i zalogowanego użytkownika, widoku listin-gu newsów i widoku edycji obiektu. Porówna-niu zostały poddane wersje 3.3.3, 4.0b, oraz 4.0b z wykorzystaniem systemu Chameleon, który dokonuje kompilacji szablonów zpt do postaci byte-codu, przynosząc poprawę szyb-kości ich renderowania do kilkudziesięciu pro-

PloneWybierając narzędzie mające zapewnić bezpieczną i sprawną komunikację wewnątrz-firmową, warto zwrócić uwagę na Plone – otwarty system CMS oferujący szeroki zakres możliwości dysponujący wieloma dodatkowymi produktami, które pozwalają dopasować go do własnych potrzeb. W poniższym artykule przedstawione zostało zastosowanie Plona w roli firmowego intranetu.

Dowiesz się:• Jak stworzyć serwis intranetowy przy wyko-

rzystaniu systemu CMS Plone; • W jaki sposób dokonać instalacji i podsta-

wowej konfiguracji Plona; • Jak dostosować możliwości Plona do własnych

potrzeb za pomocą dodatkowych produktów.

Powinieneś wiedzieć: • Znać podstawowe zagadnienia dotyczące

języka Python.

Poziom trudności

Zbuduj firmowy intranet

ProduktProdukt – w nomenklaturze zope'owej określa się w ten sposób zewnętrzny moduł, zazwy-czaj w postaci pakietu pythona, pozwalający na rozszerzenie standardowej funkcjonalności w dowolny sposób. Spis wszystkich wydanych produktów Plona można znaleźć na stronie [1] – w chwili obecnej jest ich tam ponad 1200. Przykład instalacji dodatkowego produktu został opisany w czwartym rozdziale niniejszego artykułu.

ZopeSkelModuł ZopeSkel jest kolekcją szablonów dla narzędzia PasteScript pozwalających na bły-skawiczne tworzenie szkieletów projektów dla Zopa i Plona. Można za jego pomocą w pro-sty sposób przy użyciu tekstowego kreatora stworzyć inicjalną strukturę produktu Plona lub czystą konfigurację buildouta dla wybranej wersji Plona.

Page 33: SDJ_05_2010_PL

05/201032

ProgramowaniePlone – zbuduj firmowy intranet

www.sdjournal.org 33

cent. Chameleon będzie możliwy do wykorzy-stania w Plonie 4, jednak zostanie uwzględnio-ny w domyślnej konfiguracji w wersji 5.0, nad którą również postępują prace. Głównym ce-lem wersji 5.0 jest dalsza poprawa wydajno-ści oraz wprowadzenie rewolucyjnych zmian w interfejsie użytkownika dzięki zastosowa-niu Deco – rozwiązaniu pozwalającemu użyt-kownikowi na zarządzanie layoutem strony przy pomocy akcji 'drag and drop'.

Ważną cechą odróżniającą Plone od więk-szości CMS-ów jest wykorzystywanie obiekto-wej bazy danych – ZODB. Świetnie sprawdza się ona tam, gdzie mamy do czynienia z hie-rarchiczną strukturą i wieloma typami treści, a więc w zastosowaniach webowych. Jest ła-twa w utrzymaniu, stabilna i niezawodna. Do jej zalet należy również elastyczność w tworze-niu modeli danych.

Zastosowanie technologii ZEO [3] umożli-wia korzystanie z wielu procesów klienckich z pojedynczego serwera bazy ZODB (Zope Sto-rage Server). Z kolei komercyjne rozwiązanie ZRS [4] umożliwia dokonywanie replikacji ba-zy, dzięki czemu eliminowany jest pojedynczy punkt awarii. Może być to również osiągnięte za pomocą rozszerzenia 'RelStorage' [5], które pozwala na przechowywanie danych w postaci zserializowanych obiektów w bazie relacyjnej i wykorzystanie jej możliwości replikacji.

Interfejs użytkownikaOd samego początku swojego istnienia Plone cechuje się przejrzystym i intuicyjnym inter-fejsem użytkownika. Na Rysunku 2 pokaza-ny jest zrzut ekranu przedstawiający widok dla zalogowanego użytkownika. Struktura danych jest łatwa do odczytania – pasek gór-nych zakładek przedstawia obiekty stworzo-ne na najwyższym poziomie, a poniżej wy-świetlana jest ścieżka (breadcrumbs) umożli-wiająca śledzenie aktualnej lokalizacji. Sekcja treści podzielona jest na 3 kolumny. W lewej i prawej kolumnie wyświetlane są zarządza-ne kontekstowo portlety. Plone oferuje stan-dardowo kilka typów portletów, np. kontek-stowe menu nawigacji, listę ostatnio mody-fikowanych obiektów, listę najnowszych wy-darzeń czy aktualności. Ponadto istnieje wie-le rodzajów portletów, które można zainsta-lować w postaci dodatkowych produktów. W środkowej kolumnie wyświetlany jest wi-dok aktualnego obiektu. Zalogowany użyt-kownik, posiadający uprawnienia do modyfi-kacji danego obiektu, widzi zielone obramo-wanie zawierające linki do akcji, które może na nim wykonać, np. przejść do widoku edy-cji, usunąć dany obiekt czy zmienić jego stan w obiegu informacji. Oprócz tego, użytkow-nik z uprawnieniami do zarządzania porta-lem ma możliwość przejścia do widoku usta-wień witryny, gdzie może dokonywać zmian w globalnych ustawieniach portalu, zarzą-

Lisitng 1. buildout.cfg

[buildout]

parts =

zope2

productdistros

instance

# Rozszeezenie konfiguracji o plik versions.cfg zawierający numery wersji

# modułów wchodzących w skład danej wersji Plona

extends =

http://dist.plone.org/release/3.3.4/versions.cfg

versions = versions

# Adresy url pod którymi wyszukiwane są pakiety

find-links =

http://dist.plone.org/release/3.3.4

http://dist.plone.org/thirdparty

# Dodatkowe produkty

eggs =

Products.LinguaPlone

stxnext.pdb

# Moduły deweloperskie

develop =

[versions]

# Możliwość ustawienia specyficznego numeru wersji pakietu

Products.LinguaPlone = 3.0

plone.recipe.zope2instance = 3.6

[zope2]

# Recepta instalacji zope2

recipe = plone.recipe.zope2install

fake-zope-eggs = true

url = ${versions:zope2-url}

[productdistros]

# Recepta pozwalająca na zdefiniowanie dodatkowych produktów,

# które nie występują w postaci pakietów

recipe = plone.recipe.distros

urls =

nested-packages =

version-suffix-packages =

[instance]

# Recepta pozwalająca na dokonanie inicjalnej konfiguracji środowiska zope2

recipe = plone.recipe.zope2instance

zope2-location = ${zope2:location}

user = admin:pass

http-address = 8080

#debug-mode = on

#verbose-security = on

eggs =

Plone

${buildout:eggs}

# Rejestracja dodatkowych produktów nie należących do przestrzeni nazw 'Products'

zcml =

stxnext.pdb

products =

${buildout:directory}/products

${productdistros:location}

Page 34: SDJ_05_2010_PL

05/201034

ProgramowaniePlone – zbuduj firmowy intranet

www.sdjournal.org 35

dzać użytkownikami i grupami czy konfigu-rować dodatkowe produkty.

Plone został pierwszym systemem CMS, który spełnił wymagania wytycznych doty-czących dostępności treści internetowych (WCAG) w wersji 2.0 na poziomie AA. Jest również zgodny z Sekcją 508 – aktem praw-nym Kongresu Stanów Zjednoczonych regu-lującym i gwarantującym prawa osób niepeł-nosprawnych w dostępie do treści elektro-nicznych. Jako ciekawostkę można przytoczyć fakt, że arkusze stylów stworzone dla Plona 2.0 zostały luźno wykorzystane podczas two-rzenia layoutu Wikipedii.

Istnieje możliwość łatwej zmiany domyślnej skórki na jedną z wielu dostępnych w postaci dodatkowych produktów lub stworzenie wła-snej. Jest kilka podejść pozwalających na doko-nanie zmian w wyglądzie systemu. Pierwsze

z nich ogranicza się do zastąpienia lub zmia-ny standardowych szablonów stylów własny-mi. Innym stosowanym sposobem jest po-dział na warstwę zarządzania, w której zalogo-wani użytkownicy mogą swobodnie pracować w przejrzystej klasycznej skórce Plona oraz na warstwę prezentacji, gdzie opublikowana treść wyświetlana jest w widokach przygotowanych na podstawie szablonów danego projektu gra-ficznego. Najnowsze podejście polega na auto-matycznym osadzaniu elementów z generowa-nego przez Plone htmla w przygotowanym sta-tycznym projekcie html za pomocą reguł napi-sanych w plikach xml.

Instalacja i podstawowa konfiguracjaZope – serwer aplikacji, na którym działa Plo-ne, jest aplikacją wieloplatformową. Można

więc dokonać instalacji Plona niezależnie od systemu operacyjnego, którym dysponujemy. Na stronie projektu dostępne są przygotowa-ne zautomatyzowane instalatory dla każdego z systemów, które pozwalają na dokonanie pro-stej instalacji za pomocą jednego kliknięcia.

Innym sposobem przeprowadzenia insta-lacji jest wykorzystanie systemu zc.buildo-ut, który umożliwia dokonanie bardziej za-awansowanej konfiguracji. Przykładowy plik konfiguracyjny buildout.cfg został przedsta-wiony w Listingu 1, można go wygenerować dzięki modułowi 'ZopeSkel', który zawiera zestaw szablonów do generowania szkiele-tów projektów Zope/Plone za pomocą narzę-dzia PasteScript.

# instalacja pakietu ZopeSkel

$ easy_install-2.4 ZopeSkel

# wygenerowanie skryptu instalacyjnego

i pliku konfiguracji

dla Plone 3

$ paster create -t plone3_buildout

Edytując wygenerowany plik buildout.cfg, można wskazać dodatkowe produkty, które mają zostać zainstalowane, wpisując ich na-zwy w sekcji 'eggs' (oraz w sekcji 'zcml', jeżeli dany produkt nie należy do przestrzeni nazw 'Products'), zdefiniować port, na którym bę-dzie działać aplikacja oraz dokonać bardziej zaawansowanych ustawień związanych z eks-ploatacją aplikacji. Po dokonaniu konfigura-cji można uruchomić proces instalacji, wy-konując najpierw skrypt bootstrap.py (za po-mocą instancji interpretera python w wer-sji 2.4.3+):

$ python2.4 ./bootstrap.py

# uruchomienie procesu instalacji:

$ ./bin/buildout -v

Po uruchomieniu procesu instalacji wszyst-kie niezbędne pakiety zostaną pobrane z in-ternetowych repozytoriów. Po pomyślnym zakończeniu instalacji można utworzyć ini-cjalnego użytkownika, za pomocą którego bę-dzie można zalogować się i zarządzać aplika-cją z poziomu przeglądarki:

$ ./bin/instance adduser admin password

Następnie można uruchomić aplikację za po-mocą skryptu ./bin/instance służącego do kontrolowania aplikacji:

# uruchomienie aplikacji:

$ ./bin/instance start

# zatrzymanie aplikacji:

$ ./bin/instance start

# uruchomienie aplikacji w trybie

"foreground":

$ ./bin/instance fg

Rysunek 1. Porównanie wydajności mierzonej w liczbie obsługiwanych żądań na sekundę, uwzględniając wyświetlanie różnych widoków

Rysunek 2. Przykładowy widok dla zalogowanego użytkownika

Page 35: SDJ_05_2010_PL

05/201034

ProgramowaniePlone – zbuduj firmowy intranet

www.sdjournal.org 35

Po zainstalowaniu dodatkowego produktu zazwyczaj należy jeszcze dokonać wczytania jego ustawień inicjalnych. Może tego dokonać użytkownik posiadający prawa Zarządzające-go za pomocą panelu 'Dodaj/Usuń produkty' znajdującego się w ustawieniach witryny.

Plone jako intranetJak już wcześniej zostało wspomniane, głów-ną zaletą Plona jest szeroki zakres oferowanych możliwości. Poniżej zostały opisane jego cechy, dzięki czemu świetnie nadaje się do wykorzy-stywania go w roli firmowego intranetu.

Zarządzanie użytkownikami i grupamiJednym z podstawowych wymagań stawia-nych przed systemem intranetowym jest moż-liwość sprawnego zarządzania użytkownika-mi. Plone charakteryzuje się rozbudowanym systemem uprawnień, pozwalającym na spro-stanie nawet bardzo zaawansowanym wyma-ganiom, które mogą być wymagane podczas tworzenia intranetu. Po pierwsze, użytkowni-kom można przypisywać role globalne wiążące się z przypisaniem danych uprawnień. Na Ry-sunku 3 przedstawiony jest panel zarządzania użytkownikami. Standardowo istnieje 6 ról:

• 'Użytkownik' – rola przypisywana do-myślnie każdemu użytkownikowi, po-zwalająca na zalogowanie się w systemie i tworzenie treści we własnym katalo-gu domowym, ale bez możliwości publi-kowania treści. Umożliwia przeglądanie opublikowanych obiektów.

• 'Czytelnik' – rola pozwalająca na przeglą-danie treści, która nie została opubliko-wana

• 'Wspierający' – użytkownik posiadający tę rolę może tworzyć nową treść w porta-lu, ale nie może edytować obiektów utwo-rzonych przez innych użytkowników.

• 'Edytor' – daje możliwość edytowania ist-niejącej treści.

• 'Recenzent' – rola umożliwiająca publi-kowanie i wycofywanie z publikacji treści w portalu.

• 'Zarządzający' – rola pozwalająca na glo-balne administrowanie portalem – posia-da wszystkie uprawnienia do zarządzania treścią i użytkownikami oraz ustawienia-mi portalu.

Oprócz wyżej wymienionych ról, które mo-gą być przypisane danym użytkownikom, istnieje również rola 'Właściciela', która jest przypisywana automatycznie użytkowniko-wi w kontekście utworzonych przez niego obiektów i wiąże się z możliwością dokony-wania ich modyfikacji.

Oprócz przypisywania ról globalnych, Plo-ne daje również możliwość nadawania upraw-nień lokalnych – dla poszczególnych obiek-

tów lub obszarów serwisu. Na przykład moż-na przydzielić danemu użytkownikowi moż-liwość edytowania lub publikowania treści w katalogu zawierającym dokumenty związa-ne z obszarem jego działalności w firmie, nie dając jednocześnie tych uprawnień w innych katalogach, które nie są związane pełnioną przez niego funkcją. Role lokalne są domyśl-nie dziedziczone przez elementy znajdujące się w danym kontenerze.

Kolejnym mechanizmem ułatwiającym za-rządzanie użytkownikami i ich uprawnienia-mi jest możliwość tworzenia i zarządzania gru-pami, do których można przypisywać użyt-

kowników. Grupom mogą być również przy-pisywane role, zarówno globalne, jak i lokal-ne. Uprawnienia posiadane przez grupy są przypisywane użytkownikom do nich przy-należącym. Pozwala to na przykład na stwo-rzenie grupy 'Marketing', której zostaną nada-ne uprawnienia do zarządzania treścią w czę-ści serwisu związanej z dokumentami prze-znaczonymi do realizacji działań marketingo-wych firmy, a każdego nowego użytkownika, który powinien posiadać takie uprawnienia, wystarczy przypisać do tej grupy.

Rysunek 4 przedstawia ekran zarządzania rolami lokalnymi dla katalogu 'Aktualności'.

Rysunek 3. Panel zarządzania użytkownikami

Rysunek 4. Ekran zarządzania rolami

Rysunek 5. Schemat 'Intranetowego/Extranetowego obiegu informacji'

Page 36: SDJ_05_2010_PL

05/201036

ProgramowaniePlone – zbuduj firmowy intranet

www.sdjournal.org 37

Jak widać, pozwala on na nadanie uprawnień dla danego użytkownika lub grupy, ale także wyświetla uprawnienia przez nie posiadane z racji dziedziczenia z katalogów nadrzędnych lub posiadania przypisanej roli globalnej.

Obieg informacjiKolejnym z zadań stawianych przed syste-mem intranetowym jest możliwość ustalenia obiegu informacji, pozwalającego na utrzy-manie kontroli nad treścią. Plone pozwala na wybór jednego z ośmiu standardowych obie-gów, wśród których znajdują się 'Intranetowy/Extranetowy obieg infomracji' oraz 'Intrane-towy obieg informacji dla folderów'. Schemat pierwszego z nich został przedstawiony na Rysunku 5. Każdy ze stanów, w którym znaj-duje się dany obiekt, cechuje się odpowiedni-mi wymaganymi uprawnieniami do dokony-wania danych akcji. Na przykład jeżeli doku-ment znajduje się w stanie 'Prywatny' – tylko jego właściciel lub użytkownik o roli 'Zarzą-dzającego' lub 'Edytora' może dokonywać na nim modyfikacji. Obiekt taki nie jest widocz-ny dla wszystkich zalogowanych użytkowni-ków. Obieg ten rozróżnia stany 'Opubliko-wany wewnętrznie', kiedy dany dokument jest widoczny dla wszystkich zalogowanych użytkowników, ale nie widzą go użytkowni-cy niezalogowani, oraz 'Zewnętrznie widocz-ne', kiedy również niezalogowani użytkow-nicy mają uprawnienia do wyświetlania tre-ści dokumentu. Dla każdego z typów treści użytkownik o globalnej roli 'Zarządzającego' może ustawić dany rodzaj obiegu informacji w panelu 'Ustawienia typów treści'. Oprócz standardowych obiegów oferowanych przez Plona, można również wykorzystać któryś z istniejących jako produkty zewnętrzne. Można także stworzyć własny obieg, definiu-jąc odpowiednie stany, przejścia, wymagane uprawnienia oraz akcje wykonywane podczas zmiany stanu, np. wysyłanie powiadomień mailowych do osób, których może dotyczyć dana zmiana stanu.

Podstawowe typy treściPlone domyślnie oferuje kilka podstawowych typów treści takich jak 'Folder', 'Strona', 'Ak-tualność' czy 'Wydarzenie'. Charakteryzują się one zestawem pól do wpisywania odpo-wiednich informacji oraz funkcją pełnioną w serwisie. Na przykład 'Strona' posiada pole do wpisania tytułu, krótkiego opisu wyświe-tlanego w listingach oraz pola zawierającego edytor wysiwyg do wstawienia treści. Ponad-to można zdefiniować słowa kluczowe bra-ne pod uwagę przy zwracaniu wyników wy-szukiwania, datę publikacji oraz datę ważno-ści – pozwalające ustawić okres, w którym obiekt będzie wyświetlany, autorów doku-mentu oraz dokonać ustawień decydujących o tym, czy strona ma być wyświetlana w na-

Rysunek 6 Przykładowy ekran de�nicji kryteriów zbioru

Rysunek 7. Przykładowa lista historii zmian

Page 37: SDJ_05_2010_PL

05/201036

ProgramowaniePlone – zbuduj firmowy intranet

www.sdjournal.org 37

wigacji, czy będzie możliwe dodawanie ko-mentarzy do strony itp. Kolejnymi domyślny-mi typami Plona są 'Plik' i 'Obraz', które po-zwalają na przechowywanie uploadowanych plików binarnych. Jednym z ciekawszych ty-pów jest 'Zbiór', który pozwala na grupowa-nie obiektów spełniających podane kryteria. Można na przykład stworzyć za jego pomocą zbiór obiektów typu 'Strona', które znajdują się w stanie 'Wysłany do akceptacji', przykła-dowy ekran definicji kryteriów zbioru został przedstawiony na Rysunku 6. Obiekty speł-niające podane kryteria będą wyświetlone w listingu zbioru, który może wyglądać jak listing standardowego folderu. Jeżeli standar-dowo istniejące typy treści okażą się niewy-starczające – istnieje możliwość wykorzysta-nia innych typów charakteryzujących się po-żądanymi właściwościami, które można zain-stalować w postaci dodatkowych produktów lub stworzyć własny produkt definiujący nie-zbędny typ treści.

Historia zmianPlone umożliwia zapisywanie historii zmian obiektów, co może okazać się bardzo przy-datne podczas nanoszenia zmian przez róż-nych użytkowników na danym dokumencie. Po dokonaniu edycji użytkownik może dodać w komentarzu czego dotyczyły jego zmiany. Komentarz ten będzie wyświetlony w liście historii zmian przeprowadzonych na danym obiekcie. Przykładowa lista zawierająca histo-rię zmian przedstawiona jest na Rysunku 7. Oprócz komentarza wyświetlany jest link po-zwalający porównać zmiany pomiędzy kolej-nymi wersjami, zmiany pomiędzy daną wer-sją a wersją aktualną, oraz przycisk pozwalają-cy przywrócić daną wersję dokumentu. Przy-kładowe porównanie zmian w dokumencie przedstawione zostało na Rysunku 8.

Wersje roboczeKolejnym mechanizmem przydatnym w ser-wisie intranetowym, gdzie nad danym doku-mentem może pracować wiele osób, jest me-chanizm wersji roboczych. Użytkownik może stworzyć wersję roboczą dokumentu, na któ-rym zamierza dokonywać zmian we własnym katalogu domowym. Aż do momentu umiesz-czenia swoich zmian w głównej wersji doku-mentu lub ich anulowania – możliwość edy-cji wersji głównej dokumentu staje się zabloko-wana dla pozostałych użytkowników i jest opa-trzona informacją o dokonywaniu zmian wraz z linkiem do wersji roboczej oraz do widoku porównania wersji roboczej do wersji głównej (wyświetlanym w podobny sposób jak na Ry-sunku 8).

Wewnętrzna wyszukiwarkaWyszukiwanie treści jest istotnym elemen-tem w systemie intranetowym. Plone dyspo-

nuje wewnętrzną wyszukiwarką, która pozwa-la na przeszukiwanie treści portalu na podsta-wie zawartośći pól podlegających indeksowa-niu, takich jak 'Tytuł', 'Opis' czy 'Treść'. Po-nadto do obiektu można przypisać słowa klu-czowe, które również będą brane pod uwa-gę przy zwracaniu wyników wyszukiwania. Ważną cechą Plona jest możliwość indekso-wania tekstu załączanych plików np. w posta-ci dokumentów MSWord lub PDF. Plone ofe-ruje również możliwość zaawansowanego wy-szukiwania, gdzie oprócz tekstu można podać inne kryteria, takie jak typ wyszukiwanego ele-

mentu, autor, stan w obiegu informacji czy da-tę utworzenia. Istnieje możliwość integracji z zewnętrznym systemem wyszukiwania jak na przykład Xapian czy Solr, w przypadku któ-rego można skorzystać z gotowego produktu – collective.solr.

Blokowanie aktualnie edytowanych dokumentówPlone domyślnie posiada mechanizm bloko-wania możliwości edycji treści, która aktualnie podlega edytowaniu przez innego użytkowni-ka. Zapobiega to sytuacjom, w których użyt-

Rysunek 8. Przykładowe porównanie zmian w dokumencie

Rysunek 9. Interfejs kon�guracji produktu PloneLDAP

Page 38: SDJ_05_2010_PL

05/201038

ProgramowaniePlone – zbuduj firmowy intranet

www.sdjournal.org 39

kownicy mogliby nieświadomie dokonywać zmian w tym samym czasie na jednym obiek-cie. Użytkownik ma możliwość świadomego odblokowania danego obiektu.

Dodatkowe produktyPlone, oprócz tego, że jest gotowym do wyko-rzystania produktem, stanowi również plat-formę, na podstawie której tworzone są roz-

wiązania pozwalające sprostać indywidual-nym potrzebom jego odbiorców. W ten sposób powstało wiele dodatkowych produktów roz-szerzających podstawowe możliwości Plona np. o integrację z serwerem LDAP, narzędzia do zarządzania projektem (ticket tracking) czy tworzenie treści w wielu wersjach językowych. Praktycznie wszystkie z nich są dostępne jako rozwiązania otwarte, więc można z nich korzy-stać czy nawet dostosowywać je do własnych, bardziej specyficznych potrzeb. Poniżej zosta-ły krótko przedstawione dodatkowe produkty, które mogą być przydatne w zastosowaniu Plo-na jako systemu intranetowego.

PloneLDAPProdukt pozwalający na dokonanie integra-cji z LDAP-em – zarówno standardowym ser-werem LDAP, jak i Microsoft Active Directory Server. Dzięki niemu dane użytkowników zde-finiowanych w wewnątrz-firmowym LDAP-ie mogą być wykorzystywane w Plonie – użyt-kowników można tworzyć, usuwać, wyszuki-wać i przypisywać im role. Można również wy-korzystywać grupy użytkowników zdefinio-wane w LDAP-ie – można tworzyć nowe gru-py i je usuwać oraz zarządzać przypisywaniem im użytkowników.

Zainstalowanie innego produktu – sim-plon.plone.ldap umożliwia dokonanie konfi-guracji połączenia z serwerem LDAP, mapo-wania właściwości zdefiniowanych w LDAP-ie na właściwości w Plonie oraz innych ustawień globalnych w interfejsie Plona. Na Rysunku 9 przedstawiony został interfejs konfiguracji produktu PloneLDAP.

Singing & DancingJest to produkt umożliwiający kompono-wanie, kolejkowanie i wysyłanie wiadomo-ści newsletter do subskrybentów. Może być wykorzystywany w intranecie do cykliczne-go informowania użytkowników o nowej tre-ści w serwisie drogą mailową. Funkcjonal-ność oferowana przez ten produkt umożli-wia dokonanie konfiguracji za pomocą inter-fejsu Plona – kiedy newsletter ma być wysyła-ny (periodycznie lub manualnie), jaka treść ma wchodzić w skład wysyłanej wiadomości (lista utworzona za pomocą zbioru z ustawiony-mi odpowiednimi kryteriami lub ręcznie wy-brana treść) oraz do kogo ma zostać wysłana wiadomość. Podczas dokonywania subskryp-cji, użytkownik musi dokonać jej potwierdze-nia za pomocą linku wysyłanego w wiadomo-ści potwierdzającej subskrypcję.

Products.CalendarXProdukt, który oferuje szeroko konfiguro-walny kalendarz ułatwiający użytkownikom współdzielenie informacji o wydarzeniach. W intranecie może pełnić rolę informacyjną oraz ułatwić kooperację użytkownikom serwi-

Rysunek 10. Współdzielony kalendarz – wydarzenia w kontekście miesiąca

Rysunek 11. Współdzielony kalendarz – wydarzenia w kontekście tygodnia

Page 39: SDJ_05_2010_PL

05/201038

ProgramowaniePlone – zbuduj firmowy intranet

www.sdjournal.org 39

su. Na Rysunku 10 przedstawiony został wi-dok kalendarza wyświetlającego wydarzenia odbywające się w danym miesiącu. Po najecha-niu kursorem na wydarzenie – na kalendarzu podświetlone zostają dni, w których wydarze-nie ma miejsce, oraz wyświetlane są dodatko-we informacje o tym wydarzeniu. CalendarX oferuje kilka widoków wyświetlających wyda-rzenia w różnych kontekstach, na przykład na Rysunku 11 przedstawiony został widok ka-lendarza wyświetlającego wydarzenia w kon-

tekście tygodnia z podziałem na godziny. Kon-figuracja kalendarza umożliwia dostosowanie go do własnych potrzeb.

Products.PloneboardW serwisie pełniącym rolę firmowego intra-netu przydatną funkcjonalnością może być forum internetowe. Produkt Ploneboard da-je możliwość prowadzenia w Plonie forum in-ternetowego z możliwością moderacji komen-tarzy. Jego funkcjonalność nie jest mocno roz-

budowana. Do zalet tego produktu należy ła-twość w używaniu.

Products.QuillsProdukt umożliwiający użytkownikom in-tranetu prowadzenie bloga związanego np. z ich aktualną działalnością wewnątrz firmy. Umożliwia on stworzenie przez użytkownika struktury bloga oraz daje możliwość tworze-nia wpisów. Ponadto daje możliwość wyświe-tlenia portletów zawierających np. archiwum wpisów na blogu z podziałem na miesiące czy chmurę tagów.

Products.PlonePopollJest to produkt umożliwiający zarządzanie sondami internetowymi. Dzięki niemu użyt-kownicy firmowego intranetu będą mieli moż-liwość oddania swojego głosu na pytanie posta-wione przez twórcę sondy. Podobnie jak Plone-board, produkt ten cechuje się prostotą w ob-słudze. Twórca sondy może zdefiniować pyta-nie, możliwe odpowiedzi oraz liczbę jednocze-snych możliwości wyboru. Użytkownikowi po oddaniu głosu mogą zostać przedstawione wy-niki sondy w postaci diagramu słupkowego. Przykładowy ekran z wynikami sondy został przedstawiony na Rysunku 12.

Collective.plonetruegalleryProdukt dający możliwość wyświetlania ob-razków uploadowanych przez użytkowników intranetu w postaci atrakcyjnej galerii. Po wy-braniu widoku galerii dla folderu zawierające-go uploadowane obrazki, wyświetlane są one w postaci galerii zgodnie z wybranymi usta-wieniami, pozwalającymi na wybranie roz-miaru wyświetlanych obrazków, opcji automa-tycznego przewijania po ustalonym czasie czy wyświetlania listy zawierającej miniaturki wy-świetlanych obrazków. Przykładowy widok ga-lerii został przedstawiony na Rysunku 13.

PodsumowaniePrzedstawione w artykule informacje doty-czące systemu CMS Plone prezentują jedynie podstawowe jego cechy, dzięki którym moż-na rozważać wykorzystanie go w charakterze serwisu intranetowego. O bardziej szczegó-łowych zagadnieniach można dowiedzieć się dzięki szerokiej dokumentacji i wielu tutoria-lom, które można znaleźć na stronie projektu [6]. Ponadto, dzięki temu, że wokół Plona wy-tworzyła się duża i otwarta społeczność – in-formacje można czerpać również z forów in-ternetowych oraz list dyskusyjnych.

Rysunek 12. Produkt PlonePopoll – widok z wynikami sondy

RADOSŁAW JANKIEWICZAutor bierze udział w projektach związanych z wdrożeniami systemu CMS Plone. Jest entuzja-stą ruchu open source i stara się aktywnie udzielać w społeczności skupionej wokół Plona.Kontakt z autorem: [email protected]

Rysunek 13. Produkt plonetruegallery – widok galerii obrazków

Referencje

• (1) http://plone.org/products;• (2) http://blog.hannosch.eu/2010/01/plone-4-how-much-faster-is-it.html;• (3) http://zodb.org/documentation/guide/zeo.html;• (4) http://www.zope.com/products/zope_replication_services.html;• (5) http://pypi.python.org/pypi/RelStorage/1.4.0b3;• (6) http://plone.org/.

Page 40: SDJ_05_2010_PL

05/201040

ProgramowaniePython 3

www.sdjournal.org 41

Dostępna od jakiegoś czasu wer-sja trzecia języka Python jest przedmiotem wielu kontrower-

sji. W społeczności programistów wytwo-rzyło się sporo mitów i obaw związanych z niekompatybilnością wstecz tej wer-sji z linią 2.x. W tym artykule pokażemy, że nie taki diabeł straszny, jak go malują, a stopniowe otwieranie się na wersję trze-cią niesie za sobą sporo wymiernych korzy-ści. Nawet jeżeli nie planujesz w najbliż-szej przeszłości przesiadać się na nową edy-cję Pythona, znajomość nowinek jest przy-datna, bo spora część z nich trafia również do edycji 2.x.

Trudna droga do zenHistoria rozwoju języka Python sięga póź-nych lat osiemdziesiątych, kiedy Guido van Rossum rozpoczął implementację na-stępcy języka ABC działającą pod rozpro-szonym systemem operacyjnym Amoeba. Od tego czasu funkcjonalność po funk-cjonalności, możliwości języka rosły. Już pierwsza opublikowana wersja 0.9 posiada-ła wiele rozpoznawalnych do dzisiaj struk-tur danych (m.in. listy i słowniki) oraz kon-

strukcji językowych (m.in. wyjątki i modu-ły). Musiało jednak minąć wiele czasu, za-nim Python zaczął wspierać programowa-nie funkcyjne, unikod, obsługę pakietów, zbieranie nieużytków (ang. garbage collec-tion) czy wiele innych dzisiaj przyjmowa-nych za oczywiste cech języka. Przykłado-wo, metody na typie string pojawiły się dopiero w wersji 2.0, iteratory i generatory w wersji 2.2. Do tej wersji zresztą istniało rozróżnienie na typy danych implemento-wane na poziomie języka C oraz typy dane komponowane w języku Python. Jedne ty-py miały bardzo ograniczoną możliwość in-terakcji z drugimi poprzez typowo obiekto-we konstrukcje takie jak dziedziczenie czy polimorfizm.

Wraz z ewolucją języka i biblioteki stan-dardowej, niektóre z rozwiązań stosowa-nych w starszych programach uznawano z czasem za chybione, czy to z powodu czy-telności, wydajności, ograniczonej elastycz-ności czy wręcz po prostu błędnego działa-nia. W tym samym czasie, z roku na rok, Python stawał się coraz bardziej popular-ną platformą, na której budowane były sys-temy wielkiej skali. Ta popularność stała się dla języka przekleństwem, ponieważ w ce-lu zachowania zgodności wstecz z istnieją-cymi programami, język musiał nadal ob-sługiwać nawet najbardziej przestarzałe rozwiązania. Taka sytuacja jest o tyle nie-ciekawa, iż dostępność wielu równorzęd-nych możliwości oprogramowania tego sa-

mego problemu jest w sprzeczności z pod-stawową filozofią Pythona. Co więcej, taka sytuacja może być groźna, ponieważ niejed-nokrotnie zupełnie nowy kod pisany przez początkujących programistów, bezwiednie powiela złe szablony programistyczne, któ-re od dawna są niewspierane.

T-1000, Blues Brothers 2000, Python 3000W społeczności programistów Pythona krą-żył żart, że zmiany konieczne do uporząd-kowania języka zostaną w końcu zaimple-mentowane w przyszłej wersji 3000. Po-dobny do Terminatora T-1000 z płynnego metalu, mityczny, idealny Python 3000 po-jawiał się na listach dyskusyjnych tak czę-sto, że przyjęła się nawet skrócona wersja nazwy: Py3K. Ostatecznie w roku 2006 Guido van Rossum zaskoczył społeczność stwierdzeniem, że planuje realną imple-mentację Pythona 3000 i wydanie go jako wersji 3.0. Z olbrzymiego worka z życzenia-mi dla Pythona 3000 wybrano spójny ze-staw zmian i zaczął się żmudny proces im-plementacji. Ostatecznie, 3 grudnia 2008 roku wersja 3.0 ujrzała światło dzienne.

Zmiany w Pythonie 3.0 są z jednej strony fundamentalne, z drugiej jednak to nadal w gruncie rzeczy ten sam doskonały język programowania. Różnice dotyczą głównie pozbycia się zaszłości historycznych, naj-częściej takich, gdzie domyślne zachowa-nie języka nie było intuicyjne i jego nowi użytkownicy produkowali w konsekwencji suboptymalny kod. Przyjrzyjmy się po ko-lei najważniejszym zmianom wprowadzo-nym w wersji trzeciej.

Bajty lub znakiPython 3.0 wprowadza rozróżnienie da-nych, na których operuje program, na dwa rodzaje: dane binarne i dane znakowe. Te

Python 3

Łukasz przeprowadzi Cię przez nowości w najświeższej edycji języka programowania Python i pokaże, w jaki sposób wpływają na sposób tworzenia programów.

Dowiesz się:• Dlaczego nowa wersja języka programowa-

nia Python odważnie zrywa z przeszłością, jakie nowe rozwiązania proponuje i co to oznacza dla zwykłego programisty.

Powinieneś wiedzieć:• Czym jest język Python i znać przynajmniej

podstawy programowania w wersji drugiej. Przyda się znajomość sposobu interakcji z interpreterem w celu zrozumienia przykła-dów kodu.

Poziom trudności

...czyli co nowego w trzeciej edycji języka

Page 41: SDJ_05_2010_PL

05/201040

ProgramowaniePython 3

www.sdjournal.org 41

pierwsze to tablice bajtów (typ bytes), któ-rych treść może być dowolna, dlatego in-terpreter nie pozwala na traktowanie da-nych w nich zawartych jako tekst. Takie da-ne mogą być odczytywane z plików, trans-mitowane przez sieć itd. Drugi rodzaj da-nych natomiast to abstrakcyjny tekst (typ str) przechowujący znaki: interpreter nie pozwala na transmisję takiego tekstu przez sieć lub do pliku bez uprzedniego okre-ślenia, z jakiego kodowania znaków nale-ży przy tej operacji skorzystać. Jawna kon-wersja między ciągami bajtów a łańcucha-mi tekstowymi jest doskonałym pomysłem, ponieważ pozwala wydzielić zadania zwią-zane z przetwarzaniem tekstu od zadań związanych z transmisją lub przechowa-niem danych.

Przy okazji przedstawiania różnicy w traktowaniu tekstu i danych między wer-sjami 2 i 3 często zwraca się dużą uwagę na fakt, że wszystkie łańcuchy znaków w wer-sji trzeciej to Unicode. W praktyce podczas korzystania z tekstu w Pythonie 3 bardzo rzadko trzeba myśleć o Unikodzie. Moż-na po prostu założyć, że Python 3 operu-je na abstrakcyjnym tekście, który może przechować praktycznie nieograniczony zestaw znaków [1]. Dopóki operujemy na takim tekście w ramach programu, nie ma dla nas znaczenia jego reprezentacja binar-na (Listing 1).

Dopiero w momencie, kiedy zapisujemy taki tekst na dysku lub przesyłamy przez sieć, należy nadać mu odpowiednie kodo-wanie znaków (Listing 2).

Podobnie, przy odczycie nowego tekstu z dysku Python musi wiedzieć, w jakim ko-dowaniu znaków ten tekst jest przechowy-wany (Listing 3).

W celu ułatwienia pracy programisty do-dano nowy tryb dostępu do plików: tryb tekstowy. W trybie tym konwersja z typu bytes na pythonowy str odbywa się w mo-mencie odczytu z dysku, odczytywanie więc odbywa się znak po znaku (a nie bajt po bajcie) (Listing 4).

Jak widać na powyższym przykładzie, odczytując plik w trybie tekstowym Py-thon 3 za naszymi plecami buforuje zawsze odpowiednią ilość bajtów, aby odczytać peł-ne znaki. Twórcy Pythona 3 poszli jednak jeszcze o krok dalej i zauważyli, że odczyt plików w programach pythonowych naj-częściej odbywa się właśnie w trybie teksto-wym, a najpopularniejszym kodowaniem znaków jest UTF-8. Stąd domyślne otwar-cie pliku w trybie "r" bez jawnego poda-nia typu danych ("rb" lub "rt") spowoduje otwarcie w trybie tekstowym. Jeżeli progra-mista pominie deklarację kodowania zna-ków w tak otwieranym pliku, Python 3 za-kłada UTF-8:

Listing 1. Obsługa tekstu międzynarodowego

>>> names = []

>>> first_name = "łukasz"

>>> names.append(first_name[0].upper() + first_name[1:])

>>> names

['Łukasz']

>>> names.append("René")

>>> names.append("Krüger")

>>> text = " ".join(names)

>>> text

'Łukasz René Krüger'

Listing 2. Kodowanie tekstu w celu transmisji danych

names_file = open("/tmp/names", "wb")

>>> names_file.write(text)

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

TypeError: must be bytes or buffer, not str

>>> encoded_text = text.encode("utf-8")

>>> encoded_text

b'\xc5\x81ukasz Ren\xc3\xa9 Kr\xc3\xbcger'

>>> names_file.write(encoded_text)

21

>>> names_file.close()

Listing 3. Wczytanie tekstu z postaci binarnej

>>> names_file = open("/tmp/names", "rb")

>>> byte_content = names_file.read()

>>> byte_content

b'\xc5\x81ukasz Ren\xc3\xa9 Kr\xc3\xbcger'

>>> byte_content.decode("utf-8")

'Łukasz René Krüger'

Listing 4. Odczyt pliku w trybie binarnym i tekstowym

>>> byte_file = open("/tmp/names", "rb")

>>> byte1 = byte_file.read(1)

>>> byte1

b'\xc5'

>>> byte1.decode("utf-8")

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

UnicodeDecodeError: 'utf8' codec can't decode byte 0xc5 in position 0: unexpected

end of data

>>> text_file = open("/tmp/names", "rt", encoding="utf-8")

>>> char1 = text_file.read(1)

>>> char1

'Ł'

>>> char1.encode("utf-8")

b'\xc5\x81'

Listing 5. Dostęp do indeksów sekwencji i atrybutów obiektów w formatowaniu

>>> 'Pierwszy element: {keys[0]:>10s}'.format(keys=sorted(sys.modules.keys()))

'Pierwszy element: __main__'

>>> f = open('/tmp/names', 'r')

>>> 'Kodowanie pliku {f.name}: {f.encoding}'.format(f=f)

'Kodowanie pliku /tmp/names: UTF-8'

Page 42: SDJ_05_2010_PL

05/201042

ProgramowaniePython 3

www.sdjournal.org 43

>>> text_file = open("/tmp/names", "r")

>>> text_file.encoding

'UTF-8'

>>> text_file.readline()

'Łukasz René Krüger'

Przetwarzanie tekstuRozumiejąc już różnicę między tekstem a danymi binarnymi w wersji trzeciej Pytho-na, skupmy się chwilowo na samym tekście i nowych cechach w jego przetwarzaniu. Du-żą rolę w trakcie operacji na tekście ma moż-liwość wstrzykiwania w łańcuchy teksto-we danych zewnętrznych, po odpowiednim sformatowaniu. Funkcjonalność tę zapo-czątkowała funkcja printf w języku C i do czasu Pythona 3 niewiele się w tej kwestii zmieniło. Klasyczna notacja jest nadal do-stępna, ale w przyszłości zostanie ostatecz-nie usunięta z języka. W jej miejsce wprowa-dzono nowy mechanizm, o znacznie zwięk-szonej elastyczności: format(). Podstawo-wą różnicą w stosunku do tradycyjnego jest fakt, że obiekt formatowany sam definiuje, w jaki sposób jego formatowanie przebiega:

>>> x = 0.3/3

>>> x.__str__()

'0.1'

>>> x.__repr__()

'0.09999999999999999'

>>> x.__format__("0.2f")

'0.10'

Jak widać, każda z implementacji wbu-dowanych funkcji w danym typie służy do czegoś innego. Ta ostatnia, ze względu na swoją konfigurowalność poprzez argu-ment, jest szczególnie przydatna w prze-twarzaniu tekstu. Do tego celu można używać zarówno wbudowanej funkcji format(), jak i nowej metody format() na łańcuchach znaków:

>>> format(x, "6.1f")

' 0.1'

>>> format(x, "<6.1f")

'0.1 '

>>> '{0:6.1f}, {0:<6.1f}'.format(x)

' 0.1, 0.1 '

Metoda format() z ostatniego przykła-du korzysta z nowej notacji {indeks _

argumentu:sposób _ formatowania}. W powyższym przykładzie dwukrotnie wykorzystano argument o indeksie ze-rowym podany do metody format(), za każdym razem formatując go inaczej. In-deksowanie jest jednak bardzo elastycz-ne i nie ogranicza się do prostego podawa-nia numeru argumentu funkcji format(). Możliwe jest również odwoływanie się do konkretnych indeksów w kolekcjach lub wręcz atrybutów obiektów (Listing 5).

Sposób formatowania jest opisywany w sposób bardzo zbliżony do tradycyjnego, możliwe wartości opisuje Tabela 1.

Kolejną dużą zmianą w przetwarza-niu tekstu jest przekształcenie wyrażenia print w funkcję. Pozwala to z jednej stro-ny na bardziej spójny kod, gdzie składnia print nie jest wyjątkiem. Z drugiej stro-ny pozwala na podmianę implementacji funkcji print() inną w trakcie wykona-nia programu, dzięki czemu można w bar-dziej elegancki sposób obsługiwać ko-munikaty diagnostyczne. Kilka przykła-dów możliwości nowej funkcji print (Li-sting 6).

Najlepsza rzecz od odkrycia krojonego chleba: iteracjaOd czasu wprowadzenia do języka genera-torów i iteracji, podejście do rozwiązywa-nia wielu problemów obliczeniowych dia-metralnie się zmieniło. Możliwość opisy-wania problemu, jak gdyby był wykonywa-ny sekwencyjnie, powoduje z jednej stro-ny dużą czytelność, a z drugiej brak na-rzutu pamięciowego i wydajnościowego na uprzednie przygotowanie struktur danych. To, co w wersji drugiej Pythona wymagało stosowania metod z przedrostkiem iter lub x, w Pythonie 3 jest zachowaniem domyśl-nym (Listing 7).

Zmiana ta jest o tyle istotna, że teraz za-chowanie naturalne - domyślne, jest zacho-waniem generującym algorytmy o znacznie lepszej charakterystyce pamięciowej i wy-dajnościowej. Jeżeli jednak nasz konkretny algorytm będzie wymagał klasycznej listy, możemy ją nadal uzyskać za pomocą jaw-nej konwersji:

>>> modules_keys = list(sys.modules.keys())

>>> modules_keys.sort()

>>> modules_keys[:3]

['__main__', '_abcoll', '_bisect']

Cukierki składnioweTzw. syntactic sugar to cechy języka bez istotnego wkładu funkcjonalnego, które jednak pozwalają w lepszy sposób wyrażać zamiary programisty. Najczęściej cukierki takie cechuje zwięzłość i czytelność. W kul-turze programistów Pythona cechy te są szczególnie pożądane, nic więc dziwnego, że wersja trzecia przyniosła kilka nowych. Najciekawsze wydaje się składanie słowni-ków i zbiorów (ang. odpowiednio dictiona-ry comprehensions, set comprehensions), przykładowo (Listing 8).

Ciekawa wydaje się też możliwość ano-towania funkcji i metod. Język sam w so-bie nie interpretuje tych anotacji, ale są dostępne w czasie uruchomienia do intro-spekcji. Taka konstrukcja może posłużyć do eleganckiego implementowania różnego ro-dzaju kontroli typów, interfejsów lub doku-mentowania kodu (Listing 9).

Tabela 1. Zapis łańcuchów formatujących w Python 3

Rodzaj Wartość Znaczenie

Wyrównanie < wyrównanie do lewej

^ wyrównanie do środka

> wyrównanie do prawej

Szerokość liczba ilość znaków, które zajmie całe pole

Dokładność liczba dla zmiennych typu int i float, ilość miejsc po przecinku

Kod d formatowanie dziesiętnej liczby całkowitej

f formatowanie dziesiętnej liczby zmiennoprzecinkowej

s formatowanie łańcucha znaków

e formatowanie liczby w notacji naukowej

x formatowanie liczby szesnastkowej

o formatowanie liczby ósemkowej

b formatowanie liczby binarnej

% formatowanie liczby w procentach (1.0 == 100%)

Tabela 2. Reorganizacja bibliotek HTTP

Python 2.x Python 3

httplib http.client

BaseHTTPServer http.server

CGIHTTPServer http.server

SimpleHTTPServer http.server

Cookie http.cookies

cookielib http.cookiejar

Page 43: SDJ_05_2010_PL

05/201042

ProgramowaniePython 3

www.sdjournal.org 43

Biblioteka standardowaPython 3.0 posiada w pełni zrewidowaną bibliotekę standardową. Usunięto z niej wiele nieutrzymywanych bibliotek, zlikwi-dowano też biblioteki o powtarzającej się funkcjonalności lub rzadko wykorzystywa-ne. Pełną listę można znaleźć pod adresem [2]. Struktura pozostałych bibliotek zosta-ła przeorganizowana tak, aby lepiej odpo-wiadała logicznie funkcjonalności zawar-tej w danej bibliotece. Ponadto, nazewnic-two bibliotek zostało dostosowane do ofi-cjalnych reguł stylu [3]. Przykład zmian dla pakietów odpowiedzialnych za komunika-cję HTTP przedstawia Tabela 2.

Zmiany występujące również w Pythonie 2.6Szereg oryginalnych zmian z Pythona 3.0 pojawiło się również w wersji 2.6. W po-dobny sposób wersja 2.7 będzie poszerzo-na o część nowych cech wersji 3.1 i nad-chodzącej 3.2. Takie przenoszenie wstecz nowych funkcjonalności ma na celu uła-twienie późniejszej aktualizacji progra-mów do wersji trzeciej języka. Użytkow-ników Pythona, którzy nie śledzą na bieżą-co zmian w kolejnych jego odsłonach, mo-że zaskoczyć, że następujące funkcjonalno-ści są dostępne zarówno w wersji 3.0, jak i w 2.6:

• wyrażenie with, uogólniające zarzą-dzanie kontekstowe zasobami (np. pli-kami) [5]

• pakiet multiprocessing, pozwalają-cy implementować aplikacje równole-głe wykorzystujące osobne procesy za-miast wątków [6]

• dekoratory klas [7] • nowa składnia obsługi wyjątków:

except Error as varError2 as var2 • usunięte zostało rzucanie łańcuchów

znaków jako wyjątków (zaszłość z wer-sji 0.9!)

Oprócz wymienionych wyżej i kilku mniej istotnych, Python 2.6 zawiera rów-nież opisane w poprzednich sekcjach no-we formatowanie łańcuchów, nową funk-cję print czy też nową bibliotekę io.

Zmiany usuwające przestarzałe cechy językaW wersji 3.0 na dobre usunięto wiele roz-wiązań, które od dawna były uznawane przez całe środowisko za przestarzałe i pro-blematyczne. Na pierwszy ogień poszły kla-sy starego typu, czyli rodzaj klas stworzony przed zunifikowaniem typów C i typów py-thonowych. Wraz z nimi dokonano porząd-ków w składni języka (m.in. zrezygnowano ze znaku nierówności <>, rozpakowywania

krotek w listach argumentów def fun(a,

(b, c)), usunięto notację `var` na rzecz repr(var)). Ponadto wynikiem dzielenia jest teraz zawsze liczba zmiennoprzecin-kowa, a niejawne importy zależne są nie-dozwolone.

Zmiany dotknęły też samą definicję klas, która teraz wspiera dynamiczne podawa-nie klas bazowych, zmienia określanie me-taklas oraz pozwala dołączać dowolne ar-gumenty kluczowe do definicji klas (Li-sting 10).

Listing 6. Nowa funkcja print w działaniu

>>> print('Hello', 'World')

Hello World

>>> print('Hello', 'World', end='!\n\n')

Hello World!

>>> print(1, 2, 3, sep='; ')

1; 2; 3

>>> print('Error message!', file=sys.stderr)

Error message!

Listing 7. Domyślne wykorzystanie iteratorów w Pythonie 3

>>> for i in range(10):

... print(i, end=' ')

... if i == 9: print()

...

0 1 2 3 4 5 6 7 8 9

>>> type(range(10))

<class 'range'>

>>> isinstance(range(10), list)

False

>>> import sys

>>> type(sys.modules)

<class 'dict'>

>>> type(sys.modules.keys())

<class 'dict_keys'>

>>> for i in sys.modules.keys():

... if i[0] == '_':

... print(i, end=' ')

...

_collections _sre __main__ _heapq _weakref _codecs _bisect _functools _locale _io

_weakrefset _abcoll

Listing 8. Składanie słowników i zbiorów

>>> some_dict = {'a':1, 'b':2, 'c':3, 'd':3, 'e':3}

>>> inverted = {v: k for k, v in some_dict.items()}

>>> inverted

{1: 'a', 2: 'b', 3: 'd'}

>>> some_set = {value/2 for value in some_dict.values()}

>>> some_set

{0.5, 1.5, 1.0}

>>> other_set = {1,3,5}

Listing 9. Anotacje

>>> def fun(a: "non-empty", b: "positive integer") -> "string":

... pass

...

>>> fun.__annotations__

{'a': 'non-empty', 'b': 'positive integer', 'return': 'string'}

Page 44: SDJ_05_2010_PL

05/201044

Programowanie

3000 niezgodnościZe względu na rozmach zmian w inter-preterze i bibliotece standardowej, progra-my tworzone z myślą o Pythonie 2.x nie są zgodne z nową wersją. Takie założenie twórców stanowi jednak nie lada wyzwa-nie dla programistów, ponieważ w prakty-ce nawet najprostszy program pisany z my-ślą o wersji 2.x nie uruchomi się pod wersją 3.x bez zmian w kodzie. W konsekwencji większość istniejących bibliotek zewnętrz-nych na dzień dzisiejszy nie wspiera jesz-cze nowej edycji Pythona. Na szczęście ilość

kompatybilnych pakietów rośnie, nadal jed-nak bardzo łatwo trafić na bibliotekę, bez której nie wyobrażamy sobie życia, a która nie działa pod "trójką". Wspomnieć można choćby o takich gigantach jak Python Ima-ging Library, virtualenv, easy_install [8] czy WSGI. Z czasem jednak przybywa uaktual-nionych pakietów, ostatnio przeportowane zostały biblioteki Jinja2 i lxml. Część biblio-tek zawiera wersje kompatybilne w repozy-toriach kodu źródłowego i można liczyć na to, że w niedalekiej przyszłości ujrzą światło dzienne na PyPI [9].

Co więc robić? Czekać dalej, aż masa kry-tyczna zostanie przekroczona? Właśnie ta-kie oczekiwanie powoduje, że przejście z wersji 2.x do 3.x zajmuje tak długo. Każ-dy większy projekt pisany w oparciu o Py-thona ma z pewnością wydzielone bibliote-ki, które da się przeportować, bo nie są bez-pośrednio zależne od nieistniejących kom-ponentów. Utrzymywanie dla nich wersji zgodnej z Pythonem 3 jest o tyle mądre, iż w momencie, kiedy giganci środowiska py-thonowego wydadzą wersje zgodne z Py-thonem 3, nasze projekty będą gotowe na migrację. PyQt już działa, portowane są fra-meworki Django i Pylons. W dalszej per-spektywie ujrzymy też ZOPE i Twisted.

Jak portować? Nie jest to na szczęście takie trudne, jakby się mogło wydawać. W dystrybucji Pythona 3 zawarte jest na-rzędzie 2to3 [10], które dużą część pracy wykonuje automatycznie za programistę. Kiedy narzędzie to trafia na odpowiednio dostosowany kod 2.x, potrafi wyproduko-wać w pełni działającą wersję dla Pythona 3. Nie zawsze jest tak łatwo, niemniej jed-nak warto spróbować. Portowanie za pomo-cą 2to3 działa szczególnie dobrze dla bi-bliotek operujących na tekście, w odróżnie-niu od takich, które operują na danych bi-narnych. Dodatkowo, możliwe jest dystry-buowanie pakietów źródłowych opartych o distribute, które w przypadku wykrycia wersji trzeciej Pythona automatycznie wy-konają konwersję przy użyciu 2to3. Ozna-cza to, że możemy przygotować pakiety w PyPI, które będą poprawne zarówno dla wersji drugiej jak i trzeciej. Jedyną wadą ta-kiego rozwiązania jest fakt, że kod źródło-wy pozwalający na bezbłędną pracę zarów-no w interpreterze w wersji 2 jak i w wersji najnowszej, nie jest tak ładny jak może być kod pisany z myślą o tylko jednej gałęzi.

PodsumowanieW ostatecznym rozrachunku wersja 3 Py-thona jest dużym krokiem naprzód. Wpro-wadzenie szeregu niezgodnych wstecz mo-dyfikacji do języka i biblioteki standardo-wej było dla twórców posunięciem ryzy-kownym, ponieważ biorąc pod uwagę popu-larność języka i mnogość bibliotek zewnę-trnzych, niebagatelnie utrudnia migrację na wersję najnowszą. Jednocześnie jednak zmiany te porządkują język i umożliwiają je-go dalszy rozwój.

Listing 10. Argumenty kluczowe w de�nicji klas

>>> class A: pass

>>> class B: pass

>>> bases = A, B

>>> class C(*bases): pass

>>> C.__bases__

(<class '__main__.A'>, <class '__main__.B'>)

>>> class Meta(type):

... def __new__(cls, name, bases, cls_dict, message):

... print('new', message)

... return type.__new__(cls, name, bases, cls_dict)

... def __init__(cls, name, bases, cls_dict, message):

... print('init', message)

... super().__init__(name, bases, cls_dict)

...

>>> class D(metaclass=Meta, message="This is D"): pass

new This is D

init This is D

>>> D()

<__main__.D object at 0x100667f10>

>>> class E(metaclass=Meta, message="This is E"): pass

new This is E

init This is E

Przypisy

• [1] Jest to pewne uproszczenie, Unicode posiada kilka możliwych reprezentacji binar-nych, spośród których Python może wewnętrznie wykorzystywać dwie: UCS-2 i UCS-4. Ta pierwsza posiada alfabet ograniczony do 65536 podstawowych symboli, ta druga po-zwala na reprezentację pełnego alfabetu Unicode, wymaga natomiast dwukrotnie więk-szej ilości bitów na przechowanie takiego samego ciągu znaków. Zmiana sposobu kodo-wania znaków Unicode jest możliwa na etapie kompilacji interpretera, domyślnie używa-ny jest bardziej popularny zestaw UCS-2.

• [2] http://www.python.org/dev/peps/pep-3108/• [3] http://www.python.org/dev/peps/pep-0008/• [4] Oczywiście bardziej złożone aplikacje zdecydowanie zyskają, korzystając z biblioteki

logging, która daje znacznie większą konfigurowalność.• [5] http://www.python.org/dev/peps/pep-0343/• [6] http://www.python.org/dev/peps/pep-0371/• [7] http://www.python.org/dev/peps/pep-3129/• [8] Istnieje ambitny projekt porządkujący kwestię paczkowania oprogramowania pytho-

nowego: distribute (http://python-distribute.org/ ). Ze względu na to, że odchodzi on od koncepcji paczek egg, nie implementuje on funkcjonalności easy_install. Zostanie ona zastąpiona przez PIP (http://pip.openplans.org/ ), który posiada wersję zgodną z Pytho-nem 3 w repozytorium.

• [9] Python Package Index: http://pypi.python.org/pypi• [10] http://docs.python.org/library/2to3.html

ŁUKASZ LANGAAutor interesuje się muzyką, reżyserią dźwię-ku i fotogra�ą, sprawdza się jako młody tata, a w wolnym czasie prowadzi własną �rmę progra-mistyczną. Kontakt z autorem: [email protected]

Page 45: SDJ_05_2010_PL
Page 46: SDJ_05_2010_PL

05/201046

ProgramowanieTesty wydajnościowe z Funkloadem

www.sdjournal.org 47

Istnieje wiele różnych sposobów testo-wania wydajności aplikacji interneto-wych. Większość programistów zetknę-

ła się z tym problemem i to zapewne nie je-den raz. Wśród rozwiązań niekomercyjncyh jednym z najbardziej popularnych (a zara-zem jednym z najstarszych – wersja 1.0 zo-stał wydana w 1998 roku) jest JMeter. Jest to projekt Jakarty, w 100% napisany w Javie opublikowany na licencji Apache 2.0. Posia-da dość liczną grupę wiernych użytkowni-ków, sam jednak nigdy nie mogłem się do niego przekonać.

Nie podoba mi się jego drzewiasta struk-tura, która wymaga przeklikania się przez gąszcz parametrów w celu skonfigurowa-nia testu. Bardzo ucieszył mnie zatem fakt, że środowisko Pythona ma swoje własne narzędzie w wielu miejscach przewyższa-jące wspomnianego JMetera. Mowa o pro-jekcie Funkload, który od prawie 5 lat zdo-bywa coraz większą popularność. Pierwsza jego wersja została wydana w roku 2005 przez francuską firmę Nuxeo. Został na-pisany w Pythonie, w 100% open source, wydany na licencji GNU General Public License v2.

Specyfika Funkloada Funkload to bardzo ciekawa alternatywa, która łączy testy wydajnościowe z jednost-kowymi. Poszczególne scenariusze zapisy-wane są w formacie pythonowych unitte-stów, a API obsługujące interakcję HTTP z testowanym serwerem oparte jest o webu-nity. Umożliwia to automatyczne pobiera-nie wszystkich zasobów strony (tj arkuszy styli, obrazków, skryptów JavaScript), ob-sługę cookies, HTTP, HTTPS, GET, POST, BasicAuthentication, ResponseRedirects etc. Większość parametrów można oczywi-ście konfigurować. Scenariusze mogą być uruchamiane masowo (tzw. bench) – mo-żemy wtedy testować obciążenie naszej aplikacji. Istnieje także możliwość odpale-nia testów jednostkowych (tzw. test), które w dość ciekawy sposób mogą weryfikować poprawność działania serwisu (np. jako te-sty akceptacyjne).

Sam funkload zawiera kilka ciekawych na-rzędzi pomocniczych:

• proxy oparte na TCPWatch, nagrywają-ce scenariusz z operacji realizowanych w przeglądarce (opisane szczegółowo poniżej);

• funkcje do tworzenia danych testo-wych;

• monitor obciążenia serwera; • generator raportów (ReST, HTML,

PDF, XML);• generator raportów różnicowych.

Ponieważ scenariusze to czysty Python, możemy skorzystać z całego bogactwa te-go języka (np. w celu wygenerowania loso-wych danych, skorzystania z wyrażeń regu-larnych etc).

Instalacja dla systemów UbuntuFunkloada można zainstalować na kilka spo-sobów. Jeżeli jesteś szczęśliwym użytkowni-kiem Ubuntu, możesz skorzystać z repozy-toriów systemu operacyjnego – wystarczy więc wykonać:

$ sudo apt-get install funkload

W ten sposób zostanie zainstalowany Funkload wraz ze wszystkimi wymaga-nymi bibliotekami. Jeżeli posiadasz przy-najmniej wersję Ubuntu Karmic Koala, zostanie zainstalowana wersja Funklo-ada 1.10.0-3 (najnowsza to 1.11.0). Nie-stety w przypadku starszych dystrybucji dostępna wersja ma ponad 3 lata. W ta-kim przypadku zawsze możesz skorzystać z ręcznej instalacji.

Instalacja za pomocą easy_installDrugim sposobem, polecanym wszystkim użytkownikom, jest ręczna instalacja za po-mocą skryptu easy_install. Sam Funkload wymaga kilku dodatkowych bibliotek i na-rzędzi, tak więc ZANIM zainstalujemy Fun-kloada, musimy upewnić się, że je mamy. W przypadku Ubuntu (Listing 1).

Na innych systemach trzeba zainstalować powyższe biblioteki Pythona ręcznie. Gnu-Plot wymagany jest do generowania rapor-tów i jest opcjonalny. Podobnie TCPWatch – wymagany jest tylko, jeśli chcemy skorzy-stać z nagrywarki proxy. Będziesz potrzebo-wać także narzędzia easy_install. Jeśli posia-dasz Ubuntu:

Testy wydajnościowe z FunkloademMasz problemy z wydajnością swojej aplikacji? Nie wiesz gdzie leży problem? A może po prostu chcesz wiedzieć czy twoja konfiguracja wytrzyma prawdziwe obciążenie? W dodatku szukasz rozwiązania OpenSource? Przeczytaj koniecznie ten artykuł...

Dowiesz się:• Jak zainstalować Funkloada;• Jak nagrać scenariusz testu wydajnościo-

wego;• Jak wygenerować raport i jak go zinterpre-

tować.

Powinieneś wiedzieć:• Co to są testy wydajnościowe.

Poziom trudności

Page 47: SDJ_05_2010_PL

05/201046

ProgramowanieTesty wydajnościowe z Funkloadem

www.sdjournal.org 47

$ sudo apt-get install python-setuptools

Możesz zainstalować go też ręcznie:

$ cd /tmp

$ wget http://peak.telecommunity.com/

dist/ez_setup.py

$ sudo python ez_setup.py

Nareszcie możemy zainstalować samego Funkloada:

$ sudo easy-install -U funkload

W efekcie powinny zostać zainstalowane dodatkowe skrypty:

• fl-run-test – uruchamia testy jednost-kowe;

• fl-run-bench – uruchamia testy wydaj-nościowe;

• fl-record – uruchamia nagrywarkę proxy;

• fl-build-report – generuje raport; • fl-monitor-ctl – uruchamia narzędzie

monitorujące maszynę;• fl-credential-ctl – narzędzie do prze-

chowywania haseł; • f l-install-demo – test poinstalacyj-

ny.

Pierwszy test wydajnościowyJednym ze sposobów tworzenia testów jest skorzystanie z nagrywarki proxy. W ten spo-sób bardzo szybko stworzymy szkielet na-szego scenariusza (który nagrywarka proxy zapisze w postaci gotowego unittestu Py-thona). Uruchomienie proxy jest dość pro-ste (Listing 2).

Parametr port definiuje numer por-tu, na którym proxy będzie nasłuchiwać, foo_bar to nazwa naszego testu (po skoń-czonym nagrywaniu zostaną utworzone dwa pliki test_FooBar.py oraz FooBar.conf). Ostatni parametr jest ważny, jeśli go zapo-mnimy fl-record nie stworzy żadnych pli-ków, tylko zrzuci wszystko na standardo-we wyjście.

Następnym krokiem jest konfiguracja na-szej przeglądarki, tak aby wszystkie żądania przekazywała do nagrywarki. W przypadku Firefoxa ustawienia te powinny wyglądać tak jak na Rysunku 1.

Po skonfigurowaniu proxy, wystarczy wpisać adres testowanej strony, aby rozpo-cząć nagrywanie. W moim przypadku bę-dzie to http://sdjournal.org/. Następnie kli-kam na podstrony serwisu, które zamie-rzam poddać testom. Po skończonym te-ście w terminalu naciskamy Ctrl+C, aby za-kończyć nagrywanie. Funkload powinien utworzyć dwa pliki, przyjrzyjmy się im do-kładniej.

FooBar.confPlik konfiguracyjny naszego testu. Ilość opcji konfiguracyjnych jest dość spora (zo-

stały one opisane szczegółowo na stronie projektu). Postaram się przedstawić tylko te najważniejsze:

Dodatki do Funkloada dostępne na http://pypi.python.org:

• collective.funkload – bardzo pomocny wrapper do Funkloada integrujący go z aplikacja-mi Plone/Zope;

• collective.funkbot – integracja raportów Funkloada z buildbotem (narzędzie do tzw. cią-głej integracji);

• collective.recipe.funkload – integracja Funkloada z buildoutem (system do tworzenia, konfigurowania i wdrażania aplikacji, popularny w środowisku Plone, Django, Pylons, Repoze etc).

Listing 1. Instalacja wymaganych bibliotek

$ sudo apt-get install python-dev python-xml python-setuptools \

python-webunit python-docutils gnuplot

$ sudo apt-get install tcpwatch-httpproxy --without-recommends

Listing 2. Nagrywarka proxy Funkloada

$ fl-record --port=8080 foo_bar

Hit Ctrl-C to stop recording.

HTTP proxy listening on :8080

Recording to directory /tmp/tmpehfOBw_funkload.

Rysunek 1. Kon�guracja Firefoxa

Page 48: SDJ_05_2010_PL

05/201048

ProgramowanieTesty wydajnościowe z Funkloadem

www.sdjournal.org 49

• url – adres url testowanego serwe-ra. Zmieniając ten parametr, możemy w prosty sposób testować różne serwe-ry za pomocą tego samego scenariusza;

• cycles – lista cykli testów wydajnościo-wych wraz z liczbą równoległych użyt-kowników;

• duration – długość trwania jednego cy-klu (w sekundach).

test_FooBar.pyScenariusz naszego testu w formacie pytho-nowego unittestu (Listing 3).

Jak widać na powyższym listingu, Funklo-ad utworzył za nas gotowy scenariusz wraz z wszystkimi żądaniami: zarówno GET, jak i POST. Możemy teraz dowolnie zmodyfi-kować test, dodać dodatkowe kroki etc. Po skończonej edycji przyszedł czas na odpale-nie testów.

UruchamianieNajpierw sprawdźmy, czy test działa po-prawnie:

$ fl-run-test -v test_FooBar.py

-------------------------------

Ran 1 test in 29.944s

OK

Status OK oznacza, że wszystko jest w po-rządku, możemy przystąpić do testów wy-dajnościowych:

Listing 3. Scenariusz testowy Funkloada

import unittest

from funkload.FunkLoadTestCase import FunkLoadTestCase

from webunit.utility import Upload

from funkload.utils import Data

class FooBar(FunkLoadTestCase):

"""XXX

This test use a configuration file FooBar.conf.

"""

def setUp(self):

"""Setting up test."""

self.logd("setUp")

self.server_url = self.conf_get('main', 'url')

def test_foo_bar(self):

server_url = self.server_url

self.get(server_url + "/",

description="Get /")

self.get(server_url + "/aktualnosci",

description="Get /aktualnosci")

self.get(server_url + "/o-nas",

description="Get /o-nas")

self.get(server_url + "/download",

description="Get /download")

self.get(server_url + "/magazine/article",

description="Get /magazine/article")

self.get(server_url + "/magazine",

description="Get /magazine")

self.get(server_url + "/newsletter", description="Get /newsletter")

self.get(server_url + "/forum",

description="Get /forum")

self.get(server_url + "/wspolpraca",

description="Get /wspolpraca")

self.get(server_url + "/subscription",

description="Get /subscription")

self.post(server_url + "/user/create", params=[

['user[login]', 'username'],

['user[email]', '[email protected]'],

['user[password]', 'password'],

['user[password_confirmation]', 'password'],

['user[newsletter]', '0'],

['user[newsletter]', '1'],

['user[website_url]', ''],

['commit', 'Zarejestruj si\xc4\x99']],

description="Post /user/create")

def tearDown(self):

"""Setting up test."""

self.logd("tearDown.\n")

if __name__ in ('main', '__main__'):

unittest.main()

Rysunek 2. Raport Funkloada w HTML

Page 49: SDJ_05_2010_PL

05/201048

ProgramowanieTesty wydajnościowe z Funkloadem

www.sdjournal.org 49

$fl-run-bench test_FooBar.py FooBar.test_

foo_bar

Pierwszy parametr definiuje plik z testa-mi, a następnie lista Klas.funkcji do prze-testowania.

W rezultacie powinniśmy otrzymać (Li-sting 4).

W tym krótkim podsumowaniu, które Funkload zwrócił na standardowe wyjście, możemy wydzielić trzy części:

• konfiguracja – czyli parametry, z który-mi zostały odpalone testy (url, ilość cy-kli, parametry czasu etc);

• benchmarking – czyli dokładny opis poszczególnych cykli z informacją, jak długo trwał test, z jakim statusem się zakończył etc.;

• wynik testu – ostateczne podsumowa-nie z podziałem na testy, które zakoń-czyły się pozytywnie (success), które za-kończyły się błędem serwera testowego (failures), lub błędem samego testu (er-rors).

Dodatkowo został utworzony plik xml, na bazie którego możemy stworzyć raport:

$fl-build-report --html foo_bar-bench.xml

--output-

directory=./output

Tak wygląda fragment wygenerowanego ra-portu w HTML (Rysunek 2).

PodsumowanieW artykule przedstawiłem najbardziej podstawowe wykorzystanie Funkloada. Ilość opcji i konfiguracji jest naprawdę spo-ra. Zachęcam do zapoznania się z doku-mentacją (niestety tylko w języku angiel-skim) na stronie projektu. Pomimo wie-lu zalet, które starałem się zaprezentować, Funkload ma też ograniczenia. Jednym z najbardziej irytujących jest brak możli-wości uruchomienia testu na wielu ma-szynach oraz używanie osobnego wątku dla każdej symulowanej sesji. Pojedynczą aplikację w ten sposób przetestujemy, wy-dajność bardziej złożonej farmy serwerów już raczej nie.

Nadal jednak jest to narzędzie bardzo uży-teczne, które umożliwia pisanie testów przy minimalnym nakładzie pracy, a ich kod jest przejrzysty i w 100% w Pythonie.

Listing 4. Podsumowanie testu wydajnościowego

=====================================

Benching FooBar.test_foo_bar

=====================================

Configuration

=============

* Current time: 2010-02-24T19:09:03.845624

* Configuration file: /virtualenv/funkload/FooBar.conf

* Log xml: /virtualenv/funkload/foo_bar-bench.xml

* Server: http://sdjournal.org

* Cycles: [1, 2, 3]

* Cycle duration: 30s

* Sleeptime between request: from 0.0s to 2.0s

* Sleeptime between test case: 1.0s

* Startup delay between thread: 0.2s

Benching

========

Cycle #0 with 1 virtual users

-----------------------------

* Current time: 2010-02-24T19:09:03.846084

* Starting threads: . done.

* Logging for 30s (until 2010-02-24T19:09:34.047316): done.

* Waiting end of threads: . done.

* Waiting cycle sleeptime 1s: ... done.

* End of cycle, 33.11s elapsed.

* Cycle result: **SUCCESSFUL**, 0 success, 0 failure, 0 errors.

Cycle #1 with 2 virtual users

-----------------------------

* Current time: 2010-02-24T19:09:36.955814

* Starting threads: .. done.

* Logging for 30s (until 2010-02-24T19:10:07.360480): done.

* Waiting end of threads: .. done.

* Waiting cycle sleeptime 1s: ... done.

* End of cycle, 49.01s elapsed.

* Cycle result: **SUCCESSFUL**, 0 success, 0 failure, 0 errors.

Cycle #2 with 3 virtual users

-----------------------------

* Current time: 2010-02-24T19:10:25.969717

* Starting threads: ... done.

* Logging for 30s (until 2010-02-24T19:10:56.577034): done.

* Waiting end of threads: ... done.

* Waiting cycle sleeptime 1s: ... done.

* End of cycle, 44.80s elapsed.

* Cycle result: **SUCCESSFUL**, 0 success, 0 failure, 0 errors.

Result

======

* Success: 1

* Failures: 0

* Errors: 0

Bench status: **SUCCESSFUL**

W Sieci

• Strona projektu Jmeter: http://jakarta.apache.org/jmeter/index.html• Strona projektu Funkload: http://funkload.nuxeo.org

ANDRZEJ MLECZKOPlone Integrator. Pracuje dla RedTurtle Technolo-gy – jednej z czołowych �rm Plone w Europie.Kontakt z autorem:[email protected]

Page 50: SDJ_05_2010_PL

05/201050

ProgramowanieDjango

www.sdjournal.org 51

O ile dokumentacja Django i blo-gosfera obfitują w zachęcające tu-toriale i przewodniki dla począt-

kujących, dość rzadko znaleźć można ar-tykuły wykraczające poza przysłowiowy "pierwszy blog w 15 minut". Tego typu przewodniki dotykają często bardzo płyt-kiej warstwy tego, co w rzeczywistym pro-jekcie składa się na udany system. W tym krótkim artykule zdrapujemy zewnętrz-ną warstwę lukru i sprawdzamy, co moż-na znaleźć pod spodem. Dzielimy się sze-regiem doświadczeń z produkcji rzeczy-wistych aplikacji internetowych opartych o Django.

Od początku przygotuj się na wdrożenieŁatwość rozpoczęcia pracy nad zupełnie no-wym projektem w Django wywołuje optymi-styczne wrażenie, że samo wdrożenie syste-mu na serwerze produkcyjnym nie będzie się wiele różniło i wobec tego nie wymaga spe-cjalnie wielkiej uwagi ze strony programi-stów. Nic bardziej mylnego, a początkowo poczynione założenia i uproszczenia mogą w ostateczności utrudnić uruchomienie ser-

wisu w warunkach polowych. O czym więc warto pamiętać?

Po pierwsze, chcąc nie chcąc, żaden nietry-wialny projekt nie jest samotną wyspą i korzy-sta z szeregu bibliotek zewnętrznych. Od po-czątku więc warto zadbać o odpowiednie au-tomatyczne zarządzanie zależnościami. Mo-żemy to zrobić zarówno paczkując nasz pro-jekt narzędziami distutils lub distribute [1], albo też korzystając z buildout [2]. Naj-gorszym możliwym pomysłem jest natomiast ręczne zarządzanie zależnościami. W natło-ku bibliotek zewnętrznych łatwo zapomnieć o jednej z nich. Przy odrobinie pecha apli-kacja wystartuje i dopiero po pewnym cza-sie okaże się, że środowisko jest niekomplet-ne. Co więcej, nawet gdy będziemy pamięta-li o tym, żeby zainstalować wszystkie biblio-teki, brak zadeklarowania konkretnych wersji pakietów zewnętrznych może w przyszłości spowodować nieoczekiwaną awarię aplikacji, kiedy któraś z wykorzystywanych bibliotek zmieni się w sposób niezgodny z wcześniej-szą implementacją. Jest to o tyle niebezpiecz-na sytuacja, że pakiety zewnętrzne same za-wierają zależności i podobne zagrożenie mo-że dotyczyć ich samych, jeżeli nie zadeklaro-wali konkretnych wersji swoich zależności.

Drugim dobrym zwyczajem, którego nie wymusza sam framework, jest wydzielenie z głównego settings.py ustawień specy-ficznych dla konkretnej instancji aplikacji. Do takich ustawień należą m.in. SECRET_KEY, DATABASE_*, ADMINS, DEBUG, TIME_

ZONE itp. Dzięki rozdziałowi ustawień glo-balnie prawdziwych od tych, które z pew-nością będą się różnić między konkretnymi instalacjami systemu, unikamy kilku nie-bezpiecznych sytuacji. Z jednej strony cho-dzi o nieświadome użycie nieprawidłowych ustawień w obcym środowisku (np. strefa czasowa lub włączenie trybu DEBUG), z dru-giej o przypadkowe nadpisanie odpowied-nich dla danego środowiska ustawień przez ustawienia programisty podczas aktualiza-cji kodu źródłowego w repozytorium (o bez-względnej konieczności używania systemu wersjonowania kodu [3] nie muszę chy-ba przekonywać?). Istnieje kilka podejść do stworzenia takiego podziału, z których naj-prostszym jest importowanie ustawień lo-kalnych na końcu globalnego settings.py. Wadą takiego podejścia jest jednak to, że nie możemy odnieść się w pliku lokalnym do globalnie zdefiniowanych zmiennych, nie mówiąc już o możliwości modyfikacji ta-kich ustawień. W szczególności chcieliby-śmy mieć możliwość dodania w pliku lokal-nym dodatkowych middleware'ów lub ap-p'ów, np. poprzez konstrukcję:

INSTALLED_APPS += (

'debug_toolbar',

)

Zwróćmy uwagę na operator . Rozwiąza-niem, które umożliwia wszystkie te rze-czy, a dodatkowo daje możliwość przecho-wywania wielu zbiorów ustawień lokalnych i przełączania się między nimi z linii pole-ceń, jest [4].

Trzecią istotną sprawą jest zadbanie o to, żeby już początkowa instalacja systemu w środowisku produkcyjnym posiadała po-prawnie wypełnione tabele w bazie danych. Możliwość zarządzania takimi danymi po-czątkowymi dają fixtures [5], bardzo pro-

Django

Łukasz pokaże Ci kilka mniej oczywistych wskazówek, które pozwolą uniknąć błędów projektowych przy pierwszych serwisach tworzonych w oparciu o framework Django.

Dowiesz się:• W jaki sposób można zadbać o sukces wdro-

żenia systemu, jak najlepiej dzielić funkcjo-nalność w serwisie i o jakich cechach Djan-go należy pamiętać podczas tworzenia apli-kacji WWW.

Powinieneś wiedzieć:• Czym jest framework Django i znać podsta-

wy programowania w oparciu o wersję 1.1 lub nowszą. Możesz oprzeć się na artyku-le wprowadzającym do tworzenia aplikacji WWW w Django w tym numerze.

Poziom trudności

Doświadczenia z pracy z frameworkiem

Page 51: SDJ_05_2010_PL

05/201050

ProgramowanieDjango

www.sdjournal.org 51

sty w użyciu mechanizm umieszczania da-nych w bazie. Posiadając odpowiedni zbiór danych początkowych w aktualnej instan-cji bazy danych, wystarczy użyć polecenia manage.py dumpdata. Umożliwia ono wyeks-portowanie istniejących danych do formatu XML, YAML lub JSON i późniejsze załado-wanie ich do bazy danych poprzez manage.py loaddata nazwa_pliku_z_danymi. Takie pli-ki powinny być przechowywane w katalo-gu fixtures w danym app'ie. Przypadkiem szczególnym jest plik initial_data.xml (lub .yml czy .json), którego zawartość jest za-wsze umieszczana w bazie danych podczas polecenia manage.py syncdb.

Odważnie dziel funkcjonalność serwisu na osobne app'ySerwisy tworzone w oparciu o Django są dzie-lone na tzw. aplikacje (ang. apps), nazywane przez społeczność po prostu app'ami. Każ-dy app implementuje wydzieloną niewielką funkcjonalność, a działający system jest zbio-rem wielu takich aplikacji. Kiedy konkretne app'y są projektowane tak, żeby ich zakres od-powiedzialności był możliwie wąski, moż-na łatwo podmienić jeden z nich na inny bez konieczności ingerencji w resztę serwisu. Ła-twiej też testować jednostkowo implemen-tację, kiedy jest ona podzielona na niewiel-kie kawałki.

Dobrym zwyczajem jest wobec tego bar-dzo liberalne podejście do dzielenia funkcjo-nalności swojego projektu na szereg niewiel-kich aplikacji.

Ostrożnie z wykorzystaniem zewnętrznych app'ówBardziej ostrożnie należy jednak podchodzić do wykorzystania w projekcie zewnętrznych app'ów. Największym problemem jest w ta-kim przypadku sytuacja, kiedy okazuje się, że zewnętrzny app spełnia 90% naszych po-trzeb, ale musimy go dostosować do nasze-go projektu. Co wówczas? Pobieramy kod źródłowy i dołączamy do naszej dystrybucji? Trzymamy nadal osobno i łatamy w locie? Je-żeli to pierwsze, to co w przypadku, gdy app zostanie zaktualizowany przez twórców? Na ile łatwo będzie można przenieść nasze zmia-ny między wersjami?

Nie ma oczywistej odpowiedzi na te pyta-nia. Wszystko zależy od konkretnego app'a, konkretnych zmian, od specyfiki tworzonego serwisu i pewnej dozy szczęścia. Doświadcze-nie pokazuje jednak, że często najbezpiecz-niejszym rozwiązaniem jest mimo wszyst-ko dołączenie kodu zewnętrznej aplikacji do źródeł naszego projektu i po naszych lokal-nych zmianach traktowanie go jak własnego kodu [6]. Tracimy możliwość bezbolesnego korzystania z aktualizacji zewnętrznych źró-deł, obarczamy się odpowiedzialnością za dal-

szy rozwój app'a, a jednak zyskujemy coś bar-dzo istotnego: pełną dowolność w reorganiza-cji źródeł w sposób maksymalnie dostosowa-ny do naszych potrzeb. Ostatecznie nie mo-żemy przecież powiedzieć klientowi, że cze-goś "nie da się", tylko dlatego, że korzystamy z zewnętrznej biblioteki do jakiejś funkcjo-nalności.

manage.py runserver oszukujeMożliwość uruchomienia rozwijanej aplika-cji jednym prostym poleceniem jest nieoce-nioną oszczędnością czasu. Ta reklamowa-na w każdym tutorialu funkcjonalność ma jednak dwie cechy, które szybko ugryzą każ-dego programistę Django. Pierwsza z nich związana jest z faktem, że automagicznie serwuje nam media wbudowanej aplikacji admina. To, co na pierwszy rzut oka wyda-je się zaletą, w momencie faktycznej instala-cji serwisu na serwerze produkcyjnym naj-częściej dokłada nam bólu głowy, bo jest to jedna z wielu drobnych rzeczy, o których nie pomyśleliśmy.

W trakcie samego programowania runserver potrafi nas również zmylić co do rzeczywistego zachowania budowanego ser-wisu w środowisku produkcyjnym. Jako że runserver jest jednowątkową aplikacją dzia-łającą w ramach jednego procesu, nie prze-testujemy w nim żadnych przypadków uży-cia związanych z równoległym dostępem do serwisu przez wielu użytkowników. Z tego samego powodu, uruchamiając serwis przez nie uzyskujemy pełnej informacji na temat zachowania zapytań AJAX, pamięci współ-dzielonej (patrz następna wskazówka) lub sterowania współbieżnością (np. sekcje kry-tyczne).

settings.py to nie pamięć współdzielonaPoczątkujący programiści czasem naduży-wają wolności, jaką dają ustawienia projek-tu Django, które są klasycznym plikiem źró-dłowym Pythona. Najbezpieczniej jednak traktować je jako ni mniej ni więcej tylko plik konfiguracyjny. Ustawienia są bowiem ładowane wielokrotnie podczas cyklu życia serwisu, osobno dla każdego procesu serwe-ra, osobno dla poleceń wykonywanych z li-nii poleceń. Nawet w trakcie wykonania po-jedynczego polecenia manage.py plik konfi-guracyjny jest parsowany kilkukrotnie. Wy-korzystanie więc go w formie uproszczo-nej pamięci współdzielonej może prowa-dzić do trudnych do zdiagnozowania błę-dów. Zawsze, kiedy wymagamy od serwisu przechowania dowolnych danych między żądaniami użytkowników, powinno się to odbyć przez mechanizm sesji, cache (patrz następna wskazówka) lub oczywiście ba-zę danych.

Kilka gorzkich słów o buforowaniuDomyślny model implementacji cache w Django jest podatny na tzw. efekt gromad-ki psów (ang. dog-pile effect). Polega on na tym, że w przypadku kiedy wygaśnie waż-ność cache dla danego klucza więcej niż je-den klient zapyta o jego wartość jednocze-śnie, każde zapytanie spowoduje rozpoczę-cie procedury odświeżania cache. Efekt ten jest szczególnie ewidentny przy bardzo wie-lu klientach lub w przypadku, gdy czas od-świeżenia cache znacznie przekracza średni okres między kolejnymi zapytaniami do sys-temu. W konsekwencji tego problemu spo-ry procent zasobów serwera jest marnowany na zbędną, bo wielokrotnie powielaną, pra-cę. Istnieje szereg podejść do rozwiązania te-go problemu w Django, gotowe rozwiązania można znaleźć w [4] i [7].

Pomijając wymienioną wyżej niedosko-nałość Django, problem z buforowaniem w zasadzie sprowadza się do odpowiedzi na dwa pytania: jak dużo możemy wrzucić do cache i jak długo możemy tak wrzuco-nych danych nie aktualizować. Django udo-stępnia szereg skrótów dla typowych sche-matów buforowania, wliczając w to m.in. dekorator @django.views.decorators.c

ache.cache_page. W wielu przypadkach buforowanie całości danej strony nie bę-dzie akceptowalne (np. dlatego, że w części strony widoczne są kontekstowe informa-cje danego użytkownika), ale jeżeli więk-szość wizyt na stronie nie składają użyt-kownicy zalogowani, warto z niego skorzy-stać ze względu na olbrzymie zyski wydaj-nościowe. W takim przypadku warto jed-nak pamiętać o ustawieniu w settings.py ignorowania cache dla użytkowników za-logowanych (poprzez dyrektywę CACHE_

MIDDLEWARE_ANONYMOUS_ONLY.Jeżeli chodzi o to, jak długo trzymać dane

w cache, kwestia jest znowu indywidualna i każdy większy serwis ma w tej kwestii wła-sną politykę. Z punktu widzenia użytkow-nika istotne jest to, żeby obecność mechani-zmów buforujących była dla niego przezro-czysta. Innymi słowy, jeżeli w serwisie użyt-kownik zauważa, że konkretne dane są nie-spójne, bo część z nich jest ewidentnie nieak-tualna, wówczas czas buforowania jest dla te-go przypadku zbyt długi.

Ostatnia uwaga: włączanie buforowania podczas programowania znacząco utrudnia sensowne usuwanie błędów w logice aplika-cji. Z drugiej strony konieczne jest dogłębne przetestowanie systemu z włączonym cache tak, aby wykluczyć wszystkie sytuacje błęd-nego buforowania. Szczególnie ważne jest za-pewnienie, że poprzez cache nie przeciekają dane wymagające specyficznych uprawnień od użytkowników systemu.

Page 52: SDJ_05_2010_PL

05/201052

Programowanie

Schizofrenia użytkowników, czyli zarządzanie profilamiKonwencją Django jest wydzielenie osobnych modeli na konto użytkownika i profil użyt-kownika. Ten pierwszy zawiera tylko podsta-wowe informacje takie jak login, e-mail i ha-sło, w tym drugim natomiast programista może zawrzeć dowolne interesujące go cechy użytkownika (np. datę urodzenia, awatar czy kraj pochodzenia). Takie rozdwojenie zarzą-dzania pojedynczym użytkownikiem umoż-liwia jednocześnie uzyskanie zgrabnej archi-tektury Django, bywa jednak również powo-dem problemów. Profil użytkownika łączy się z modelem User poprzez podanie w konfigu-racji nazwy modelu, który ma zostać użyty jako profil użytkownika (w zmiennej AUTH_PROFILE_MODULE). Tak skonfigurowany pro-fil jest dostępny w obiektach użytkownika przez metodę get_profile(). Wywołanie tej metody powoduje wykonanie drugiego zapy-tania do bazy danych [8]. Warto o tym pamię-tać, gdy ilość zapytań do bazy danych stanie się wąskim gardłem w wydajności systemu. Ponadto, kod źródłowy z licznymi wywoła-niami w stylu user.get_profile().birth_date wygląda po prostu brzydko.

Dodatkowo, istniejącą funkcjonalność konta użytkownika zdefiniowaną w modelu django.contrib.auth.models.User trud-no jest zmodyfikować. Przykładowo, zmiana sposobu działania serwisu na taki, w którym adres e-mail jest jednocześnie nazwą użyt-kownika, albo inny sposób decydowania o tym, czy konto użytkownika jest aktyw-ne w serwisie czy nie, są dość podchwytli-we w implementacji. Z wymienionych wy-żej powodów użyteczne okazuje się korzy-stanie na przestrzeni całego serwisu z obiek-tów profilu użytkownika (w razie potrzeby z pobieraniem w jednym zapytaniu skoja-rzonego konta użytkownika przez select_related()). Z bazy pobieramy więc profil użytkownika, a nie dane konta. Taki obrót sprawy umożliwia skoncentrowanie się nie na domyślnej funkcjonalności, ale na mody-fikacjach pod konkretny serwis.

Nie daj się porwać magii modeliSystem mapowania obiektowo-relacyjne-go w Django jest wystarczająco magiczny, że często można zapomnieć o tym, że pod spodem leży rzeczywista relacyjna baza da-nych. W dodatku, prostota definiowania mo-deli może być niekiedy myląca. O sztuczkach i kruczkach ORM w Django można napisać osobny artykuł, przy tej okazji skupmy się je-dynie na dwóch drobnych kwestiach o znacz-nych konsekwencjach. Zacznijmy od usuwa-nia obiektów z bazy.

ORM Django domyślnie umożliwia usu-wanie obiektów z bazy w sposób permanent-ny. Dodatkowo, symuluje on działanie ON DELETE CASCADE z baz SQL, co oznacza, że obiekty zależne od usuwanego są także usu-wane z bazy w nieodwracalny sposób. Dla przykładu, jeżeli usuniemy kategorię na fo-rum, wszystkie wpisy w tej kategorii są usu-wane wraz z nią. Najczęściej jest to zachowa-nie pożądane. Pożądane na tyle często, że w tym jednym przypadku, kiedy byśmy go so-bie nie życzyli, łatwo można o nim zapo-mnieć. Problem dotyczy bezpośrednich za-leżności definiowanych w modelach przez ForeignKey. Co możemy zrobić?

Najładniejszym rozwiązaniem wydaje się implementacja tzw. soft delete, czyli usu-wanie z widoku w serwisie poprzez usta-wianie odpowiedniego pola w modelu (np. category.removed = True). Dzięki możli-wości zdefiniowania dla modelu dedykowa-nego menedżera (django.db.models.Ma-nager), możemy ograniczyć wyświetlanie obiektów danego modelu na przestrzeni całe-go serwisu tylko do takich, które spełniają da-ny warunek (np. removed == False). W ten sposób nie musimy dodatkowo przy każdym wyświetlaniu modelu z soft delete pamiętać o przefiltrowaniu "obiektów usuniętych".

Drugą kwestią, którą warto poruszyć, jest dziedziczenie w modelach. Django umożli-wia definiowanie modeli z wykorzystaniem tego mechanizmu obiektowego, co daje nam możliwość właściwego rozdziału funkcji za-wartych w modelu. Przykładowo, funkcjo-

nalność soft delete moglibyśmy zaimple-mentować jako klasę abstrakcyjną dla mode-li i jedynie dziedziczyć po raz zdefiniowanej funkcjonalności w przyszłości. W takiej sy-tuacji należy jednak pamiętać o dwóch kwe-stiach: żeby nie zapomnieć o wywoływaniu metod zdefiniowanych w klasach bazowych (poprzez odwołanie do super()). Najczęściej implementowanymi metodami w takich kla-sach są właśnie save() i delete().

Google pamięta...o ile znajdzie. Innymi słowy, struktura lin-ków w serwisie jest jedną z najistotniejszych kwestii, którą warto zaplanować i zaimple-mentować poprawnie od pierwszego dnia. Zmiany w sposobie linkowania treści w sys-temie już w trakcie jego działania spowodują, że wielu użytkowników będzie odbijało się od serwisu przez nieaktualne linki zaindeksowa-ne przez wyszukiwarki lub dodane do kata-logów stron, agregatorów treści lub zakładek w przeglądarce. Na co zwrócić uwagę?

Po pierwsze, wyszukiwarki wysoko ocenia-ją linki zawierające słowa kluczowe zapytania użytkownika. Żeby dołączyć je do naszych adresów URL, możemy użyć automatyczne-go pola typu SlugField w modelu lub filtra slugify w szablonach HTML. W obu przy-padkach trzeba mieć jednak na uwadze, że wbudowana implementacja slug'ów zawiera tylko znaki alfanumeryczne ASCII, przemil-czając inne znaki podczas konwersji. W kon-sekwencji, tekst "Żółć?" zostanie przekonwer-towana na pusty łańcuch znaków. Warto wo-bec tego mieć własną, trochę pewniejszą im-plementację.

Po drugie, ludzie dzielą się linkami! Z tego powodu każda strona serwisu powinna być dostępna pod unikalnym, ale również sensow-nym i zrozumiałym dla człowieka linkiem. Je-żeli to możliwe, należy ograniczać dostęp do treści przez zapytania POST lub parametry GET, odpowiednie identyfikatory przekazu-jąc bezpośrednio w strukturze linków.

PodsumowaniePoza wyżej wymienionymi wskazówkami, można wymienić jeszcze olbrzymią rzeszę in-nych. Są to wszystko rzeczy, na które każdy programista rzeczywistej aplikacji interneto-wej prędzej czy później się natknie. Właśnie to doświadczenie, zdobywane dzień do dniu podczas pracy z Django, jest najcenniejszą war-tością i odróżnia dopracowane rozwiązania od kolejnego przepisu na blog w 15 minut.

Przypisy

• [1] http://packages.python.org/distribute/• [2] http://www.buildout.org/• [3] Przy tej okazji warto polecić któryś ze współczesnych rozproszonych systemów ta-

kich jak Git lub Mercurial. Szczególnie ten drugi znajduje duży poklask w społeczności programistów Pythona. Joel Spolsky przygotował świetne wprowadzanie do tego syste-mu: http://www.hginit.com/ .

• [4] (1, 2) http://packages.python.org/langacore.kit.django/• [5] http://docs.djangoproject.com/en/dev/howto/initial-data/• [6] Na tyle, na ile pozwala na to licencja.• [7] http://www.djangosnippets.org/snippets/155/• [8] Wynik tego zapytania jest potem trzymany w obiekcie modelu tak, żeby kolejne wy-

konania nie musiały za każdym razem pytać bezpośrednio bazy danych. Jest to jednak buforowanie tylko na poziomie pojedynczego żądania HTTP.

ŁUKASZ LANGAAutor interesuje się muzyką, reżyserią dźwięku i fo-togra�ą, sprawdza się jako młody tata, a w wol-nym czasie prowadzi własną �rmę programistycz-ną. Kontakt z autorem: [email protected]

Page 53: SDJ_05_2010_PL
Page 54: SDJ_05_2010_PL

05/201054

Bazy danychKrótki wstęp do ZODB

www.sdjournal.org 55

Mnie najbardziej interesują rozwiąza-nia, które pozwalają przy możliwie najmniejszym wysiłku udostępnić

mechanizm trwałego przechowywania danych moim pythonowym aplikacjom. Wśród nich zdecydowanie najpopularniejsze jest ZODB.

ZODB, czyli Zope Object Database, to obiek-towa baza danych, pozwalająca na zapisywanie grafu pythonowych obiektów. To dziecko Ji-ma Fultona, jednego z twórców Zope [Wikipe-dia.ZODB]. Prace nad nią rozpoczęły się grubo ponad 10 lat temu i trwają właściwie nieprze-rwanie do dzisiaj. Jako część projektu, Zope jest najczęściej wykorzystywana przy budowie CMSów (jest podstawowym mechanizmem zapisu danych Plone).

Co odróżnia ZODB od innych baz danych? Przede wszystkim przezroczystość: aplikacje operujące na pythonowych obiektach wyma-gają jedynie minimalnych zmian, by z niej ko-rzystać. Przy pracy z ZODB nie trzeba uży-wać mapperów (takich jak np. SQLAlchemy), bo nie ma potrzeby przekształcania przetwa-rzanych danych na inną postać. Nie ma tu ta-bel, kolumn, kluczy obcych, tylko zwykłe kla-sy, słowniki i listy (no, nie jest tak do końca, ale o tym w dalszej części artykułu). W zasadzie w ZODB można zapisać każdy obiekt, który da się zserializować za pomocą modułu pickle (co

wyklucza możliwość zapamiętywania gniazdek sieciowych, otwartych plików itp.). W swej isto-cie bardzo przypomina rozbudowaną wersję klasy Shelf z modułu shelve w standardowej dystrybucji Pythona [PSF.Shelve]. Rożni się od niej przede wszystkim:

• obsługą transakcji;• wsparciem dla undo;• wieloma typami storage (enginami zapisu

danych) umożliwiających zapis bazy w pli-kach (FileStorage), strukturze katalogów (DirStorage), na pośredniczącym serwerze Zope Enterprise Objects (ClientStorage), bazie relacyjnej (RelStorage) i pamięci (De-moStorage);

• długoletnim testowaniem w warunkach bojowych w setkach tysięcy instancji Zo-pe, Plone, Groka na całym świecie.

Czym ZODB różni się od baz relacyjnych, takich jak PostgreSQL czy Oracle? Przede wszystkim ścisłym powiązaniem z Pytho-nem. Format zapisu obiektów (pickle) zde-cydowanie utrudnia bezpośrednie opera-cje na tej bazie programom napisanym w in-nych językach. Od RDBMSów różni się rów-nież sposobem optymalizacji: bazy relacyjne

są w większości dostosowywane do dużej ilo-ści współbieżnych zapisów (charakterystycz-nych dla środowisk OLTP), ZODB spraw-dza się przede wszystkim tam, gdzie jedno-czesnych modyfikacji jest stosunkowo mało (np. w CMSach, bazach konfiguracji, itp.). Ze względu na przyjęty model optymistycznego blokowania (nie ma tu znanych z innych baz wysokich poziomów izolacji), najlepiej pracu-ją aplikacje, w których współbieżne procesy nie modyfikują tych samych obiektów - gdy poszczególne wątki aplikacji zaczynają wal-czyć o dostęp do tych samych danych, ZODB znacząco zwalnia. Jest to spowodowane ko-niecznością rozwiązywania konfliktów (jeśli zapisywane obiekty mają zaimplementowa-ną ich obsługę) lub powtarzaniem transakcji. Duże natężenie zmian może też powodować trudny do opanowania rozrost bazy: wszyst-kie znane mi storage ZODB zapisują dane w trybie append only. Nie modyfikują w miej-scu wcześniej zapisanych obiektów, ale za-chowują ich kolejno uaktualnione wersje. Po-zwala to w na implementację transakcji i un-do, ale powoduje rośnięcie bazy danych z każ-dą modyfikacją zapisanych w niej informacji. Zmusza to administratora ZODB do cyklicz-nego pakowania bazy, które usuwa wszystkie nieaktualne wersje obiektów. Jednak jeśli nie stawia się na niej bankowych systemów trans-akcyjnych, ZODB potrafi zaskoczyć wydajno-ścią i przede wszystkim szybkością pisania wykorzystujących ją aplikacji.

O tym, że ZODB jest faktycznie bardzo pro-sta w obsłudze, najlepiej jest się przekonać sa-

Krótki wstęp do ZODBPrawie każda aplikacja, z wyjątkiem najprostszych skryptów, musi w jakiś sposób gromadzić przetwarzane dane. Może je zapisywać bezpośrednio w plikach, w relacyjnych bazach danych czy w jednej z modnych ostatnio baz NoSQL, takich jak CouchDB czy MongoDB. Wiele z cech tych nowych projektów ma najpopularniejsza pythonowa obiektowa baza danych – ZODB.

Dowiesz się:• Jak otworzyć ZODB;• Jak zapisywać i odczytywać obiekty w

ZODB;

Powinieneś wiedzieć:• Jak pracuje się ze słownikami i listami w Py-

thonie;

Poziom trudności

Pythonowe podejście do obiektowych baz danych

NoSQL [Wikipedia.NoSQL] to termin, jakim określa się bazy danych, które, głównie dla lep-szej skalowalności, odchodzą od ścisłego modelu relacyjnego i gwarancji ACID. Oprócz Co-uchDB i MongoDB do tego grona zalicza się np. BigTable Google, Cassandra Facebooka czy Dynamo Amazona. Zwykle nie obsługują transakcji powodujących zmiany w kilku obiektach, nie strukturyzują ściśle przechowywanych danych, oferując w zamian dużą szybkość i możli-wość prostego skalowania poziomego (na wiele maszyn).

Page 55: SDJ_05_2010_PL

05/201054

Bazy danychKrótki wstęp do ZODB

www.sdjournal.org 55

memu. By oszczędzić Czytelnikowi samo-dzielnej konfiguracji środowiska testowego, przygotowałem prościutką aplikację przetwa-rzającą dane o odcinkach serialu „Dr House” [Wikipedia.HouseEpisodes] – jej kod źródło-wy jest na dołączonej do czasopisma płycie. Aplikacja korzysta z zc.buildout (opisanego szczegółowo w innym artykule), by nie insta-lować niezbędnych pakietów globalnie. Testo-wałem ją pod Pythonem w wersji 2.5.4 i 2.6.4 pod Ubuntu, powinna też bez większych pro-blemów dać się uruchomić pod Windowsem i OSXem. Po rozpakowaniu jej źródeł na dysku, wystarczy w katalogu głównym aplikacji wy-konać polecenia (Listing 1).

To spowoduje ściągnięcie niezbędnych pakie-tów i utworzenie skryptu bin/demo, który uru-chamia wszystkie przykłady z artykułu. Każ-dy, kto chciałby krok po kroku wypróbowywać kod z listingów, powinien skorzystać ze skryptu bin/przyklady – skrypt uruchamia interpreter IPythona ze wszystkimi potrzebnymi pakieta-mi na ścieżce.

Tyle tytułem nieco przydługiego wstępu – po przygotowaniu środowiska do ekspery-mentów można przejść do konkretów. Zacznę od uzyskania dostęp do obiektu root (Listing 2.) – specjalnego słownika, który jest tworzony przy pierwszym uruchomieniu bazy. Można go porównać do katalogu głównego dysku – dopie-ro do niego można dodawać własne hierarchie obiektów. ZODB obsługuje model utrwalania przez dostępność (ang. persistence by reachabili-ty) – by zachować obiekt w bazie, wystarczy po-wiązać go z już zapisanym obiektem (jako wła-sność czy element w przypadku list i słowni-ków). Na początku w bazie jest tylko root, więc siłą rzeczy to z nim należy powiązać struktury przeznaczone do utrwalenia.

Aby uzyskać dostęp do roota, trzeba stwo-rzyć trzy obiekty:

• storage, zajmujący się niskopoziomową se-rializacją obrabianych danych;

• bazę danych, stworzoną do koordynacji połączeń oraz do wykonywania operacji porządkowych i analitycznych;

• połączenie, które utrzymuje cache obiek-tów z bazy w pamięci i zajmuje się ich wczytywaniem i zapisywaniem.

Najczęściej wykorzystywanym typem storage jest FileStorage, który po prostu zapisuje obiek-ty we wskazanym pliku na dysku. To obecnie najwydajniejszy i najbardziej dopracowany en-

gine ZODB, ma jednak jedną wadę: pozwa-la na pracę z bazą tylko jednemu procesowi (który może uruchamiać wiele wątków). Jeśli do bazy musi mieć dostęp kilka procesów, na-leży skorzystać z serwera ZEO i powiązanego z nim ClientStorage (można też użyć niedaw-no uaktualnionego RelStorage).

Po utworzeniu wybranego storage engine’u tworzy się instancję bazy danych (3 linia listin-gu 2). Storage i baza danych mogą być w apli-kacjach wielowątkowych swobodnie współ-dzielone, każdy wątek musi mieć jednak do-stęp do osobnej instancji połączenia (two-rzonego w 4 linii listingu 2) – podobnie, jak w większości modułów zapewniających do-stęp do baz relacyjnych. Nasze świeżo utwo-rzone połączenie ma metodę root, która daję dostęp do „katalogu głównego” bazy, z którego można korzystać, jak z każdego innego słowni-ka (co widać na Listingu 3).

Wszystkie działania na ZODB odbywają się w transakcjach, co ułatwia utrzymanie spójno-ści bazy, gdy coś pójdzie nie tak. Dodatkowo, mechanizm transakcji można dosyć łatwo roz-szerzać tak, że możliwe staje się zsynchronizo-wane cofanie zmian np. w bazach relacyjnych czy w plikach utworzonych na dysku. Oczywi-ście nie dzieje się to w jakiś magiczny sposób – cofnięcie zmian poza ZODB trzeba oprogramo-wać samemu, maszyneria ZODB udostępnia je-dynie punkty styku, które pozwalają wykony-wać odpowiednie akcje przy rozpoczynaniu, potwierdzaniu i anulowaniu transakcji. Z tych mechanizmów intensywnie korzysta Zope i je-go pochodne: umożliwiają ciągłe utrzymywanie aplikacji w spójnym stanie, nawet przy występo-waniu nieprzewidzianych błędów.

Przy tworzeniu połączenia (metodą open() obiektu bazy danych), domyślnie tworzo-na jest również nowa transakcja: aby utrwalić

Listing 1. Instalacja aplikacji

python bootstrap.py

bin/buildout

lub

bin\buildout.exe (pod Windows)

Listing 2. Otwieranie bazy danych i uzyskiwanie dostępu do roota

>>> from ZODB import FileStorage, DB

>>> storage = FileStorage.FileStorage(‘Data.fs’) # Tworzenie storage

>>> db = DB(storage) # Tworzenie obiektu bazy

>>> conn = db.open() # Otwieranie połączenia

>>> root = conn.root() # Uzyskiwanie dostępu do roota

Listing 3. Dodawanie prostych obiektów do roota

>>> root['opis'] = 'Baza odcinków serialu Dr House'

>>> root['aktualny sezon w TVP'] = 4

>>> # items() to standardowa metoda słowników

>>> # w pythonie, zwraca listę par (klucz, wartość)

>>> root.items()

[('opis', 'Baza odcink\xc3\xb3w serialu Dr House'),

('aktualny sezon w TVP', 4)]

>>> len(root)

2

Listing 4. Zapisywanie w bazie mutowalnych obiektów

>>> from malloc.zodb.modele import Scenarzysta, Odcinek, Sezon

>>> from persistent.mapping import PersistentMapping

>>> david_shore = Scenarzysta('David', 'Shore')

>>> lawrence_kaplow = Scenarzysta('Lawrence', 'Kaplow')

>>> root['scenarzysci'] = PersistentMapping({

... 'David Shore': david_shore,

...'Lawrence Kaplow': lawrence_kaplow})

>>> sz1 = Sezon('I')

>>> sz1[1] = Odcinek(u'Pilot', (david_shore, ))

>>> sz1[2] = Odcinek(u'Ojcostwo', (lawrence_kaplow, ))

>>> root['sezony'] = PersistentMapping({'I': sz1})

>>> transaction.commit()

>>> odc_1_1 = root['sezony']['I'][1]

>>> odc_1_1.tagi.add('pilot')

>>> odc_1_1.tagi.add('tasiemiec')

>>> odc_1_1._p_changed = True

>>> transaction.commit()

Page 56: SDJ_05_2010_PL

05/201056

Bazy danych

świeżo wprowadzone zmiany, wystarczy ją za-twierdzić:

>>> import transaction

>>> transaction.commit()

I voila, stworzone przed chwilą obiekty zostały zapisane w bazie. Gdyby z jakiś powodów trze-ba było anulować wprowadzone zmiany, wy-starczy wywołać:

>>> transaction.abort()

i obiekty utrwalone w bazie automagicznie po-wrócą do stanu sprzed uruchomienia trans-akcji.

Prawda, że proste? Normalna praca z ZODB tak właśnie wygląda. Trzeba jednak pamiętać, by wszystkie mutowalne obiekty, które ma-ją być efektywnie zapisywanie w ZODB, dzie-dziczyły po klasie persitent.Persistent. Zwykłe klasy, słowniki czy listy nie są traktowane przez ZODB jako odrębna całość, są zapisywane z naj-bliższą instancją podklasy Persistent (w skraj-nym przypadku z rootem). Może to prowadzić do trudnych do uchwycenia błędów (takich jak np. niezapisywanie zmian czy tworzenie nieza-leżnych kopii obiektu zamiast referencji), więc należy się tego, o ile jest możliwe, wystrzegać. W przypadku, gdy użycie zwykłego mutowal-nego obiektu jest niezbędne, po jego zmianie należy ustawić własność _p_changed powiąza-nego z nim trwałego obiektu na True, co wymu-si zapisanie całej struktury do bazy.

W przykładzie zaprezentowanym w Listin-gu 4, własność tagi, obiektu odcinek_1_1 jest zwykłym zbiorem i proste dołożenie do niej ele-

mentów nie zostanie zauważone przez ZODB. Z punktu widzenia bazy, wszystkie operacje na tagach to tylko odczyt obiektu odcinek_1_1 , który nie powoduje ustawienie jego flagi _p_changed. Obiekt dziedziczący po Persistent, który przed zatwierdzeniem transakcji ma usta-wioną własność _p_changed na False, nie zo-stanie zapisany w bazie, co skutkuje pustym zbiorem tagów przy ponownym wczytaniu od-cinka (linie 114-156 skryptu demo pokazują w praktyce ten problem).

Podsumowując, wszędzie, gdzie jest to moż-liwe, warto używać klas takich jak persistent.list.PersistentList zamiast list czy persi-stent.mapping.PersistentMapping zamiast słowników, a własne klasy po prostu wywodzić z persistent.Persistent. Na szczęście ta za-leżność nie jest specjalnie uciążliwa i nie wyma-ga żadnych dodatkowych prac, co można zoba-czyć na przykładzie definicji Scenarzysty z pli-ku modele.py:

Domyślam się, że w tym momencie wszyscy są już nieco znudzeni czytaniem o tym, jak efek-tywnie zapisywać obiekty w ZODB, i chcieliby wreszcie zobaczyć, w jaki sposób się je odczytu-je i przetwarza. Osoby, które liczyły na demon-strację jakiejś egotycznej odmiany OQLa, mu-szę rozczarować: głównym językiem zapytań ZODB jest... Python. Tak jak wspominałem wcześniej, to, co składuje się w ZODB, to zwy-kłe Pythonowe obiekty, które można przetwa-rzać tak samo, jak by istniały wyłącznie w RA-Mie. Na przykład, na listingu 5 można zoba-czyć, jak wyświetlić wszystkie odcinki pierw-szego sezonu House, listing 6 zawiera kod po-kazujący wszystkie odcinki, których scenarzy-stą jest David Shore.

W tej sytuacji szybkość aplikacji korzysta-jącej z ZODB zależy w głównej mierze od za-projektowania odpowiedniej struktury danych: nie ma tu optymizera znanego z baz relacyj-nych, cały plan zapytań trzeba przygotować sa-memu. Z jednej strony utrudnia to tak popu-larne w RDBMSach przeglądanie bazy zapy-taniami ad-hoc i nie udostępnia jednego, stan-dardowego sposobu implementacji, z drugiej daje możliwość bardzo niskopoziomowego tu-ningu. Co prawda istnieją rozwiązania wspo-magające indeksowanie i wyszukiwanie obiek-tów takie jak Plone'owy Catalog [Plone.ZCa-talog] (swoją drogą świetny materiał na kolej-ny artykuł), jednak programista ma tu zupeł-nie wolną rękę i to od niego zależy, czy będzie je wykorzystywał, czy wszystkie zapytania na-pisze samemu. W efektywnym przetwarzaniu dużych ilości danych pomagają również dosto-sowane do potrzeb ZODB B-drzewa z pakietu BTree. Udostępniają interfejs słowników, ale róż-nią się od nich:

• efektywniejszym formatem zapisu na dysk;

• brakiem konieczności załadowania całego obiektu do pamięci;

• zapisywaniem w bazie tylko zmienionych fragmentów, nie całej kolekcji;

• zoptymalizowanymi implementacjami z ustalonym typem klucza i wartości (np. IOBTree – słownika obiektów indeksowa-nego liczbami całkowitymi czy OIBTree służącym do zapisywania liczb całkowi-tych indeksowanych obiektami Pythona).

W aplikacji napisanej na potrzeby tego artyku-łu B-drzewa są wykorzystywane do implemen-tacji Sezonu z pliku modele.py:

from BTrees.IOBTree import Btree

class Sezon(BTree):

def __init__(self, nazwa,

odcinki=None):

self.nazwa = nazwa

if odcinki:

self.append(odcinki)

Na tym, ze względu na szczupłość miejsca, muszę zakończyć to wprowadzenie do ZODB.

Mam nadzieję, że zainteresowałem czytelni-ków na tyle, by przynajmniej zapoznali się bli-żej z tą pythonową bazą danych, a może nawet użyli ją w którymś z projektów nie związanych bezpośrednio z Zope lub Plone. Udanych eks-perymentów!

MICHAŁ WĘGRZYNEKKonsultant i programista zajmujący się wdraża-niem systemów ERP i dostosowywaniem ich do potrzeb swoich klientów. Pythona używa do pisa-nia intranetowych aplikacji i obróbki [email protected]

Listing 5. Zwracanie wszystkich zapisanych odcinków I sezonu

>>> for odcinek in root[‘sezony’][‘I’].values():

... print ‘\t’, odcinek

<Odcinek „Pilot”>

<Odcinek „Ojcostwo”>

Listing 6. Zwracanie odcinków ze scenariuszem Davida Shore

>>> david_shore = root[‘scenarzysci’][‘David Shore’]

>>> for sezon in root[‘sezony’].values():

... for odcinek in sezon.values():

... if david_shore in odcinek.scenarzysci:

... print odcinek

<Odcinek „Pilot”>

Listing 7. Zwracanie odcinków ze scenariuszem Davida Shore

>>> david_shore = root['scenarzysci']['David Shore']

>>> for sezon in root['sezony'].values():

... for odcinek in sezon.values():

... if david_shore in odcinek.scenarzysci:

... print odcinek

<Odcinek "Pilot">

Page 57: SDJ_05_2010_PL
Page 58: SDJ_05_2010_PL

05/201058

Bazy danychSieć dziś i jutro

www.sdjournal.org 59

Mniej więcej rok temu Google ogłosił, że liczba zindeksowa-nych stron przekroczyła jeden

bilion (10^12). Przeglądając zasoby Inter-netu, mamy wrażenie, że w sieci znalazło się już wszystko. Czym zatem może ona nas jeszcze zaskoczyć? Okazuje się, że wbrew pozorom dzisiejsza sieć ma wciąż wiele ograniczeń. W obecnej postaci World Wide Web jest zbiorem stron i dokumentów po-łączonych za pomocą odnośników oraz apli-kacji, z których na co dzień korzystamy. Jest to naturalną konsekwencją faktu, że sieć by-ła w swoim zamyśle stworzona przez ludzi i dla ludzi.

Spójrzmy na jeden z najciekawszych zbio-rów informacji umieszczonych w sieci – Wi-kipedię. Znajdziemy tam ponad 3 000 000 haseł. Spróbujmy zatem odpowiedzieć na kil-ka, wydawać by się mogło, prostych pytań:

• Którzy malarze urodzili się w Warsza-wie przed 1950 rokiem?

• Jakie filmy zostały nakręcone przez re-żyserów urodzonych w Krakowie?

• Jakie gry komputerowe z gatunku FPS miały premierę w 2009 roku?

Okazuje się, że znalezienie odpowiedzi na te pytania wymaga ogromnego nakła-du ręcznej pracy. Co więcej, wynik otrzy-many z angielskojęzycznej wersji serwisu będzie istotnie różny od polskiej. Nie jest to jednak problem samej Wikipedii. Jeżeli spojrzymy na serwisy generowane automa-tycznie w oparciu o bazy danych, to w dal-szym ciągu ujrzymy dane prezentowane w postaci stron HTML. Odbudowanie na ich podstawie zbioru danych źródłowych wymaga złożonego ‘reverse engineeringu’, a w niektórych wypadkach bywa wręcz niemożliwe.

Fakt, że mimo ogromnej liczby informa-cji znajdujących się w sieci, odpowiedź na zadane pytania jest bardzo trudna, zain-spirował grupę badaczy do opracowania koncepcji tzw. sieci semantycznej. Arty-kuł opublikowany w miesięczniku Scien-tific American (m.in. przez współtwór-cę usługi WWW Timothy’ego Berner-sa-Lee) stał się podstawą wizji, w której in-teligentne agenty „rozumiejące” znaczenie umieszczonych w sieci informacji i usług będą potrafiły wyręczyć nas w skompli-kowanych zadaniach. Np. zaplanują po-dróż i dokonają rezerwacji niezbędnych biletów, oczywiście porównując przed za-kupem ich ceny. Zanim jednak będziemy mogli oddać się upragnionemu wypoczyn-kowi w tropikach, spróbujmy przyjrzeć się teoretycznym podstawom sieci seman-tycznych.

Trochę teoriiKluczem do rozumienia przez ludzi in-formacji zawartych w Internecie jest zdol-ność interpretacji tekstu pisanego języ-kiem naturalnym. Znaczenie słowa „za-mek” (w drzwiach czy zamek na skarpie) potrafimy wychwycić z kontekstu wypo-wiedzi i nie musimy się w tym celu posługi-wać odniesieniem do wystąpienia tego sło-wa w słowniku. Niestety, mimo znacznego postępu w interpretacji języka naturalnego przez komputery, przetwarzanie informacji w postaci czysto tekstowej stanowi dla nich w dalszym ciągu znaczne wyzwanie. Zatem spróbujmy pomóc maszynom w zrozumie-niu naszych intencji. Zaczniemy od proste-go zdania „Krzysztof jest ojcem Michała”. Aby sformalizować nasz przekaz, skorzysta-my ze specyfikacji opisu metadanych, zwa-nej RDF (Resource Description Framework). Informacja o zasobach jest reprezentowana za pomocą zdania złożonego z trzech ele-mentów: podmiotu, predykatu i obiektu (ang. Subject, Predicate, Object). Warto tu za-znaczyć, że RDF nie narzuca konkretnej for-my zapisu takiej trójki. Popularnymi forma-tami serializacji są XML i N3. Możemy też ująć naszą relację w postaci tabeli lub rów-noważnego grafu:

Taka „trójka” (ang triple) może być da-lej przetwarzana przez komputer. Jednak aby współdzielić jej rozumienie z innymi komputerami w sieci, konieczne jest wpro-wadzenie struktury pozwalającej na po-grupowanie naszych pojęć. Taką struktu-rą jest schema RDF (RDFS). Umożliwia ona przede wszystkim zdefiniowanie klas (rdfs:Class) oraz użycie ich do defini-cji dziedzin (rdfs:domain) i przeciwdzie-dzin (rdfs:range) dla naszych właściwo-ści (rdf:Property). Skodyfikujmy więc na-szą wiedzę w postaci schemy RDF pokaza-nej na Listingu 1.

Sieć dziś i jutro

Po pierwszej rewolucji Internetu przyszła jej kolejna faza: Web 2.0 i dzięki powstaniu serwisów społecznościowych wciągnęła do sieci miliony nowych użytkowników. Co przyniesie ze sobą trzecia fala? Zapraszamy do czytania.

Dowiesz się:• Co to są sieci semantyczne;• Jak będzie wyglądał Internet przyszłości;• Jak już dzisiaj można poznać technologie

semantyczne.

Powinieneś wiedzieć:• Podstawowa znajomość baz danych;• Otwartość na nowe struktury danych.

Poziom trudności

Rozpowszechnienie Internetu zmieniło na zawsze sposób, w jaki pracujemy, odpoczywamy i komunikujemy się ze sobą

Page 59: SDJ_05_2010_PL

05/201058

Bazy danychSieć dziś i jutro

www.sdjournal.org 59

Jak widać, przemyciliśmy tu sporą dawkę ‘rozumienia’ relacji międzyludzkich. Każdy Mężczyzna jest Osobą, oraz może być w re-lacji jest_ojcem z inną Osobą. Łatwo zauwa-żyć, że niewielkim nakładem pracy może-my rozszerzać nasz aparat pojęciowy o kolej-ne klasy. Co więcej, RDFS pozwala na budo-wę hierarchii klas (rdfs:subClassOf) i wła-ściwości (rdfs:subPropertyOf). Nie zagłę-biając się dalej w formalną specyfikację RDF i RDFS, polecam zapoznanie się z praktycz-nymi zastosowaniami, które zyskały już spo-rą popularność, jak np. języki RSS czy Du-blin Core.

Ostatnim elementem naszego stosu spe-cyfikacji potrzebnych komputerom do „głę-bokiego zrozumienia” informacji znajdują-cej się w sieci jest Ontologia. Aby nie wkro-czyć na ścieżkę rozważań filozoficznych, pro-ponuję przyjąć na nasze potrzeby, że jest to po prostu kolejny krok rozbudowy metajęzyka. Dzięki temu możemy uchwycić subtelne re-lacje pomiędzy koncepcjami, którymi posłu-gujemy się na co dzień, opisując wiedzę z da-nej dziedziny. Zilustruje to najlepiej rozsze-rzenie poprzedniego przykładu. Zamodeluj-my (Listing 2.) dodatkowo fakty, że nie moż-na być jednocześnie obiektem klasy Mężczy-zna i Kobieta (owl:disjointWith), oraz że relacja bycia rodzeństwem jest symetryczna (&owl;SymmetricProperty).

Dodatkowy nakład pracy zwróci nam się bardzo szybko. Od tej pory system będzie mógł sam np. wywnioskować, że (A, jest_ro-dzenstwem, B) na podstawie informacji, że (B, jest_rodzenstwem, A).

W zależności od naszych potrzeb może-my wybrać mniej lub bardziej zaawanso-wany dialekt ontologii używanych w sieci (OWL-Lite, OWL-DL, OWL-Full). Cena, ja-ką płacimy za coraz większe możliwości wy-razu języka, którym się posługujemy, to ro-snąca złożoność modelu koncepcyjnego na-szej dziedziny wiedzy. Jednym słowem ry-zykujemy, że im więcej mądrości będzie-my chcieli zawrzeć w naszej ontologii, tym mniejsza szansa na znalezienie kogoś, kto będzie się chciał z nami porozumieć. Nale-ży również uwzględnić aspekt technologicz-ny, który będzie się przekładał na tzw. „czas pracy procesora”.

Sieć jutra już dziśWracając do pytań zadanych w pierwszej części artykułu, widzimy, że gdy poświęci-my czas na zbudowanie odpowiedniego me-tamodelu dla naszej informacji, znalezienie odpowiedzi na zadane pytania stanie się ła-twiejsze. Co więcej, taką odpowiedź będzie można wygenerować automatycznie. Na tym jednak nie kończą się możliwości Web 3.0. Dzięki zawarciu w naszych dokumen-tach informacji o znaczeniu użytych pojęć

za pomocą publicznie dostępnych Ontolo-gii oraz nadaniu wszystkim zasobom unikal-nych identyfikatorów, możliwe będzie rze-czywiste powiązanie wiedzy dziś rozproszo-nej i uwięzionej w serwisach internetowych w jedną całość. Przykładem takiej inicjatywy jest projekt Linked Data. W ramach niego działają dziesiątki witryn współdzielących dane w oparciu o standardy RDF i URI. Ilu-stracja 2 przedstawia wielkość chmury da-nych z lipca 2009.

Czego zatem potrzebujemy, aby zacząć przygodę z Linked Data? Oczywiście na-rzędzia, które pozwoli na przechowywanie

i przetwarzanie ogromnej liczby zdań zło-żonych z trójki (Podmiot, Predykat, Obiekt) oraz dostarczy nam implementacji języka za-pytań i wnioskowania.

Oracle Semantic TechnologiesOracle Semantic Technologies (OST) to zbiór narzędzi realizujących przetwarza-nie danych semantycznych. Funkcje prze-twarzania OST są dostępne w bazie da-nych Oracle Enterprise Edition wersji 11g, z opcją Spatial (pierwsze moduły były już dostępne w wersji 10g) i Partitioning. W systemach wymagających dużej wydajności

Tabela 1. Jedna z form zapisu informacji o zasobach

Podmiot Predykat Obiekt

Krzysztof jest_ojcem Michał

Listing 1. De�nicja klas

<?xml version="1.0"?>

<rdf:RDF xmlns:rdf="http://w3.org/1999/02/22-rdf-syntax-ns#"

xmlns:rdfs="http://w3.org/2000/01/rdf-schema#">

<rdfs:Class rdf:ID="Osoba"></rdfs:Class>

<rdfs:Class rdf:ID="Mężczyzna">

<rdfs:subClassOf rdf:resource="#Osoba" />

</rdfs:Class>

<rdf:Property rdf:ID="jest_ojcem">

<rdfs:domain rdf:resource="#Mężczyzna" />

<rdfs:range rdf:resource="#Osoba" />

</rdf:Property>

</rdf:RDF>

Listing 2. Dodatkowe relacje pomiędzy klasami

...

<owl:Class rdf:ID="Mężczyzna">

<rdfs:subClassOf rdf:resource="#Osoba" />

<owl:disjointWith rdf:resource="#Kobieta" />

</rdfs:Class>

...

<owl:ObjectProperty rdf:ID="jest_rodzenstwem">

<rdf:type rdf:resource="&owl;SymmetricProperty" />

<rdfs:domain rdf:resource="#Osoba" />

<rdfs:range rdf:resource="#Osoba" />

</owl:ObjectProperty>

Rysunek 1. Opis zasobu za pomocą relacji

��������� ����������������

Page 60: SDJ_05_2010_PL

05/201060

Bazy danychSieć dziś i jutro

www.sdjournal.org 61

i spełnienia zadanych norm bezpieczeństwa, Oracle Semantic jest wspierany przez opcje Advanced Compression (zmniejszając na-wet o 75% gabaryty woluminów), Real Ap-plication Cluster, Virtual Private Database i Advanced Security.

Integralną częścią rozwiązania OST jest repozytorium RDF (tzw. trójek) – będące w sferze przetwarzania semantycznego od-powiednikiem relacyjnego modelu danych. Do identyfikacji zasobów sieci semantycz-nej wykorzystywane są identyfikatory URI. Językiem zapytań stosowanym przy danych semantycznych jest SPARQL, którego im-plementacja w bazie danych jest realizowa-na przez funkcję SEM_MATCH (wcześniej SDO_RDF_MATCH).

Wyobraźmy sobie drzewo genealogiczne pewnej rodziny, które następnie opiszemy, korzystając z OST:

Pracę z OST zacznijmy od stworzenia tabe-li family_rdf_data przechowującej model se-mantyczny rodziny:

CREATE TABLE family_rdf_data (id NUMBER,

triple SDO_RDF_

TRIPLE_S);

W kolejnym kroku stwórzmy model seman-tyczny:

execute SEM_APIS.create_rdf_

model('family',

'family_rdf_data',

'triple');

Wprowadźmy osoby i podstawowe relacje pomiędzy nimi:

-- Waldemar jest ojcem Zuzanny

INSERT INTO family_rdf_data VALUES (1,

SDO_RDF_TRIPLE_S('family',

'http://www.oracle.com/family/Waldemar',

'http://www.oracle.com/family/fatherOf',

'http://www.oracle.com/family/Zuzanna'));

...

Określmy role społeczne - Michał jest męż-czyzną:

INSERT INTO family_rdf_data VALUES (14,

SDO_RDF_TRIPLE_S('family',

'http://www.oracle.com/family/Michał',

'http://www.w3.org/1999/02/22-rdf-syntax-

ns#type',

'http://www.oracle.com/family/

Mężczyzna'));

Obiekt Osoba to klasa składająca się z dwóch podklas Mężczyzny i Kobiety:

INSERT INTO family_rdf_data VALUES (17,

SDO_RDF_TRIPLE_S('family',

'http://www.oracle.com/family/Person',

'http://www.w3.org/1999/02/22-rdf-syntax-

ns#type',

'http://www.w3.org/2000/01/rdf-

schema#Class'));

Mężczyzna i Kobieta są podklasami Osoby:

INSERT INTO family_rdf_data VALUES (18,

SDO_RDF_TRIPLE_S('family',

'http://www.oracle.com/family/Male',

'http://www.w3.org/2000/01/rdf-

schema#subClassOf',

'http://www.oracle.com/family/Person'));

INSERT INTO family_rdf_data VALUES (19,

SDO_RDF_TRIPLE_S('family',

'http://www.oracle.com/family/Female',

Rysunek 2. Chmura danych Linked Data (żródło: Wikipedia)

Page 61: SDJ_05_2010_PL

05/201060

Bazy danychSieć dziś i jutro

www.sdjournal.org 61

'http://www.w3.org/2000/01/rdf-

schema#subClassOf',

'http://www.oracle.com/family/Person'));

Przykład zapytańPoniżej prezentujemy sposób wykorzystania funkcji SEM_MATCH w celu znalezienia odpowiednich zależności.

Nasze pierwsze zapytanie (z wyłączonym silnikiem wnioskowania) zwróci obiekty kla-sy „Mężczyzna”:

SELECT m

FROM TABLE(SEM_MATCH(

'(?m rdf:type :Male)',

SEM_Models('family'),

null,

SEM_ALIASES(SEM_ALIAS('','http:

//www.oracle.com/

family/')),

null));

Wynik:

M

----------------------------------

http://www.oracle.com/family/Michal

http://www.oracle.com/family/Tomek

Wprowadźmy teraz regułę „Jeżeli ktoś jest czyimś bratem (brotherOf), to jest męż-czyzną”:

INSERT INTO family_rdf_data VALUES (24,

SDO_RDF_TRIPLE_S('family',

'http://www.example.org/family/brotherOf',

'http://www.w3.org/2000/01/rdf-

schema#domain',

'http://www.example.org/family/Male'));

A następnie włączmy silnik wnioskowa-nia:

BEGIN

SEM_APIS.CREATE_ENTAILMENT(

'rdfs_rix_family',

SEM_Models('family'),

SEM_Rulebases('RDFS'));

END;

/

Możemy teraz wykonać ponownie zapy-tanie:

SELECT m

FROM TABLE(SEM_MATCH(

'(?m rdf:type :Male)',

SEM_Models('family'),

SDO_RDF_Rulebases('RDFS'),

SEM_ALIASES(SEM_ALIAS('','http://

oracle.com/family/

')),

null));

Otrzymamy wynik:

M

----------------------------------

http://www.oracle.com/family/Michal

http://www.oracle.com/family/Tomek

http://www.oracle.com/family/Waldemar

http://www.oracle.com/family/Grzegorz

http://www.oracle.com/family/Darek

Proszę zwrócić uwagę, że silnik OST zna-lazł dodatkowych mężczyzn, dzięki wnio-skowaniu na podstawie reguły „Jeżeli ktoś jest czyimś bratem (brotherOf), to jest mężczyzną”.

Silniki wnioskowania OWL i RDFSJak widać, sercem rozwiązań analiz seman-tycznych są mechanizmy dokonujące spraw-dzenia relacji pomiędzy zasobami, realizo-wanymi za pomocą silników wnioskowa-nia (ang. Entailment) RDF i wcześniej opi-sanych dialektów ontologii OWL (bardziej rozbudowanych reguł powstałych na bazie niedoskonałości RDF). Wnioskowanie jest możliwe w oparciu o logiczne zależności po-między regułami (rules). Reguły to obiek-ty wykorzystywane do oznaczania zależno-ści pomiędzy informacjami. Są nimi opera-tory IF, filtry ograniczające wyniki operato-ra IF i THEN. W Oracle Semantic wspiera-

ne są predefiniowane zbiory reguł (RULE-BASE): RDFS, OWLSIF, RDFS++, OWLPri-me, SKOSCORE.

Cechą prezentowanego rozwiązania jest dodatkowo możliwość tworzenia własnych reguł. Oto przykład:

EXECUTE SEM_APIS.CREATE_RULEBASE('family_

rb');

INSERT INTO mdsys.semr_family_rb VALUES(

'grandparent_rule',

'(?x :parentOf ?y) (?y :parentOf ?z)',

NULL,

'(?x :grandParentOf ?z)',

SEM_ALIASES(SEM_ALIAS('','http:

//www.oracle.com/

family/')));

Prezentowane rozwiązanie, oferujące dodat-kowe spojrzenie na dane poprzez analizę pewnych zdefiniowanych zależności, posia-da również wbudowane, wydajne mechani-zmy ładowania dużych woluminów danych. Są nimi: import poprzez API Java, operacje DML (INSERT, UPDATE) oraz ładowanie hurtowe z plików płaskich.

Linki do dokumentacji, przykłady i ma-teriały informacyjne znajdziemy na stro-nie: http://www.oracle.com/technology/tech/semantic_technologies/index.html

bądź prościej, wpisując w wyszukiwarce hasło: Oracle Semantic Technologies.

Prezentowany przykład został opra-cowany na bazie dokumentacji dołączo-nej do produktu Oracle Semantic Tech-nologies dostępnej pod adresem: http://download.oracle.com/docs/cd/E11882_0 1 / a p p d e v. 1 1 2 / e 1 1 8 2 8 / s d o _ rd f _ c o n -cepts.htm#insertedID10

PAWEŁ KORZEC, KRZYSZTOF WYSOCKIPaweł Korzec, Sales Consultant, Oracle PolskaKrzysztof Wysocki, Consulting Solution Manager, Oracle PolskaRysunek 3. Drzewo genealogiczne – przykład danych semantycznych

�������� ������

��������

�������������������������������������

�����������������

��������������

����������������

��������������

������������

�������������

������� ����� ������

Krzysztof Wysocki Paweł Korzec

Page 62: SDJ_05_2010_PL

05/201062

Aplikacje biznesoweWdrożenia SAP

www.sdjournal.org 63

W trakcie wdrożenia produkt ta-ki powinien być odpowiednio skonfigurowany, dostosowany

do potrzeb danej organizacji i - jeśli zacho-dzi taka potrzeba - rozszerzony dodatko-wymi modułami. Dopiero wtedy realizuje procesy biznesowe klienta i w pełni spełnia oczekiwania użytkowników.

Zaniedbanie którejkolwiek z kluczo-wych czynności przewidzianych w metody-ce wdrożeniowej może spowodować nie tyl-ko opóźnienie w realizacji projektu, ale i cał-kowite jego fiasko.

Niniejszy artykuł przedstawia wybrane problemy mogące pojawić się w trakcie re-alizacji projektu oraz sugerowane sposoby ich rozwiązywania.

Podobnie jak w przypadku standardo-wych projektów wytwarzania oprogramo-wania, proces wdrażania nie może odbywać się ad hoc, bez określonych zasad, reguł i wy-tycznych. Firma wdrożeniowa nie może po prostu zainstalować produktu SAP w środo-wisku klienta bez uprzedniej analizy wyma-gań, zaprojektowania rozwiązania i przete-stowania go. Cały proces powinien być sta-rannie zaplanowany, podzielony na etapy,

a produkty poszczególnych etapów uzgod-nione z klientem.

Organizacja procesu wdrożeniowego jest z reguły poniekąd narzucona przez produ-centa rozwiązania SAP BO – który, ba-zując na doświadczeniu wielu projektów wdrożeniowych oraz wypracowanych naj-lepszych praktykach, udostępnia partne-rom (czyli z punktu widzenia klienta, fir-mom wdrożeniowym) metodologię wdro-żeniową.

W niniejszym artykule przedstawimy opis metodyki wdrożenia systemu SAP Bu-siness One (oparty na dokumencie Zinte-growanej koncepcji wdrożenia i funkcjono-wania opracowanej przez SAP AG).

Metodyka wdrażania SAP BOMetodyka dzieli projekt wdrożeniowy na fa-zy, w ramach których realizowane są kroki wdrożenia ze szczególnym uwzględnieniem kamieni milowych, związanych z powstawa-niem konkretnych produktów projektu oraz ich odbiorem przez klienta.

Jednym z kluczowych założeń realiza-cji projektu wdrożeniowego według przed-stawionej metodyki jest współpraca klienta (szczególnie grupy wdrożeniowej) z zespo-łem konsultantów firmy wdrożeniowej.

Metodyka wdrożeniowa SAP Business One obejmuje trzy etapy:

• Etap przedwdrożeniowy;• Etap wdrożenia;

• Etap rozwoju rozwiązania biznesowego w systemie SAP.

Każdy etap składa się z faz. I tak, etap przedwdrożeniowy obejmuje dwie fazy:

• Faza 0 - Przygotowanie sprzedaży sys-temu. W tej fazie wykonywana jest ocena projektu wdrożeniowego SAP BO przez firmę wdrożeniową oraz po-dejmowana decyzja o zakupie syste-mu. W wyniku fazy powinna powstać wstępna ocena projektu wdrożenio-wego – czyli m.in. dokument opisu-jący zakres organizacyjny, opis proce-sów biznesowych, które zostaną obję-te rozwiązaniem SAP oraz dokumenty zarządcze w postaci wstępnego harmo-nogramu oraz budżetu.

• Faza 1 – Określenie wymagań. Celem tej fazy jest pozyskanie wymagań klien-ta co do zakresu i funkcjonalności roz-wiązania SAP. Wymagania te będą sta-nowić podstawę projektu wdrożenio-wego. Identyfikacja wymagań może być wykonana na dwa sposoby: poprzez opracowanie dokumentu Opis wyma-gań lub przeprowadzenie szczegółowej analizy, wyniku której powstaje doku-ment Analizy Przedwdrożeniowej. Opis wymagań jest zestawieniem wszystkich wymagań klienta i obejmuje zazwyczaj: procesy biznesowe danej organizacji, które mają być wspomagane systemem SAP, dokumenty tworzone w systemie, raporty wykorzystywane przez użyt-kowników końcowych, specyfikę da-nych przetwarzanych przez system.

Analiza przedwdrożeniowa charakteryzu-je się większym stopniem szczegółowości, niż sam opis wymagań. Podstawowym jej celem jest identyfikacja procesów bizneso-

Wdrożenia SAP – droga przez mękę?Wdrażanie systemu SAP jest przedsięwzięciem wymagających ścisłej współpracy ze strony firmy wdrożeniowej i klienta. Rozwiązania typu SAP posiadają z reguły gotową funkcjonalność podstawową ogólną lub dedykowaną dla danej branży.

Dowiesz się:• Jak wygląda proces wdrożenia systemu SAP

Business One;• Jakie problemy możesz napotkać w proce-

sie wdrożenia SAP.

Powinieneś wiedzieć:• Podstawowa wiedza z zakresu zarządzania

projektem informatycznym;• Podstawowa wiedza z zakresu wdrożeń sys-

temów klasy ERP

Poziom trudności

Co może się nie udać?

Page 63: SDJ_05_2010_PL

05/201062

Aplikacje biznesoweWdrożenia SAP

www.sdjournal.org 63

wych oraz funkcjonalności objętych plano-wanym wdrożeniem. Analiza ta skupia się na określeniu procesów i powiązań mię-dzy nimi. Obejmuje nie tylko procesy, któ-re będą objęte wdrożeniem i są realizowa-ne w standardzie SAP BO, ale i wymagają-ce specjalnych rozwiązań poza standardem SAP BO. W wyniku analizy przedwdroże-niowej określane są również interfejsy do innych systemów oraz raporty i dokumen-ty produkowane przez projekt wdrożenio-wy. Analizę wykonuje zespół wdrożeniowy (dostawca SAP BO), wymagana jest jednak ścisła współpraca klienta.

W wyniku fazy 1 powstaje dokument Opis wymagań lub dokument Anali-za przedwdrożeniowa, uzgodniony przez klienta i firmę wdrożeniową. Kolejnym eta-pem jest etap wdrożenia. Obejmuje on na-stępujące fazy:

• Faza 1 – Organizacja projektu. Celem tej fazy jest zorganizowanie projektu wdrożeniowego – w zakresie zarządza-nia, zasobów, logistyki, etc. Faza ta roz-poczyna się określeniem Sponsora pro-jektu oraz powołaniem przez klienta Koordynatora projektu i grupy wdro-żeniowej odpowiedzialnej za wdroże-nie SAP BO w organizacji. Ze strony dostawcy powoływany jest Kierownik Projektu oraz zespół konsultantów. Kie-rownik Projektu opracowuje dokument Karty Projektu wdrożenia SAP BO, któ-ry obejmuje m.in. strategię wdrożenia, podstawowe i dodatkowe cele bizneso-we projektu oraz jego uzasadnienie biz-nesowe, krytyczne czynniki powodze-nia projektu, ryzyka, zakres (na podsta-wie wyników etapu przedwdrożenio-wego np. Analizy Przedwdrożeniowej), ramowy oraz szczegółowy harmono-gram, procedury oraz środowisko tech-niczne projektu. Ponadto, Kierownik Projektu inicjalizuje Dokumentację pro-jektu klienta (czyli rozwiązań projekto-wych) oraz Dziennik Projektu (czyli do-kument realizacji projektu). Dokumen-ty te będą w dalszym ciągu uzupełnia-ne przez konsultantów. W fazie 1 okre-ślane są również potrzeby szkoleniowe grupy wdrożeniowej i użytkowników końcowych systemu.

• Faza 2 – Instalacja systemu SAP BO. Celem tej fazy jest zainstalowanie roz-wiązania SAP w środowisku klienta. W tej fazie przygotowywane jest śro-dowisko techniczne (na podstawie wy-ników analizy potrzeb sprzętowych dla wdrożenia systemu wykonanej w fazie przedwdrożeniowej), po czym insta-lowane oprogramowanie SAP BO oraz ewentualnie inne aplikacje (np. pro-

dukty dostawcy wspomagające standar-dową funkcjonalność SAP). Prace insta-lacyjne dokumentowane są w Dokumen-tacji projektu klienta.

• Faza 3 – Szkolenie grupy wdrożeniowej (czyli użytkowników ze strony klienta). Zakres szkolenia jest inny w zależności od odbiorców – cała grupa wdrożenio-wa otrzymuje ogólną prezentację syste-mu SAP BO, natomiast specjaliści da-nego obszaru (np. Kadry i Płace) biorą udział w warsztatach funkcjonalnych przedstawiających dany obszar oraz po-wiązania z innymi modułami rozwiąza-nia.

• Faza 4 – Przygotowanie rozwiązania biznesowego w SAP BO. Polega to na opracowaniu koncepcji rozwiązania biznesowego(rozumianego jako spo-sób realizacji procesów), odpowiedniej konfiguracji systemu SAP BO i ewentu-alnym przygotowaniu elementów roz-wiązania poza standardem SAP (w ra-mach Dokumentacji projektu klienta), przygotowaniu scenariuszy testowych i przetestowaniu gotowego systemu. W zakres prac wchodzi też określenie i skonfigurowanie ról oraz uprawnień użytkowników. Dostosowanie systemu do wymagań oraz konfiguracja jest za-daniem firmy wdrożeniowej; testy na-tomiast powinny być wykonane przez klienta.

• Faza 5 – Przygotowanie systemu SAP BO do pracy. Celem tej fazy jest przygo-towanie rozwiązania do produktywnej pracy w organizacji klienta. Zadanie to obejmuje: sfinalizowanie przygotowa-nia raportów i dokumentów, przygoto-

wanie interfejsów oraz migrację danych z dotychczasowych systemów. Dodat-kowo, dostawca (lub sam klient – zależ-nie od ustaleń) opracowuje dokumenta-cję dla użytkowników końcowych oraz realizuje zaplanowane szkolenia. Wy-konywane są też całościowe testy roz-wiązania celem weryfikacji popraw-ności funkcjonalności. Zakończeniem prac jest wykonanie audytów – przygo-towania użytkowników do pracy z sys-temem oraz samego systemu: jego goto-wości do pracy w środowisku produk-cyjnym.

• Faza 6 – Nadzór nad pracą produktyw-ną systemu. Polega na wyłączeniu do-tychczasowych systemów (które zosta-ły zastąpione rozwiązaniem SAP BO) i przejściu na obsługę procesów wdro-żonym systemem SAP. Zaleca się rów-nież, by dostawca czuwał nad zamknię-ciem pierwszego okresu rozrachunko-wego w działaniu systemu. Zakończe-niem fazy jest protokół zamknięcia pro-jektu wdrożeniowego SAP BO.

Ostatnim etapem projektu wdrożeniowe-go jest etap rozwoju rozwiązania bizneso-wego w systemie SAP BO. Rozwój obejmu-je jedną fazę:

• Faza 1 – Przygotowanie i uruchomie-nie funkcjonalności rozszerzonej SAP. Obejmuje to określenie zakresu funk-cjonalności rozszerzonej i sporządze-nie odpowiedniej umowy pomiędzy stronami, opracowanie funkcjonalności rozszerzonej oraz procedur testowych, przetestowanie rozwiązania i przeka-

Analiza przedwdrożeniowa Analiza przedwdrożeniowa realizowana jest w czterech krokach. Oto one:

1. Przygotowanie się klienta do wywiadówZanim firma wdrożeniowa przystąpi do wywiadów umożliwiających wykonanie analizy, klient musi się odpowiednio przygotować. Obejmuje to zebranie następujących informacji: schemat organizacyjny przedsiębiorstwa klienta, przebieg procesów biznesowych, obieg dokumentów oraz raportów.

2. Przeprowadzenie wywiadówPrzeprowadzenie wywiadów jest zadaniem firmy wdrożeniowej. Celem wywiadu jest zebra-nie informacji niezbędnych do określenie procesów biznesowych i funkcjonalności objętych wdrożeniem oraz interfejsów stałych, raportów, formularzy i dokumentów. W ramach anali-zy przedwdrożeniowej należy również ustalić wymagania sprzętowe dla wdrożenia systemu SAP BO. Prowadzący wywiad powinni także umieć na bieżąco wychwycić niespójności oraz próbować wyjaśnić ewentualne wątpliwości (np. co do zakresu wdrożenia).

3. Opracowanie dokumentu Analizy przedwdrożeniowejInformacje zebrane w trakcie wywiadów są podstawą do opracowania dokumentu Analizy przedwdrożeniowej. Przygotowanie dokumentu jest zadaniem firmy wdrożeniowej.

4. Akceptacja dokumentu Analizy przedwdrożeniowejSamo przygotowanie dokumentu Analizy przedwdrożeniowej nie stanowi podstawy do podjęcia dalszych prac projektowych. Dokument musi być zatwierdzony przez klienta oraz firmę wdrożeniową.

Page 64: SDJ_05_2010_PL

05/201064

Aplikacje biznesoweWdrożenia SAP

www.sdjournal.org 65

zanie rozszerzenia do pracy na produk-cji. W razie konieczności wykonywane jest też wyłączenie starych systemów, zastępowanych przez funkcjonalność rozszerzoną. W wyniku fazy otrzymu-je się funkcjonalność rozszerzoną syste-mu oraz protokół zamknięcia projektu wdrożenia funkcjonalności rozszerzo-nej systemu SAP.

Ryzyko w implementacji systemu SAP BOProblemy z wdrożeniem systemu SAP BO mogą pojawiać się na kilku płaszczyznach. Najczęstszymi – i jednocześnie najbardziej krytycznymi dla powodzenia projektu – są zagrożenia związane z prawidłowym pozy-skaniem i zaimplementowaniem wymagań. Określenie wymagań wymaga dogłębnego zrozumienia procesów biznesowych w orga-nizacji klienta, zidentyfikowanie procesów, które mogą być objęte rozwiązaniem SAP BO, zweryfikowanie, w jakim stopniu SAP umożliwi realizację tych procesów, wyszu-

kanie ewentualnych luk lub problemów (np. procesów, których SAP nie wspiera, brakują-cych danych) i wspólne z klientem znalezie-nie rozwiązania.

Ustalone i zaakceptowane wymagania stanowią podstawę do zaplanowania prac i opracowania projektu rozwiązania – dla-tego też niezmiernie ważne jest, by były określone prawidłowo i kompletnie. Odpo-wiedzialność identyfikacji wymagań leży w głównej mierze po stronie firmy wdroże-niowej, jednak nie jest możliwa bez ścisłej współpracy klienta. I tu pojawia się kolejny problem – brak zaangażowania przedstawi-cieli klienta w prace związane z projektem wdrożeniowym. Brak zaangażowania nieko-niecznie musi wynikać z lekceważenia całe-go przedsięwzięcia lub negowania potrzeby i zasadności wdrożenia – zazwyczaj wiąże się po prostu z brakiem czasu pracowników klienta. Nie każda firma chcąca wdrożyć SAP BO ma możliwość oddelegowania ze-społu pracowników do wspierania prac do-stawcy – czasem, by nie zaburzać stabilno-

ści i ciągłości bieżącej pracy, pracownicy wy-stępują jedynie w roli konsultantów, mogąc poświęcić dostawcy wyznaczoną ilość cza-su (i ani godziny więcej), wykonując jedno-cześnie swoje normalne obowiązki. W przy-padku wdrożeń realizowanych w dużych or-ganizacjach, obejmujących wiele złożonych procesów biznesowych, może to nie wystar-czać – i firma wdrożeniowa skazana jest na długie oczekiwanie na odpowiedź w kluczo-wych kwestiach, lub na domyślanie się prawi-dłowego rozwiązania.

Poniżej opisano dokładniej najczęściej po-jawiające się problemy oraz możliwe sposo-by przeciwdziałania im.

Brak zaangażowania przedstawicieli klientaZdarzają się sytuacje, w których klient zle-ca dostawcy wdrożenie systemu SAP BO, dostarcza wstępną listę wymagań, po czym uważa, że jego rola w procesie się skończy-ła – a reszta należy już do firmy wdrożenio-wej. Zdarzają się również sytuacje, w któ-rych klient nie zapewnia wystarczające-go wsparcia swoich pracowników w proce-sie analizy przedwdrożeniowej, wychodząc z założenia, iż rolą dostawcy jest opracowa-nie analizy lub – po prostu – nie chcąc od-rywać swojego personelu od właściwych za-dań. W wyniku takiego podejścia, dostawca nie jest w stanie poprawnie zidentyfikować wymagań i, co za tym idzie, zaimplemen-tować rozwiązania odpowiadającego ocze-kiwaniom odbiorców. Argumenty w stylu „potrzebujemy wsparcia Waszych pracowni-ków, by określić, co dokładnie system ma ro-bić, jakie procesy wspierać, jakie dokumen-ty i w jakiej postaci produkować” nie zawsze odnoszą pożądany skutek.Co można zrobić?Najlepszą metodą jest po prostu uświa-domić klientowi złożoność i mnogość do-stępnych w SAP funkcji – wiele z nich nie jest klientowi potrzebnych, inne muszą zo-stać odpowiednio dostosowane do potrzeb i oczekiwań użytkowników. Dostawca nie jest w stanie przewidzieć wymagań wyni-kających ze specyfiki danej branży i przed-siębiorstwa – nawet jeśli posiada spore do-świadczenie we wdrażaniu rozwiązań SAP BO w danym obszarze. Może zaproponować rozwiązanie, dostarczyć swoich uwag – ale to klient jest stroną decydującą o ostatecz-nym kształcie wymagań.

Dostawca powinien uświadomić kliento-wi, iż bez jego wsparcia projekt jest z góry ska-zany na niepowodzenie – określić ryzyko wy-nikające z niepełnej lub niepoprawnej identy-fikacji wymagań, jego późniejsze skutki w po-staci zmian do funkcjonalności, opóźnień wynikających z konieczności poprawy czy zmiany dotychczasowych wymagań etc. Rysunek 2. Dokumenty projektowe

Rysunek 1. Etapy projektu wdrożeniowego

Page 65: SDJ_05_2010_PL

05/201064

Aplikacje biznesoweWdrożenia SAP

www.sdjournal.org 65

Nieadekwatna dokumentacja systemu Załóżmy, że jesteś Kierownikiem Projektu mającego na celu wdrożenie systemu SAP BO u klienta. Twój zespół zaczyna groma-dzić wymagania systemu od użytkowni-ków. Po uzyskaniu wszystkich potrzebnych informacji, dochodzisz do wniosku, iż po-siadasz wystarczające dane do implementa-cji „docelowego” systemu. A ponieważ ro-zumiesz działanie „docelowego” systemu, wiesz, jak ma funkcjonować i jakie są wyma-gania względem niego – zapominasz o udo-kumentowaniu „bieżącego” systemu (czy-li systemu, który będzie zastąpiony rozwią-zaniem SAP).

Podczas testów akceptacyjnych okazuje się, że system nie spełnia oczekiwań użyt-kowników końcowych, a uczestnicy testów UAT nie są w stanie walidować Twojego roz-wiązania. Klient jest rozczarowany propono-wanym rozwiązaniem – a Ty musisz teraz udowodnić, że zaimplementowane rozwią-zanie SAP jest zgodne z potrzebami bizne-sowymi i wymaganiami. Znajdujesz się w sy-tuacji, w której nie możesz wyjaśnić, w jaki sposób nowe rozwiązanie SAP ma taką sa-mą – lub rozszerzoną – funkcjonalność po-przedniego systemu. Padają pytania – jak SAP zastępuje funkcjonalność starego syste-mu, i czy w ogóle to robi?

Co można zrobić?Określ, czy istnieje dokumentacja w po-

staci specyfikacji funkcjonalnej, diagramów architektury, diagramów przepływu proce-su opisująca obecną funkcjonalność syste-mu. Współpracuj z klientem – w szczegól-ności z programistami, którzy projektowali stary system – by zrozumieć niuanse i ob-szary wysokiego ryzyka w systemie, który będzie zastępowany rozwiązaniem SAP. Do-kumenty, które zgromadzisz, pozwolą po-znać obecny system oraz w dużym stopniu zdeterminować sposób zastąpienia starego systemu nowym.

Opracuj wstępne przypadki testowe, któ-re w szczególności weryfikują, w jaki sposób przetestować funkcje starego systemu zastą-pione rozwiązaniem SAP. Przypadki te po-winny być skontrolowane przez uczestni-ków testów UAT. Dokumentuj każdą infor-mację zwrotną od recenzentów i (w miarę możliwości) postaraj się stworzyć prototyp docelowego rozwiązania – tak, by umożli-wić użytkownikom końcowym wgląd w no-wą funkcjonalność i jak najszybsze dostar-czenie uwag co do stopnia spełnienia oczeki-wań wobec systemu.

Niedopracowane wymaganiaWiele organizacji (szczególnie związanych z administracją) produkuje setki wymagań formułowanych jako „system powinien…”.

Wymagania powinny skupić się na tym, co system będzie robił – i to dość dokładnie.

Organizacje, które przymierzają się do wdrożenia SAP BO, często zaczynają od określania wymagań – zamiast najpierw zorientować się w funkcjonalności systemu – i w efekcie wymagania mają się nijak do możliwości systemu SAP.

Niektóre firmy mają w zwyczaju doku-mentować wymagania w postaci plików Excel – to utrudnia śledzenie wymagań (zwłaszcza historii zmian), zmiany treści wymagań i efektywne komunikowanie ich do zaangażowanych osób, określanie właści-cieli wymagań, tworzenie RTM (macierz śle-dzenia wymagań), wreszcie – zarządzanie cyklem życia wymagań.

Instytucja wdrażająca SAP jest później obarczona zadaniem interpretacji wyma-gań spisanych w Excelu, planowaniem im-plementacji wymagań i weryfikacji stopnia pokrycia wymagań testami. Dostawca SAP zamiast skupić się na uszczegóławianiu wy-magań, traci wiele czasu na zarządzanie sa-mą listą i niemal na ślepo próbuje imple-mentować wymagania w treści określonej przez klienta – nawet jeśli niektóre z nich są niepotrzebne, niezgodne z funkcjonalnością SAP lub wręcz niemożliwe do realizacji.

W czasie testów, lub później – po insta-lacji systemu SAP BO, często rozpoczynają się debaty na temat funkcjonalności rozwią-zania – które nie spełnia wymagań określo-

nych w Excelu, a użytkownicy końcowi tra-cą zaufanie do aplikacji. Co można zrobić?Jeśli organizacja, w której wdrażasz SAP BO, wykonuje testy oparte na wymaganiach, za-proponuj narzędzie zarządzania wymaga-niami (Requisite Pro, Caliber-RM). Dopracuj wszystkie wymagania, upewnij się, że każde jest wystarczająco zrozumiane i dla każdego można określić atrybuty: wykonalność, ko-nieczność, spójność, kompletność, priory-tet, poprawność, możliwość śledzenia, jed-noznaczność.

Zaprezentuj swoje (i zespołu wdroże-niowego) zrozumienie wymagań za pomo-cą diagramów przepływu procesu oraz po-przez sesje przeglądowe. Wymagania, które nie mogą być zaimplementowane, powinny być oznaczone jako wyłączenia. Wymagania spoza zakresu projektu wdrożeniowego na-leży komunikować zespołowi .

Najprawdopodobniej wymagania nie osią-gną nigdy stanu „perfekcji” (gdyż to mogło-by sparaliżować projekt), lecz powinny być zdefiniowane jasno i zrozumiałe – tak, by ze-spół mógł kontynuować prace z akceptowal-nym poziomem ryzyka (w przeciwieństwie do prowadzenia prac implementacyjnych na podstawie wymagań będącym jedynie hasła-mi). Po osiągnięciu akceptowalnego poziomu ryzyka, zespół funkcjonalny może rozpocząć konfigurację systemu, a zespół techniczny – implementację nowych aplikacji.

SAP – ale po co mi to? Korzyści.SAP to nie tylko sprawniejsza rejestracja i wystawianie dokumentów. To nie tylko repozyto-rium danych.Sedno korzyści z systemu SAP polega na zupełnie nowej jakości i szybkości informacji, któ-re kierownictwo otrzymuje do dyspozycji. Dzięki tym danym można decydować o zmianach cen, ofert, dostawców, można również zapobiegać zmniejszeniu płynności finansowej.System SAP umożliwia wyraźną poprawę gospodarki środkami finansowymi. SAP wspiera za-rządzanie budżetem poprzez prognozy kosztowe i przepływowe. Prognoza płynności wspo-maga decyzje w zakresie finansowania działalności bieżącej. Raporty prezentowane przez system mogą w dowolnym momencie dostarczyć informacji o planowanych datach wpły-wu należności i koniecznych płatnościach – co pozwala zarządzać bieżącymi rozrachunkami z klientami i dostawcami. Dzięki temu można świadomie i kompleksowo kształtować polity-kę kredytu kupieckiego, bez obaw o utratę płynności.Ogromną korzyścią rozwiązania SAP jest możliwość stworzenia kompleksowego, efektyw-nego systemu kontrolingu, w odróżnieniu od tradycyjnych aplikacji księgowych, niezinte-growanych z obszarem zaopatrzenia i sprzedaży firmy. Dzięki SAP można w pełni realizo-wać postulat kontrolingu – sterowanie wszystkimi działaniami w firmie, które dają podstawę efektywnego podejmowania decyzji i w efekcie pozwalają na osiągnięcie zysku.System SAP zapewnia precyzyjne i aktualne informacje o rentowności poszczególnych pro-duktów, usług, działów, segmentów rynku – dzięki kalkulacji kosztów i przychodów na nie przypadających.„Ile kosztuje dany produkt?”, „Ile kosztuje dana usługa?”, „Jaki wpływ będzie miała zmiana cen produktów przez dostawców?” - to niektóre z pytań wpływających na stan finansowy i przyszłość firmy. Na te pytania SAP daje odpowiedź – poprzez zawarte w systemie mechani-zmy symulacji i kalkulacji kosztów produkcji oraz jej rentowności. Stosując różne modele wy-ceny i alternatywne zestawy składników (produktu, usługi) kierownictwo danego przedsię-biorstwa może precyzyjnie zaplanować koszty danego produktu lub usługi i na bieżąco mo-nitorować rentowność działalności. Analiza rozbieżności (odchyleń danych rzeczywistych od danych planowanych) umożliwia szybką identyfikację słabych punktów i podjęcie odpo-wiednich decyzji naprawczych.Dużym udogodnieniem są zestawy narzędzi typu business intelligence, czyli elastycznych dodatków umożliwiających samodzielne kreowanie raportów.

Page 66: SDJ_05_2010_PL

05/201066

Aplikacje biznesoweWdrożenia SAP

www.sdjournal.org 67

Wymagania powinny być określone dla każdego obszaru implementacji SAP BO – nie tylko dla funkcjonalności dotyka-jących tej implementacji. Kategorie wy-magań obejmują: bezpieczeństwo, work flow, raporty, interfejsy, formy, wykona-nie, archiwizację, użyteczność etc. Określ wszystkich udziałowców oraz przydziel właścicieli wymagań dla każdego obszaru. Wymagania, które spełnione są automa-tycznie (poprzez rozwiązanie produktowe SAP) i nie wymagają tworzenia przypad-ków testowych, powinny być jasno przed-stawione klientowi (np. poprzez prezen-tację demo aplikacji). Należy jednak pa-miętać, iż samo zaprezentowanie kliento-wi sposobu, w jaki produkt SAP spełnia wymagania, nie wystarcza – należy upew-nić się, że akceptacja jest oficjalna i udoku-mentowana.

Opracuj RTM, który wykaże, że wszystkie wymagania mają pokrycie i są zmapowane na przypadki testowe.

Brak weryfikacji zakresuRelacje pomiędzy firmą wdrażającą SAP BO a klientem mogą ulec radykalnemu ochło-dzeniu, przerodzić się wręcz we wrogość – jeśli klient ma poczucie, że zaimplemento-wane rozwiązanie nie pasuje do jego potrzeb biznesowych, a użytkownicy końcowi nie są w stanie wykonać czynności, które wykony-wali z łatwością za pomocą starego systemu.

Problem pojawia się, gdy klient zgłasza defekty i usterki do systemu SAP BO, któ-re dotyczą obszarów czy funkcji nie będą-cych w zakresie prac wdrożeniowych, lub nieudokumentowanych w wymaganiach do systemu. Kiedy firma wdrożeniowa identy-fikuje takie błędy jako żądania zmian (a nie

problemy z implementacją rozwiązania SAP), relacje z klientem ulegają szybkiemu pogorszeniu – z punktu widzenia klienta system nie spełnia swojej roli, z perspektywy dostawcy wymagane prace zostały wykona-ne poprawnie i według dokumentacji.Co można zrobić?Ustal produkty prac oraz kryteria odbio-ru. Klient weryfikuje i zatwierdza produk-ty zgodnie z owymi kryteriami. Brak akcep-tacji produktu lub zweryfikowanego już wy-magania, musi być udokumentowany i roz-wiązany.

Zdarzają się sytuacje, kiedy po implemen-tacji SAP wdraża się system o funkcjonalno-ści zgodnej z wymaganiami, podejmowana jest decyzja GO LIVE, system zostaje zain-stalowany w środowisku produkcyjnym, po czym klient uskarża się, iż oprogramowanie nie spełnia jego potrzeb – przy czym nie do-starcza na to konkretnych dowodów. Stwier-dzenia w stylu „system nie robi tego”, „sys-tem nie jest tak dobry, jak poprzedni”, „sys-tem jest skomplikowany” nie mówią nic. Klient powinien przekazać konkretne przy-kłady na to, że zaimplementowane rozwią-zanie nie spełnia potrzeb biznesowych czy wymagań przed podpisaniem zgody na wdrożenie produkcyjne.

Firma wdrożeniowa oraz klient powin-ni ustalić kryteria zakończenia (exit crite-ria) dla fazy testowania oraz testów akcep-tacyjnych. Umożliwi to uniknięcie proble-mów z interpretacją wyników etapu testów i pomoże w podjęciu decyzji co do dalsze-go działania (np. decyzji GO LIVE). Zale-ca się, by uczestnicy UAT wykorzystywali część poprzednio użytych w testach integra-cyjnych scenariuszy testowych. Tym sposo-bem, wykonując testy akceptacyjne, weryfi-kowana jest zarówno poprawność wyników wcześniejszych faz testowania. UAT jest do-skonałym momentem na wskazanie wszyst-kich problemów zauważonych w rozwiąza-niu SAP przed wdrożeniem na produkcję. Mimo to nadal wiele organizacji nie przykła-da do testów akceptacyjnych należytej uwa-gi i woli raczej polegać na demo systemu ja-ko dowodzie spełnienia wymagań użytkow-ników – a przecież UAT to okazja, by udo-stępnić aplikację prawdziwym użytkowni-kom końcowym.

Nieudokumentowane założenia, ryzyka, ograniczeniaZdarzają się projekty, w których w fazie po-czątkowej dokumentuje się ryzyka, założe-nia i ograniczenia bardzo pobieżnie, w zasa-dzie tylko pro forma. Takie – stworzone po-niekąd z przymusu – dokumenty umiesz-cza się gdzieś, zaś o miejscu ich przecho-wywania, a czasem i o samej ich obecności, szybko się zapomina.Rysunek 4. Rezultaty wstępnej oceny projektu wdrożeniowego

Rysunek 3. Fazy etapu 1

Page 67: SDJ_05_2010_PL

05/201066

Aplikacje biznesoweWdrożenia SAP

www.sdjournal.org 67

Kiedy projekt nabiera rozpędu i wcho-dzi w kolejne fazy, nikt nie dokonuje sto-sownych aktualizacji w dokumentacji ry-zyka, założeń i ograniczeń – które powin-ny wyjaśniać, w jaki sposób podejmowano decyzje zarządcze i co miało na nie wpływ (np. ograniczenie zakresu projektu w ja-kimś stopniu ze względu na określone ry-zyko). Często powoduje to chaos – dostaw-ca SAP nie jest w stanie obronić swoich de-cyzji czy działań wynikłych z braku odnie-sienia do udokumentowanych ograniczeń czy założeń.Co można zrobić?Udokumentowanie zagrożeń, ograniczeń i założeń to początek – ale nie koniec prac związanych z tym obszarem zarządzania projektem. Ryzyka powinny być stale pod-dawane ocenie, przeglądane i weryfikowa-ne. To, że dane ryzyko – zidentyfikowane na początku projektu – nie wystąpiło przez pierwsze 3 miesiące, nie oznacza, że nie po-jawi się w miesiącu 4.

Zgodnie z dobrymi zasadami zarządza-nia ryzykiem, zagrożenia o wysokim priory-tecie powinny być przeglądane co najmniej raz w tygodniu. Na zapobieganie wystąpie-nia ryzyka lub minimalizację jego skutków należy określić odpowiedni budżet - pozwo-li to elastyczniej poradzić sobie z ewentu-alnym pojawieniem się problemu. Osoby, w największym stopniu narażone na wpływ ryzyka, powinny być na bieżąco informowa-ne o statusie przeglądów i aktualnym stanie zagrożeń w projekcie.

W przypadku założeń i ograniczeń obo-wiązuje podobna zasada – zasada systema-tycznych przeglądów, aktualizacji danych i informowania o zmianach osób zaangażo-wanych.

Problemy dostawcy z oprogramowaniemPodczas testowania lub utrzymywania syste-mu SAP można znaleźć błędy, luki czy uster-ki, które nie mogą być naprawione przez ze-spół wdrożeniowy.

Takie błędy wynikają zwykle nie z posia-dania nieprawidłowo zaimplementowane-go rozwiązania SAP, lecz z niedoskonałości oprogramowania dostawcy. W zależności od powagi problemu, wpływ błędów na biznes może przejawiać się w następujący sposób:

• Niezadowolenie klienta / użytkowni-ków końcowych;

• Brak możliwości realizacji specyficznej funkcjonalności systemu;

• Straty finansowe;• Niestabilność systemu.

Problemy dostawcy z oprogramowaniem mogą pozostawać nierozwiązane przez

długi – przedłużający się – czas. Co wię-cej, w wyniku braku kontroli i zaangażo-wania ze strony klientów, problemy te mo-gą zostać przez pomyłkę zamknięte – pod-czas gdy w rzeczywistości nigdy ich nie rozwiązano. Co można zrobić?Ustal „punkt kontaktu” – osobę odpowie-dzialną za kontakt z klientem. Osoba ta po-winna dokumentować wszystkie próby roz-wiązania danego problemu i wyniki owych prób. Zadaniem osoby kontaktowej jest rów-nież opracowywanie i dystrybucja codzien-nego raportu o wszystkich problemach do-stawcy – a to w celu uniknięcia ryzyka od-suwania problemów na potem (w związku z brakiem odpowiedzi ze strony klienta po tym, jak dostawca zaproponuje rozwiązanie problemu).

Ze swojej strony klient powinien wspólnie z dostawcą ustalić priorytety i oczekiwane daty rozwiązania problemów.

Znikająca wiedzaZ punktu widzenia klienta jest to istotny problem i zagrożenie użytkowania wdrożo-nego systemu. Klient zatrudnia kontrakto-rów i konsultantów posiadających wiedzę – z jednej strony o wdrażanym systemie SAP – z drugiej o procesach biznesowych danej organizacji. Ludzie ci odpowiadają za kon-figurację systemu, stworzenie interfejsów pomiędzy różnymi systemami w przedsię-biorstwie, testowanie i rozwiązywanie pro-blemów zgłaszanych przez użytkowników końcowych.

Zazwyczaj jednak terminy w projekcie oraz wysokie priorytety innych zadań po-

Rysunek 5. Fazy etapu 2

Page 68: SDJ_05_2010_PL

05/201068

Aplikacje biznesoweWdrożenia SAP

www.sdjournal.org 69

wodują, że kontraktorzy i konsultanci wy-konują swoją pracę w sposób – pod pew-nym względem – zdezorganizowany i nie-ustrukturyzowany. Dezorganizacja ta doty-czy dokumentacji wykonanych zadań oraz produktów. O ile sama praca może być zre-alizowana doskonale, o tyle już udokumen-towanie jej może (z braku czasu) pozosta-wiać wiele do życzenia. Skutek? Po jakimś czasie kontraktorzy i konsultanci opuszcza-ją dany projekt, a klient pozostaje z niepełną wiedzą, jaką mu pozostawili. Pierwszy lep-szy audyt wykonany na projekcie wdroże-niowym wykaże, że klient nie do końca wie, na czym polegały prace projektowe, jak zo-stały zrealizowane, jak i gdzie są udokumen-towane (o ile w ogóle są).Co można zrobić?Częste zmiany ludzi w przypadku pracy w formie body leasingu lub wynajmu kon-sultantów są poniekąd normalne. Można jednak zminimalizować ryzyko wynikające z częstych zmian kadrowych i uniknąć utra-

ty wiedzy wraz z odejściem danego kontrak-tora czy konsultanta. W tym celu:

• Stwórz wspólne repozytorium do gro-madzenia dokumentów, notatek, wszel-kich zapisów o przebiegu i wynikach prac. Opracuj standard nazywania pli-ków (ułatwi to zorientowanie się w za-wartości danego pliku).

• Powołaj zespół QA, którego zadaniem będzie wprowadzenie i egzekwowanie standardów projektowych oraz opraco-wanie wytycznych dla poszczególnych zadań.

• Wymagaj raportów tygodniowych od wszystkich kontraktorów. Raporty te powinny zawierać listę zrealizowanych zadań i ich wyniki (np. nazwy wytwo-rzonych dokumentów).

• W przypadku odejścia kontraktora z ze-społu, zwołaj spotkanie (co najmniej 2, 3 dni przed odejściem pracownika), na którym przejrzycie produkty prac oraz

przedyskutujecie obszary zwiększone-go ryzyka. Zdobądź dane do ewentual-nego kontaktu z kontraktorem na wy-padek pojawienia się konieczności kon-sultacji.

Lekceważenie użytkowników końcowychJedną z najczęściej zaniedbywanych kwestii przy wdrożeniach systemów typu SAP czy ERP jest przetwarzanie danych w czasie rze-czywistym. Prawidłowa modyfikacja danych w locie, realizowana z łatwością przez po-przednią aplikację, może nie być wykonalna w nowym systemie. Utrudnia to korzystanie z funkcjonalności i zaburza użyteczność no-wego rozwiązania.

Innym problemem dotykającym użytkow-ników końcowych jest kwestia znajdowa-nia danych w aplikacji, produkowania wła-ściwych raportów, złożoność systemu, nie-prawidłowo skonstruowane przepływy work flow, brak procesów umożliwiających realiza-cję krytycznych zadań, rejestrowanie danych w różnych miejscach, niezgodnych z oczeki-waniami i potrzebami użytkownika.

Zignorowanie rzeczywistych potrzeb użytkowników może spowodować, że no-wy system będzie skutecznie uniemożliwiać wykonywanie standardowych, koniecznych do prawidłowego funkcjonowania organiza-cji, zadań – zamiast je wspierać i ułatwiać. Co można zrobić?Użytkownicy końcowi powinni zostać za-angażowani w fazę pozyskiwania wymagań. Dostawca musi upewnić się, iż wymagania użytkowników są poprawnie zidentyfikowa-ne i zrozumiane – w tym celu można zasto-sować diagramy przepływu procesu, prezen-tując i weryfikując poprawność projektowa-nego rozwiązania przy współpracy użytkow-ników. Wszelkie wymagania powinny być oficjalnie zaakceptowane przez użytkowni-ków końcowych, zanim dostawca przystąpi do projektowania rozwiązania.

W przypadku złożonych systemów, obej-mujących wiele procesów biznesowych, przydatne jest zastosowanie podejścia opar-tego o prototypowanie funkcjonalności i interfejsów użytkownika. Pozyskiwanie i uszczegóławianie wymagań jest o wiele ła-twiejsze, jeśli dostawca ma możliwość zade-monstrować użytkownikom projektowaną funkcjonalność systemu, sposób użytkowa-nia, ergonomię, sposób realizacji wymagań biznesowych. Informacja zwrotna od użyt-kowników umożliwia dostosowanie projek-tu aplikacji do oczekiwań na bardzo wcze-snym etapie – co znacznie zmniejsza kosz-ty i ryzyko zmian.

Ponadto, użytkownicy końcowi powinni czynnie uczestniczyć w przygotowaniu do testów akceptacyjnych – zwłaszcza w pro-Rysunek 7. Ryzyko w projekcie wdrożeniowym

Rysunek 6. Fazy etapu 3

Page 69: SDJ_05_2010_PL

05/201068

Aplikacje biznesoweWdrożenia SAP

www.sdjournal.org 69

jektowaniu scenariuszy testowych. To dla nich wdrażany jest nowy system i ich potrze-by ma realizować – ścisła współpraca jest więc nieodzowna.

Braki analizy wpływu i testów regresjiZałóżmy, że użytkownicy końcowi zgłasza-ją do help desku problemy i błędy w funkcjo-nowaniu systemu. Problemy te są następnie przekazywane do zespołu utrzymania sys-temu SAP celem rozwiązania. Osoby odpo-wiedzialne za kontrolę zmian oraz integra-cję w projekcie nie analizują istoty i wagi pro-blemu, nie dokonują oceny pracochłonności naprawy problemu ani weryfikacji, czy dany błąd dotyka funkcjonalności z zakresu pro-jektu lub czy rzeczywiście jest to błąd (odno-sząc się do udokumentowanych wymagań). Nie szacuje się całościowego kosztu rozwią-zania problemu.

Po naprawieniu błędu, poprawka insta-lowana jest do systemu produkcyjnego bez sprawdzenia, jak wpłynie ona na poprzed-nią funkcjonalność systemu i czy nie zabu-rzy jego stabilności. Nie wykonuje się testów regresji i end to end na innych modułach, by upewnić się, że poprawka nie popsuje in-nych funkcjonalności. Rezultaty są łatwe do przewidzenia – po wdrożeniu poprawki na produkcję okazuje się, że przestają działać inne rzeczy, a użytkownicy nie mogą korzy-stać z systemu.Co można zrobić?Dobrym sposobem jest powołanie zespołu kontroli zmian (CCB – change control bo-ard), którego zadaniem jest analiza wpły-wu zmian wynikających ze zgłoszonych pro-blemów. Członkowie zespołu mają obowią-zek rozważenia każdej pojedynczej propo-zycji zmiany (zarówno zmian rozumianych jako poprawki błędów, jak i żądań zmian do zatwierdzonej funkcjonalności systemu) uwzględniając: priorytet, poziom wysiłku, pracochłonność testów, dostępność zaso-bów, koszt i wpływ na harmonogram projek-tu. Ważnym czynnikiem jest też konieczność zmiany – czyli analiza, na ile dana modyfi-kacja jest niezbędna dla prawidłowego funk-cjonowania systemu (bowiem po dokład-nej analizie, może okazać się, że dany pro-blem można tymczasowo obejść lub dotyczy

on funkcji wykorzystywanej bardzo rzadko – co znacznie zmniejsza ryzyko biznesowe związane z pozostawieniem błędu bez po-prawki). Analiza wpływu może wykazać, że rozwiązanie problemu jest zbyt kosztow-ne w porównaniu do potencjalnych korzyści. Jednak fakt ten da się stwierdzić dopiero po wykonaniu analizy – dlatego też powinna być ona wykonywana zawsze.

Jeśli jednak zespół kontroli zmian podej-mie decyzję o implementacji zmiany (po-prawki), powinien on zapewnić możliwość realizacji zmiany (dostępność zasobów etc. ) oraz zaplanować środki weryfikacji jej po-prawności (np. opracować scenariusze testo-we dla wewnętrznych testów oraz testów in-tegracji z systemem produkcyjnym).

Niedostosowane lub nieprzestrzegane procedury i standardyJak już wspomniano, zespół QA przy współ-pracy CCB, analityków etc. może ustalić standardy i procedury dla m.in.:

• Projektowania przypadków i scenariu-szy testowych;

• Tworzenia specyfikacji technicznych i funkcjonalnych;

• Procesu kontroli zmian oraz zarządza-nia zmianami;

• Raportowania defektów oraz ich ob-sługi.

Jednak ustalone procedury czy standardy mogą nie być przestrzegane. Z wielu po-wodów:

Zespół funkcjonalny może nie zdawać so-bie sprawy z istnienia i obowiązywania stan-dardów nazewnictwa specyfikacji. Może też nie potrafić prawidłowo pozyskiwać i doku-mentować wymagań podczas warsztatów analitycznych.

Zespół testowy może nie wiedzieć, jakie są kryteria wejścia i wyjścia dla poszczególnych faz testowania i w wyniku tego nie może od-powiednio zaprojektować scenariuszy dla kolejnych etapów.

Programiści – z braku czasu, roztargnie-nia – mogą nie przestrzegać stosowania określonej konwencji w tworzeniu kodu.

Uczestnicy UAT mogą nie być świadomi swojej roli i odpowiedzialności w testowaniu

systemu – przez co nie można prawidłowo wykorzystać ich potencjału.Co można zrobić?Wszelkie dokumenty standardów, proce-dur etc. powinny być gromadzone w okre-ślonym, znanym całemu zespołowi repo-zytorium, do którego dostęp mają wszyscy członkowie zespołu. Kierownictwo projek-tu powinno ponadto wyznaczyć kilka osób do demonstracji i szkolenia zespołu odno-śnie obowiązujących standardów i wytycz-nych – należy o tym pamiętać w szczególno-ści w przypadku nowych pracowników dołą-czających do zespołu. Trenerzy ci powinni systematycznie kontrolować jakość produk-tów i ich zgodność z ustalonymi wzorcami. Produkty, które nie spełniają wymagań, po-winny być oddane właścicielom (twórcom) do poprawki, wraz z sugestiami rozwiązania i wskazanymi miejscami wystąpienia niepra-widłowości.

PodsumowanieRozwiązanie SAP umożliwia znaczną po-prawę działalności firmy. Umożliwia prowa-dzenie i kontrolowanie bieżącej działalno-ści, planowanie zasobów, dostarcza informa-cji dla podejmowania decyzji zarządczych.

Wdrożenie systemu SAP BO nie jest pro-stą instalacją systemu z pudełka – wymaga poświęcenia pewnej ilości czasu na analizę wymagań, dostosowanie możliwości pro-duktu SAP do określonych potrzeb, oczeki-wań, procesów biznesowych danego przed-siębiorstwa. Czas ten jednak nie jest straco-ny na próżno – korzyści z działającego sys-temu z nawiązką rekompensują czas poświę-cony na przygotowanie wdrożenia i imple-mentacji. Warto mieć świadomość tego, że mimo iż wielogodzinne spotkania dotyczą-ce wymagań, analizy procesów biznesowych pozornie nie dostarczają firmie wymiernych korzyści, wartość gotowego, działające syste-mu, będzie ogromna – i będzie przynosić konkretne korzyści przez wiele lat.

W Sieci

• www.sap.com – oficjalna witryna producenta SAP. Zawiera mnóstwo informacji o samym produkcie oraz wskazów przydatnych do wdrożenia systemu.

• www.bedemiecsap.pl – porady dla menedżerów planujących wdrożenie systemu SAP.• www.sap-img.com/sap-implementation.htm – informacje, wytyczne dla poprawnej i sku-

tecznej implementacji SAP.• http://whitepapers.techrepublic.com.com/search.aspx?kw=sap+implementation – publika-

cje w tematyce wdrożenia SAP.• http://whitepapers.sapinsideronline.com - publikacje w tematyce wdrożenia SAP.

KAROLINA ZMITROWICZPracuje na stanowisku Analityka biznesowego w �rmie PROFI-DATA Sp z o.o. Karolina specjali-zuje się obecnie w modelowaniu wymagań biz-nesowych. Wcześniej pracowała jako Manager Quality Assurance w projektach informatycznych w sektorze �nansowo – bankowym.Pro�-Data to producent oprogramowania spe-cjalizujący się w budowie dedykowanych rozwią-zań informatycznych dla ubezpieczeń, banko-wości i administracji publicznej. Firma świadczy usługi wdrożeń i konsultingu systemu SAP. Firma posiada certy�kat jakości ISO 9001:2008 w zakre-sie produkcji oprogramowania i wdrażania goto-wych rozwiązań informatycznych. Kontakt z autorem: [email protected]

Page 70: SDJ_05_2010_PL

05/201070

Aplikacje biznesoweC++ Qt 4.5

www.sdjournal.org 71

W latach 90-tych, kiedy to ukazy-wało się czasopismo Bajtek dla mikrokomputerów takich jak

Atari, ZX Spectrum, Commodore, Amiga, zaczęła się moja przygoda z programowa-niem – w tamtych czasach byłem bardzo młody, gdyż miałem 9 lat – jednak miałem szczęście; byłem posiadaczem małego kom-puterka Atari 130XE. W tamtym okresie mój starszy brat kupował to czasopismo, a ja przeglądając je, widziałem często tzw. Listingi z programami w Basicu. Nie rozumiałem za wiele z tych wszystkich instrukcji zawartych w Listingach. Zacząłem je po prostu przepisy-wać z ciekawości, aby zobaczyć, co będzie się działo, gdy uruchomię program. W ten spo-sób nauczyłem się programować w języku Basic. Pewnie drogi Czytelniku zastanawiasz się, do czego zmierzam, a być może już wiesz, co chcę napisać dalej. Otóż najlepszą metodą, aby nauczyć się programować, jest po prostu zacząć programować, gdyż z samego czytania nie nauczysz się tego – przynajmniej na sa-mym początku. Oprócz tego ważna jest do-ciekliwość i tzw. drążenie tematu. Wobec te-go zapraszam Cię, abyś zaczął czytać artykuł i jednocześnie wdrażał go w praktyce.

Integracja Qt z EclipsePrzedstawię integrację Qt z Eclipse w systemie Windows oraz Linux. Zacznijmy w takim ra-zie od systemu Windows.

WindowsZe strony http://www.eclipse.org/downloads/ pobieramy Eclipse IDE for C/C++ Develo-pers i instalujemy. Następnie ze strony http://sourceforge.net/projects/mingw/files/ pobieramy MinGW-5.1.6.exe i instalujemy. Teraz musimy pobrać Qt SDK for Open Source C++ develop-ment ze strony http://qt.nokia.com/downloads/sdk-windows-cpp i instalujemy – zostanie za-instalowany Qt Creator, Qt Linguist oraz naj-nowsza biblioteka Qt. Kolejnym krokiem będzie pobranie ze strony http://qt.nokia.com/developer/eclipse-integration wersji instalacyjnej dla systemu Windows, czyli qt-eclipse-integra-tion-win32-1.5.3.exe. Uruchamiamy instalację i w kolejnych krokach podajemy ścieżkę dostę-pu do eclipse.exe oraz podajemy katalog z insta-lacją MinGW. Następnie uruchamiamy Eclipse przy pomocy Start Eclipse with MinGW, który został utworzony przez qt-eclipse-integration.

LinuxZe strony http://www.eclipse.org/downloads/ pobieramy Eclipse IDE for C/C++ Developers dla systemu Linux – mamy do wyboru wer-sję 32 i 64 bitową. Następnie uruchamia-my eclipse, wybieramy Help>Install New So-ftware i jako nową stronę wpisujemy http://download.eclipse.org/tools/cdt/releases/galileo,

instalujemy CDT. Pobieramy ze strony http://qt.nokia.com/developer/eclipse-integration qt-eclipse-integration – mamy do wyboru wersję 32 i 64 – rozpakowujemy archiwum do kata-logu Eclipse, nadpisując go. Musimy również zainstalować Qt SDK for Open Source C++ de-velopment dla systemu Linux ze strony http://qt.nokia.com/download/sdk-linux-x11-32bit-cpp lub http://qt.nokia.com/download/sdk-li-nux-x11-64bit-cpp w zależności od architek-tury Twojego systemu. Ostatecznie urucha-miamy Eclipse z opcją -clean, co zapewni wy-czyszczenie cachu Eclipse.

PlanowanieZaczynając tworzenie aplikacji komputero-wej, powinnyśmy rozpocząć od zaplanowania aplikacji. Możemy to wykonać przy pomocy specjalnego programu lub po prostu na kart-ce papieru. Ważne jest, abyś sobie wizualnie uzmysłowił sobie, co chciałbyś uzyskać. Przy-kład bardzo prostego planu możesz zobaczyć na Rysunku 1.

Dobrze, więc wiemy już, że będzie menu, główne okno aplikacji oraz status bar.

Projekt typu Qt Gui ProjectZatem po przełączeniu się do perspektywy Qt C++ tworzymy nowy projekt typu Qt Gui Project. Wpisujemy nazwę projektu Kalkula-tor i w następnym oknie w polu wyboru UI type wybieramy QmainWindow, jak na Ry-sunku 2. W następnym oknie pozostawiamy domyślnie wybrane moduły Core i Gui. Mo-duł Core jest to jądro biblioteki Qt, korzysta-ją z niego pozostałe moduły, natomiast drugi moduł Gui zawiera komponenty graficznego interfejsu użytkownika.

Klikamy dalej i jeśli pojawi się komunikat taki jak: No default Qt version is set. Open the Qt page in the preferences dialog and add a Qt version, to oczywiście w takim przypadku kli-kamy button Open prefferences. Otworzy się

C++ Qt 4.5

W artykule zostanie zaprezentowana budowa bardzo prostej aplikacji okienkowej w oparciu o bibliotekę Qt – będzie to zwykły kalkulator z prostymi wyrażeniami arytmetycznymi.

Dowiesz się:• Jak skonfigurować środowisko programi-

styczne dla Windows i Linux;• Jak stworzyć graficzny interfejs użytkownika;• Jak programować obiekty umieszczone na

formatce.

Powinieneś wiedzieć:• Jak programować w C++ w stopniu podsta-

wowym;• Jak obsługiwać program Eclipse.

Poziom trudności

Podstawy budowy aplikacji przy użyciu biblioteki Qt

Page 71: SDJ_05_2010_PL

05/201070

Aplikacje biznesoweC++ Qt 4.5

www.sdjournal.org 71

okno preferencji pojektu. W drzewie po lewej klikamy opcję Qt, a następnie po prawej stro-nie klikamy przycisk Add. W nowo otwartym oknie w pierwszym polu wpisujemy 4.5, czy-li wersję naszej biblioteki. W polu Bin Path ustawiamy katalog, który zawiera program qmake. W moim przypadku jest to katalog C:\Qt\2009.04\qt\bin, jako że uruchomiłem

Eclipse pod systemem Windows Xp. W na-stępnym polu Include Path ustawiamy kata-log, który zawiera bibliotekę Qt dla programi-sty, czyli w moim przypadku jest to ścieżka C:\Qt\2009.04\qt\include, tak jak na Rysunku 3. Teraz zastosuj zmiany i jeśli pojawi się ko-munikat typu: Some Projects` Qt versions have changed. A rebuild of the projects is required for changes to take effect. Do a full rebuild now?, to klikamy przycisk Yes.

Możemy teraz uruchomić projekt, klikając ikonkę Run na pasku Eclipse i jeśli pojawi się okno z prośbą ustawienia Debuggera, wybie-ramy np. opcję MinGW gdb Debugger. Uru-chomi się puste okno z tytułem Main Win-

dow. Możemy już zamknąć okno – na razie nie będzie nam potrzebne.

Jeśli spojrzymy do zakładki Project Explo-rer, zobaczymy strukturę projektu Kalkula-tor, która powinna zawierać Binaries, Inclu-des, debug, release, kalkulator.h, ui_kalkula-tor.h, kalkulator.cpp, main.cpp, Kalkulator. pro, kalkulator.ui, Makefile, Makefile.Debug, Ma-kefile.Release.

Ustawiamy główne okno aplikacji, projektowanie menuKlikamy dwa razy na pliku kalkulator.ui – zo-staniemy przełączeni do edytora Qt Designer. Zmienimy teraz tytuł okna z MainWindow na

Rysunek 1. Przykład prostego planu aplikacji komputerowej

��������������

���������������������������

����������

�������������������������������������������������������������

������������������������

���������������������������������������������

Rysunek 2. Wybór QmainWindow w polu UI type

Listing 1. De�nicja slotów w klasie Kalkulator – plik kalkulator.h

#ifndef KALKULATOR_H

#define KALKULATOR_H

#include <QtGui/QMainWindow>

#include "ui_kalkulator.h"

class Kalkulator : public QMainWindow

{

Q_OBJECT

public:

Kalkulator(QWidget *parent = 0);

~Kalkulator();

private slots:

void dzialanie_ce_clicked();

void dzialanie_7_clicked();

void dzialanie_8_clicked();

void dzialanie_9_clicked();

void dzialanie_dzielenie_

clicked();

void dzialanie_4_clicked();

void dzialanie_5_clicked();

void dzialanie_6_clicked();

void dzialanie_mnozenie_clicked();

void dzialanie_1_clicked();

void dzialanie_2_clicked();

void dzialanie_3_clicked();

void dzialanie_odejmowanie_

clicked();

void dzialanie_0_clicked();

void dzialanie_separator_

dziesietny_

clicked();

void dzialanie_rownosc_clicked();

void dzialanie_dodawanie_

clicked();

void wyjscie_clicked();

void skopiuj_tekst_clicked();

void oprogramie_clicked();

private:

Ui::KalkulatorClass ui;

float wynik_dzialania;

};

#endif // KALKULATOR_H

};

#endif // KALKULATOR_H

Page 72: SDJ_05_2010_PL

05/201072

Aplikacje biznesoweC++ Qt 4.5

www.sdjournal.org 73

Kalkulator. W tym celu musimy otworzyć okno o nazwie Qt C++ Property Editor, które może być już umieszczone po prawej stronie Eclipse, jeśli jednak tak nie jest, to możemy je otworzyć, wybierając z menu Window>Show view>Othe-r>Qt>Qt C++ Property Editor. Odszukajmy na liście windowTitle i zmieńmy nazwę MainWin-dow na Kalkulator. Możemy jeszcze ustawić maksymalną szerokość i wysokość okna aplika-cji. W tym celu na liście znajdź opcję maximum-Size, rozwiń ją i ustaw opcję Width i Height na 400, tak jak na Rysunku 4. Teraz okno będzie mogło być skalowane maksymalnie do 400 px szerokości i wysokości. Możesz również ustawić minimalną szerokość i wysokość, ale w naszym przypadku nie będzie to potrzebne, jednak gdy-byś chciał to zmienić, to wystarczy odszukać na liście opcję minimumSize i dokonać odpowied-nich zmian.

Teraz utworzymy proste menu. Na format-ce w miejscu napisu Type Here kilkamy dwa razy i wpisujemy &Kalkulator, gdzie znak '&' ozna-cza, że po wciśnięciu kombinacji klawiszy alt+K rozwinie się lista Kalkulator w menu. W opcji Kalkulator w miejscu Type Here klikamy po-nownie dwa razy i dodajemy opcję &Wyjście. Zaznaczamy opcję Wyjście w menu i w oknie Qt C++ Property Editor sekcji QObject zmieniamy wartość objectName na actionWyjscie. W sekcji QAction odnajdujemy shortcut, klikamy lewym klawiszem myszy na pustym polu wartości i wciskamy kombinację klawiszy ctrl+x. W ten sposób przypiszemy skrót klawiatury do opcji Wyjście. Następnie tworzymy obok opcji Kalku-lator opcję &Edycja, gdzie dodajemy opcję Sko-

piuj tekst a następnie zmieniamy wartość w po-lu objectName na actionSkopiujTekst. Dodajemy również w pustym polu shortcut skrót klawiatu-ry ctrl+c. Dodajemy jeszcze jedną pozycję w me-nu o nazwie &Pomoc, a w nim opcję &O progra-mie – zmieniamy wartość pola objectName na actionOProgramie. Jeśli chcesz zobaczyć, jak wy-gląda twoje menu bez kompilacji kodu, kliknij w puste miejsce na formatce, a następnie z me-nu wybierz QtDesigner>Preview in>Windows Xp – możesz zobaczyć, jak będzie wyglądać Twoje okno w różnych środowiskach.

Mając już całe menu, możemy dodać opis do paska informacji Status Bar. W tym celu wy-bierz na formatce opcję Wyjście i w oknie Qt C++ Property Editor opcję statusTip i jako war-tość wpisz Wyjście z kalkulatora. Dalej wybierz opcję z menu Skopiuj tekst i w wartości opcji sta-tusTip wpisz Skopiuj wynik kalkulatora. Na koń-cu wybierz opcję O programie i ustaw statusTip na Informacje o programie. Jeśli chcesz zobaczyć, jak wyświetlają się informacje, wybierz z menu QtDesigner>Preview in>Windows Xp.

Na koniec można byłoby dodać linię od-dzielającą Status Bar od reszty aplikacji. W tym celu kliknij na formatce w pustym miejscu i z okna komponentów Qt C++ Wid-get Box przeciągnij i upuść Horizontal Line na formatkę, a następnie ustaw ją nad Status Bar i rozciągnij na całą szerokość aplikacji.

Projektowanie głównej zawartości oknaZaprojektujemy teraz przyciski i pole wyni-kowe naszej aplikacji. W tym celu przeciągnij

Line Edit z okna Qt C++ Widget na format-kę i rozwiń opcję geometry, a następnie ustaw x i y na 10 oraz Width na 380 a Height na 40. Zmień wartość pola font na większą czcionkę, np. 16. W polu text ustaw wartość 0. Rozwiń pole alignment, zmień opcję Horizontal na Ali-gnRight. Zaznacz checkbox w polu readOnly – dzięki temu pole edycji będzie tylko do odczy-tu. Ustaw również wartość w polu objectNa-

Rysunek 3. Ustawienie wersji Qt, ścieżki Bin Path oraz Include Path

Rysunek 4. Ustawienie maksymalnej szerokości i wysokości aplikacji. Opcja maximumSize

Rysunek 5. Projektowanie przycisków i pola wynikowego kalkulatora

Rysunek 6. Po wybraniu opcji Lay out in a grid oraz Adjust size

Page 73: SDJ_05_2010_PL

05/201072

Aplikacje biznesoweC++ Qt 4.5

www.sdjournal.org 73

Listing 2. Główna funkcjonalność aplikacji – plik kalkulator.cpp

#include "kalkulator.h"

#include <stdio.h>

#include <QMessageBox>

#include <QTextCodec>

#include <QClipboard>

Kalkulator::Kalkulator(QWidget *parent)

: QMainWindow(parent)

{

ui.setupUi(this);

wynik_dzialania = 0;

// definiujemy sloty dla akcji actionWyjscie,

actionOProgramie i actionSkopiujTekst

connect(ui.actionWyjscie, SIGNAL(triggered()), this,

SLOT(wyjscie_clicked()));

connect(ui.actionOProgramie, SIGNAL(triggered()), this,

SLOT(oprogramie_clicked()));

connect(ui.actionSkopiujTekst, SIGNAL(triggered()), this,

SLOT(skopiuj_tekst_clicked()));

}

Kalkulator::~Kalkulator()

{

}

/*

* Metoda wyświetla okienko typu information po kliknięciu w

menu pomoc -> o programie

*/

void Kalkulator::oprogramie_clicked()

{

// definiowanie kodowania znaków w systemie

QTextCodec::setCodecForCStrings(QTextCodec::

codecForName("windows-1250"));

// wyświetlenie okienka typu information

QMessageBox::information(this, "Kalkulator","Prosty

kalkulator z podstawowymi\

ndziałaniami arytmetycznymi:\n+,-,*,/

\n\nautor: Łukasz Klejnberg, IdealSol

utions.pl",QMessageBox::Ok);

}

/*

* Metoda zamyka wszystkie okna

*/

void Kalkulator::wyjscie_clicked()

{

QApplication::closeAllWindows();

}

/*

* Metoda kopiuje wynik kalkulatora do pamięci

*/

void Kalkulator::skopiuj_tekst_clicked()

{

QClipboard *kopiuj_wynik = QApplication::clipboard();

kopiuj_wynik->setText(ui.wynik->text());

}

/*

* Zerowanie pola wynik

*/

void Kalkulator::dzialanie_ce_clicked()

{

ui.wynik->setText("0");

wynik_dzialania = 0;

}

/*

* Dodanie do pola wynik cyfry 7 lub liczby 777...

*/

void Kalkulator::dzialanie_7_clicked()

{

if(ui.wynik->text() == "0" || ui.wynik->text() == "")

{

ui.wynik->setText("7");

}

else

{

ui.wynik->setText(ui.wynik->text().append("7"));

}

}

/*

* Dodanie do pola wynik cyfry 8 lub liczby 888...

*/

void Kalkulator::dzialanie_8_clicked()

{

if(ui.wynik->text() == "0" || ui.wynik->text() == "")

{

ui.wynik->setText("8");

}

else

{

ui.wynik->setText(ui.wynik->text().append("8"));

}

}

/*

* Dodanie do pola wynik cyfry 9 lub liczby 999...

*/

void Kalkulator::dzialanie_9_clicked()

{

if(ui.wynik->text() == "0" || ui.wynik->text() == "")

{

ui.wynik->setText("9");

}

else

{

ui.wynik->setText(ui.wynik->text().append("9"));

}

}

/*

* Dodanie do pola znaku dzielenia /

*/

void Kalkulator::dzialanie_dzielenie_clicked()

{

ui.wynik->setText(ui.wynik->text().append("/"));

}

/*

* Dodanie do pola wynik cyfry 4 lub liczby 444...

*/

void Kalkulator::dzialanie_4_clicked()

{

if(ui.wynik->text() == "0")

{

ui.wynik->setText("4");

}

Page 74: SDJ_05_2010_PL

05/201074

Aplikacje biznesoweC++ Qt 4.5

www.sdjournal.org 75

Listing 2. Główna funkcjonalność aplikacji – plik kalkulator.cpp

else

{

ui.wynik->setText(ui.wynik->text().append("4"));

}

}

/*

* Dodanie do pola wynik cyfry 5 lub liczby 555...

*/

void Kalkulator::dzialanie_5_clicked()

{

if(ui.wynik->text() == "0")

{

ui.wynik->setText("5");

}

else

{

ui.wynik->setText(ui.wynik->text().append("5"));

}

}

/*

* Dodanie do pola wynik cyfry 6 lub liczby 666...

*/

void Kalkulator::dzialanie_6_clicked()

{

if(ui.wynik->text() == "0")

{

ui.wynik->setText("6");

}

else

{

ui.wynik->setText(ui.wynik->text().append("6"));

}

}

/*

* Dodanie do pola wynik znaku mnożenia *

*/

void Kalkulator::dzialanie_mnozenie_clicked()

{

ui.wynik->setText(ui.wynik->text().append("*"));

}

/*

* Dodanie do pola wynik cyfry 1 lub liczby 111...

*/

void Kalkulator::dzialanie_1_clicked()

{

if(ui.wynik->text() == "0")

{

ui.wynik->setText("1");

}

else

{

ui.wynik->setText(ui.wynik->text().append("1"));

}

}

/*

* Dodanie do pola wynik cyfry 2 lub liczby 222...

*/

void Kalkulator::dzialanie_2_clicked()

{

if(ui.wynik->text() == "0")

{

ui.wynik->setText("2");

}

else

{

ui.wynik->setText(ui.wynik->text().append("2"));

}

}

/*

* Dodanie do pola wynik cyfry 3 lub liczby 333...

*/

void Kalkulator::dzialanie_3_clicked()

{

if(ui.wynik->text() == "0")

{

ui.wynik->setText("3");

}

else

{

ui.wynik->setText(ui.wynik->text().append("3"));

}

}

/*

* Dodanie do pola wynik znaku odejmowania -

*/

void Kalkulator::dzialanie_odejmowanie_clicked()

{

ui.wynik->setText(ui.wynik->text().append("-"));

}

void Kalkulator::dzialanie_0_clicked()

{

if(ui.wynik->text() != "0")

{

ui.wynik->setText(ui.wynik->text().append("0"));

}

}

/*

* Dodanie do pola wynik znaku separatora dzięsiętnego .

*/

void Kalkulator::dzialanie_separator_dziesietny_clicked()

{

if(ui.wynik->text() != "")

{

ui.wynik->setText(ui.wynik->text().append("."));

}

}

/*

* Obliczanie wyniku ciągu znaków z pola wynik

*/

void Kalkulator::dzialanie_rownosc_clicked()

{

// tworzenie obietu typu QString i zapisanie wartosci z

pola wynik

QString str = ui.wynik->text();

// tworzenie obiektu typu QByteArray i zapisanie wartosci

z obiektu str przekształconego

funkcją toUtf8()

QByteArray enc = str.toUtf8();

// tworzenie wskaznika tab_dzialan na obiekt typu char

Page 75: SDJ_05_2010_PL

05/201074

Aplikacje biznesoweC++ Qt 4.5

www.sdjournal.org 75

me na wynik. Kolejno przeciągnij 17 widgetów Push Button i nazwij je kolejno CE, 7, 8, 9, /, 4, 5, 6, x, 1, 2, 3, -, 0, ',', =, +, jak na Rysunku 5 (aby zmienić nazwę przycisku, kliknij na nim pra-wym klawiszem myszy i wybierz opcję Chan-ge text...). W każdym z tych przypadków ustaw objectName na dzialanie_ce, dzialanie_7, dzialanie_8, dzialanie_9, dzialanie_

dzielenie, dzialanie_4, dzialanie_5, dzialanie_6, dzialanie_x, dzialanie_1,

dzialanie_2, dzialanie_3, dzialanie_

odejmowanie, dzialanie_zero, dzialanie_separator_dziesietny, dzialanie_rownosc, dzialanie_dodawanie. Dodaj również pod przyciskami Vertical Spacer. Możesz również dostosować tekst na przyciskach, jak np. na Rysunku 5. Następnie klikamy prawym kla-wiszem myszy w pustym miejscu na formatce i z menu kontekstowego wybieramy opcję Lay out>Lay out in a grid. Klikamy jeszcze raz pra-

wym klawiszem myszy na formatce i wybiera-my z menu kontekstowego Lay out> Adjust si-ze. Elementy umieszczone w oknie zostaną au-tomatycznie rozmieszczone oraz okno apli-kacji zostanie dopasowane do zawartości, jak na Rysunku 6. Ostatecznie wybieramy z me-nu Eclipse opcję Project>Clean z zaznaczoną opcją build project. Zostanie wygenerowany no-wy plik ui_kalkulator.h, który będzie zawierał ustawienia interfejsu użytkownika.

Listing 2. Główna funkcjonalność aplikacji – plik kalkulator.cpp

// tablica tworzona jest dynamicznie w zaleznosci od

rozmiaru obiektu enc

char *tab_dzialan = new char[enc.size()+1];

// kopiowanie zawartosci enc do tablicy tab_dzialan

strcpy(tab_dzialan,enc.data());

int j=0;

// zliczanie ilosci wystapien działań arytmetycznych

for(int i=0;(enc.size())>=i;i++)

if(tab_dzialan[i] == '+' || tab_dzialan[i] == '-'

|| tab_dzialan[i] == '*' || tab_

dzialan[i] == '/')

j++;

// tworzenie obiektu typu string

std::string tablica_liczb[j+1];

// tworzenie obiektu typu char

char tablica_wyrazen[j];

// rozdzielenie wyrażeń arytmetycznych i liczby na dwie

odrębne tablice

for(int i=0,j=0;(enc.size())>=i;i++)

{

if(tab_dzialan[i] == '+' || tab_dzialan[i] == '-'

|| tab_dzialan[i] == '*' || tab_

dzialan[i] == '/')

j++;

if(tab_dzialan[i] == '+')

tablica_wyrazen[j] = '+';

else if(tab_dzialan[i] == '-')

tablica_wyrazen[j] = '-';

else if(tab_dzialan[i] == '*')

tablica_wyrazen[j] = '*';

else if(tab_dzialan[i] == '/')

tablica_wyrazen[j] = '/';

else

tablica_liczb[j] += tab_dzialan[i];

}

// obliczanie wyniku

// funkcja strtod() wykonuje zamiane stringu na float

// funkcja c_str() wykonuje zamiane stringu na char

for(unsigned i=0;(sizeof(tablica_liczb)/

sizeof(char*))>i;i++)

{

if(tablica_wyrazen[i] == '+')

{

wynik_dzialania += strtod(tablica_liczb[i].c_str(),

NULL);

}

else if(tablica_wyrazen[i] == '-')

{

wynik_dzialania -= strtod(tablica_liczb[i].c_str(),

NULL);

}

else if(tablica_wyrazen[i] == '*')

{

wynik_dzialania *= strtod(tablica_liczb[i].c_str(),

NULL);

}

else if(tablica_wyrazen[i] == '/')

{

if( strtod(tablica_liczb[i].c_str(), NULL) != 0)

{

wynik_dzialania /= strtod(tablica_liczb[i].c_

str(), NULL);

}

else

{

ui.statusbar->setWhatsThis("Blad dzielenia przez

0!");

wynik_dzialania = 0;

// definiowanie kodowania znaków w systemie

QTextCodec::setCodecForCStrings(QTextCodec::

codecForName("windows-1250"));

// okienko typu warning z informacją o błędzie

dzielenia przez 0

QMessageBox::warning(this, "Komunikat o

błędzie","Błąd dzielenia przez

0!",QMessageBox::Ok);

}

}

else

wynik_dzialania = strtod(tablica_liczb[i].c_str(),

NULL);

}

// zapisanie nowego wyniku do pol wynik

// metoda number klasy QString powoduje zamianę liczby

na string

ui.wynik->setText(QString::number(wynik_dzialania));

wynik_dzialania = 0;

}

/*

* Dodanie do pola wynik znaku dodawania +

*/

void Kalkulator::dzialanie_dodawanie_clicked()

{

ui.wynik->setText(ui.wynik->text().append("+"));

}

Page 76: SDJ_05_2010_PL

05/201076

Aplikacje biznesowe

UWAGA: plik ui_kalkulator.h będzie za-wsze nadpisany przy nowej kompilacji pro-jektu.

Programowanie funkcjonalności kalkulatoraAby zaprogramować funkcjonalność naszych przycisków, posłużymy się edytorem sygna-łów i slotów. W tym celu należy kliknąć na formatce, a następnie wybrać z menu Eclip-se QtDesigner >Editor Mode>Edit Signals/Slots lub kliknąć na odpowiedniej ikonce o tej sa-mej nazwie. Zostaniemy przełączeni w spe-cjalny tryb, gdzie przy pomocy myszki bę-dziemy mogli zdefiniować po części zacho-wanie przycisków. Klikamy pierwszy przycisk CE lewym klawiszem myszy i nie puszczając go, przenosimy kursor myszy w puste miej-sce na formatce i puszczamy. Otworzy się okno z możliwością wybrania sygnału (po le-wej) i slotu (po prawej). W oknie po lewej wy-bieramy clicked(). Później zaznaczamy opcję (checkbox) Show signals and slots inherited from Qwidget i klikamy przycisk edit w oknie po prawej. Otworzy się nowe okno z możli-wością dodawania nowych slotów i sygnałów. Dodajemy nowy slot (kliknij ikonkę +), któ-ry będzie się nazywał dzialanie_ce_clicked(). Akceptujemy zmiany i zaznaczamy na liście nasz nowy slot, a następnie ponownie akcep-tujemy zmiany. Teraz na formatce w miejscu definiowania slotu i sygnału pojawi się na-pis na przycisku CE clicked() oraz na zakoń-czeniu strzałki dzialanie_ce_clicked(), tak jak na Rysunku 7. Zdefiniowaliśmy pierwszy sy-gnał i slot. Proste, prawda? No dobrze, to te-raz dodamy pozostałe sygnały i sloty taką sa-mą metodą. Dla ułatwienia podaję nazwy no-wych slotów, które będziemy definiowali po kolei: dzialanie_7_clicked(), dzialanie_8_clicked(), dzialanie_9_clicked(), dzialanie_dzielenie_clicked() , dzialanie_4_clicked(), dzialanie_

5_clicked(), dzialanie_6_clicked(), d z i a l a n i e _ m n o z e n i e _ c l i c k e d ( ) ,

dzialanie_1_clicked(), dzialanie_

2_clicked(), dzialanie_3_clicked(), dzialanie_odejmowanie_clicked() , dzialanie_0_clicked(), dzialanie_

separator_dziesietny(), dzialanie_

rownosc_clicked(), dzialanie_dodawanie_clicked(). Po zdefiniowaniu wszystkich przycisków możemy wyjść z trybu edycji slo-tów i sygnałów, wybierając z menu Eclipse Qt-Designer>Editor Mode>Edit Widgets.

UWAGA: aby usunąć definicję sygnału i slo-tu z formatki, należy kliknąć na linii, a następ-nie z menu kontekstowego wybrać opcję delete.

Zdefiniowaliśmy sygnały i sloty, ale to nie wszystko, program sam nie będzie działał. Musimy teraz dodać zdefiniowane sloty do pliku kalkulator.h. W tym celu w klasie Kal-kulator tworzymy wpis private slots: a na-stępnie dodajemy nasze sloty jak poniżej:

private slots:

void_dzialanie_ce_clicked();

void_dzialanie_7_clicked();

na Listingu 1 zamieściłem definicje.Teraz musimy zdefiniować nasze nowe

metody (sloty) w pliku kalkulator.cpp, przy-kładowo dla slotu dzialanie_ce_clicked() wykonujemy taki wpis:

void Kalkulator::dzialanie_ce_clicked()

{

ui.wynik->setText('0');

}

Na Listingu 2 przedstawiłem całą funkcjo-nalność aplikacji. Kalkulator jest bardzo pro-

sty, ale chodziło głównie o przybliżenie pod-stawowych funkcji Qt. Listing jest opisa-ny. Warto zwrócić uwagę na klasę QMessa-geBox, dzięki której wyświetlamy dwa okna informacyjne O programie oraz Błąd dziele-nie przez 0! (Rysunek 8 i 9). Ciekawą meto-dą jest również clipboard, która przynależy do klasy QClipboard, dzięki niej stworzymy możliwość kopiowania wyniku kalkulatora do pamięci komputera (schowka).

PodsumowanieNa przykładzie bardzo prostej aplikacji – kal-kulator z podstawowymi działaniami arytme-tycznymi – przedstawiłem w skrócie ogrom-ne możliwości biblioteki Qt w połączeniu z C++. Jeśli chcesz, możesz rozbudować kal-kulator o nowe funkcje – jak np. sprawdzanie liczby pod kątem separatora dziesiętnego, tak aby nie było możliwości dodawania dwóch kropek obok siebie itp., możesz również za-blokować możliwość dodawania wielu wyra-żeń arytmetycznych obok siebie – wszystko zależy od Twojej wyobraźni, chęci poznania wiedzy i nauczenia się czegoś nowego. W ar-tykule nie została przedstawiona jeszcze jed-na ważna cecha Qt, jakim są okna dialogowe – tworzenie takich okien jest naprawdę bar-dzo proste, a wywołanie okienka dialogowego wiąże się ze zdefiniowaniem odpowiedniego połączenia sygnału i slotu. Jeśli temat Qt zain-teresował Cię, możesz wrócić do archiwalne-go numeru tego czasopisma z kwietnia 2009 r., jest tam obszerny artykuł na temat Qt. Nie-stety na polskim rynku nie ma żadnej dobrej książki o Qt. Dla ułatwienia w tabelce zamie-ściłem kilka przydatnych adresów.

ŁUKASZ KLEJNBERGŁukasz Klejnberg jest właścicielem �rmy Ideal So-lutions zajmującej się projektowaniem aplikacji internetowych, jak i komputerowych. Kompute-rami zajmuje się od roku 1989. W zawodzie pro-gramisty pracuje od 2000 roku.Kontakt z autorem: [email protected]

Rysunek 7. De�nicja sygnału i slotu dla przycisku CE

Rysunek 8. Okienko wywołane przez QMessageBox::warning()

Rysunek 9. Okienko wywołane przez QMessageBox::information()

W Sieci

• http://qt.nokia.com – serwis www biblioteki Qt;• http://qt.nokia.com/doc/4.5/index.html – dokumentacja biblioteki Qt;• http://qt.nokia.com/4.4/tutorials-tutorial.html – przykładowe tutoriale;• http://cartan.cas.suffolk.edu/oopdocbook/opensource/ – książka w wersji online – Intro-

duction to Design Patterns in C++ with Qt4;• http://www.pdf-search-engine.com/qt4-pdf.html – różne przydatne pliki w pdf dotyczące

biblioteki Qt.

Page 77: SDJ_05_2010_PL
Page 78: SDJ_05_2010_PL

05/201078

Efektywność pracyNa kiedy?

www.sdjournal.org 79

W niektórych firmach panuje zwy-czaj, że menadżer/dyrektor/prezes od czasu do czasu przechadza się

pomiędzy pracującymi programistami i dopy-tuje ich o postęp prac. Najczęściej otrzymu-je odpowiedź w stylu: Nie można wywołać me-tod serwisu, jest też trochę problemów z dostaniem się do sesji tamtego portletu... Z punktu widzenia programisty nie ma nic bardziej irytującego niż opowiadanie o swojej pracy komuś, kto nie jest w stanie zrozumieć większości interesujących spraw i problemów, o których mowa. Nato-miast menadżer chce się jedynie dowiedzieć, ja-ki jest postęp prac. Kłopot w tym, że programi-sta często tego nie wie.

Strata czasuNie ma absolutnie niczyjej winy w tym, że za-dania programistyczne są zazwyczaj ciekawe. Nie ma też niczyjej winy w tym, że programi-ści zazwyczaj pasjonują się swoją pracą. Pomysł na zaimplementowanie jakiejś funkcjonalności jest już sam w sobie tak fascynujący, że aż chce się od razu zacząć programować.

Jeśli pierwszą czynnością, od której zdarza nam się zaczynać programowanie, jest progra-mowanie, to jesteśmy w nie lada tarapatach. Nie ma nic złego w budowaniu zrębów syste-mu, aby określić wstępny jego kształt. Jednak tego typu zręby i prototypy mają złośliwy zwy-

czaj przedostawania się do środowiska produk-cyjnego. Później już nikt nie ma czasu na popra-wianie działających rozwiązań ad hoc. Nikt też ostatecznie nie pamięta, na jakich założeniach były oparte – lepiej zatem ich nie dotykać.

Część programistów uważa czynność plano-wania swojej pracy za niepotrzebną stratę cza-su. Twierdzą, że zadania programistyczne są tak oczywiste, że można od razu przystąpić do im-plementacji. Z pewnością za takim przekona-niem kryją się pewne osobiste doświadczenia i mogą one dawać pomyślne rezultaty. Naszym zdaniem w większości przypadków świadome planowanie jest bardziej użyteczne niż jego brak.

Wyobraź sobie, że rozpoczynasz pracę nad nowym zadaniem. Na pierwszy rzut oka wyda-je się proste, więc bez większych przygotowań implementujesz kolejne elementy. Po dwóch godzinach utkasz na jakiejś bardziej złożonej kwestii, rozwiązujesz ją i znów brniesz dalej. Po jakimś czasie znów zatrzymujesz się na na pew-nym istotnym szczególe. Działanie w tym try-bie skutkuje jednym z największych wrogów efektywnej pracy – rozproszeniem. Nie bez powodu metodyki z nurtu Agile zalecają stały rytm pracy, stałą długość iteracji, stałą ilość go-dzin przepracowanych w ciągu dnia. Gdy prze-rywasz programowanie z powodu nieprzewi-dzianych trudności, gubisz rytm, a jego odzy-skanie pochłania sporo energii. Żeby kontynu-ować, musisz rozwiązać napotkane problemy i zastanowić się nad następnymi krokami. Oto sedno całej sprawy: nawet jeśli nie planujesz, to planujesz – lokalnie! Opracowujesz zadanie w wąskim kontekście i zaczynasz programować. Gdy już wykonasz te kilka przewidzianych kro-ków, zatrzymujesz się, żeby zastanowić się nad

tym, co robić dalej. Taka praca przypomina tro-chę jazdę samochodem z uszkodzonym prze-wodem paliwowym: raz jedzie się płynnie, in-nym razem samochód gaśnie co chwilę. Rzadko kiedy jesteś pewien, co stanie się za chwilę.

Skoro przed planowaniem (w jakiejkolwiek formie) nie ma ucieczki, to korzystniej jest robić to świadomie, aby wykonywanie zadania prze-biegało zgodnie z naszymi intencjami. Punk-tem wyjścia dla tworzenia planu jest zadanie zdekomponowane na poszczególne kroki oraz oszacowane (o technikach z tym związanych pi-saliśmy w numerze 4/2010).

TerminyBez względu na to, ile rzeczy można by jesz-cze ulepszyć, każde zadanie trzeba będzie kie-dyś skończyć. Moment oddania zadania zazwy-czaj jest narzucony odgórnie i najczęściej jedyna rzecz, na jaką mamy wpływ, to sposób pracy nad zadaniem. Możemy programować dzień i noc, rozpoczynając w ostatniej lub sensownie rozło-żyć prace w czasie. Cały kunszt dobrego plano-wania zawiera się w tej jednej wytycznej: wyko-nać zlecone prace na określony termin i nie za-programować się przy tym na śmierć.

Zakładamy, że z szacowań wynika, iż narzu-cony termin ukończenia zadania jest w ogóle możliwy do osiągnięcia. Gdyby w programo-waniu wszystko było przewidywalne, to mógł-byś planować prace w odniesieniu do terminu zakończenia zadania. Z pewnością nie jeden raz przekonałeś się, że dość często sprawy przybie-rają niespodziewany obrót z powodu różnego rodzaju zaskakujących okoliczności. Korzyst-nie jest zarezerwować pewien bufor czasowy na wypadek tych właśnie okoliczności. Z tego względu pierwszym krokiem jest wyznaczenie wewnętrznego terminu zakończenia zadania. Termin ten przypada przed ostatecznym termi-nem zakończenia, tworząc bufor bezpieczeń-stwa. Na początek możesz przyjąć, że jego dłu-gość będzie wynosić 10% szacowanego czasu na

Na kiedy?

Z opracowywaniem zadań programistycznych wiążą się dwa kluczowe pytania: ile to zajmie? oraz na kiedy? Pierwsze pytanie dotyczy szacowania zadań, czyli określania w jednostkach czasu, jak długo potrwają prace. Drugie pytanie dotyczy planowania – czyli osadzania oszacowanych czynności w kalendarzu. W artykule zajmujemy się tym drugim pytaniem.

Dowiesz się:• Dlaczego warto planować swoją pracę i w

jaki sposób to robić.

Powinieneś wiedzieć:• Tylko to, co już wiesz.

Poziom trudności

Planowanie zadań programistycznych

Page 79: SDJ_05_2010_PL

05/201078

Efektywność pracyNa kiedy?

www.sdjournal.org 79

wykonanie zadania. Z wewnętrznego terminu jesteś odpowiedzialny sam przed sobą. Prakty-kowanie wyznaczania tego terminu przyniesie Ci następujące korzyści:

• przeciwdziałasz konsekwencjom prawa Parkinsona, które mówi, że zadanie zaj-mie co najmniej tyle czasu, ile zostało nań przeznaczona; w tym przypadku próbu-jesz „ściskać” terminy;

• ograniczając czas na wykonanie zadania, kierujesz swoją uwagę na to, co jest w nim najważniejsze i absolutnie konieczne; uni-kasz niekończącego się udoskonalania ko-du (nie mylić z refaktoryzacją!);

Punkty pośredniePewnie jechałeś kiedyś autostradą. Prosta, mo-notonna droga wydawała się nie mieć końca. Gdyby nie oznaczenia zjazdów, to po pewnym czasie straciłbyś orientację. Z upływem czasu coraz trudniej byłoby Ci stwierdzić, jak dłu-go jedziesz, ile kilometrów zostało jeszcze do końca podróży i gdzie właściwie się znajdujesz. Dzieje się tak, ponieważ brakuje punktów od-niesienia, które informowałyby Cię o kolejnych etapach wycieczki.

W innym obrazku jedziesz krętą miejską dro-gą. Mijasz znajome budynki i skrzyżowania. Być może w myślach odliczasz kolejne etapy: kino, centrum handlowe, rondo, szpital. Czy taka jazda nie upływa znacznie szybciej niż podróżowanie przez okolicę, w której brak jest jakichkolwiek punktów odniesienia? To oczywiście złudze-nie, gdyż czas (przynajmniej na Ziemi) jest bez-względny i dla każdego upływa w ten sam spo-sób. Jedyna różnica pomiędzy opisanymi scena-mi polega na tym, że w drugiej jest na co czekać, a w pierwszej nie. Jadąc drugą trasą, miałeś nie-świadomie obrane punkty pośrednie, dzięki którym w dowolnym momencie wiedziałeś, na jakim etapie jesteś i kiedy osiągniesz cel.

Ta sama zasada obowiązuje podczas plano-wania zadań. Wyznaczenie dobrego celu im-plementacji to część sukcesu. Punkty pośred-nie prac pomogą Ci efektywnej go osiągnąć. Być może poniższe korzyści zachęcą Cię do tej praktyki:

• Zmierzasz do celu jak po nitce, odhaczając kolejne osiągnięte punkty pośrednie;

• Zawsze wiesz, na którym etapie obecnie się znajdujesz;

• Zawsze wiesz, co jeszcze musisz zrobić, aby zakończyć zadanie;

• Wracając do zadania po dłuższej przerwie, szybciej wdrożysz się w temat.

W kalendarzuElementem, który spina cały Twój pracowicie przygotowany plan, będzie kalendarz. Konkret-na forma kalendarza jest bez znaczenia, wybierz taką, która najbardziej Ci odpowiada. Gdybyś jednak szukał inspiracji, to polecamy wydruk miesięczny z darmowego kalendarza Sunbird.

Ważnym zadaniem kalendarza jest uwzględ-nianie ilości czasu, które możesz poświęcić na pracę nad planowanymi zadaniami. Jeśli jed-nym z Twoich dodatkowych obowiązków jest reagowanie na błędy właśnie wdrożonego sys-temu i zajmuje Ci to 50% czasu pracy, to czas wykonywania zadań wydłuża się. Na przykład z szacowania wynika, że zadanie powinno za-jąć jeden osobodzień, lecz Twoje obowiązki sprawiają, że będziesz wykonywał je przez dwa dni. Teraz dokładnie widzimy ogromną różni-cę pomiędzy pytaniem ile to zajmie? a na kie-dy zrobisz?

Kalendarz możesz przygotować w następu-jący sposób:

• wykreśl dni wolne od pracy i urlopy;• zaznacz termin oddania wykonywanego

zadania;

• zaznacz termin wewnętrzny;• oznacz początek zadania oraz punkty po-

średnie;• ilość dni, które zaznaczasz wynika z szaco-

wania;• uwzględnij ilość czasu, który możesz po-

święcić na zadanie;• postępuj podobnie przy każdym zadaniu,

używając różnych kolorów.

Przygotowany i opublikowany w widocznym miejscu kalendarz jest Twoją mapą, dzięki któ-rej sprawnie poruszasz się w kierunku ukoń-czenia zleconych funkcjonalności. Jeśli teraz Twój menadżer lub prezes zechce Cię odwie-dzić i zapytać o postęp prac, możesz oszczę-dzić mu szczegółów, których pięknem i tak się nie zachwyci. Po prostu sięgasz do kalendarza i czytasz to, co jest tam napisane.

Przegląd zadaniaW omawianym procesie planowania i wykony-wania zadań brakuje ostatniej domykającej rze-czy – sprzężenia zwrotnego. Żadne szacowa-nie i planowanie nie wniesie nic konkretnego do Twojego warsztatu, jeśli nie zapewnisz sobie możliwie szybkiej pętli uczenia. Jak w opisywa-nych praktykach zazwyczaj sugerowaliśmy kon-kretne rozwiązania, a wybór pozostawialiśmy tobie, tak teraz z całym naciskiem podkreślamy, że absolutnie musisz uczyć się na własnych błę-dach. Bez wyciągania wniosków i wdrażania ich w życie, nowe techniki na nic się nie przydadzą, nie poprawią Twojej efektywności.

Prostym sposobem na uruchomienie pę-tli uczenia jest zarezerwowanie kilku chwil na przeprowadzenie przeglądu zakończonego za-dania (ang. after action review). Podczas przeglą-du zadaj sobie następujące pytania:

• Czy szacowania zostały utrzymane? Jeśli nie, to dlaczego?

• Czy bufor bezpieczeństwa został właści-wie dobrany?

• Jakie problemy wystąpiły i jak im zaradzić w przyszłości?

• Czego nowego się nauczyłem?

Wnioski z tak przeprowadzonego przeglądu zastosuj od razu podczas wykonywania na-stępnego zadania. Możesz również podzielić się nimi i przedyskutować z resztą zespołu.Rysunek 1. Proces planowego wykonywania zadania. T – termin zakończenia zadania, Tw – termin

wewnętrzny, P1, P2 – punkty pośrednie

Planowanie zadania

• Na podstawie szacowań wyznacz termin oddania zadania – to termin, który zobowiązu-jesz się dotrzymać przed użytkownikiem;

• Ustal swój wewnętrzny termin zakończenia zadania – to termin, który zobowiązujesz się dotrzymać przed samym sobą, rezerwując bufor na nieprzewidziane okoliczności;

• Im bliżej wyznaczonych terminów, tym uważniej przyglądaj się postępowi prac;• Im większa presja czasu, tym częściej zastanawiaj się, czy to, co robisz, jest tym, co powi-

nieneś robić;

MICHAŁ BARTYZEL, MARIUSZ SIERACZKIEWICZTrenerzy i konsultanci w �rmie BNS IT. Badają i roz-wijają metody psychologii programowania, po-magające programistom lepiej wykonywać ich pracę. Na co dzień autorzy zajmują się zwiększa-niem efektywności programistów poprzez szkole-nia, warsztaty oraz coaching i trening. Kontakt z autorami: [email protected], [email protected]

Page 80: SDJ_05_2010_PL

KLUB PROOferta skierowana dla firm

Jeżeli Twoja firma jest prenumeratorem Software Developer’s Journal za comiesięczną dopłatą 50 PLN +VAT możesz dodatkowo otrzymać reklamę.

Wystarczy tylko, aby profil Twojej firmy pokrywał się z naszym magazynem.

Wyślij do nas: logo firmy, dane kontaktowe i informację o firmie

Reklama przez 12 kolejnych numerów tylko za 600 PLN +VAT.

Jeżeli nie posiadasz jeszcze prenumeraty możesz ją zamówić w atrakcyjnej cenie.

Dla nowych prenumeratorów specjalna oferta – 690 PLN.

Future ProcessingFuture Processing to dynamiczna firma technolo-giczna działająca na globalnym rynku oprogramo-wania. Jesteśmy zespołem wysokiej klasy specja-listów posiadających wiedzę i doświadczenie nie-zbędne do realizacji ambitnych projektów informa-tycznych. Jeśli programowanie to Twoja pasja do-łącz do nas! (możliwość pracy zdalnej).

http://www.future-processing.pl

Skontaktuj się z nami:tel. +48 22 877 20 80fax: +48 22 877 20 [email protected]

Kei.plKei.pl działa na rynku usług hostingowych od 2000 roku. Do naszych zadowolonych Klientów z du-mą możemy zaliczyć wiele przedsiębiorstw sekto-ra MSP, instytucji oraz osób prywatnych. W ofer-cie Kei.pl znajdują się pakiety hostingowe, a także usługi dla wymagających Użytkowników – platfor-my e-Biznes oraz serwery fizyczne.

http://www.kei.pl

Opera SoftwareOpera Software’s vision is to deliver the best In-ternet experience on any device. We are offering browser for PC/desktops and embedded pro-ducts that operates across devices, platforms and operating systems. Our browser can deliver a faster, more stable and flexible Internet expe-rience than its competitors.

http://www.opera.com

Architektury systemów ITTwórca frameworków JUVE i serwera aplikacji AVAX oferuje usługi, doradztwo, rozwiązania do tworzenia nowoczesnych, dużych systemów i roz-wiązań informatycznych/internetowych, integrujące architektury ery post-J2EE/.NET, wykorzystujące MDD/MDA dla dziedzin – bankowość, telekomuni-kacja, handel, e-commerce, ERP/Workflow/CRM, rozwiązania internetowe, portalowe.www.mpsystem.com [email protected]

WSISiZ w WarszawieINFORMATYKA ZARZĄDZANIEstudia stopnia I i II (stacjonarne i niestacjonar-ne) specjalności: inżynierskie, magisterskie i licencjackie. Szczegółowe plany studiów, opi-sy poszczególnych specjalności – zapraszamy na stronę uczelni.

http://www.wit.edu.pl

Playsoft Playsoft jako lider portowania aplikacji na plat-formy mobilne wciąż powiększa bazę swo-ich klientów: EA Mobile, Sega, THQ, Kona-mi. W ramach rozszerzania swojej działalno-ści, poszukujemy doświadczonego programi-sty, który byłby odpowiedzialny za tworzenie aplikacji na platformy Iphone, Windows Mobi-le, Android.http://www.playsoft.fr

Page 81: SDJ_05_2010_PL

KLUB PRO

TTS Company Sp. z o.o.Sprzedaż i dystrybucja oprogramowania komputero-wego. Import programów na zamówienie. Ponad 200 producentów w standardowej ofercie. Chcesz kupić oprogramowanie i nie możesz znaleźć polskiego do-stawcy? Skontaktuj się z nami – sprowadzimy nawet pojedyncze licencje.

http://www.OprogramowanieKomputerowe.pl

Softline rozwiązania mobilneWiodący producent systemów mobilnych, do-stawca aplikacji użytkowych dla biznesu (Sym-bian OS, Windows Mobile, J2ME ) zaprasza do współpracy. Zostań naszym partnerem. Dołącz do zespołu.

http://www.softline.com.pl

INFOTEX SP.J Śmietanowski i Wsp.Dystrybutor XP Unlimited – Serwer Terminali dla Windows XP i VISTA. Program umożliwia łącze-nie się z dowolnego klienta Windows, Linux z wy-korzystaniem protokołu RDP. Cena wersji Classic dla 5 użytkowników - 165€, dla nieograniczonej liczby - 235€. Ponadto oferujemy opiekę serwiso-wą i aplikacje internetowe na zamówienie.

http://www.infotex.com.pl

Proximetry Poland Sp. z o.o.Proximetry Poland Sp. z o.o. jest polskim od-działem amerykańskiej firmy Proximetry Inc. – dostawcy systemów zarządzania sieciami bez-przewodowymi opartymi na technologiach WiFi i WiMAX. Naszą misją jest dostarczenie klien-tom rozwiązań poprawiających jakość usług (QoS) dostarczanych drogą radiową. Dołącz do najlepszych i zostań członkiem naszej ekipy!

http://www.proximetry.com

Systemy bankowe, ISOF HEUTHES istnieje na rynku od 1989 r. Obok systemów informatycznych dla banków, ofe-ruje nowoczesne oprogramowanie do obsługi firm. System ISOF jest udostępniany klientom w trybie SaaS lub licencji. Pracuje na platfor-mie Linux i zawiera m.in. takie moduły jak CRM, DMS, Magazyn, Sprzedaż, Logistyka oraz Rachunkowość. http://www.isof.pl

Volantis jest uznanym na całym świecie dostawcą rozwiązań mobil-nych dla firm udostępniających informacje oraz dane przez Internet (operatorów telefonii komórkowej, stacji tv, czasopism internetowych). Dzięki zasadzie „stwórz raz, uruchamiaj gdziekolwiek” nasze rozwią-zania zmniejszają złożoność, koszt i czas dostarczenia na rynek ser-wisów i aplikacji. Volantis jest członkiem organizacji W3C, a naszymi klientami są światowi potentaci telekomunikacyjni.

Page 82: SDJ_05_2010_PL

zajawka

Szanowni Czytelnicy, Niedawno obchodziliśmy 15-lecie SDJ. 15 lat największego polskiego pisma dla programistów. Przez ten czas zbudowaliśmy oddane grono czytelników i markę wśród największych firm branżowych na rynku. Nie chce-my jednak stać w miejscu. Świat się zmienia a wraz z nim media. Dlatego chcielibyśmy z przyjemnością poin-formować, że Software Developer’s Journal staje w awangardzie światówych mediów. Otwieramy się na przy-szłośc, a ...„Przyszłośc leży on-line”. Od następnego numeru ( 6/2010 ) Software Developer’s Journal będzie dostępny tylko w wersji elektro-nicznej. Co ważniejsze będzie on pismem całkowicie darmowym! Jesteśmy przekonani, że zmiana formatu wydawania magazynu, a także szersza dostępność sprawi, że SDJ stanie się jeszcze bardziej opiniotwórczym medium o programowaniu w Polsce. Nadal będziemy prezentować aktualną, a także sprawdzoną wiedzę z dziedziny programowania. Współpracując z najwiekszymi firmami na rynku, a także pasjonatami programo-wania, uczelniami wyższymi itp chcemy dostarczac naszym czytelnikom wiedzy potrzebnej do pracy, a także dalszego rozwoju osobistego. Pierwszy elektroniczny numer SDJ pojawi się ostatniego dnia kwietnia. Każdy następny będzie dostępny do pobrania ostatniego dnia miesiąca. Jednocześnie nie zapominamy o materiałach dodatkowych do artku-łów. Wszelkie aplikacje, kody a także materiały video będą dostępne na naszej stronie www. Mam głęboką nadzieję, że ta zmiana przypadnie Państwu do gustu. Chcemy aby SDJ jak najbardziej odpo-wiadał Państwa oczekiwaniom, dlatego będziemy wdzięczni za wszelkie sugestie i uwagi dotyczące naszego, wspólnego magazynu.

Redaktor naczelny SDJŁukasz Łopuszański

05/201082

Zajawka

Page 83: SDJ_05_2010_PL
Page 84: SDJ_05_2010_PL