České vysoké učení technické v Praze Fakulta elektrotechnická Katedra počítačové grafiky a interakce
Bakalářská práce
Hodnocení restaurací Petr Pokorný
Vedoucí práce: Ing. Radek Dobiáš
Studijní program: Softwarové technologie a management Obor: Web a multimédia Leden 2010
ii
Poděkování Na tomto místě bych rád poděkoval svému vedoucímu Ing. Radku Dobiášovi za návrh zajímavého tématu práce, přátelský přístup a ponechanou volnost při tvorbě projektu.
iii
iv
Prohlášení Prohlašuji, že jsem práci vypracoval samostatně a použil jsem pouze podklady uvedené v přiloženém seznamu. Nemám závažný důvod proti užívání tohoto školního díla ve smyslu §60 Zákona č. 121/2000 Sb., o právu autorském, o právech souvisejících s právem autorským a o změnách některých zákoníků (autorský zákon).
V Praze dne 12. 12. 2009
...................................................... v
vi
Abstract This bachelor thesis deals with the design and implementation of a web application that enables users to search for and rate restaurants and other catering establishments. The thesis first explores existing websites that offer similar functionality. Then, the application design is described, followed by the main part, which focuses on the implementation using Django web framework and jQuery JavaScript library.
Abstrakt Tato bakalářská práce se zabývá návrhem a implementací webové aplikace, umožňující uživatelům vyhledávat a hodnotit restaurace i jiná stravovací zařízení. Práce nejprve zkoumá existující řešení, která nabízí podobnou funkcionalitu. Poté je popsán návrh aplikace, následovaný stěžejní částí, která se zaměřuje na implementaci pomocí webového frameworku Django a JavaScriptové knihovny jQuery.
vii
viii
Obsah 1
Úvod................................................................................................................................................................................ 1
2
Popis problému, specifikace cíle ......................................................................................................................... 3 2.1
2.1.1
Funkční požadavky ............................................................................................................................... 3
2.1.2
Nefunkční požadavky ........................................................................................................................... 3
2.2
3
Požadavky na systém ..................................................................................................................................... 3
Srovnání existujících řešení ........................................................................................................................ 4
2.2.1
České hospůdky ...................................................................................................................................... 4
2.2.2
E-hospody ................................................................................................................................................. 5
2.2.3
Top Pub ...................................................................................................................................................... 5
2.2.4
Nelso............................................................................................................................................................ 6
Použité technologie .................................................................................................................................................. 7 3.1
Django .................................................................................................................................................................. 7
3.1.1 3.2
Pinax ..................................................................................................................................................................... 8
3.3
PostgreSQL ......................................................................................................................................................... 8
3.4
jQuery ................................................................................................................................................................... 8
3.4.1
4
Python ........................................................................................................................................................ 8
AJAX ............................................................................................................................................................. 9
3.5
Google Maps API .............................................................................................................................................. 9
3.6
Apache HTTP Server ...................................................................................................................................... 9
Analýza a návrh řešení.......................................................................................................................................... 11 4.1
Doménový model........................................................................................................................................... 11
4.2
Případy užití .................................................................................................................................................... 13
4.3
Architektura..................................................................................................................................................... 14
4.3.1 4.4
MTV ........................................................................................................................................................... 14
Návrh User experience ................................................................................................................................ 14
4.4.1
Informační architektura.................................................................................................................... 14
4.4.2
Uživatelské rozhraní........................................................................................................................... 15
4.4.3
Grafický návrh ....................................................................................................................................... 17 ix
5
Realizace ..................................................................................................................................................................... 19 5.1
Krátké porovnání vývojových prostředí .............................................................................................. 19
5.1.1
NetBeans ................................................................................................................................................. 19
5.1.2
Eclipse (Aptana) ................................................................................................................................... 19
5.1.3
Komodo .................................................................................................................................................... 19
5.2
Verzování .......................................................................................................................................................... 20
5.3
Django aplikace .............................................................................................................................................. 20
5.3.1
Facilities................................................................................................................................................... 20
5.3.2
Locations ................................................................................................................................................. 22
5.3.3
Reviews .................................................................................................................................................... 24
5.3.4
Photos ....................................................................................................................................................... 25
5.3.5
Users.......................................................................................................................................................... 25
5.3.6
Common................................................................................................................................................... 26
5.4
JavaScriptové moduly .................................................................................................................................. 26
5.4.1
jQuery Popup ......................................................................................................................................... 26
5.4.2
Photos ....................................................................................................................................................... 28
5.4.3
jQuery Collapser ................................................................................................................................... 30
5.4.4
Reviews .................................................................................................................................................... 31
5.4.5
Products................................................................................................................................................... 31
5.4.6
jQuery Layout Manager ..................................................................................................................... 31
5.4.7
Maps .......................................................................................................................................................... 32
5.4.8
List.............................................................................................................................................................. 33
5.4.9
Status ........................................................................................................................................................ 34
5.5
Optimalizace odezvy .................................................................................................................................... 34
5.5.1
Načítání stránky ................................................................................................................................... 34
5.5.2
JavaScript ................................................................................................................................................ 35
5.6
Optimalizace pro vyhledávače ................................................................................................................. 36
5.6.1 5.7
Nasazení ............................................................................................................................................................ 36
5.7.1 5.8
x
Sitemaps .................................................................................................................................................. 36 Automatizace nasazení ...................................................................................................................... 37
Ladění výkonu serverové části ................................................................................................................ 37
5.8.1
Databáze .................................................................................................................................................. 37
5.8.2
Cache ......................................................................................................................................................... 38
5.8.3 5.9
6
7
Škálování ................................................................................................................................................. 38
Bezpečnost ....................................................................................................................................................... 38
5.9.1
SQL injection .......................................................................................................................................... 38
5.9.2
XSS.............................................................................................................................................................. 39
5.9.3
CSRF .......................................................................................................................................................... 39
Testování .................................................................................................................................................................... 40 6.1.1
Testování funkčnosti .......................................................................................................................... 40
6.1.2
Profilování .............................................................................................................................................. 40
6.1.3
Použitelnost ........................................................................................................................................... 40
Závěr ............................................................................................................................................................................. 42 7.1
Pokračování vývoje....................................................................................................................................... 42
7.1.1
Jiná použití vzniklého systému ...................................................................................................... 43
8
Seznam použitých zkratek .................................................................................................................................. 44
9
Použité zdroje ........................................................................................................................................................... 45
xi
xii
1 Úvod Tato práce pojednává o tvorbě webové aplikace sloužící k vkládání, vyhledávání a hodnocení restaurací i jiných stravovacích zařízení. Čtenáře seznámí s kompletním procesem vzniku systému, od návrhu, přes implementaci pomocí moderních technologií používaných v současnosti pro vývoj webových aplikací, až po nasazení v produkčním prostředí. V první části se podíváme na bližší specifikaci funkčnosti vytvářeného systému a také na srovnání několika existujících webů, které se zabývají obdobnou tématikou. Při analýze existujících řešení jsem se soustředil na identifikaci jejich hlavních nedostatků, abych se jich při návrhu projektu mohl snáze vyvarovat. Proces návrhu je pak popsán v kapitole Analýza a návrh řešení, spolu s detailnějším popisem fungování a architektury systému. Základem systému je poměrně mladý, rychle se rozvíjející webový open-source framework Django, postavený na jazyce Python. Ten zajišťuje zejména serverovou část aplikace. Velká část práce se však zaměřuje i na tvorbu klientské části, tedy uživatelského rozhraní ve webovém prohlížeči, kde je základním kamenem JavaScriptová knihovna jQuery. Systém také hojně využívá zdarma dostupných webových služeb společnosti Google týkajících se map a geokódování. Tyto i mnohé další použité technologie jsou podrobněji rozebrány ve 3. kapitole. Vytvořený systém splňuje některé z hlavních vlastností charakteristických pro projekty označované jako Web 2.0, zejména se jedná o obsah tvořený uživateli, bohaté uživatelské rozhraní, folksonomie1 či propojení s jinými webovými službami. V části Realizace jsou popsány a ilustrovány na konkrétních příkladech různé techniky a postupy, používané k implementaci těchto vlastností. Kromě samotného programování systému se práce také dotýká řady dalších témat, přímo souvisejících s vývojem webů. Mezi ty patří například optimalizace rychlosti webových stránek, optimalizace pro vyhledávače nebo bezpečnost webových aplikací a způsoby obrany proti nejčastějším typům útoků. Závěr práce se věnuje testování aplikace z různých hledisek a nastínění dalšího možného rozvoje.
1
Systém kategorizace na základě popisků přiřazených uživateli, namísto pevně dané struktury.
1
2
2 Popis problému, specifikace cíle Cílem projektu je vytvoření informačního systému s webovým rozhraním, který bude umožňovat vkládání, vyhledávání a hodnocení restaurací i jiných stravovacích či zábavních zařízení. Systém musí umožňovat detailní hodnocení těchto zařízení, včetně možnosti ohodnotit i konkrétní produkt (např. jídlo v restauraci), nahrávání fotek, práci s mapami; zároveň však musí být i snadno ovladatelný.
2.1 Požadavky na systém Požadavky na systém byly vytvořeny na základě schůzek se zadavatelem. Ten však neměl naprosto přesnou představu o výsledném produktu, proto jsou některé požadavky jen velmi obecné. Výsledná funkcionalita systému vychází kromě nich i z vlastního návrhu a poučení se z nedostatků nalezených v existujících řešeních.
2.1.1 Funkční požadavky Webové rozhraní Rozlišování uživatelských rolí Možnost registrace Možnost přihlášení pomocí OpenID2 Vytváření stravovacích zařízení různých typů Jejich snadné vyhledávání Zobrazení na mapě Detailní hodnocení Vkládání obrázků Internacionalizace
2.1.2 Nefunkční požadavky Snadná rozšiřitelnost Dobrá udržovatelnost Dodržování standardů W3C3
2 Otevřený standard pro autentizaci uživatelů, umožňující přihlášení k různým službám na základě jedné digitální identity [30] 3 World Wide Web Consortiun – mezinárodní konsorcium vyvíjející otevřené standardy pro web a webové aplikace
3
2.2 Srovnání existujících řešení Součástí zadání je také nalezení a prozkoumání již existujících dostupných řešení, které alespoň částečně odpovídají zadaným požadavkům. Podařilo se mi jich nalézt celou řadu, z níž většina si byla velmi podobná. Žádné řešení však nenabízelo nějaký komplexnější systém hodnocení. Dále uvedu několik příkladů webů, na které jsem při hledání narazil.
2.2.1 České hospůdky www.ceske-hospudky.cz Prototyp většiny webů tohoto typu. Uživatelsky nepříliš přívětivé – stránka je velmi úzká (800 pixelů), písmo je téměř na hranici čitelnosti (10px) a lze zde vidět různé další prohřešky proti správnému provedení uživatelského rozhraní (tlačítko vypadá stejně jako nadpis, některé odkazy nejsou rozlišeny od běžného textu). Vyhledávat lze podle pevně daných typů podniků, výběrem ze seznamu velkých měst, okresů a částí Prahy, nebo zadáním názvu podniku či ulice. Typ, vlastnosti a nabídku podniku lze Obrázek 2-1: www.ceske-hospudky.cz nastavit celkem 2804 zaškrtávacími políčky, podle kterých nicméně nelze vyhledávat. Tyto položky jsou pak všechny vypsány v detailech podniku, takže pokud vkladatel nějakou službu či produkt zapomene zaškrtnout nebo nemá čas vše projít, je tato položka automaticky prezentována jako nenabízená. Hodnotit lze ve čtyřech pevně daných kategoriích šesti stupni a komentářem. Nahrát fotky k podniku může pouze uživatel, který ho do systému vložil, případně byl ověřen jako majitel.
Tip: pomocí rozšíření prohlížeče Firefox – Firebug a FireQuery, je možné (mimo jiné) na libovolnou stránku snadno vložit knihovnu jQuery. Zaškrtávací políčka jsem poté v tomto případě mohl snadno spočítat zadáním $(‘input[type=”checkbox”]’).length do konzole JavaScriptu. 4
4
2.2.2 E-hospody www.e-hospody.cz Kopie Českých hospůdek s několika rozdíly. Je zde vylepšený, přehlednější design. Šest hlavních typů podniku má vlastní doménu druhého řádu, podniky spadající do více typů jsou pak zobrazeny na všech webech (hlavním důvodem bude pravděpodobně záměr získat lepší umístění ve vyhledávačích). Je zde již možno vyhledávat podle výše zmíněných zaškrtávacích políček, takže ty už zde mají poněkud větší smysl. Aby se však uživatel k pokročilému vyhledávání dostal, musí nejprve něco vyhledat pomocí jednoduchého formuláře umístěného na všech stránkách, kde lze vyplnit název Obrázek 2-2: www.e-hospody.cz či město. Dále je také možnost vyhledávat pomocí výběru kraje a následně okresu a města, nicméně výsledky pak již nelze dále nijak filtrovat. Dalším bonusem oproti Českým hospůdkám je zobrazení mapky v detailu podniku. Nevýhodou naopak zkomplikování vložení podniku nutností nejprve vytvořit ekonomický subjekt, který podnik provozuje. Hodnocení zde probíhá identicky. Proti svému vzoru je tento web vylepšen i po technické stránce. Nabízí sémantický XHTML kód oproti HTML tabulkám a URL typu /hospudka.php?uid=123456 nahradily pro uživatele i vyhledávače přívětivější adresy, které odpovídají obsahu stránky (/hospoda-na-ruzku).
2.2.3 Top Pub www.toppub.cz Tento web má celkem elegantní a přehledný design a návrhu uživatelského rozhraní toho nelze mnoho vytknout. Lze si zde vybrat z šesti typů podniku a poté vyhledávat podle kraje, okresu a fulltextově v názvu, adrese nebo v poštovním směrovacím čísle. Hodnotí se zde opět v pevně daných kategoriích (tentokrát jich je 5), které jsou stejné pro všechny typy podniku, a komentářem. Je zde ovšem nabídnuto větší rozlišení jednotlivých hodnocení – 0 až 100%. Ovládací prvek je proveden jako posuvník vytvořený pomocí JavaScriptu. U podniků je pak zobrazeno pouze
Obrázek 2-3: www.toppub.cz
5
průměrné hodnocení v jednotlivých kategoriích a vypsány jména a komentáře uživatelů a datum hodnocení. Není tedy vidět kdo jak hodnotil, takže nelze poznat například jestli se hodnocení uživatelů nějak vyvíjí v čase, což může být důležité, například pokud podnik změnil majitele a výrazně se změnil. Hodnocení je sice možné odhadnout z komentáře, nicméně ten není povinný a uživatelé ho často nepíší. Jinak toho tento systém příliš mnoho nenabízí. Chybí zejména fotky. Nejvíce mě však překvapilo, že uživatelům není umožněno přidávat do systému podniky.
2.2.4 Nelso www.nelso.cz Tento web se od ostatních značně liší, jeho záměrem totiž není primárně hodnocení, ale vyhledávání. Mnoha svými vlastnostmi je však pro tuto práci značně relevantní. Není zaměřen pouze na stravovací a zábavní zařízení, ale na libovolné podniky či firmy. Uživatelé sice nemohou definovat vlastní, ale je zde na výběr z 286 typů. Vyhledávání je jednoduché a efektivní. Do pole „Co“ lze zadat název, typ, či jiné vyhledávací slovo a do pole „Kde“ jakýkoliv řetězec určující lokalitu. Výsledky vyhledávání jsou stránkovány po deseti a je zobrazena i mapa s jejich Obrázek 2-4: www.nelso.cz umístěním. Při najetí kurzoru nad jednotlivé vyhledané podniky jsou zvýrazněny jejich značky na mapě. Nicméně toho lze využít pouze u prvních tří až pěti výsledků (pro drtivou většinu uživatelů), při posunutí stránky k dalším již mapa není vidět. Mapa je však k dispozici i na stránce detailu podniku. Hodnotit je zde možné jen velmi jednoduše – stupněm 1 až 5. Navíc hodnocení nikde nejsou zobrazena ani se podle nich nedají řadit výsledky vyhledávání. Je možné, že se zobrazí průměrné hodnocení, až když daný objekt ohodnotí určitý počet uživatelů. Pravděpodobnější však je, že tato funkcionalita nebyla zatím v systému implementována. Nelso dále přihlášeným uživatelům umožňuje k podnikům psát komentáře, nahrávat fotky, přidávat vyhledávací slova a dokonce měnit údaje o firmě. Chvályhodná je možnost přihlášení do systému pomocí OpenID. Zajímavým faktem je, že Nelso je také postaveno na frameworku Django, který zatím není příliš rozšířen. (Zjistil jsem to pokusným zadáním adresy /admin, kde byla výchozí přihlašovací stránka Django administrace.)
6
3 Použité technologie V této kapitole představím jednotlivé technologie, na kterých je celý projekt postaven. Vybrané technologie uvádím před částí zabývající se návrhem systému, protože tyto technologie svými možnostmi návrh do značné míry ovlivňují. Ve skutečnosti fáze návrhu systému a výběru vhodných technologií probíhaly paralelně.
3.1 Django Django je webový aplikační framework napsaný v jazyce Python, který se volně drží architektonického vzoru model-view-controller. Jeho primárním cílem je umožnit snadné vytváření komplexních, databází řízených webových aplikací. Klade důraz na znovu-použitelnost a snadnou připojitelnost komponent, rychlý vývoj a princip DRY – don’t repeat yourself, neopakuj se (někdy také označovaný jako DIE – duplication is evil). [1] Mezi nejdůležitější komponenty systému patří: Object-relational mapper (ORM) – systém pro objektově-relační mapování. Ten se stará o propojení Python objektů s relační databází. Umožňuje definovat doménové objekty pomocí Python tříd a zajišťuje jejich pohodlné vytváření, ukládání, hledání a mazání. Automatické administrační rozhraní. To je vygenerováno podle datového modelu, je vysoce konfigurovatelné a umožňuje celkem pohodlně provádět CRUD (create, retrieve, update and delete – vytvořit, vyvolat, změnit a smazat) operace nad datovým modelem i běžným uživatelům. Elegantní vytváření a mapování URL. Jednotlivé adresy lze snadno definovat pomocí regulárních výrazů. Šablonovací systém. Ten je postaven na principu dědičnosti, což umožňuje maximální flexibilitu, dodržení DRY a slušnou přehlednost šablon. Důraz je také kladen na znemožnění „programování“ v šablonách a vynucení oddělení aplikační logiky a prezentace. Pro prezentační logiku je pak umožněno vytváření vlastních tagů a filtrů, které se v šablonách použijí. Samozřejmě jsou k dispozici také další nástroje, které lze nalézt ve většině webových frameworků a čím se Django stává populárnější, tím více lze nalézt znovupoužitelných aplikací vytvořených uživateli. (Aplikacemi jsou zde nazývány jednotlivé moduly, které dohromady tvoří výsledný projekt.)
7
3.1.1 Python Základem Djanga je jazyk Python [2], který se zde používá kromě samotného programování logiky i na definici modelů a konfiguraci. Django také přejímá jeho filosofii [3]. Jedná se o dynamický interpretovaný jazyk, umožňující objektově orientované, procedurální a v omezené míře i funkcionální a aspektově orientované programování. Klade důraz na krátký a dobře čitelný zdrojový kód. To lze ocenit mimo jiné i v situacích, kdy k nějakému kódu není k dispozici dokumentace. Dobrým pomocníkem při vývoji je také možnost spustit Python v interaktivním režimu. Django nabízí příkaz pro spuštění této interaktivní konzole v proměnném prostředí vytvářeného projektu a umožňuje tak snadné testování vytvářených modelů a funkcí, rychlé zkoušení kódu případně i (hromadnou) úpravu objektů uložených v databázi. S rozšířením iPython [4] je pak k dispozici i barevné zvýrazňování či doplňování.
3.2 Pinax Pinax [5] je kolekcí řady Django aplikací integrovaných ve funkčních projektech, které se dají použít jako výchozí bod při budování webu. Snaží se vyřešit obecné úlohy, které má hodně webů společných a dát tak vývojářům možnost více se soustředit na to, čím se jejich projekt skutečně zabývá. Pro tuto práci jsem využil hlavně části pro práci s uživatelskými účty.
3.3 PostgreSQL Jako databázový systém jsem zvolil PostreSQL [6]. Jedná se o prověřené výkonné a spolehlivé řešení. Nicméně moje aplikace na něm není nijak závislá a lze jí provozovat s libovolným databázovým řešením, které je podporováno Django ORM. Tedy momentálně ještě MySQL, SQLite a Oracle. Preferováno je, aby zvolený databázový systém podporoval transakce a cizí klíče.
3.4 jQuery Django není svázáno s žádným JavaScriptovým frameworkem (tak jako je například Ruby on Rails s Prototype) a nechává tak vývojářům volné ruce ve výběru. Já jsem pro práci s JavaScriptem zvolil knihovnu jQuery [7]. Ta oproti „čistému“ JavaScriptu velmi usnadňuje práci s DOM (document object modelem – objektovou reprezentací XML či HTML dokumentů) – vyhledávání pomocí CSS 3 selektorů, intuitivnější manipulace a práce s atributy. Také umožňuje pohodlnější práci s událostmi, včetně možnosti navěsit události na elementy, které v dokumentu ještě nejsou, ale mohou být v budoucnu vytvořeny. Dále nabízí systém pro snadnou tvorbu animací, které jsou často důležitou součástí
8
uživatelského rozhraní. JQuery taktéž odstiňuje vývojáře od problémů s odlišnou implementací JavaScriptu v různých prohlížečích. Vše toto navíc zvládne s téměř zanedbatelnou velikostí 19KB. Zásadní vlastností je i velmi snadná rozšiřitelnost. Díky tomu, a také velké popularitě této knihovny, již vzniklo velké množství užitečných rozšíření vytvořených uživateli.
3.4.1 AJAX jQuery velmi usnadňuje i asynchronní komunikaci JavaScriptu se serverem (přenášení dat bez nutnosti znovu načíst celou stránku). Tato technika je často označována zkratkou AJAX – asynchronous JavaScript and XML. XML je jeden z formátů, který lze použít pro přenos dat mezi skriptem a serverem. JQuery však podporuje i práci s jinými formáty, ve své aplikaci jsem nejčastěji pro tuto komunikaci použil JSON (JavaScript object notation), jehož výhodou oproti XML je zejména menší velikost, a HTML, pro přenos částí stránky vygenerovaných na serveru. Užitečné jsou též funkce pro serializaci formulářů pro snadné odeslání dat na server.
3.5 Google Maps API Služba poskytovaná firmou Google, která umožňuje komukoli zdarma vložit na vlastní web obdobu Google Maps. Vložená mapa je plně interaktivní a nabízí bohaté programové rozhraní, pomocí kterého ji lze konfigurovat a manipulovat s ní, včetně možnosti přidávat nové vrstvy. Kromě map nabízí Google i služby provádějící geokódování – převod adresy na zeměpisné souřadnice, i zpětný zpětné geokódování – souřadnice na adresu. Ty jsou dostupné jak přes JavaScriptový objekt, tak přímo jako webová služba přes protokol HTTP. [8]
3.6 Apache HTTP Server Django web lze nasadit více způsoby, preferován je však právě Apache [9], spolu s modulem mod_wsgi5, který nahradil dříve používaný mod_python. Předčí ho ve výkonu i jednoduchosti nastavení. Moji volbu tohoto serveru rovněž podpořily mé předchozí zkušenosti s Apachem a jeho konfigurací.
WSGI - Web Server Gateway Interface, jednoduché univerzální rozhraní mezi webovými servery a aplikacemi pro jazyk Python 5
9
Obrázek 3-1: použité technologie
10
4 Analýza a návrh řešení 4.1 Doménový model Doménový model definuje entity v systému a vztahy mezi nimi. Obecně by měl být nezávislý na konkrétní implementaci, Django však nabízí některé užitečné mechanismy, které jsem se rozhodl využít již v této fázi návrhu. Vzhledem k rozsáhlosti modelu zde uvedu pouze zjednodušené schéma (Obrázek 4-1) pro snazší pochopení fungování aplikace. Jsou v něm znázorněny pouze hlavní doménové třídy a vztahy. Kompletní model ve standardní UML notaci je pak k dispozici v příloze.
Obrázek 4-1: Základní schéma systému. Modře jsou označeny třídy frameworku Django.
11
Všechny třídy systému vycházejí z třídy Model (django.db.models.Model), která ve většině případů reprezentuje tabulku v databázi. Kvůli přehlednosti je tento vztah dědičnosti u většiny tříd vynechán. Teoreticky by tato třída ani neměla být součástí doménového modelu, uvádím ji zde kvůli vztahu s aplikací Contenttypes. Tato aplikace, dodávaná společně s Djangem, mapuje jednotlivé modely v projektu a umožňuje tvorbu tzv. generic relations – obecných vazeb mezi objekty. Pokaždé, když je do projektu nainstalován nový model, vznikne nová instance třídy ContentType, která pak tento model (tedy typ obsahu) reprezentuje. Vazby lze pak tvořit pomocí dvojice atributů – cizí klíč na ContentType a primární klíč cílového objektu (vazby lze tvořit pouze s objekty, které používají jako primární klíč stejný datový typ, jaký je zvolen pro tento atribut). Tento systém umožňuje při vytváření aplikací snadno dodržet jednu z hlavních filosofií Djanga – loose coupling – tedy volné vazby mezi jednotlivými částmi systému (co nejmenší vyžadovaná znalost napojené časti), z čehož pak vyplývá snadná znovu-použitelnost. Hlavní funkcí vytvářeného systému je hodnocení. To probíhá pomocí vkládání recenzí (Review), jež mohou obsahovat 0 až n hodnocení (Rating) různého typu (RatingType). Hodnocení mohou mít různou váhu (důležitost, v projektu označováno jako significance), která vychází z důležitosti typu hodnocení a váhy skupiny, jejímž je autor recenze členem. Hodnotit lze objekty, které jsou instancemi třídy vycházející z abstraktní třídy Reviewable. Pro detailnější popis viz 5.3.3 Reviews. Hodnotí se stravovací i jiná zařízení. Tato jsou v systému reprezentována třídou Facility, kterou v češtině označuji jako podnik. K rozlišení různých typů podniků jsem se rozhodl použít tagy (tags, česky přeložitelné jako štítky či cedulky, pro rozšířenost termínu tag na internetu jsem se však rozhodl ho nepřekládat). Ty lze kromě určení typu použít i k označení různých vlastností či vybavení podniku. Pro práci s tagy jsem použil již hotovou aplikaci Django-tagging, která je součástí Pinaxu. Pro zaznamenání umístění podniku slouží třída Location. S podnikem je ve vazbě 1:1, což by obecně nebylo považováno za příliš dobrý návrh, nepřináší to však žádné zásadní nevýhody a vytvořený model umístění tak lze jednoduše použít i jinde. Třída Product reprezentuje produkt, eventuálně i službu nabízenou podnikem. Například se může jednat o nějaké jídlo v restauraci. Třída je stejně jako podnik potomkem Reviewable a tudíž ji lze stejným způsobem hodnotit. K rozlišení různých typů opět slouží tagy. Jak k podnikům, tak k jejich produktům je také potřeba umožnit nahrávání fotek. K tomu slouží třída Photo, která reprezentuje jeden obrázek a lze ji navázat k libovolnému objektu pomocí výše zmíněné generické vazby. Pro správu uživatelů, skupin a oprávnění jsem využil aplikaci django.contrib.auth. Ta umožňuje nastavení oprávnění na vytváření, změnu a mazání jednotlivých typů obsahu. Oprávnění týkající se přímo jednotlivých objektů je pak řešeno přímo v aplikační logice (není to příliš komplikované, takže jsem se rozhodl nezapojovat do projektu nějaký složitější autorizační systém).
12
4.2 Případy užití Pro systém existuje mnoho případů užití, z nichž velkou část tvoří manipulace (vytváření, úpravy, mazání) s různými typy obsahu. Ty nejpodstatnější jsou zobrazeny na obrázku Obrázek 4-2. Nepřihlášený uživatel může obsah pouze prohlížet. Přihlášený ho může i vytvářet a měnit ten, který sám vytvořil. V rámci přihlášených uživatelů je možné v systému vytvářet různé uživatelské role a nastavovat jim oprávnění pro různé typy objektů (což je možné i přímo pro jednotlivé uživatele).
Obrázek 4-2: hlavní případy užití systému
13
4.3 Architektura Jednou z hlavních výhod použití frameworku je odpadnutí nutnosti vymýšlet základní princip fungování aplikace. To jak bude členěna do různých vrstev, jak bude přijímat a zpracovávat požadavky či pracovat s daty. Kdybych se rozhodl aplikaci vytvořit od základu, pouze s použitím programovacího jazyka, a chtěl bych, aby byla přijatelně konzistentní, udržovatelná a rozšířitelná, musel bych nakonec nějaký framework vytvořit sám. To by jednak zabralo velkou část času, který jsem mohl tomuto projektu věnovat, ale hlavně by výsledek byl stěží srovnatelný s již dostupnými řešeními, která byla vyvíjena několik let lidmi s bohatými zkušenostmi s vývojem webových aplikací.
4.3.1 MTV Architektura Djanga vychází z tradičního vzoru model-view-controller (MVC), navrženého pro oddělení datového modelu, aplikační logiky a prezentace. Málo kde ho lze nalézt aplikovaný v čisté podobě, většinou to ani není vhodné. Django verze toho to vzoru je někdy nazývána MTV – modeltemplate-view. Model je klasický model, většinou odpovídá jedné tabulce v databázi. View je Python funkce, která řeší, jaká data uživatel dostane, ale ne v jaké podobě. K tomu slouží templates – šablony. Je tak oddělen obsah od prezentace. Jako controller by se dalo označit samotné Django a jeho mechanismus, který odešle požadavek na to správné view. Pro vývojáře to znamená pouze namapování view-funkcí na URL adresy pomocí regulárních výrazů.
4.4 Návrh User experience Termín User experience, známý pod zkratkou UX, lze přeložit jako „uživatelský zážitek“. Zastřešuje všechny aspekty interakce uživatele s nějakým produktem či službou. U webových stránek či aplikací se jedná zejména o informační architekturu, použitelnost a vzhled. Na webu je to často zásadní disciplína, protože pokud se uživatel na stránkách nevyzná nebo se mu špatně používají, může velmi rychle odejít pryč a s velkou pravděpodobností se už nevrátí. Obzvlášť pokud existuje nějaká konkurence, která mu bude vyhovovat více.
4.4.1 Informační architektura Informační architektura (IA) je obor zabývající se návrhem a organizací informačních systémů s cílem umožnit uživateli snadný přístup k obsahu (informacím) který hledá. U webů pod tento obor spadá zejména návrh struktury, kategorizace obsahu, návrh navigace, rozmístění informací na stránkách, logické pojmenování objektů či umožnění efektivního vyhledávání. V tomto projektu jsem se snažil o co nejjednodušší strukturu, čehož jsem dosáhl za pomoci dynamického uživatelského rozhraní. Veškerá běžná práce s obsahem probíhá na dvou stránkách. Na stránce vyhledávání podniků je možné podniky vyhledávat podle umístění (zadáním nějakého
14
údaje o poloze nebo pomocí mapy) či názvu a také filtrovat pomocí tagů. Výsledky jsou ihned zobrazovány na též stránce. Po kliknutí na nějaký podnik se uživatel dostane na stránku detailu podniku, kde se nachází veškerý obsah, který se k danému podniku váže. Informace, recenze, fotky, produkty. Zde je také možné obsah přidávat, upravovat či mazat. Vše probíhá přímo na stránce, provedené úpravy jsou okamžitě viditelné a uživatel nemusí přemýšlet o tom, kde se nachází nebo jak se dostat zpět.
Obrázek 4-3: Struktura webu
Ovládací prvky pro přidávání obsahu (podniků, recenzí, fotek…) jsou viditelné i pro nepřihlášené uživatele, aby viděli, že systém umožňuje uživatelům obsah vkládat. Při pokusu něco přidat je pak nejprve zobrazena výzva k přihlášení nebo registraci. Ovládací prvky pro editaci nebo mazání jsou naopak zobrazeny pouze v případě, že má uživatel právo danou akci provést. Na webu jsou i další stránky, i když již ne tak důležité, některé uživatele stejně mohou zajímat – aktuality (je web stále naživu?), žebříčky (které podniky si vedou nejlépe v hodnocení), o projektu, a další mohou být v budoucnu přidány. Pro navigaci mezi těmito hlavními sekcemi slouží menu v horní části stránky, kde jsou jednotlivé položky s velkými ikonami, což umožní rychlou orientaci zejména nově příchozím uživatelům. Vracející se uživatel však může spíše ocenit více prostoru pro obsah (obzvlášť pokud má menší širokoúhlý displej) a proto jsem přidal možnost horní panel s navigací zmenšit.
4.4.2 Uživatelské rozhraní Uživatelské rozhraní jsem se snažil navrhnout tak, aby bylo co nejvíce intuitivní pro uživatele, co ho uvidí poprvé, a zároveň aby práce s ním byla dostatečně efektivní i při opakovaném použití. Snažil jsem se dodržet obecné zásady návrhu použitelného grafického rozhraní, jako jasně označené ovládací prvky, konzistentní vzhled, použití vhodných metafor a pojmenování, omezení 15
textů na minimum (čím méně slov, tím více lidí si text přečte [10]), a také zažité principy ovládání, na které jsou uživatelé zvyklí z jiných aplikací. Abych umožnil efektivní práci s obsahem přímo ve stránce, uvažoval jsem nejprve nad „inline” editací (úpravy obsahu přímo tam, kde se nachází; např. odstavec textu se změní v textové pole, kam je možné psát), nicméně tento přístup jsem kvůli vysoké náročnosti na vývoj a údržbu (při změně stránky bude pravděpodobně nutné upravit i rozhraní pro editaci), kterou nevyvážily nějak závažné výhody, zavrhl. Místo toho jsem se rozhodl pro využití modálních dialogů. To jsou v podstatě „okna“ vyžadující nějakou interakci od uživatele, než je možné vrátit se do hlavního okna aplikace. Využil jsem je kromě úprav obsahu také k prohlížení obrázků nebo možnosti zobrazit větší mapu. Jako rozhraní pro hodnocení objektů jsem zvolil sadu posuvníků (slider), které při změně polohy mění barvu a obsahují popisky k různým hodnotám, aby bylo úplně jasné, jaké hodnocení je kladné a jaké záporné a aby se nestávalo, že si různí uživatelé pod stejným hodnocením představí různé věci. Stejné barvy jsou pak použity i při zobrazování hodnocení v celém systému.
Obrázek 4-4: formulář pro vytvoření/úpravu recenze zobrazený v modálním okně
16
Na stránkách, se kterými budou uživatelé nejčastěji interagovat, tedy vyhledávání a detail podniku, jsem chtěl docílit co nejlepší využití plochy displeje. Obsah je tam tříděn do sloupců, které mají definovanou minimální šířku, a podle rozlišení zobrazitelné plochy v uživatelově prohlížeči je spočítáno maximální množství sloupců, které je možné zobrazit. Ty jsou pak roztaženy tak, aby plochu rovnoměrně vyplnily. Na stránce vyhledávání je obsah i horizontálně omezen, aby nebylo nutné scrollovat a mapa s vyznačenými pozicemi jednotlivých podniků byla stále viditelná. Uživatelé s menším rozlišením tedy dostanou výsledky po menších částech na více stránkách. Snažil jsem se docílit co nejkratší dobu odezvy jak při načítání samotných stránek, tak při provádění veškerých operací pomocí JavaScriptových rozhraní, a u všech akcí, které mohou zabrat nějaký čas (komunikace se serverem – uživatel může být na pomalém připojení) zobrazit indikaci probíhající operace. V situacích, kdy se aktuálně zobrazovaná stránka nějak mění (něco se objevuje, mizí, vysouvá,…) jsem chtěl použít animace, ze kterých uživatel nejsnáze pochopí, co se právě děje. Narazil jsem však na velký problém s výkonem u pomalejších počítačů v kombinaci s některými prohlížeči (Firefox), zejména při velkém počtu elementů na stránce. Bohužel neexistuje snadný a spolehlivý způsob jak zjistit výkon počítače uživatele a náročné animace provádět pouze pokud to zvládne, takže zde musely přijít na řadu nějaké kompromisy.
4.4.3 Grafický návrh Snažil jsem se docílit jednoduchého konzistentního vzhledu, který nebude strhávat pozornost od obsahu. Nevytvářel jsem klasický návrh v grafickém programu, místo toho jsem vzhled aplikace vytvářel a upravoval rovnou pomocí CSS, často přímo v prohlížeči pomocí Firebugu, který má dobrou podporu pro psaní stylů (doplňování, snižování a zvyšování hodnot šipkami na klávesnici – pro rychlé nalezení správných vzdáleností, rozměrů či velikostí fontů), které jsou okamžitě viditelné na stránce. Většinu použitých ikon tvoří obrázky stažené z internetu s licencí umožňující volné použití, které bylo většinou potřeba pouze lehce upravit, a jen pár jsem jich musel vyrobit od nuly, což ušetřilo poměrně dost času. 4.4.3.1 CSS3 Pro některé vizuální prvky jsem se rozhodl využít nové CSS3 vlastnosti, které jsou již v některých prohlížečích podporovány. Zejména se jedná o zaoblené rohy a stíny, podporované dnes v prohlížečích založených na vykreslovacích jádrech Gecko (Firefox) a Webkit (Safari, Chrome) a pravděpodobně se brzy objeví i v Opeře (poslední verze vykreslovacího jádra Presto je již podporuje). Nejedná se o nijak zásadní vlastnosti, a proto není nutné, aby je viděli všichni uživatelé, a není tak třeba kvůli nim zanášet HTML kód spoustou extra elementů a vytvářet (a nechat uživatele stahovat) další obrázky. Po čase jsem ale zjistil, že použití těchto pokročilých vlastností má značný negativní dopad na rychlost vykreslování. Příliš se to neprojeví na statické stránce, ale působí to značné problémy při animacích. Musel jsem tedy využití těchto vlastností značně zredukovat.
17
Obrázek 4-5: stránka vyhledávání
18
5 Realizace V této kapitole popíšu jednotlivé části tvořící projekt. Zaměřím se na neobvyklé problémy a řešení, která považuji za zajímavá.
5.1 Krátké porovnání vývojových prostředí Vyvíjet webové aplikace v Djangu lze i v primitivním textovém editoru (což se třeba o Javě už v podstatě říct nedá, kvůli kvantům kódu, která za nás IDE6 generuje). Vhodné vývojové prostředí však může velmi zvýšit produktivitu. Pro tento projekt jsem potřeboval IDE s dobrou podporou Pythonu, HTML, CSS a JavaScriptu. Zde uvedu krátké porovnání těch, která jsem vyzkoušel.
5.1.1 NetBeans Robustní prostředí původně určené pouze pro Javu, dnes však už mimo jiné nabízí i podporu Pythonu. Dokáže celkem slušně napovídat, samozřejmě ne tak jako pro Javu – to u dynamického jazyka ani není možné. Se 4 GHz procesorem a 4 GB RAM už ani nebylo tak pomalé, jak ho pamatuji z dřívějších dob. Nicméně mělo jednu zásadní chybu – ošklivě vykreslovalo fonty a neumožňovalo v tomto ohledu žádné nastavení. To mě odradilo od dalšího zkoumání tohoto IDE.
5.1.2 Eclipse (Aptana) Stejně jako NetBeans je i Eclipse IDE napsané v Javě, používá ale nativní knihovny pro GUI7, takže zde nebyl žádný problém s vykreslováním fontů. Jeho modifikace pod názvem Aptana je speciální verze pro webové vývojáře, která Eclipse obohacuje o podporu HTML, CSS a JavaScriptu. Nabízí i nápovědu a doplňování pro jQuery. Pro podporu Pythonu je pak potřeba nainstalovat plugin PyDev. S tímto řešením jsem byl vesměs spokojen a nějakou dobu jsem ho používal.
5.1.3 Komodo Pak jsem však narazil na Komodo. Toto prostředí již není napsané v Javě, ale je postavené na vykreslovacím jádru Gecko, používajícím formát XUL8. Je taktéž dostupné na většině platforem, ale oproti Javě je značně rychlejší. Komodo podporuje všechny mnou požadované jazyky, včetně podpory pro jQuery, a jako jediné nabízí i zvýrazňování syntaxe pro Django šablony. Navíc umožňuje velmi snadnou a rychlou tvorbu snippetů (kousků kódu), které urychlují práci zejména Integrated development environment – vývojové prostředí Graphical User Interface - grafické uživatelské rozhraní 8 XML User Interface Language 6 7
19
se zmíněnými šablonami. Komodo bylo tedy mojí výslednou volbou vývojového prostředí pro Django.
5.2 Verzování U takto rozsáhlého projektu je vhodné použít nějaký systém pro správu verzí, i když na něm pracuje pouze jeden člověk. Já ho navíc využil i jako zálohovací systém a také pro nasazování aplikace na serveru (více v části 5.7). Přemýšlel jsem o použití nyní stále populárnějšího GITu, ale nakonec jsem se rozhodl neztrácet čas s jeho učením a použil Subversion [11], se kterým jsem již měl zkušenosti z dřívějška.
5.3 Django aplikace V této části popíši jednotlivé Django aplikace, které tvoří výsledný projekt. Aplikace mají podobu standardních Python modulů.
5.3.1 Facilities Tato aplikace má na starosti práci s podniky a jejich produkty. Nabízí rozhraní pro jejich přidávání, úpravy, vyhledávání a mazání. Obrázek 5-1 ilustruje třídy datového modelu a jejich provázanost s ostatními aplikacemi.
20
Obrázek 5-1: Schéma datového modelu aplikace Facilities, šedě jsou označeny modely z jiných aplikací.
Třída OpeningHours, kterou jsem pro přehlednost vynechal ve schématu celého projektu (Obrázek 4-1), slouží ke správě otvíracích hodin podniků. Funkce (views) pro přidávání, změnu a mazání objektů jsou napsány tak, aby dokázaly zpracovat jak standardní požadavky, tak požadavky zaslané asynchronně pomocí XHR9. Typ požadavku je zjištěn pomocí hlavičky HTTP_X_REQUESTED_WITH, proto je nutné ji ve skriptu správně nastavit (jQuery i ostatní JavaScriptové frameworky to dělají automaticky). Django pro usnadnění této kontroly poskytuje funkci HttpRequest.is_ajax(). Na normální požadavek, např. na změnu informací o podniku, tak funkce vrátí celou HTML stránku s formulářem a po jeho odeslání (v případě, že byl formulář správně vyplněn) bude uživatel přesměrován zpět na stránku podniku, kde již uvidí provedené změny. Pokud však bude požadavek odeslán pomocí JavaScriptu, vrátí fuknce pouze samotný formulář (uživateli se otevře jako popup okno – více v části 5.4.1.1) a po odeslání vrátí pouze část stránky, která byla změněna a skript na stránce ji nahradí. Pro AJAX komunikaci je zde používán formát HTML. Alternativou bylo použít JSON, který by obsahoval pouze čistá data, což by značně zmenšilo objem přenesených dat. Nicméně ani velikost přenášeného HTML zde není nijak závratná (největší prodlevu stále představuje samotné navázání spojení). A hlavně, použití JSONu by znamenalo, že skript musí obsahovat nějakou funkčnost, která přijatá data umístí do stránky. V případě změn v šabloně by pak bylo nutné XMLHttpRequest – rozhraní umožňující komunikaci mezi skriptovacím jazykem webového prohlížeče a serverem 9
21
ji aktualizovat. Toto řešení by tedy příliš nevyhovovalo DRY přístupu. HTML je na serveru generováno stejnou šablonou, která je použita při tvorbě původní stránky. 5.3.1.1 Vyhledávání Hlavním nástrojem pro vyhledávání a zobrazování výsledků je třída, kterou jsem pojmenoval ListManager. Její instanci lze nastavit různé parametry vyhledávání či filtrování a lze od ní získat aktuální stránku se seznamem výsledků. Lze například zadat zeměpisné souřadnice vymezující oblast hledání, přidat nebo odebrat tagy, podle kterých jsou výsledky filtrovány, nastavit počet objektů na stránce či zvolit stránku. Při zavolání metody get_list() pak instance ListManageru s nastavenými kritérii sestaví pomocí Django ORM dotaz do databáze, který vrátí požadované výsledky. Ty jsou pak z funkce vráceny ve formě Python listu. Instance ListManageru je mezi jednotlivými dotazy uchována v session. Uchování stavu na serveru usnadňuje tvorbu dotazů (většinou pocházejících z JavaScriptových rozhraní, více v části o JS). Ty tak mohou obsahovat pouze část parametrů, které se přidají k již existujícímu vyhledávání, např. přidat filtrování podle tagu „restaurace“ nebo přejít na stránku 4. Navíc se uživatel může později vrátit na stránku vyhledávání a uvidí ji ve stavu, v jakém ji opustil. To ho ale pravděpodobně už nebude zajímat, když se na web vrátí za delší dobu, proto je ListManager po určité době (nastavitelné v konfiguraci projektu) ze session odstraněn. Pro vyhledávání a obsluhu stránky s výsledky bylo potřeba vytvořit řadu view funkcí, které pracují systémem: získat instanci ListManageru ze session nebo vytvořit novou, nastavit jí nějaký parametr, získat z ní seznam objektů a vytvořit z něj odpověď. Opakovat tento postup v každé funkci by samozřejmě vůbec nebylo DRY, Python zde nabízí elegantní řešení ve formě tzv. dekorátorů. To jsou funkce umožňující „obalit“ jiné funkce nějakým kódem. V kódu je lze použít pomocí syntaxe inspirované anotacemi v Javě – napsáním @jmeno_dekoratoru před funkci, kterou chceme upravit. Pro funkce pracující s ListManagerem jsem tedy vytvořil dekorátor listmanager_operation, který funkci předá získanou či vytvořenou instanci ListManageru jako parametr a po skončení funkce z této instance získá aktuální seznam objektů a vytvoří z něj odpověď. Třídu ListManager, byť je zatím používána pouze pro práci s podniky (Facilities), jsem napsal univerzálně. Dokáže stejným způsobem pracovat s libovolným Django modelem. Pouze pokud chceme vyhledávat pomocí umístění, musí mít daný model vazbu na Location (nebo jiný model obdobně uchovávající zeměpisnou polohu); pokud chceme mít možnost filtrovat podle tagů, musí být napojen na django-tagging. Protože Python umožňuje duck-typing10, neznamenalo to v podstatě žádnou práci navíc.
5.3.2 Locations Aplikace locations slouží k uchování polohy v podobě adresy a zeměpisných souřadnic (pokud se je podaří získat). Model Location, který uchovává informace o umístění, nemá žádné externí vazby a může být použit v libovolném modelu pomocí cizího klíče. Styl programování, kdy je typ objektu určován jeho atributy a metodami, místo explicitní příslušnosti k nějakému typu („Když to vypadá jako kachna a kváká jako kachna, asi to bude kachna.“) 10
22
Tento model po uložení zkontroluje, jestli se změnila adresa nebo jestli se jedná o nový záznam, a pokusí se na základě vložené adresy zjistit souřadnice. Tento proces je nazýván geocoding. Zjištěné souřadnice je možné manuálně opravit, případně je zadat, pokud se je nepodařilo nalézt vůbec. Toto typicky probíhá přetažením značky na mapě na správné místo. Pro zjištění souřadnic je využívána webová služba Google geocoding. Pro pohodlné používání z Pythonu jsem použil knihovnu Geopy [12]. Ta umožňuje kromě služby od Googlu použít i řadu dalších. Bylo by tedy možné zkusit další poskytovatele, pokud by se adresu nepodařilo na první pokus najít. Služba od Googlu se však ukázala, minimálně pro území ČR, jako naprosto dostačující.
23
5.3.3 Reviews Tato aplikace umožňuje hodnocení libovolných Django-modelů v uživatelsky definovatelných kategoriích.
Obrázek 5-2: Schéma datového modelu aplikace Reviews
Hodnotí se pomocí modelu Review (recenze). Recenze se skládá z komentáře a množiny hodnocení (Rating). Každé hodnocení je nějakého typu (RatingType). Uživatel může nějaký objekt hodnotit nejvýše jedním hodnocením od každého typu a to pouze těch typů, které se vztahují k hodnocenému objektu. To je definováno cílovým typem obsahu (ContentType) a případně cílovými tagy daného typu hodnocení. Tagy nejsou povinné (aplikace je použitelná i s modely, které nepoužívají tagy) a v případě že typ hodnocení nemá žádné nastaveny, znamená to, že je jím možno hodnotit všechny objekty. Každý typ hodnocení zároveň může obsahovat nějaké „nápovědy k hodnocení“ (RatingHint). To jsou popisky vázané k nějaké hodnotě, které mohou usnadnit uživateli orientaci. Recenzi je možno připojit k libovolnému objektu pomocí generické vazby. U hodnocených objektů však většinou chceme mít možnost spočítat celkové hodnocení nebo průměry v jednotlivých typech. Proto vznikla abstraktní třída Reviewable, která zastřešuje společnou logiku pro hodnocené objekty. Nejvhodnější způsob, jak pomocí této aplikace umožnit hodnocení nějakého modelu, je tedy odvodit ho od této třídy. 24
5.3.3.1 Váha hodnocení Ne každé hodnocení je stejně důležité, některá třeba mohou mít spíše informační charakter, než že by zásadně vypovídala o kvalitě hodnoceného objektu. Důležitost (váha) je tedy dána typem hodnocení. Kromě toho ji ale také může ovlivnit, který uživatel hodnocení udělil. Pomocí modelu GroupRatingSignificance lze nastavovat důležitost skupinám uživatelů (django.contrib.auth.Group). Pokud je uživatel členem více skupin s různou definovanou důležitostí, platí pro něj ta nejvyšší. Celková průměrná hodnocení objektů jsou počítána jako klasický vážený průměr všech udělených hodnocení.
Rovnice 5-1: výpočet průměrného hodnocení
Počítání průměrných hodnocení je tedy poměrně náročná operace. Kdyby se měla počítat dynamicky, operace jako seřazení objektů podle hodnocení by, i pro poměrně málo objektů, znamenaly extrémní zátěž pro server. Hodnoty je tedy nutné ukládat do databáze a aktualizovat při každém přidání, smazání nebo změně recenze, nebo jiné události, která ovlivní výsledek (např. uživatel byl přiřazen do skupiny, která má vyšší důležitost). 5.3.3.2 Integritní omezení Kromě standardních omezení definovaných databázovým modelem obsahuje tato aplikace ještě řadu dalších, které je nutno kontrolovat přímo v kódu aplikace: Pokud je uživatel veden jako vlastník objektu, nemůže k němu přidat recenzi. Každý uživatel může přidat pouze jednu recenzi ke každému objektu. Recenze může obsahovat pouze jedno hodnocení od každého typu. Recenze může obsahovat pouze hodnocení těch typů, které danému objektu náleží.
5.3.4 Photos Aplikace Photos slouží k přidávání fotek k objektům. Jedná se o nadstavbu aplikace Djangophotologue (součást Pinaxu). Definuje model Image, reprezentující jeden obrázek, který lze opět navázat na libovolný objekt pomocí generické vazby. Je rozšířením modelu photologue.ImageModel, což mu automaticky umožňuje generovat různé velikosti, stupně komprese či efekty. Aplikace také nabízí rozhraní pro asynchronní upload obrázků pomocí jQuery pluginu Uploadify.
5.3.5 Users Tato aplikace slouží ke správě uživatelských profilů. Protože model uživatele (django.contrib.auth.User) je přímo z Djanga, není vhodné ho nějak upravovat (dříve nebo později by nastaly problémy při přechodech na nové verze). Je však možné definovat model, který bude 25
k uživateli ve vazbě 1:1 a bude obsahovat dodatečné informace. Instanci tohoto modelu je možné vytvořit při každém vytvoření nového uživatele pomocí signálů.
5.3.6 Common Aplikaci common jsem vytvořil pro umístění kódu, který je využíván ostatními aplikacemi a nejedná se o tak velkou funkčnost, aby mělo smysl vytvářet pro ni aplikaci. Jsou zde i různá rozšíření aplikací třetích stran, ze kterých nechci zbytečně vytvářet mírně upravené klony, protože bych tím přišel o nové vlastnosti a vylepšení budoucích verzí a vytvořil bych další kód, který by bylo potřeba udržovat.
5.4 JavaScriptové moduly V této části se budu věnovat JavaScriptovým modulům, tvořícím podstatnou část uživatelského rozhraní. Na internetu je v dnešní době dostupné velké množství různých JavaScriptových řešení a nepochybně by se dalo najít i pár takových, které bych mohl využít pro některé UI prvky, které jsem měl v plánu pro projekt vytvořit. Používání hotových řešení v JavaScriptu však mnohdy není tak snadné, jako u kódu na straně serveru. U komponent uživatelského rozhraní je radno držet se konzistentního stylu jak ve vzhledu, tak v ovládání. Úpravy a konfigurace nutné k dosažení tohoto cíle, spolu s navázáním komponenty na zbytek systému, mohou ale v některých případech zabrat téměř tolik času, jako vytvoření komponenty od základu. A když je pak nutné například rozšířit funkčnost, jde to většinou lépe a rychleji s vlastním kódem. Toto je jeden důvod, proč jsem se rozhodl některé věci napsat od základu místo využití nějakého existujícího řešení. Dalším důvodem bylo, že jsem si chtěl vyzkoušet programování složitějších věcí v JavaScriptu, potažmo jQuery, než s jakými jsem měl zkušenosti do té doby. JQuery vyniká ve snadném vykonávání jednoduchých úkolů a jejich zřetězení pro vykonání komplexnějších úkolů. Když ale dojde na tvorbu komponent, u kterých chceme udržovat stav, být schopni je programově ovládat, propojovat s ostatními částmi systému, nebo schopnost správně se vypořádat dynamicky se měnícím DOM, nenabízí již framework jednoznačná řešení. Během tvorby projektu jsem vyzkoušel různé přístupy k tvorbě složitějších stavových komponent (čistě objektově orientovaný přístup, událostmi řízené programování nebo využití interní jQuery cache) a měl jsem možnost porovnat jejich vhodnost v různých situacích. Nevýhodou je ne zcela konzistentní JavaScriptový kód napříč celým projektem.
5.4.1 jQuery Popup Modální dialog ve formě jQuery pluginu. Tvoří zásadní součást uživatelského rozhraní tohoto projektu. Umožňuje vytvořit dialog z vloženého HTML kódu nebo pouze prázdný. Po vytvoření ho lze samozřejmě nadále upravovat. Výchozí kód obsahuje pouze zavírací tlačítko a stín za dialogovým oknem, který vizuálně naznačuje, že je v popředí. Ve výchozím stylu je také element 26
H1, pokud je v dialogu obsažen, zobrazen ve formě titulní lišty. Při zobrazení dialogu je zbytek stránky překryt poloprůhlednou barvou. Pro zavření dialogu lze kromě tradičního tlačítka s obrázkem křížku použít i klávesu ESC nebo kliknout do pozadí, což jsou způsoby, které uživatelé očekávají díky zkušenostem s jinými aplikacemi. Plugin nabízí i možnost konfigurace některých věcí, například jestli má být odstraněn z DOM po zavření, jak dlouho mají trvat animace (plynulé objevení a zmizení) a také možnost „zrychlení“. Ta spočívá v dočasném přidání elementu style do hlavičky dokumentu během zobrazení dialogu, který ze stránky odebere náročné styly jako stíny nebo kulaté rohy a umožní tak plynulejší chod i na pomalejších počítačích, pokud je v dialogu zobrazen nějaký náročný obsah – v projektu jsem to využil při zobrazení okna s Google mapou.
5.4.1.1 AJAX Popup Typickým případem užití modálního dialogu v tomto projektu je zobrazení nějakého formuláře, pomocí kterého je možné manipulovat s daty na stránce. Za tímto účelem jsem vytvořil funkci ajax_popup(), která vytvoří dialog s obsahem, který získá ze serveru na zadané URL adrese. Ta je pak navázána na všechny odkazy na stránce, které jsou označeny třídou ajax_popup. Tyto odkazy vedou na stránky obsahující formuláře pro vkládání či úpravu dat. Po kliknutí se pak formulář zobrazí v dialogovém okně, ale zároveň je zachována kompatibilita pro prohlížeče se zakázaným JavaScriptem, ve kterých se standardně zobrazí stránka s formulářem. Pro navázání funkce jsem použil jQuery metodu live(), která naváže požadovanou funkci i na elementy, které jsou do DOM přidány až po jejím zavolání. Tak se například po přidání recenze nemusím starat o navázání funkce na tlačítko (odkaz) „upravit“, to bude automaticky fungovat a vyvolá dialog s formulářem pro úpravy, protože má v šabloně nastavenou výše zmíněnou třídu. V dialogu samozřejmě nechceme zobrazit celou stránku, včetně záhlaví, zápatí a navigace. Bylo by možné z cílové stránky vyříznout pouze požadovaný obsah pomocí jednoduchého selektoru, ale znamenalo by to zbytečné stahování celé stránky a také o něco větší zátěž serveru, který jí musel celou vygenerovat. Lepším řešením je, že server pozná, že šlo o dotaz typu XHR a pošle pouze tu část stránky, kterou chceme v dialogu zobrazit (jak již bylo zmíněno v kapitole 5.3.1). To také umožňuje umístit do odpovědi další logiku, která řeší chování stránky po odeslání formuláře. Typicky je to validace a promítnutí odezvy serveru na odeslaná data do stránky. Toto řešení lze navíc použít poměrně jednoduše, stačí použít pro výstup z view funkce jinou šablonu, viz Obrázek 5-3: Skládání šablon.
27
Obrázek 5-3: Skládání šablon
5.4.2 Photos Modul photos zajišťuje rozhraní pro správu fotek. Cílem bylo umožnit co nejjednodušší a nejefektivnější nahrávání, prohlížení a případné mazání obrázků, a také možnost přidat k obrázkům titulky a krátké popisky. 5.4.2.1 Upload Postup, kterého jsem chtěl docílit pro nahrávání fotek je: uživatel klikne na tlačítko „nahrát fotky“, uvidí standardní dialog operačního systému pro výběr souborů, kde budou vyfiltrovány pouze obrazové formáty a bude moct vybrat více souborů najednou. Po vybrání souborů uvidí indikátor průběhu nahrávání na server a po dokončení nahrávání se fotky (respektive jejich náhledy) objeví na příslušném místě na stránce. Toho však pomocí JavaScriptu nelze dosáhnout. Jednak nelze asynchronně provést upload souboru a za druhé nelze přizpůsobit systémový dialog pro výběr souborů, aby umožnil vícenásobný výběr. (A filtrování pouze obrázkových souborů, ale to není zas tak zásadní.) Asynchronní upload by možná bylo možné vyřešit pomocí nějakých hacků za použití iframe, s více násobným výběrem si už nejsem jist. Každopádně by to znamenalo hodně práce a nejistý výsledek (o čemž také svědčí neexistence čistě javascriptových pluginů pro upload souborů, které by to umožňovaly). Požadované funkčnosti lze dosáhnout použitím Java appletu nebo Flashe (nejspíš by to bylo možné i pomocí nějakých dalších technologií, ale ty už nebudou mít tak masovou podporu v prohlížečích). Vybral jsem tedy, dle mého názoru, menší zlo – Flash, a použil jsem pro upload jQuery plugin Uploadify [13], který nabízí dobrou konfigurovatelnost. Na serveru jsem kromě funkce zpracovávající příchozí soubory vytvořil i view, které vrátí sadu náhledů nahraných fotek ve formátu HTML, kterou lze vložit mezi ostatní náhledy po dokončení 28
uploadu všech souborů. Generování náhledů může nějaký čas trvat, pokud uživatel nahrál obrázky ve vysokém rozlišení. Během této doby je uživatel informován o probíhající akci animovaným indikátorem. 5.4.2.2 Zpracování náhledů Náhledy, ihned viditelné na stránce, nabízejí rozhraní pro další práci s fotkami. Po najetí myší na náhled je se na něm objeví sada ikon, podle toho co může daný uživatel s obrázkem dále dělat. Pro ikony jsem zvolil obecně používané metafory, pro jistotu je však po najetí na ikonu zobrazen i krátký popisek akce. Obrázek náhledu je po nejetí myši rozostřen pro lepší čitelnost ikon a popisků. Tohoto efektu jsem dosáhl vygenerováním rozostřené verze obrázku na serveru. Ta je pak v pozadí před-načtena do prohlížeče a poté může být jako další vrstva prolnuta s původním obrázkem. Vždy je k dispozici největší ikona lupy, umístěná uprostřed a zabírající největší, snadno zasáhnutelnou plochu, která spustí prohlížeč obrázků. Dále ikona „i“ pro zobrazení informací o tom kdo a kdy danou fotku nahrál. Pokud má uživatel právo obrázek upravovat (titulek, popisek) nebo mazat (správce nebo autor obrázku), uvidí i ikony pro tyto akce. Pokud je uživatel oprávněn měnit údaje o podniku, uvidí ještě ikonu umožňující použít zvolený obrázek jako hlavní fotku pro podnik, která je pak u podniku viditelná ve výpisu či v mapě. Aby stránka působila lépe při načítání, obrázky se nezobrazují postupně po řádcích pixelů, jak jsou načítány, což je standardní chování prohlížečů. Místo toho se plynule zobrazí až po té, co jsou načteny celé. Toho jsem docílil tak, že jakmile je načten DOM a tedy všechny elementy obsahující náhledy, jsou tyto skryty a na událost onload obsaženého img elementu je navázána funkce, která je plynule zobrazí. Je však nutné tento kód provádět pouze pro obrázky, které již nejsou z nějakých důvodů v paměti (cache) prohlížeče. V tom případě se zobrazí okamžitě a tento kód by u nich způsobil zmizení a následné zobrazení. Toto je možné detekovat pomocí atributu complete na img elementu, který značí, že již byl načten. 5.4.2.3 Prohlížeč obrázků Prohlížeč obrázků jsem postavil na výše popsaném pluginu pro tvorbu dialogů. Snažil jsem se v něm vyřešit problémy s použitelností, na které často narážím u podobných systému používaných na webu. Hlavně se jedná o rychlý přechod mezi fotkami a jasně viditelné ovládací prvky. Mnoho systémů pro prohlížení obrázků zbytečně dlouho zobrazuje animace přechodů mezi obrázky (např. nejdřív první obrázek zmizí, poté se velikost dialogu pomalu přizpůsobí velikosti dalšího obrázku, který se teprve potom začne pomalu objevovat) a také mnohdy není jasné, že lze na další obrázek přejít (např. se šipky pro přechod zobrazí až po najetí myší na obrázek, nebo dokonce na kraj obrázku, zatímco kliknutím doprostřed se okno zavře) a uživatel pak mnohdy pokaždé okno s obrázkem zavře a klepne na další náhled na stránce. Ve svém prohlížeči používám pro přechod pouze rychlé prolnutí obrázků. Pro maximálně rychlou odezvu je také vždy následující obrázek před-načítán do paměti prohlížeče. Pro umožnění i ne-sekvenčního prohlížení, bez nutnosti stále zavírat a otvírat prohlížeč, jsem do něj zabudoval i zobrazení náhledů ve formě „filmového pásu“. Protože je vždy načítán pouze zobrazovaný obrázek a jeden následující (načítat jich více nebo dokonce vše by tvořilo zbytečnou zátěž serveru při vyšší návštěvnosti, ale hlavně by to moc nepotěšilo uživatele, kteří musí za data platit), je dobré opět zobrazit indikaci, že obrázek je právě 29
načítán, než je možné ho zobrazit. Zjistil jsem však, že to nepůsobí příliš dobře, pokud je obrázek načten velmi rychle (což je většinou) a indikátor načítání pouze „problikne“, a tak jsem se rozhodl ho zobrazovat, až pokud se obrázek nenačte ani po půl vteřině.
Obrázek 5-4: prohlížeč obrázků
5.4.3 jQuery Collapser Další jQuery plugin, který jsem pojmenoval Collapser. V zásadě je to lehce modifikovaná verze UI prvku známého jako akordeon11. Používám ho k lepšímu zobrazení většího množství opakujících se prvků na stránce – zobrazení recenzí a produktů v detailu podniku. Rozdílem oproti klasickému akordeonu je možnost „rozbalit“ všechny položky najednou (pokud si uživatel bude např. chtít projít všechny recenze a nebude chtít vždy „překlikávat“ na další). Programové rozhraní pluginu umožňuje kromě základních operací (rozbalit nebo sbalit položku nebo rozbalit/sbalit vše) také vložení nové položky a nahrazení existující novou. (Pro smazání není
Akordeon v GUI je vertikálně poskládaný seznam položek, které jdou expandovat (rozbalit, roztáhnout) pro zobrazení dalších informací. Při zobrazení nějaké položky se aktuálně zobrazená položka opět sbalí. 11
30
žádná speciální funkčnost potřeba, stačí prostě daný element smazat z DOM.) Plugin také podporuje umístění jednoho Collapseru v položce jiného (použito u recenzí produktů).
5.4.4 Reviews Modul starající se o manipulaci s recenzemi. Využívá výše popsaný AJAX Popup pro zobrazení formulářů pro vkládání či úpravu recenzí a Collapser pro zobrazení recenzí u podniků i produktů. Pro vytvoření posuvníku pro hodnocení jsem se rozhodl použít jQuery UI Slider [14]. Pro změnu barvy v závislosti na pozici jsem použil obrázek pozadí obsahující všech 100 možných pozadí (nechtěl jsem pouze čistou barvu, ale přechod, pro vytvoření zajímavějšího, plastičtějšího vzhledu) a při posouvání jsem měnil jeho offset. Při nastavování jednotlivých hodnocení jsem taky chtěl uživateli živě ukazovat momentální průměrnou hodnotu jeho recenze. Použít pro tento účel AJAXové volání na server v podstatě nepřicházelo v úvahu, protože hodnotu je třeba aktualizovat i několikrát za vteřinu, když uživatel táhne posuvníkem. Kód pro počítání průměru tak bylo nutné duplikovat a napsat ho i v JavaScriptu a také bylo potřeba do šablony vyexportovat důležitosti jednotlivých typů hodnocení, použitých v recenzi. Další věcí, kterou bylo v tomto modulu třeba vyřešit, byla aktualizace průměrného hodnocení pro recenzovaný objekt při jeho změně (přidání/smazání/změna nějaké recenze). Toho bylo docíleno opět stažením nové verze vyrenderované do HTML na serveru a jejím nahrazením té staré.
5.4.5 Products Modul pro práci s produkty. Využívá v podstatě pouze výše zmíněné komponenty, hlavní náplní práce na něm bylo řešení problémů, které se vyskytli při použití více instancí jednotlivých modulů na jedné stránce. Jejich řešení mě naučilo důležitost používání jmenných prostorů (i anonymních) v JavaScriptu. Extra funkčností je pouze zpožděné načítání fotek jednotlivých produktů až po „rozbalení“ detailnějších informací o produktu. Pokud by totiž u některého podniku bylo přidáno hodně produktů s fotkami, načítání stránky by zabralo zbytečně dlouhou dobu a v mnoha případech by to bylo i zbytečné plýtvání přenesenými daty. Této funkčnosti je dosaženo odebráním atributu src img elementům a navázání jeho vrácení na událost rozbalení rodičovského elementu.
5.4.6 jQuery Layout Manager Tento jQuery plugin se stará o přizpůsobení rozložení stránky různým rozlišením zobrazitelné plochy na straně uživatele. Jak již bylo popsáno v části 4.4.2, jedná se o zobrazení vhodného počtu sloupců, do kterých je obsah rozdělen a přizpůsobení jejich šířky tak, aby vyplnily celou dostupnou plochu. Toho je dosaženo spočítáním maximálního počtu sloupců, které je možné zobrazit a nastavením jejich šířky na odpovídající hodnotu v procentech. Možná konfigurace pluginu zahrnuje jQuery selektor pro sloupce, které chceme rovnat, minimální požadovanou šířku sloupce, délku animace při přechodu k novému rozložení a URL kam plugin posílá poslední spočítanou šířku sloupců, aby při příštích zobrazeních stránky mohla být správně nastavena již přímo v HTML kódu vráceném serverem. 31
Kontrola a případná úprava zobrazení proběhne po zavolání pluginu a také při každé následující změně dostupné plochy, ke které může dojít změnou velikosti okna nebo zvětšením/zmenšením (zoom) obsahu v prohlížeči (pokud je nastaven tak, že zvětšuje celou stránku a ne pouze text).
5.4.7 Maps Tento modul má na starosti práci s mapami. Tvoří jakousi obalovou vrstvu nad Google Maps API, která řeší jeho integraci do systému a poskytuje rozhraní pro jeho propojení s ostatními komponentami. Pro použití na různých místech a pro různé účely jsem se rozhodl vytvořit jeden rozsáhlejší konfigurovatelný model namísto více oddělených. Zajímavým problémem bylo propojení mapy s dalšími komponentami na stránce vyhledávání. Byla potřeba obousměrná komunikace, jednak bylo nutné zajistit aktualizaci seznamu výsledků při změně polohy na mapě (v režimu hledání podle umístění), ale také aktualizaci značek na mapě při změně stránky, řazení či filtrování seznamu výsledků. Také bylo třeba propojit zvýraznění značky na mapě při pohybu myši nad položkou seznamu a naopak. Pro tento účel jsem vytvořil objekt MapConnector, který je předán jako parametr callback funkci12, zavolané po inicializaci mapy. Tento objekt umožňuje navěšení funkcí na důležité události mapy (např. získání sady objektů ze serveru po změně polohy) a, pro komunikaci opačným směrem, nabízí funkce pro manipulaci s mapou (např. zobrazit sadu objektů na mapě).
Obrázek 5-5: Propojení JS komponent
12
Funkce, která je předaná jiné funkci jako argument a je zavolána po jejím vykonání.
32
5.4.7.1 Hledání podle umístění Pro vyhledávání podniků podle umístění jsem využil opět službu geocoding od firmy Google, nyní však přes JavaScriptové rozhraní v podobě třídy GClientGeocoder. Po zadání nějaké lokace do pole pro hledání, je tento „dotaz“ geo-lokalizován. V odpovědi serveru se nachází souřadnice středu nalezeného umístění (pokud je nějaké nalezeno), kromě toho se zde nachází (v nezdokumentované části, kterou však lze snadno objevit pomocí FireBugu) i sada čtyř souřadnic pojmenovaná LatLonBox, která reprezentuje ohraničení nalezené oblasti. Díky ní je tak na mapě možné zobrazit správné měřítko i střed tak, aby mapa správně zobrazovala celou nalezenou oblast (ať už se jedná o stát, městskou část, ulici, či jiný zeměpisný celek). Mapa je poté pomocí nalezené lokace nastavena a objekty jsou z databáze na serveru vytaženy stejným způsobem, jako kdyby uživatel mapu na hledané místo nastavil ručně – zasláním dotazu obsahujícím souřadnice ohraničení momentálně viditelného území na mapě. Výhodou tohoto postupu je odpadnutí vytváření složité kategorizace jednotlivých objektů do různých úrovní zeměpisných celků, ve kterých by šlo efektivně hledat, a vytváření algoritmů pro analýzu zadaných požadavků na vyhledávané místo. To už programátoři v Googlu za posledních pár let dostali na velmi slušnou úroveň, a tak je možné například do vyhledávacího pole napsat „Praha, Staré Město“, „staré město praha“, ale i „pha stare mst“, a pokaždé dostat správný výsledek. 5.4.7.2 Geolokace uživatele Zajímavou funkcí, kterou nabízí Google loader, využívaný v projektu pro dynamické stahování Google Maps API, je možnost zjištění přibližné zeměpisné lokace uživatele. Nefunguje sice úplně vždy, ale funguje často a rozhodl jsem se jí využít pro výchozí nastavení mapy. Většina uživatelů, kteří web navštíví, tak ihned uvidí podniky nacházející se v jejich blízkosti. (Samozřejmě za předpokladu, že nějaké takové budou v systému uloženy.)
5.4.8 List Tato komponenta nese zodpovědnost za zobrazení seznamu výsledků (nalezených objektů). Informuje server (resp. ListManager, viz 5.3.1.1) o maximálním počtu položek, které je možné v aktuálním rozlišení zobrazit a vytváří Paginator, tedy ovládací prvek, který umožňuje přecházení mezi jednotlivými stránkami výsledků. Také se stará o zpracování dotazů pro vyhledávání. Při hledání podle umístění deleguje dotaz přes MapConnector mapě, hledání podle názvu pak rovnou posílá na server. 5.4.8.1 Podpora „zpět“ v prohlížeči Většina prohlížečů se snaží uživatelům urychlit pohyb po webových stránkách. Jedním ze způsobů, kterým se tomu snaží napomoct, je načítání stránek z cache při akci „zpět“, bez toho aby se dotázaly serveru, jestli se stránka nezměnila. To je většinou vhodné chování – pokud se stránka nemění dynamicky a prohlížeč to nebere v potaz a při návratu jí zobrazí ve stavu, ve kterém byla, když na ni uživatel původně přišel. To je případ právě stránky s vyhledáváním. Přesvědčit prohlížeče, aby si stránku neukládaly, nebylo úplně snadné. Pro většinu z nich se to podařilo pomocí HTTP hlaviček Cache-Control: no-cache, no-store, must-revalidate a Pragma: no-cache. Operu však nešlo přesvědčit žádnou hlavičkou a bylo nutné ji ke znovu načtení stránky donutit krátkým JS kódem. 33
5.4.9 Status Komponenta Status obsluhuje tzv. „stavový řádek“. To je oblast, která informuje uživatele o výsledcích vyhledávání. Komponenty, které nějakým způsobem manipulují s výsledky, ji využívají k informování uživatele o počtu nalezených výsledků, případně o chybách. Stavový řádek je taktéž využit pro indikaci právě probíhající operace. Pro lepší optickou indikaci výsledků, a také pro upoutání pozornosti uživatele ke stavovému řádku, jsem přidal možnost krátkého zvýraznění nějakou barvou (pozadí plynule přejde do dané barvy a zase zpět během jedné sekundy). Stavová oblast je zvýrazněna různými barvami pro stav, kdy byly nalezeny nějaké výsledky, nebylo nic nalezeno nebo pokud došlo k chybě. K chybě může za normálních okolností dojít pouze, pokud uživatel zadá nějakou lokaci, kterou Google geocoder nebude schopný nalézt.
5.5 Optimalizace odezvy Rychlá odezva, ať již se jedná a načtení stránky nebo rychlost reakce ovládacích prvků, má značný vliv na použitelnost a tím pádem na celkový uživatelský zážitek. V této části nastíním některé metody, které jsem použil pro rychlejší načítání a vykreslování stránek. Tipy pro různé optimalizační techniky jsem čerpal zejména ze stránek Yahoo Exceptional Performace [15] a Page Speed [16].
5.5.1 Načítání stránky 5.5.1.1 Co nejnižší počet dotazů na server Základní pravidlo, patřící mezi ty nejdůležitější. Stahování jednotlivých částí stránky – skriptů, stylů, obrázků – většinou tvoří hlavní část prodlevy před zobrazením stránky. Navazování spojení při tom zabere většinou mnohem déle, než samotné stahování a proto je nutné držet počet dotazů na minimu. Je proto dobré JS a CSS soubory používané na stránce zkombinovat vždy do jednoho. Vložit skripty a styly přímo do stránky je vhodné pouze v případě, že mají pouze malou velikost, znemožní se tím totiž jejich uložení v prohlížeči a musí být stahovány pokaždé znovu. Menší nevýhodou spojení veškerého JavaScriptu do jediného souboru je skutečnost, že pokud dojde k chybě v kterékoli jeho části, může to způsobit nefunkčnost veškerého JS kódu na stránce (soubor se po chybě přestane zpracovávat). Také je dobré spojovat i obrázky používané jako pozadí. V CSS se pak použijí v kombinaci se souřadnicemi jejich umístění ve velkém spojeném obrázku. 5.5.1.2 Nastavení dlouhé doby vypršení pro statický obsah Hlavička Expires udává, jak dlouho je stáhnutý soubor platný. Prohlížeč si ho uloží do paměti (cache) a do vypršení této doby se nebude serveru dotazovat na novou verzi. Tím se ušetří nejen stahování dat, ale i celý dotaz na server.
34
Ideální je tedy nastavení dlouhé doby (např. 10 let) pro všechny statické soubory. Pouze je potřeba pamatovat na to, že v případě změny je nutné změnit název souboru. Na serveru Apache to lze nastavit pomocí modulu mod_expires a direktivy ExpiresDefault, která určuje čas vypršení relativně od času požadavku. 5.5.1.3 Komprimování textových komponent pomocí Gzipu Gzipováním se velikost textových souborů (na webu jde hlavně o HTML, JS, CSS, XML, JSON) sníží přibližně o 70%. Přijímání odpovědí v tomto formátu podporují všechny moderní prohlížeče. Na Apachi slouží k tomuto účelu mod_deflate (pojmenovaný podle algoritmu, na kterém je gzip založen). Při nastavování je třeba dát pozor, aby se komprese aplikovala pouze na zmíněné textové soubory. Komprese obrázků, flashových či jiných již komprimovaných souborů by vedla pouze ke zbytečnému plýtvání procesorovým časem a velikost by se nijak nesnížila (někdy možná naopak lehce zvýšila). 5.5.1.4 Zmenšení a komprese JavaScriptu a CSS Tento krok se týká úprav skriptů a stylů tak, aby se chovaly stejně, ale byly menší. To obnáší například vynechání zbytečných znaků (nepotřebných mezer, tabulátorů, odřádkování, komentářů) nebo přejmenování proměnných na jednopísmenné názvy. Tím lze tyto soubory dále zmenšit a urychlit tak jak načítání, tak i následné zpracování v prohlížeči. Pro tento účel jsem použil nástroj YUI Compressor [17]. 5.5.1.5 Jak na to v Djangu? Výborným nástrojem pro automatizaci výše zmíněných technik v Django projektech je aplikace Django-compress [18]. Umožňuje generování kombinovaných JS a CSS souborů i použití libovolného filtru pro jejich zpracování (v mém případě tedy výše zmíněný YUI Compressor). Zároveň umí k názvům výsledných souborů přidávat číslo verze a automaticky ho měnit, pokud se změní nějaký ze zdrojových souborů.
5.5.2 JavaScript 5.5.2.1 Omezení manipulace s DOM Operace prováděné na hlavním DOM stromu stránky jsou náročné, je proto dobrým postupem manipulovat s podstromy elementů „offline“ – odpojenými od hlavního stromu a až na konci je tam připojit. Při vytváření nového obsahu stránky pomocí JavaScriptu (v tomto projektu se jedná primárně o výsledky vyhledávání a „bubliny“ v mapách) jsou dvě hlavní možnosti, jak ho vytvářet. Buď vytvořit jednotlivé DOM elementy a sestavit ho z nich, nebo ho vytvořit jako textový řetězec. Druhá je rychlejší, pokud ovšem nepotřebujeme na nově vytvořené elementy navázat větší množství event handlerů (funkcí zpracovávajících události), v takovém případě je většinou výhodnější použít první metodu. Pro snazší vytváření obsahu ve formě textového řetězce jsem si vytvořil miniaturní šablonovací systém ve formě funkce, která nahradí proměnné ve vloženém řetězci (šabloně) za data v dodaném
35
objektu (ten může obsahovat i další objekty, k jejichž atributům lze přistupovat pomocí tečkové notace). 5.5.2.2 Chytré odchytávání událostí Navázání velkého množství event handlerů na DOM může prohlížeč značně zpomalit. Proto pokud máme řadu elementů, na kterých potřebujeme odchytávat události (v projektu se opět jedná o stránku s výsledky vyhledávání), je lepší navázat jeden handler na obalující element, který pak z objektu události pozná, který element událost vyvolal. Další výhodou, kromě omezení celkového počtu handlerů, je, že pokud se obsah rodičovského elementu změní (načteny nové výsledky), není potřeba navazovat žádné nové handlery.
5.6 Optimalizace pro vyhledávače Umístění ve výsledcích fulltextového vyhledávání je ovlivněno mnoha faktory. V tomto projektu jsem se věnoval pouze optimalizaci, které lze dosáhnout přímo na vlastních stránkách (tzv. on-page faktory). Mezi tyto patří zejména sémantický kód, frekvence klíčových slov, jejich umístění v nadpisech, v titulku stránky, pojmenování odkazů, URL adresy či celková struktura stránky. Nesnažil jsem se však optimalizovat pro vyhledávače za každou cenu, což může často vést i k horšímu uživatelskému zážitku. Moje optimalizace se spíše týkaly uživatelů, což je ale zároveň to, co chtějí vyhledávače nabídnout – tvorba stránek přívětivějších pro uživatele je zároveň i jakousi přirozenou optimalizací pro vyhledávače.
5.6.1 Sitemaps Sitemaps [19] je protokol sloužící k informování vyhledávačů o všech URL adresách dostupných na daném webu. Jedná se o soubor /sitemap.xml, který obsahuje seznam URL spolu s dalšími informacemi, jako například jak často se obsah na daných adresách mění, kdy se změnil naposledy nebo jakou mají prioritu. Django obsahuje šikovný sitemaps framework, který usnadňuje generování tohoto souboru pro dynamický obsah. Zároveň nabízí i funkci ping_google(), kterou lze použít pro automatické informování Googlu o novém obsahu na stránkách. Tyto nástroje umožňují robotům vyhledávačů inteligentněji procházet a indexovat obsah stránek a zobrazovat ve výsledcích vyhledávání vždy aktuální stav webu.
5.7 Nasazení Jak jsem již zmínil v kapitole o použitých technologiích, k nasazení do ostrého provozu byl použit server Apache a modul mod_wsgi. Aplikace byla pro nasazení rozdělena do tří adresářů – jeden pro zdrojové kódy, kvůli bezpečnosti znepřístupněný přes web; druhý pro statický obsah (JS, CSS, obrázky), ten naopak musí být přístupný z webu – statické soubory jsou servírovány přímo 36
Apachem; a třetí, taktéž přístupný, pro WSGI skript, který zpracovává veškeré požadavky na dynamický obsah.
5.7.1 Automatizace nasazení Nasazovat aplikaci je poměrně pracná operace a je nepraktické a ji při každém updatu provádět ručně. Pro automatické nasazování jsem vytvořil další Django aplikaci, která s použitím knihovny pysvn [20] umožňuje nasazení ze SVN repozitáře přes webové rozhraní. Provede export požadované revize, jednotlivé části projektu umístí do příslušných adresářů a inicializuje znovunačtení zdrojových kódů Apachem. Je tak možné snadno a rychle nasazovat nové změny a opravy. Zároveň, pokud je zjištěno, že nový kód obsahuje chybu, je možné se snadno vrátit na starší verzi do doby, než je chyba opravena. Pro maximální rychlost nasazení jsem zřídil SVN repozitář pro projekt přímo na stejném stroji, kde je aplikace provozována.
5.8 Ladění výkonu serverové části Serverová část aplikace musí být schopná zpracovat požadavek a vygenerovat odpověď v co nejkratší době.
5.8.1 Databáze Tradičním úzkým hrdlem webových aplikací tohoto typu je databáze. Používání ORM může svádět k nepřemýšlení o tom, co se ve skutečnosti děje během získávání dat z databáze, a může se pak snadno stát, že k zobrazení některých stránek musí aplikace vykonat až několik set SQL dotazů. 5.8.1.1 Optimalizace dotazů Je nutné si uvědomovat, kdy jsou dotazy do databáze skutečně vykonávány a snažit se psát kód tak, aby potřeboval co nejmenší počet dotazů a zároveň vytahoval z databáze jenom data, která jsou skutečně potřeba. Užitečným nástrojem v Django ORM je funkce QuerySet.select_related(), která sleduje relace a automaticky vytahuje z databáze všechny související objekty. To může často radikálně snížit celkový počet dotazů. Pro hodně provázané databáze je však potřeba omezit hloubku sledování, případně i jména sloupců, jinak může tato funkce nadělat víc škody než užitku. 5.8.1.2 Denormalizace Mít normalizovanou databázi je z mnoha důvodů výhodné, ale často je to stav neslučitelný s dostatečným výkonem. Občas je proto dobrým řešením mít v databázi i nějaká redundantní data nebo výsledky výpočtů provedených nad daty. Sice to zvyšuje náročnost zápisů, ale může to velmi urychlit čtení, které je mnohem častější. 37
V Djangu (zatím) nejsou žádné nástroje pro usnadnění denormalizace, je proto nutné se o konzistenci denormalizovaných polí starat ručně. Ve většině případů je ideálním řešením přetížení metody save() na modelech, které tato pole ovlivňují. Je důležité denormalizovat pouze tam, kde to opravdu přinese znatelný užitek, protože to zároveň zhoršuje udržovatelnost kódu.
5.8.2 Cache Další vhodnou technikou pro snížení výpočetních nároků systému je použití cache. Django umožňuje snadné cachování celého webu, jednotlivých stránek, jejich částí nebo třeba jen vykonaných dotazů do databáze či nějakých složitějších výsledků. Podporuje různé cachovací systémy, z nichž asi nejlepší volbou je memcached [21]. Použití cache je však potřeba až při poměrně velké návštěvnosti, takže jsem ho zatím v projektu neimplementoval.
5.8.3 Škálování Django počítá i s velmi vysokou návštěvností a umožňuje snadné přidání dalších serverů, pokud výkon jednoho už není dostatečný. Prvním krokem by ve většině případů (včetně tohoto projektu) bylo nejspíš přesunutí statického obsahu na jiný server. K jeho servírování je poté vhodné použít nějaký k tomuto účelu optimalizovaný web server, jako například lighttpd [22]. V tomto kroku je pouze nutné zajistit dopravení souborů uploadovaných uživateli na tento media-server. Dále by pravděpodobně následovalo oddělení databáze. K tomu stačí pouze nastavit adresu serveru v nastavení. Pokud by ani tato konfigurace nepostačovala, bylo by nutné přidat nějaké redundantní servery. Když by nestíhal webový server, kód aplikace by se zkopíroval na další a příchozí požadavky by bylo nutné distribuovat pomocí nějakého load balanceru. Totéž platí pro media server. Pokud by bylo potřeba více databázových strojů, bylo by nutné nasadit nějaký systém pro replikaci databází, pro PostgreSQL například Slony [23].
5.9 Bezpečnost I přesto, že systém nepracuje s žádnými důvěryhodnými daty, je nutné, aby byl dobře zabezpečen. Jinak by mohl ohrozit uživatele nebo způsobit ztrátu jejich důvěry v případě napadení. V této části se budu věnovat nejčastějším způsobům útoků na webové aplikace a obraně proti nim.
5.9.1 SQL injection Takto je nazýván typ útoku, kterým lze ohrozit aplikace, které vstup od uživatele bez jakéhokoli ošetření (escapování speciálních znaků) používají při tvorbě SQL dotazů. To umožňuje útočníkovi vykonávat vlastní dotazy nad databází. Django ORM ale všechny parametry při tvorbě dotazů escapuje automaticky, takže tady se není čeho bát.
38
5.9.2 XSS Pod touto zkratkou je znám další velmi rozšířený typ útoku – „cross-site scripting“. Ten využívá pro změnu špatného ošetření dat na výstupu z aplikace. To může útočníkovi umožnit vložení vlastního JavaScriptu na stránky. Pomocí toho nad nimi získá úplnou kontrolu a může kompletně změnit jejich vzhled i obsah nebo přesměrovat uživatele na jiné stránky. Kromě toho také získává kontrolu nad účty všech uživatelů, kteří si stránku zobrazí. JavaScript může vykonávat libovolné akce, které jsou uživateli k dispozici (např. změnit e-mailovou adresu a nechat si zaslat nově vygenerované heslo), nebo odesílat uživatelova data na vzdálený server. Obranou proti tomuto útoku je escapování speciálních HTML znaků (< a > jsou převedeny na < a >) ve všech nedůvěryhodných řetězcích, ze kterých je tvořena výsledná stránka. Django toto opět řeší automaticky, hodnoty všech proměnných předávaných do šablony jsou automaticky escapovány. Starat se o to ručně je potřeba pouze v místech, kde se data dostanou na stránku jinak, než pomocí vykreslení šablony; což v tomto projektu bylo při jejich získávání AJAXem ve formátu JSON a vykreslování pomocí JavaScriptu.
5.9.3 CSRF Dalším typem útoku je Cross Site Request Forgery (někdy také zkracován jako XSRF). Probíhá tak, že uživatel, mezitím co je na jednom webu přihlášen, navštíví jiný web (který je pod kontrolou útočníka – např. díky XSS), na kterém je bez vědomí uživatele odeslán požadavek na první web (pomocí JavaScriptu, přesvědčením uživatele aby kliknul na tlačítko, nebo pokud útočníkovi stačí GET požadavek, např. jako
). Prohlížeč s tímto požadavkem odešle i cookie, která uživatele identifikuje a útočník má tedy opět možnost provádět za něj libovolné operace. Jedinou účinnou ochranou proti tomuto typu útoku (která zároveň nesnižuje použitelnost a přístupnost webu), je nepoužívat GET pro jakékoli požadavky, které nějakým způsobem mění stav systému, a do formulářů odesílaných pomocí POST přidávat v hidden fieldu vygenerovaný tajný řetězec, podle kterého poté lze ověřit, že požadavek byl skutečně odeslán z našeho webu. Django má od verze 1.2 v sobě zabudovanou funkčnost, která toto snadno umožňuje pomocí speciálního template tagu a middlewaru.
39
6 Testování Testování aplikace bylo zaměřeno zejména na kontrolu správné funkčnosti, použitelnosti a profilování kódu (měření kolik času, případně paměti, zaberou jednotlivé části programu pro identifikaci míst vhodných k optimalizaci).
6.1.1 Testování funkčnosti Django podporuje automatizaci testování pomocí standardních Python modulů – doctest a unittest. Jejich použití jsem si vyzkoušel, nedostatek času mi však neumožnil napsat testy pro všechny moduly aplikace. Funkčnost jsem tak většinou testoval manuálně. Užitečnými nástroji byly již zmíněný iPython [4] a také interaktivní Python Debugger (modul pdb). Funkčnost JavaScriptového kódu jsem testoval a ladil pomocí vývojářských nástrojů ve webových prohlížečích, zejména pomocí doplňků FireBug a FireQuery pro prohlížeč Firefox. Ty nabízejí interaktivní JavaScriptovou konzoli a také přehledné zobrazení DOM včetně navázaných event handlerů a objektů uložených v jQuery data cache.
6.1.2 Profilování K profilování Django aplikací lze opět použít nástroje ze standardní knihovny Pythonu. Na internetu lze nalézt řadu aplikací a snippetů (kousků kódu), které tento úkol usnadní. Já v tomto projektu používal tento: [24]. Pro zjištění náročnosti databázových operací je vhodným nástrojem Django Debug Toolbar [25]. Ten zobrazuje jednotlivé SQL dotazy, které byly vykonány pro zobrazení stránky, včetně času, který zabraly. Profilování JavaScriptového kódu nabízí opět FireBug. Pro detailní profilování jQuery lze použít k tomu určený plugin přímo od autora jQuery [26].
6.1.3 Použitelnost Nedílnou součástí vývoje každého webu či webové aplikace by mělo být testování použitelnosti. Ta je totiž často nejméně stejně důležitá, jako správná funkčnost aplikace. Testování často pomůže rychle odhalit chyby v použitelnosti, které vývojář/designér i při maximální snaze přehlédne při návrhu. 6.1.3.1 Kognitivní průchod Prvním krokem je kognitivní průchod. Při tom se vývojář snaží procházet aplikací tak, jak čeká, že to bude dělat uživatel ve snaze dosáhnout nějakého konkrétního cíle. V každém kroku se zaměřuje hlavně na to, jestli uživatel bude vědět, kde se v aplikaci nachází, jaké akce má k dispozici, jestli dokáže identifikovat tu správnou k dosažení cíle a jestli pochopí zpětnou vazbu od systému. 40
Kognitivní průchody jsem prováděl průběžně při tvorbě uživatelského rozhraní a implementaci nových funkcí do systému. Vždy, když jsem narazil na nějaké potenciální problémy, snažil jsem se je ihned eliminovat. 6.1.3.2 Test s uživateli Vývojář (obzvláště pak nezkušený) však jen zřídka kdy odhadne, jak budou skuteční uživatelé aplikaci vnímat. Proto je nezbytné provést testy i s dalšími lidmi. Ideálně s reálnými uživateli, nebo někým kdo se jim co nejvíce přibližuje, ale na odhalení většiny chyb obvykle stačí kdokoliv, kdo se přímo nepodílel na vývoji aplikace. Pro otestování aplikace jsem nechal několik lidí provést pár scénářů (zaregistrovat se, nalézt podnik, přidat podnik, přidat recenzi nebo nahrát fotky) a požádal je, aby u toho „přemýšleli nahlas“. Sledoval jsem, jak postupovali, co jim trvalo nejdéle nebo s čím měli problémy. Dalších pár lidí jsem nechal aplikaci vyzkoušet samostatně, načež následovalo zhodnocení jejich zkušeností a připomínek na IRC. Testování odhalilo několik drobných chyb, které jsem následně opravil.
41
7 Závěr Podařilo se naplnit cíle práce a vytvořit webovou aplikaci, která splňuje všechny požadavky a je snadno použitelná pro uživatele. Její uživatelské rozhraní umožňuje pohodlné vyhledávání, vkládání podniků, hodnocení či fotek, bez jakýchkoli zbytečných kroků a s okamžitou zpětnou vazbou. Aplikace zároveň nabízí široké možnosti nastavení, umožňující ladění různých parametrů během provozu přes administrační rozhraní, jako například uživatelské skupiny a oprávnění vzhledem k jednotlivým typům objektů v systému, nebo různé typy hodnocení a jejich váha. Hlavním přínosem pro mě jsou bezpochyby zkušenosti nabyté během vytváření poměrně komplexního projektu, od návrhu až po nasazení, s použitím moderních open-source (až na Google Maps) technologií. Kromě zlepšení programátorských dovedností mi projekt také pomohl lépe si uvědomovat časovou náročnost různých úkolů. Použití Pythonu a frameworku Django bylo dobrou volbou, ve srovnání s jinými technologiemi pro tvorbu webových aplikací, se kterými jsem měl nějaké zkušenosti z dřívějška (PHP, Java Struts, JSF, ASP.NET) bylo jejich učení a práce s nimi skutečně efektivní a zábavné. Lehce rozporuplné pocity mám z Pinaxu, který sice umožňuje vytvořit základ webu se spoustou funkčnosti v rekordním čase, ale o to více času je pak nutné investovat do případných úprav a přizpůsobení. (Nutno podotknout, že když jsem začínal s vývojem projektu, tak byl Pinax teprve ve verzi 0.5 – v budoucnu se dá očekávat spousta vylepšení.)
7.1 Pokračování vývoje Během práce na projektu mě napadla spousta možných vylepšení a dalších funkcí, které by bylo dobré do aplikace přidat. Na většinu z nich se však z časových důvodů nedostalo – i přesto, že rozšiřování projektu je díky jeho architektuře velmi snadné, implementace nějaké funkčnosti, návrh a realizace uživatelského rozhraní a následné testování a ladění vždy zabere přinejlepším několik hodin, často však i několik dní. Mezi možná rozšíření projektu, která mě napadla, patří například: Možnost jednoduchého hodnocení recenzí, případně i fotek (systémem palec nahoru/palec dolů), které by ovlivňovalo pořadí jejich zobrazení a zároveň oblíbenost či karmu autora (která by se mohla projevit ve váze jeho hodnocení, případně i v jeho oprávněních) Speciální rozhraní pro mobilní zařízení (kde by se dalo dobře využít schopnosti Google Maps API pracovat s GPS) Widgety zobrazující průměrné hodnocení, které by majitelé podniků mohli umístit na svůj web
42
Speciální mini-aplikace pro různé typy zařízení – například podniky s tagem restaurace by mohly mít v detailu denní menu, hudební kluby zase program akcí Export aktualit, případně nově přidaných podniků na RSS/Twitter
7.1.1 Jiná použití vzniklého systému Vzhledem k tomu, že systém byl navržen velmi obecně, vytvořená sada Django aplikací se dá použít pro tvorbu webu zabývajícího se hodnocením téměř čehokoliv (ideálně něčeho co má nějakou zeměpisnou polohu). Například by se mohlo jednat o hodnocení horských středisek, kde by se hodnotily penziony, lyžařské areály nebo půjčovny vybavení. Asi by bylo vhodné trochu striktněji oddělit tyto značně odlišné typy zařízení, ale to by se týkalo zejména prezentační části webu, logika by mohla zůstat stejná. Stačilo by pouze vytvořit v administraci vhodné typy hodnocení. Dalším příkladem by mohl být web zabývající se zajímavými místy kam jet na výlet. Tam by mohlo být víc než samotné hodnocení důležité pohodlné nahrávání a prohlížení fotek. Vhodnou modifikací pro tento systém by tak bylo přidání podpory pro velké formáty do JavaScriptového prohlížeče obrázků.
Aplikace je momentálně dostupná na adrese http://beta.pubcloud.cz
43
8 Seznam použitých zkratek API AJAX CSS CRUD CSRF (XSRF) XSS DOM DRY XML GUI HTML HTTP IDE IRC JS JSON MVC ORM PIL SQL SVN URL XUL XHR YUI
44
application programming interface asynchronous JavaScript and XML cascading style sheets create, retrieve, update and delete cross-site request forgery cross-site scripting document object model don’t repeat yourself extensible markup language graphical user interface hypertext markup language hypertext transfer protocol integrated development environment internet relay chat JavaScript JavaScript object notation model-view-controller object-relational mapper Python imaging library structured query language Subversion uniform resource locator XML user interface language XMLHttpRequest Yahoo! UI Library
9 Použité zdroje [1]
Django | The Web framework for perfectionists with deadlines. http://www.djangoproject.com/ stav z 13. 12. 2009
[2]
Python Programming Language. http://www.python.org/ stav z 13. 12. 2009
[3]
PEP 20 -- The Zen of Python. http://www.python.org/dev/peps/pep-0020/ stav z 13. 12. 2009
[4]
FrontPage - IPython. http://ipython.scipy.org/moin/ stav z 13. 12. 2009
[5]
Pinax. http://pinaxproject.com/ stav z 13. 12. 2009
[6]
PostgreSQL: The world's most advanced open source database. http://www.postgresql.org/ stav z 13. 12. 2009
[7]
jQuery: The Write Less, Do More, JavaScript Library. http://jquery.com/ stav z 13. 12. 2009
[8]
Google Maps API - Google Code. http://code.google.com/apis/maps/ stav z 13. 12. 2009
[9]
The Apache HTTP Server Project. http://httpd.apache.org/ stav z 13. 12. 2009
45
[10] Spolsky, Joel. Designing for People Who Have Better Things To Do With Their Lives. http://www.joelonsoftware.com/uibook/chapters/fog0000000062.html stav z 13. 12. 2009 [11] subversion.tigris.org http://subversion.tigris.org/ stav z 13. 12. 2009 [12] geopy. http://code.google.com/p/geopy/ stav z 13. 12. 2009 [13] JQuery File Upload Plugin Script - Uploadify. http://www.uploadify.com/ stav z 13. 12. 2009 [14] jQuery UI. http://jqueryui.com/demos/slider/ stav z 13. 12. 2009 [15] Best Practices for Speeding Up Your Web Site. Yahoo Developer Network. http://developer.yahoo.com/performance/rules.html stav z 13. 12. 2009 [16] Web Performance Best Practices. Page Speed. http://code.google.com/speed/page-speed/docs/rules_intro.html stav z 13. 12. 2009 [17] YUI Compressor. Yahoo Developer Network. http://developer.yahoo.com/yui/compressor/ stav z 13. 12. 2009 [18] Pelme, Andreas. django-compress. http://code.google.com/p/django-compress/ stav z 13. 12. 2009 [19] sitemaps.org. http://www.sitemaps.org/ stav z 13. 12. 2009 [20] pysvn.tigris.org. http://pysvn.tigris.org/ stav z 13. 12. 2009 46
[21] memcached - a distributed memory object caching system. http://memcached.org/ stav z 13. 12. 2009 [22] lighttpd fly light. http://www.lighttpd.net/ stav z 13. 12. 2009 [23] Slony-I. http://www.slony.info/ stav z 13. 12. 2009 [24] Extended Profiling Middleware. http://www.djangosnippets.org/snippets/605/ stav z 13. 12. 2009 [25] Hudson, Rob. robhudson's django-debug-toolbar at master - GitHub. http://github.com/robhudson/django-debug-toolbar/ stav z 13. 12. 2009 [26] Resig, John. Deep Profiling jQuery Apps. http://ejohn.org/blog/deep-profiling-jquery-apps/ stav z 13. 12. 2009 [27] Python Imaging Library (PIL). http://www.pythonware.com/products/pil/ stav z 13. 12. 2009 [28] django-currencies. http://code.google.com/p/django-currencies/ stav z 13. 12. 2009 [29] django-grappelli. http://code.google.com/p/django-grappelli/ stav z 13. 12. 2009 [30] OpenID Foundation. http://openid.net/ stav z 13. 12. 2009
47
Příloha A UML diagramy
Obrázek A-1: aplikace Reviews
48
Obrázek A-2: aplikace Facilities, Photos, Locations
49
Příloha B Ukázka administračního rozhraní
Obrázek B-1: administrační rozhraní – seznam typů hodnocení
Obrázek B-2: administrační rozhraní – úprava typu hodnocení
50
Příloha C Instalační příručka 1. Prerekvizity Pro provoz aplikace je nutné mít nainstalovaný následující software (pokud používáte jiný operační systém než Linux, Windows nebo Mac OS, nejprve si ověřte, zda je pro váš OS k dispozici) : Python [2] verze 2.5 a vyšší Django [1] verze 1.2 Pinax [5] verze 0.5.1 Python Imaging Library (PIL) [27] Geopy [12] django-compress [18] django-currencies [28] django-grappelli [29] Databázový adaptér pro Python v závislosti na vaší databázi
2. Soubory Zkopírujte si na disk obsah adresáře /aplikace na přiloženém CD
3. Nastavení V souboru manage.py je nutné nastavit cestu k instalaci Pinaxu do proměnné PINAX_ROOT. V souboru settings/base.py nastavte typ, adresu a přihlašovací údaje k databázi, tajný klíč (SECRET_KEY), klíč pro Google Maps API pro vaši doménu (GOOGLE_MAPS_KEY) a případně upravte další nastavení podle vašich představ.
4. Inicializace databáze V adresáři s projektem spusťte příkaz python manage.py syncdb
51
Příloha D Obsah přiloženého CD /aplikace – kompletní zdrojové kódy systému /text – text bakalářské práce
52