VYSOKÁ ŠKOLA POLYTECHNICKÁ JIHLAVA Katedra elektrotechniky a informatiky Obor Aplikovaná informatika
Sestavování zkouškových testů - generování otázek a variant odpovědí bakalářská práce
Autor: Jan Koten Vedoucí práce: Ing. Marek Musil Jihlava 2014
Abstrakt Práce se zabývá návrhem a implementací formulářové aplikace, která je schopna automaticky generovat testy podle předem nastavených parametrů. Testy je možné ukládat do pdf a xml souborů. Součástí práce je předvést různé způsoby práce s xml soubory. V práci jsou popsány navržené a použité algoritmy, datové struktury a je zde popsáno prostředí aplikace. V aplikaci je použita databáze Microsoft Access (.mdb). Pro zálohu této databáze je navrhnuta vlastní struktura XML dokumentu.
Klíčová slova MVC, .NET/C#, PDF, XML, zkouškový test
Abstract The work deals with the design and the implementation of an application that is reliable to tests creation considering the pre-set test parameters. Created tests can be saved to pdf file or xml file. Different approaches to xml files working are considered. All proposed and used algorithms, data structures and application environment are described in this work. The database MS Access is used. Own proposed XML document structure is used to the database backup.
Keywords exam, MVC, .NET/C#, PDF, XML
Prohlašuji, že předložená bakalářská práce je původní a zpracoval/a jsem ji samostatně. Prohlašuji, že citace použitých pramenů je úplná, že jsem v práci neporušil/a autorská práva (ve smyslu 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ě některých zákonů, v platném znění, dále též „AZ“). Souhlasím s umístěním bakalářské práce v knihovně VŠPJ a s jejím užitím k výuce nebo k vlastní vnitřní potřebě VŠPJ. Byl/a jsem seznámen s tím, že na mou bakalářskou práci se plně vztahuje AZ, zejména § 60 (školní dílo). Beru na vědomí, že VŠPJ má právo na uzavření licenční smlouvy o užití mé bakalářské práce a prohlašuji, že s o u h l a s í m s případným užitím mé bakalářské práce (prodej, zapůjčení apod.). Jsem si vědom/a toho, že užít své bakalářské práce či poskytnout licenci k jejímu využití mohu jen se souhlasem VŠPJ, která má právo ode mne požadovat přiměřený příspěvek na úhradu nákladů, vynaložených vysokou školou na vytvoření díla (až do jejich skutečné výše), z výdělku dosaženého v souvislosti s užitím díla či poskytnutí licence. V Jihlavě dne
............................................... Podpis
Poděkování V první řadě bych rád poděkoval svému vedoucímu práce Ing. Marku Musilovi za rady a čas strávený diskuzemi o řešeních a postupech. Dále bych chtěl poděkovat svým blízkým za podporu při psaní této práce.
Obsah 1
Úvod.......................................................................................................................... 8
2
Požadavky na aplikaci .............................................................................................. 9
3
4
5
6
7
8
2.1
Funkce z hlediska uživatele ............................................................................... 9
2.2
Funkce aplikace a návrh prostředí.................................................................... 10
Principy pro vývoj aplikací ..................................................................................... 12 3.1
Datová, aplikační a prezentační vrstva............................................................. 12
3.2
Architektura Model View Controller ............................................................... 12
3.3
Technologie OLE DB ...................................................................................... 13
3.4
Platforma .NET a programovací jazyk C# ....................................................... 14
Algoritmy pro generování testu .............................................................................. 16 4.1
Tělo testu .......................................................................................................... 16
4.2
Možné chyby .................................................................................................... 16
4.3
Sestavení otázek a vkládání odpovědí.............................................................. 17
4.4
Třídy reprezentující datovou strukturu............................................................. 18
4.5
Třída TestBuilder ............................................................................................. 19
Databáze.................................................................................................................. 23 5.1
Návrh databáze a ERA-model .......................................................................... 23
5.2
Volba databázového systému ........................................................................... 25
5.3
Implementace databáze .................................................................................... 25
Práce s XML ........................................................................................................... 28 6.1
Teorie XML ..................................................................................................... 28
6.2
Třída XMLModerator ...................................................................................... 31
6.3
Nastavení aplikace ........................................................................................... 32
6.4
Uložení vygenerovaných testů ........................................................................ 33
6.5
Záloha databáze ................................................................................................ 34
Práce s PDF ............................................................................................................. 38 7.1
Knihovna PDFsharp ......................................................................................... 38
7.2
Třída PdfBuilder............................................................................................... 38
7.3
Zásady vkládání textu do dokumentu .............................................................. 39
7.4
Export testu a řešení do PDF ............................................................................ 40
7.5
Export oblasti do PDF ...................................................................................... 42
Prostředí aplikace .................................................................................................... 44 8.1
Menu, hlavní okno aplikace a dětská okna ...................................................... 44
8.2
Formulář pro zálohu databáze .......................................................................... 48
8.3
Agendy aplikace ............................................................................................... 48
9
8.4
Vytváření testů ................................................................................................. 52
8.5
Nastavení aplikace ........................................................................................... 55
Závěr ....................................................................................................................... 56
Seznam použité literatury ............................................................................................... 58 Seznam obrázků .............................................................................................................. 59 Seznam použitých zkratek .............................................................................................. 60 Přílohy............................................................................................................................. 61 1
Obsah přiloženého DVD ......................................................................................... 61
2
Uživatelský manuál................................................................................................. 62 2.1
První spuštění a nastavení aplikace .................................................................. 62
2.2
Hlavní formulář ................................................................................................ 62
2.3
Záloha a obnova databáze ................................................................................ 62
2.4
Vkládání, mazání a editace záznamů ............................................................... 63
2.4.1
Otázky a odpovědí .................................................................................... 63
2.4.2
Předměty ................................................................................................... 63
2.4.3
Témata ...................................................................................................... 64
2.5
Vytváření testů ................................................................................................. 64
2.5.1
Vytvoření nového testu ............................................................................. 64
2.5.2
Obnova z xml souboru .............................................................................. 65
1 Úvod Jako téma mé bakalářské práce jsem si vybral „Sestavování zkouškových testů – generování otázek a odpovědí“ od Ing. Marka Musila. Jedná se o desktopovou aplikaci, která by uživateli umožnila sestavit test podle přednastavených kritérií. Toto téma mě zaujalo, protože se jedná o databázovou aplikaci, která bude programována podle architektonického vzoru MVC a přispěje k výraznému zkrácení času nad vytvářením testů. Při vývoji bude využito komponent .NET, které se používají nejen k programování desktopových aplikací. V současné době jsem nenašel žádnou aplikaci, která by splňovala tato kritéria: jednoduchá a rychlá obsluha, databáze uložená na lokálním počítači, záloha databáze testových otázek a odpovědí, ukládání testů do pdf souborů, možnost pozdější implementace dalších funkcí do aplikace. Existuje řada bezplatných aplikací, které se zabývají touto problematikou (Test 1.1.4.25, DuTest4, Hot Potatoes v 6, …). Tyto aplikace ovšem disponují několika nevýhodami: neumožnují automatické generování testu, otázky nejsou rozděleny do předmětů a kapitol, nepřehledné GUI rozdělené do více nezávislých oken, absence exportu do pdf souboru. Dále je i několik licencovaných aplikací, které se touto problematikou zabývají. Vybral jsem z nich aplikaci EduBase, která by mou problematiku mohla řešit nejlépe. Ačkoliv tato aplikace disponuje velmi přívětivým uživatelským prostředím a je schopna komunikovat s moderními výukovými nástroji. Tak disponuje několika nevýhodami a úplně nesplňuje zadané požadavky. Hlavní nevýhodou je značná částka za licenci, která roste s množstvím uživatelů, kteří chtějí aplikaci používat současně. Dále autor neuvádí parametry, podle kterých lze automaticky sestavovat test. Cílem práce bude vyvinout aplikaci, která bude schopna automaticky generovat zkouškové testy. Vygenerované testy bude nadále možné vyexportovat do pdf nebo xml souboru. Aplikace bude pracovat s databází umístěnou na lokálním PC. Databáze bude vytvořena v nástroji Microsoft Access z kancelářského balíku Microsoft Office. Celá aplikace bude naprogramována v prostředí .NET/C#. S použitím návrhového vzoru MVC nebude problém aplikaci v pozdější době doplnit o další funkce.
8
2 Požadavky na aplikaci 2.1 Funkce z hlediska uživatele V aplikaci je implementováno mnoho funkcí a algoritmů, které generují testy nebo pracují s externími soubory. Tyto funkce z hlediska uživatele mohou být rozděleny do šesti skupin:
generování testu, řešení a oblasti,
export do XML, import z XML,
export do PDF,
správa databáze,
záloha databáze,
nastavení aplikace.
Aplikace bude umožňovat uživateli vygenerovat test podle přednastavených kritérií. V první řadě si uživatel bude muset vybrat, z jakého předmětu bude test sestaven. Předmět bude obsahovat jedno nebo více témat, z kterých bude možné si vybrat. Na rozdíl od předmětu může test obsahovat otázky z více témat. Dalším parametrem pro vytvoření testu je počet otázek. Počet otázek je pevný parametr. Například, když uživatel nastaví počet otázek na deset, tak jich test bude obsahovat deset. Další parametr je maximální počet odpovědí, tento parametr je pružný. Pokud uživatel zvolí maximální počet odpovědí pět, a otázka bude obsahovat jen tři, tak se u otázky objeví pouze tři odpovědi. Posledním kritériem generování testu je obtížnost. V aplikaci se bude nacházet pět přednastavených obtížností, každá otázka bude ohodnocena jednou z nich. Aplikace bude umožňovat si vybrat buď všechny obtížnosti, nebo jen určitý rozsah obtížností. Dále bude možné do testu zahrnout otázky se slovní odpovědí. Otázka se slovní odpovědí je taková otázka, která nemá možnosti a, b, c …, ale odpověď se zapisuje ručně. K takto vygenerovaným testům bude možné si vyexportovat již ohodnocený test, tj. test, ve kterém jsou vyznačené správné odpovědi, otázky se slovní odpovědí budou 9
obsahovat stručné znění správné odpovědi. Další dokument bude obsahovat seznam parametrů, které byly zadány před generováním testu, tj. předmět, seznam témat, seznam obtížností, počet otázek, počet otázek se slovní odpovědí a počet odpovědí. Vygenerované testy a oblasti bude možné exportovat do formátu pdf nebo xml. Pdf soubory jsou připraveny pro tisk. Xml soubory složí jako záloha, pomocí aplikace bude možné si xml soubory převést do pdf souboru. Test bude ještě obsahovat nadpis, tento nadpis je možné ponechat defaultní, nebo si zvolit vlastní. Další funkcí aplikace bude kompletní správa nad databází. Uživatel bude moci libovolně vkládat, editovat a mazat záznamy. Jediné záznamy, které nebude možné editovat ani mazat, jsou obtížnosti. Záznamy budou rozděleny do tří základních agend: otázky a odpovědi, témata a předměty. Tyto agendy budou mezi sebou přímo provázané. Změna provedená v jedné agendě se ihned projeví ve zbývajících agendách. Aby při havárii počítače nebo při nechtěném smazání záznamu nedošlo k trvalé ztrátě dat, tak aplikace bude umožňovat vytvořit zálohu databáze. Databáze se bude zálohovat do xml souboru, a z tohoto souboru ji bude možné zpětně obnovit. Pro pohodlnější ovládání bude aplikace obsahovat jednouché nastavení. První položka nastavení bude místo na pevném disku, kde se nachází databáze. Druhá položka bude složka, do které se budou ukládat všechny soubory vytvořené aplikací. A poslední bude možnost si zvolit, jestli uživatel bude chtít, aby se mu při práci se záznamy zobrazovala upozornění. Pokud uživatel neví, jak jsou záznamy propojené, jsou tyto upozornění důležitá. Například smazáním tématu dojde k smazání všech otázek a odpovědí spadající pod toto téma. Toto nastavení bude uloženo v xml souboru nazvaném config ve společné složce s aplikací.
2.2 Funkce aplikace a návrh prostředí Stěžejními funkcemi aplikace jsou funkce generování testů a správa databáze. Prostředí aplikace je navrženo tak, aby práce s aplikací byla co nejjednodušší. Tlačítka pro ovládání aplikace ve všech formulářích se nachází vždy na stejném místě a design všech je veden v jednom duchu. Počet ovládacích prvků jsem se snažil zmenšit na nezbytné minimum a aplikaci naprogramovat tak, aby co možno nejvíce operací řešila za uživatele. Celá aplikace je realizována v jednom okně s výjimkou okna pro náhled
10
vygenerovaného testu. V aplikaci je možné mít otevřeno vždy jen jedno dětské okno, to zlepšuje celkovou přehlednost. Každé okno se při otevření automaticky aktualizuje, to zlepšuje aktuálnost záznamů. Dále je aplikace vybavena automatickým zavíráním nepoužívaných oken, tj. při otevření nového okna se automaticky zavře okno předešlé, pokud bylo nějaké otevřeno. Dále je aplikace vybavena dynamicky se měnícím titulkem, pokud uživateli není zcela jasno, v jakém okně se nachází, tak stačí nahlédnout na titulek1. Ukázka vzhledu aplikace je znázorněna na obr. č. 1.
Obrázek 1: Ukázka maximalizovaného okna aplikace
1
Titulek se nachází v záhlaví formuláře společně s tlačítky pro minimalizaci, maximalizaci a zavření okna.
11
3 Principy pro vývoj aplikací 3.1 Datová, aplikační a prezentační vrstva Aplikace je rozdělena do tří vrstev: datová, aplikační a prezentační. Další vrstvou je vrstva databázová. Datová vrstva slouží k ukládání, čtení a skladování dat. Aplikační vrstva zpracovává data a provádí různé výpočty. Prezenční vrstvu můžeme označit jako vstup dat a zobrazení dat. Tato vrstva však neobsahuje zpracování dat. Pomocí této vrstvy se zobrazují výsledky a také se v ní nachází uživatelské rozhraní, v kterém uživatel bude pracovat po celou dobu, kdy bude aplikace spuštěná.
3.2 Architektura Model View Controller Princip této architektury spočívá v tom, že se použijí tři komponenty a to Model (model), View (pohled) a Controller (řadič). Model reprezentuje veškerou logiku a vše co do ní spadá. Mohou to být například různé výpočty, databázové dotazy, atd. Model se nezajímá, odkud data přišla a neví, jak a kde se budou zobrazovat. View převádí data reprezentovaná komponentou Model tak, aby byla vhodně zobrazena pro uživatele (například pomocí GUI). A nakonec komponenta Controller, ta reaguje na události a zajišťuje změny v komponentách View a Model. Jedná se vlastně o prostředníka mezi komponentami Model a View. Architektura MVC je znázorněna na obr. č. 2. Praktický příklad užití modelu může vypadat nějak takto. Uživatel v aplikaci provede nějakou událost, například klepne na tlačítko nebo v gridu a změní vybraný řádek. Komponenta Controller na tuto událost zareaguje, přistoupí ke komponentě Model a v případě potřeby provede akci (aktualizace, přidání, odběr). Komponenta Model zpracuje data a komponenta View použije zaktualizovanou komponentu model pro zobrazení dat. Před samotnou realizací aplikace pro generování testů jsem se rozhodl pro použití tohoto modelu. Důvodů pro výběr tohoto modelu je hned několik: rozložení zátěže, dobrá rozšiřitelnost a flexibilita. Prakticky to bude znamenat, že když z nějakého důvodu nebude uživateli vyhovovat databázový systém vytvořený v programu Access z kancelářského balíku Microsoft Office. Tak bude stačit opravit komponentu 12
Controller pro alternativní databázový systém. Pokud by nebylo využito MVC, pak v případě přechodu na jiný databázový systém by programátor musel prakticky přepsat celou aplikaci. To může být časově velmi náročné a obtížné. Hlavně je to neefektivní.
Obrázek 2: Softwarová architektura model-view-controller
3.3 Technologie OLE DB Technologie OLE DB je databázová architektura založená na komponentách. Tato architektura zavádí přístup k mnoha typům dat, například k relačním datům, emailům, prostým souborům a souborům tabulkových kalkulátorů. Přístup k těmto datům je možný prostřednictvím místní sítě i internetu. K datům můžeme přistupovat prostřednictvím zprostředkovatele OLE DB v prostředí .NET/C#. Třídy pro práci s touto databázi se nacházejí ve jmenném prostoru System.Data.OleDb. Připojení k databázi zprostředkovává třída OleDbConnection. Tato třída jako vstupní parametr očekává řetězec string, který obsahuje cestu k databázi a informaci o databázovém systému. Pomocí metody Open() je připojení otevřeno. Metody Close() a Dispose() spojení uzavřou. Pro vykonání dotazu nad databází slouží třída OleDbCommand, která jako vstupní parametr očekává řetězec obsahující sql příkaz a instanci třídy OleDbConnection. Pro čtení dat můžeme použít například 13
třídu OleDbDataReader. Tato třída čte záznamy z databáze řádek po řádku. Metoda ExecuteReader() třídy OleDbCommandvrací instanci této třídy. K získání hodnoty slouží metody GetInt32(int),GetString(int), kde jako vstupní parametr uvádíme pořadové číslo sloupce. Toto řešení se nazývá připojené. Další možností
je
použít
odpojeného
SqlDataAdapter.
Třída
OleDbConnection
a
jako
řešení, vstupní
kdy
se
vytvoří
instance
třídy
parametr
očekává
instanci
třídy
OleDbCommand. Pomocí metody Fill je naplněna
instance třídy DataSet, která je zároveň vstupním parametrem.
3.4 Platforma .NET a programovací jazyk C# .NET je platforma určená pro běh distribuovaných aplikací. Všechny aplikace postavené na platformě .NET jsou si velmi podobné, mají stejné charakteristické rysy. Ať se jedná o aplikace navržené pro web, desktopy nebo servery. Základem platformy je jádro, to se nazývá .NET Framework. Toto jádro je založené na principech OOP. Jádro automaticky podporuje třídy, metody, vlastnosti, konstruktory, události, polymorfismus atd. Tuto platformu jsem si vybral, protože se jedná o nejrozšířenější platformu pro osobní počítače s operačním systémem Microsoft Windows, obsahuje velký výběr komponent, dialogových oken a vývoj v ní je pohodlný. C# je vysokoúrovňový objektově orientovaný jazyk, který je vyvinut firmou Microsoft. Jak patrné z názvu, jazyk vychází z programovacích jazyků C/C++, ale mnohé má společné s programovacím jazykem Java. Základní charakteristiky jazyka C#: čistě objektově orientovaný, nativní podpora komponentového programování, jednoduchá dědičnost a násobná implementace rozhraní, vlastnosti a události, automatická správa paměti, podpora zpracování chyb a výjimek, typový programovací jazyk, podpora atributů v programování. Jazyk C# zajišťuje větší pohodlí při vývoji formulářových aplikací než C++. Jazyk C# je sice lehce pomalejší, ale mnohé věci jsou v tomto jazyku řešeny automaticky, proto je vývoj aplikací rychlejší. Objektově orientované programování v dnešní době převážně nahradilo strukturovaný přístup k programování. Objektově orientovaný přístup funguje zdola nahoru, na rozdíl od strukturálního přístupu, který funguje zezdola nahoru. To znamená, že nejprve jsou 14
navrženy základní stavební prvky (objekty), a z těch je pak celý program složen. Výhoda je, že tyto prvky lze později znovu použít. Objekty mají své vlastnosti, a mohou vykonávat různé činnosti. Každý objekt je instancí své třídy.
15
4 Algoritmy pro generování testu 4.1 Tělo testu Hlavní položkou celého testu je objekt, který zaobaluje kompletní informace o testové otázce. Tento objekt v sobě uchovává: znění otázky, pole odpovědi, pole datového typu ano/ne, které slouží k určování správnosti odpovědí a jednu proměnnou datového typu ano/ne, která označuje otázku se slovní odpovědí. Pokud je hodnota této proměnné ano, pak se jedná o otázku se slovní odpovědí, jestliže je hodnota ne, pak se jedná o klasickou otázku s výběrem možností. Pole, která se nachází ve třídě, mají totožnou délku, první pole skládající se z textových řetězců slouží k uchovávání znění odpovědí. Druhé pole slouží k rozpoznání, jestli se jedná o správnou nebo nesprávnou odpověď. Pokud vezmeme n-tý prvek z prvního pole a zjistíme, že tentýž prvek má v druhém poli hodnotu true, potom se jedná o správnou odpověď. Pokud má hodnotu false, jedná se o nesprávnou odpověď. Znázornění polí můžeme vidět na obr. č. 3.
Obrázek 3: Pole představující odpověď a správnost odpovědi
4.2 Možné chyby Při vytváření těla testu může dojít k různým chybám. Nejčastější chyby, které zde můžou vzniknout, jsou zapříčiněny nedostatkem dat. První taková chyba je způsobena tím, že uživatel zadá vyšší počet otázek, než je v databázi. Takovouto chybu není problém odhalit. Druhá a závažnější chyba je neúplnost otázky. Tím je myšleno, že otázka nemá k sobě přiřazenu žádnou správnou odpověď. V tomto případě musíme u každé otázky zkontrolovat, jestli je k ní přiřazená alespoň jedna správná odpověď.
16
4.3 Sestavení otázek a vkládání odpovědí Ještě před samotným sestavováním musí metoda, která sestavuje test znát počet otázek, maximální počet odpovědí a dostat pro práci množinu příslušných otázek a odpovědí. Prvně se vytvoří pole datového typu bool, to bude sloužit k tomu, aby se pozice správných odpovědí stále neopakovali. Tím je myšleno, aby nebyla pokaždé správná odpověď například b), a aby se pozice správné odpovědi střídala co nejvíce. Do tohoto pole se při vytvoření do každého prvku zapíše hodnota false. Když je toto hotovo, program přejde k samotnému sestavování a generování. Prvně je zavolána metoda, která vygeneruje náhodné číslo, spodní hranice tohoto čísla je nula a horní hranice je počet otázek. Podle vygenerovaného čísla je vybrána otázka, z předané množiny dat obsahující otázky. Pak je prozkoumáno pole, jestli alespoň jeden prvek má hodnotu false, pokud ano, znamená to, že na této pozici ještě nebyla vložena správná odpověď a index prvku se uloží. Pokud ne, znamená to, že všechny pozice pro správnou odpověď jsou obsazeny a je zavolána funkce, která do všech prvků pole zapíše hodnotu false. Na uložený index je vložena správná odpověď. Dále se přistupuje k vkládání špatných odpovědí. Je vygenerováno náhodné číslo, podle tohoto čísla je vybrána špatná odpověď patřící k vybrané otázce a je vložena na volnou pozici. Počet pozic je buď zadaný maximálním počtem odpovědí, nebo počtem dostupných odpovědí dané otázky. Pokud první počet je větší než druhý. Takto se pokračuje, dokud všechny pozice pro odpovědi nejsou obsazeny. Cyklus končí, když se počet vložených otázek rovná počtu otázek, který je předán metodě při startu. Celý postup je vyobrazen na obr. č. 4.
17
Obrázek 4: Vývojový diagram znázorňující algoritmus generování testu
4.4 Třídy reprezentující datovou strukturu Otázku se všemi odpověďmi a klíčem pro určení správné a nesprávné odpovědi reprezentuje třída ItemOfTest. Třída obsahuje dva konstruktory, první očekává znění otázky a počet odpovědí (string, int). Druhý konstruktor je kopírovací, ten očekává instanci třídy ItemOfTest. Pomocí tohoto konstruktoru je vytvářeno řešení testu. Dále se ve třídě nachází čtyři členské proměnné. První je typu string a je v ní uloženo znění otázky, druhá je typu bool a říká, jestli se jedná o otázku se slovní odpovědí, třetí je pole bool, podle tohoto pole se určují správné a špatné odpovědi a poslední čtvrtá je pole stringů, v tomto poli je uloženo znění odpovědí.
18
Hlavičky metod a proměnné třídy ItemOfTest. public class ItemOfTest { public ItemOfTest(string text, int count){…} public ItemOfTest(ItemOfTest iot) {…} public string text_question {get; set;} public bool txt_answer {get; set;} public bool[] answers_tf {get; set;} public string[] answers {get; set;} }
Třída Stack slouží jako uložiště pro otázky a odpovědi. Obsahuje kolekci: témat, obtížností, otázek a odpovědí. Dále se ve třídě nachází jeden bezparametrický konstruktor, který při vytvoření instance alokuje všechny kolekce. V této třídě se nacházejí metody, sloužící pro přidání objektu do kolekce. Třída slouží jako dočasné uložiště pro otázky. Hlavičky metod a proměnné třídy Stack. class Stack { private List
c_answer; public void AddAnswer(Answer value){…} public List returnAnswer(){…} private List c_question; public void AddQuestion(Question value){…} public List returnQuestion(){…} private List c_difficult; public void AddDificult(Difficult value){…} public List returnDificult(){…} }
4.5 Třída TestBuilder Pro sestavení testu je v projektu implementována třída TestBuilder. Ve třídě se nachází jeden konstruktor se šesti parametry. Parametry představují: počet otázek, maximální počet odpovědí, počet otázek se slovní odpovědí, předmět, ze kterého bude sestavovaný test, seznam témat obsažených v testu a seznam obtížností otázek, které budou v testu použity.
19
Při vytvoření instance se zmíněné parametry přiřadí členským proměnným třídy a je alokována kolekce shromažďující instance objektů ItemOfTest. Členská proměnná st typu Stack je předána metodě FillStack(…) ze třídy DataModerator, která ji naplní všemi otázkami z databáze, které splňují kritéria (předmět, téma, obtížnost). Pole report[]složí k eliminaci opakujících se pozic správných odpovědí, jak bylo popsáno v kapitole 4. S tímto polem pracuje metoda rebuiltReport(), která do všech prvků zapíše hodnotu false. Metoda generateNumber(int limit) vygeneruje náhodné číslo. Očekávaný parametr je horní mez generovaného čísla. Spodní mez je implicitně nula. Pro vytvoření samotného testu musí být zavolána metoda CreateTest(). Tato metoda prvně zavolá metodu createTestBody(). V této metodě je sestavováno samotné tělo testu. V metodě je cyklus, který proběhne tolikrát, kolik je počet otázek. Prvně je vygenerováno náhodné číslo, horní mez čísla je počet otázek. Podle tohoto čísla je vybrána otázka z st. Dále se naplní kolekce odpověďmi k této otázce, a kolekce je ještě promíchána. Kdyby kolekce nebyla promíchána a otázka by měla přiřazených víc správných odpovědí, tak by se pořád opakovala jedna a ta samá správná odpověď dokola. Pak je vyhledána správná odpověď a je uložen její index. Ostatní správné odpovědi jsou z kolekce vymazány, už nebudou potřeba. Kdyby správná odpověď nebyla nalezena, byl by zavolán příkaz continue, a pokračovalo by se k další otázce. Pak je vytvořena instance třídy ItemOfTest a přiřazena proměnné record, pokud je počet odpovědí vkládané otázky vyšší než počet odpovědí požadovaný uživatelem, je instance vytvořena s počtem odpovědí požadovaným uživatelem. Pokud ne, je tomu naopak. Dalším krokem je zkontrolování pole report[], jestli alespoň jeden prvek má hodnotu false, pokud ano přejdeme ke generování čísla pro pozici správné odpovědi. Pokud ne, je zavolána metoda rebuiltReport(). Vygenerované číslo pro správnou pozici odpovědi je otestováno, jestli nebylo v předešlých cyklech použito, jestli ne je uloženo. Jestli ano, generování probíhá do té doby, dokud není vygenerované číslo, splňující tuto podmínku. Na tuto pozici v proměnné record je vložena správná odpověď. Správnou odpověď vymažeme z kolekce, již ji nebudeme potřebovat. 20
Už zbývá jen vložit nesprávné odpovědi. Vkládání probíhá v cyklu, cyklus projíždí pole boolů v proměnné record, a ptá se, jestli je na právě testovaném indexu hodnota false. Pokud ano je vygenerováno náhodné číslo, podle kterého je vložena nesprávná odpověď do pole stringů na stejný index jako má právě testovaný prvek v poli boolů. Po skončení cyklu je proměnná record vložena do kolekce. Po skončení cyklu následuje dotaz, jestli mají být v testu otázky se slovní odpovědí, pokud ano je zavolána metoda addVerlaQst(). Tato metoda pracuje obdobně, s tím rozdílem, že nevkládá žádné odpovědi. Tímto je sestavení holého testu hotové. Po opuštění metody je zavolána metoda numberedTest(). Tato metoda má za úkol očíslovat otázky a odpovědím přidat a), b), c). Vše probíhá ve dvou v sobě vnořených cyklech. V prvním cyklu se před otázku vloží číslo. V druhém vnořeném se před odpověď vloží písmeno. To je vkládáno podle ASCII tabulky. První ASCII hodnota je 97 (malé a), a v každém cyklu je inkrementována. Jakmile vnitřní cyklus skončí, je ASCI hodnota znovu nastavena na 97. Poslední významná metoda v testu je GetRatedStockTest(). Tato metoda slouží k určení správných odpovědí. U otázek se slovní odpovědí přidá znění odpovědi uložené v databázi. Nejprve se vytvoří hluboká kopie (rated_stock_test) kolekce stock_test pomocí kopírovacího konstruktoru ve třídě ItemOfTest. Kdyby byla vytvořena mělká kopie, už by nebylo možné uložit neohodnocený test. Z této kopie nyní bude vytvořeno řešení testu. Pomocí dvou v sobě vnořených cyklů je procházena celá kolekce. Vnější cyklus prochází otázky a vnitřní cyklus prochází odpovědi. Konkrétně pole boolů, které říká, jestli je odpověď správná nebo špatná. Když v poli boolů narazí cyklus na prvek který má hodnotu true, tak na stejný prvek v poli stringů upraví následujícím způsobem. Na začátek odpovědi je přidán znak „{“ a na třetí pozici v stringu je vložen tento znak „}“. Správná otázka pak vypadá v řešení například takto „{a)} 1 + 1 = 2“. Třída TestBuilder slouží pouze k sestavení těla testu a poskytuje základní informace o testu k dalšímu zpracování.
21
Hlavičky metod a proměnné třídy TestBuilder. internal class TestBuilder { public TestBuilder(int count_question, int count_answer, int count_txt_answer, Subject subject, List tp, List df){…} private Random gen; private int count_question; public int GetCountQuestion(){…} private int count_answer; public int GetCountAnswer(){…} private int count_txt_answer; public int GetCountTxtAnswer(){…} private bool[] report; private void rebuiltReport(){…} private Stack st; private Subject subject; public Subject GetSubject(){…} private List tp; public List GetTopics(){…} private List df; public List GetDificult(){…} private List stock_test; public List GetStockTest(){…} public List GetRatedStockTest(){…} private int generateNumber(int limit){…} private void createTestBody(){…} private void addVerbalQst(){…} private void numberedTest(){…} public void CreateTest(){…} }
22
5 Databáze 5.1 Návrh databáze a ERA-model Veškeré otázky budou uloženy v databázi. Základ je, aby v databázi byla uložena otázka a k ní příslušný počet odpovědí. Jedná-li se o takovou otázku, na kterou je třeba odpovědět slovně, tudíž nemáme možnosti a, b, c, potom otázka bude obsahovat pouze jednu odpověď. Dále je třeba každou otázku ohodnotit a tím říci jak je obtížná. Posledním krokem je otázky od sebe nějakým způsobem odlišit tak, aby se do sebe nemíchali otázky z více předmětů. Takže musíme každou otázku přiřadit nějakému předmětu a zařadit jí pod téma příslušného předmětu. Databáze bude obsahovat pět tabulek: Otazka, Tema, Predmet, Odpoved, Obtiznost. Každá z těchto tabulek má svůj vlastní primární klíč, klíč je celé číslo, které je při vložení záznamu automaticky inkrementováno. ERA model databáze je znázorněn na obr. č. 5.
Obrázek 5: ERA model databáze
23
Tabulka Otazka obsahuje atributy:
id: primární klíč,
text_otazka: text znění otázky,
index_obtiznost: cizí klíč tabulky Obtiznostnost,
id_tema: je cizí klíč tabulky Tema,
slovni_odpoved: je realizována datovým typem bool a informuje o tom, že se na otázku odpovídá slovně.
Tabulka Odpoved obsahuje atributy:
id: primární klíč,
id_otazka: cizí klíč tabulky Otazka, který odkazuje na příslušnou otázku,
spravna_odpoved: datový typ bool říká, jestli je odpověď správná nebo špatná,
text_odpoved: znění odpovědi
Tabulka Obtiznost obsahuje škálu obtížností, kterými je ohodnocena každá otázka. Má pouze dva atributy a to id, které je primárním klíčem a nazev, který označuje obtížnost (lehký, standartní, těžký, velmi těžký). Tabulka Tema, zařazuje otázku do určitého tématu. Obsahuje atributy:
id: primární klíč,
nazev_tema: název tématu,
kapitola: číslo, které obsahuje téma nebo jiné alternativní označení tématu,
id_predmet: cizí klíč tabulky Predmet.
Tabulka Předmět rozlišuje otázky podle předmětů a má pouze dva atributy. A to id, které je primární klíč a nazev_predmet, který je název předmětu.
24
Relace mezi tabulkami. Tabulka Otazka je propojena vazbou 1:N s tabulkou odpověď, jedna otázka může mít více odpovědí, ale také žádnou. Dále je relace 1:N mezi tabulkou Otazka a Obtiznost, jedna otázka může mít pouze jednu obtížnost. Vazba mezi tabulkou Tema a Otazka je také 1:N, jedna otázka může spadat pouze do jednoho tématu. Poslední vazba typu 1:N se nachází mezi tabulkou Předmět a Téma, z toho plyne, že téma spadá do jednoho předmětu, ale předmět může obsahovat více témat.
5.2 Volba databázového systému Databáze bude vytvořena v programu Microsoft Access z kancelářského balíku Microsoft Office. Pro Access jsem se rozhodl, protože je nenáročný a jednoduchý na použití. Při výběru jiného databázového systému by mohlo být nutné nainstalovat další prostředky, které alternativní databázový systém může požadovat. Tento krok může být pro běžného uživatele obtížný a může dojít k situaci, kdy si s instalací nedokáže poradit. Při využití databáze vytvořené v programu Access tento krok naprosto odpadá a není vyžadována žádná další instalace.
5.3 Implementace databáze Třída ConnectoinD zajišťuje v aplikaci propojení s databází. Na tuto třídu je použit návrhový vzor singleton. Tento návrhový vzor se v programování používá, pokud chceme, aby v celém programu se vyskytovala pouze jedna instance této třídy. To zaručí, že třída bude mít vytvořenou jednu jedinou instanci a zároveň k ní budeme mít globální přístup v rámci jmenného prostoru, ve kterém se třída vyskytuje. Třída je implementována následujícím způsobem. Konstruktor je nastaven jakou soukromý, tím je zaručeno, že vytvoření instance třídy je možné pouze v metodě Instance. Vytvoření instance se probíhá uvnitř třídy a její odkaz je nastaven jako statický atribut. Instance má dále implementovanou statickou metodu get, která vytvoří a vrátí instanci třídy. Pokud je instance již vytvořena, tak ji pouze vrátí. Hlavní proměnou této třídy je conn která je datového typu OleDbConnection, tato proměnná zprostředkovává spojení aplikace a databáze. Dále se ve třídě vyskytují dvě metody a to
GetConnection() a Connect(). Metoda Connect() má 25
návratový typ void a naváže spojení s databází. Metoda getConnection() má návratový typ OleDbConnection a vrací připojení k databázi. Celá třída je napsaná tak, že pro připojení používá rozhraní OleDb a pracuje s databází MS Access. Vytváření instance třídy ConnectionD: public static ConnectionD instance; public static ConnectionD Instance { get { if (instance == null) { instance = new ConnectionD(); } return instance; } }
Metoda pro připojení k databázi: public void Connect() { if (conn != null) { conn.Close(); } conn = new OleDbConnection(conn_str); DataModerator.setConnection(); try { conn.Open(); } catch(Exception){throw;} }
Třída DataModerator provádí veškeré operace nad databází (insert, delete, update). Uvnitř třídy se nacházejí pouze statické metody a statické proměnné. Je to z důvodu, že třída nic neuchovává, pouze provádí operace s tabulkami. Vkládá, aktualizuje a vrací data z databáze. Dalším neméně důležitým úkolem této třídy je plnění zásobníku pro sestavení testu daty. Pro připojení k databázi využívá třídu ConnectionD, která zajišťuje spojení s databází. Pro přehlednost nikde jinde v programu nenajdeme metodu, která by pracovala s SQL dotazem.
26
Ukázka metody z třídy DataModerator. Jak je vidět metoda je statická a vrací datový typ int. V databázi je nastavena automatická inkrementace id. Metoda vloží záznam a vrátí hodnotu id právě vloženého záznamu. Vše je uzavřeno v bloku try, který při chybě zachytává výjimku. Ta je v případě kolize kódu zachycena a předána k dalšímu zpracování. public static int AddSubjects(Subject sb) { try { cmd = new OleDbCommand("INSERT INTO Predmet(nazev_predmet) VALUES('" + sb.Subject_name + "')", conn); cmd.ExecuteNonQuery(); cmd.CommandText = "Select @@Identity"; return (int)cmd.ExecuteScalar(); } catch (Exception) { throw; } }
Třídy reprezentující tabulky v databázi jsou k nalezení v projektu ve složce Table_class. Každá z těchto tříd reprezentuje jeden záznam (řádek) v tabulce, tudíž její členské proměnné odpovídají atributům v databázi. Z toho plyne, že datový typ proměnné je stejný jako datový typ atributu v databázi. Ve třídě se nachází pouze členské proměnné a konstruktor. Jedná se o třídy: Answer, Dificult, Question, Subject a Topic. Třída Answer reprezentuje tabulku Odpoved, třída Difficult reprezentuje tabulku Obtiznost, třída Question reprezentuje tabulku Otazka, třída Subject reprezentuje tabulku Predmet a nakonec třída Topic reprezentuje tabulku Tema. Třídy se používají pro předávání dat metodám ostatních tříd, nejčastěji v kolekcích.
27
6 Práce s XML 6.1 Teorie XML XML tedy eXtensible Markup Language je značkovací jazyk který se se používá pro ukládání nebo přenášení informací. Syntaxe jazyka XML připomíná HTML (také značkovací jazyk). Avšak pravidla pro práci XML jsou daleko přísnější. Velikou výhodou je, že XML může být použito k výměně dat mezi dvěma naprosto nesourodými systémy. XML bylo sestrojeno k popisu dat, tagy na rozdíl od HTML nejsou předdefinované, uživatel si musí předdefinovat vlastní. Syntaktická pravidla XML musí být vždy stoprocentně dodržena, avšak jsou velice jednoduchá. Zde je jednoduchý příklad XML. a) 1 + 2 = 1 b) 1 + 2 = 2 c) 1 + 2 = 3
První řádek souboru definuje verzi XML a použité kódování znaků. Druhá řádka definuje rodičovský uzel (root). Rodičovský element obsahuje dva atributy. Atribut text_question představuje text otázky a atribut txt_answer říká, jestli se jedná o otázku se slovní odpovědí. Kořenový uzel obsahuje další tři vnořené uzly, takzvané potomky (child). Tyto uzly obsahují atribut true_answer, který říká, jestli se jedná o správnou odpověď. Poslední řádka definuje konec rodiče. V XML dokumentu na rozdíl od HTML musí být každý element uzavřen a obsahovat uzavírací tag jak je vidět na předchozím příkladu. S výjimkou deklarace. Tyto tagy rozlišují malá a velká písmena. Elementy musí být v sobě vhodně vnořené, ukončovací tagy se nesmějí křížit. V dokumentu musí být pár k definici rodičovského elementu, na předchozím příkladu to je element question. Tento element může obsahovat další potomky. Atributy všech elementů musí být uzavřeny do uvozovek.
28
“Při práci s dokumenty v XML se obvykle využívají analyzátory, parsery, které pracují buď s rozhraním SAX (Simple API for XML, jednoduché rozhraní pro jazyk XML) nebo DOM (Document Object Model). “ citováno z [1] DOM nahlíží na celý XML dokument jako na objektovou stromovou strukturu. Elementy nejsou vnímány, tak jak jdou za sebou ale, jak jsou do sebe vnořené. Program pracuje s jednotlivými uzly, a dotazuje se na pod elementy těchto uzlů. Aby mohla tato struktura fungovat, musí být nejdříve celý dokument načten, aby program věděl, kde který element začíná, a kde končí. Na našem příkladu nyní předvedeme ukázku práce s XML. V následujícím příkladu vypíšeme název otázky a k ní všechny příslušné odpovědi. K vypsání použije architekturu DOM. XmlDocument document = new XmlDocument(); document.Load("example.xml"); XmlNode root = document.DocumentElement; Console.WriteLine(((XmlElement)root).GetAttribute("text_question")); foreach(XmlNode node in root.ChildNodes) { Console.WriteLine(node.InnerText); }
Nejprve vytvoříme instanci třídy XmlDocument, a poté pomocí metody Load načteme XML dokument. Nyní musíme získat kořenový element dokumentu. Vytvoříme si uzel (XmlNode), a tomu přiřadíme kořenový element. Uzel přetypujeme na element, tím získáme přístup k metodě GetAttributte a vypíšeme název otázky. V cyklu foreach procházíme všechny potomky a uzlu root, a vypisujeme text mezi tagy. SAX můžeme vnímat jako nástavbu čtení textových souborů. Elementy jsou vnímány tak, jak jdou za sebou. Stromová struktura je tedy ignorována. Platforma .NET poskytuje třídu XmlWriter, která odlišuje zápis do XML od zápisu do textových souborů. Rozhraním SAX pracuje pouze s elementy.
29
Na dalším příkladu si předvedeme výpis XML dokumentu s použitím rozhraní SAX. XmlReader reader = XmlReader.Create("example.xml"); while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element) { if (reader.Name == "question") { Console.WriteLine (reader.GetAttribute("text_question")); } } elseif (reader.NodeType == XmlNodeType.Text) { Console.WriteLine(reader.Value); } }
Ke čtení pomocí rozhraním SAX použijeme třídu XmlReader. Při vytvoření její instance jako parametr uvedeme cestu k XML souboru. Metodou Read začneme načítat soubor řádek po řádku. Když narazíme na element, zjistíme jeho jméno. Pokud se jedná o požadovaný element, vypíšeme jeho atribut. Když narazíme na text, vypíšeme ho. Další možnost je pracovat s XML pomocí serializeru, deserializeru. Touto metodou se ukládají samotné objekty. Možnosti tohoto postupu jsou, bohužel, značně omezené. Pomocí serializeru nelze ukládat kolekce objektů a práce s ukazateli je tím dost stížená. Ukládají se pouze veřejně přístupné datové složky. Takto ukládané třídy musejí mít definovaný veřejně přístupný bezparametrický konstruktor. Na následujícím příkladu si předvedeme jak objekt uložit a načíst do XML souboru. Mějme jednoduchou třídu Answer, s kterou budeme pracovat. public class { public public public public public }
Answer Answer() { } int Id {get; set;} int Id_question {get; set;} string Answer_txt {get; set;} override string ToString() { return Answer_txt; }
30
Nyní vytvoříme instanci třídy Answer a uložíme ji do XML souboru. Answer record = new Answer(); record.Id = 1; record.Id_question = 1; record.Answer_txt = "odpověď"; StreamWriter writer = new StreamWriter("example2.xml",false); XmlSerializer serializer = new XmlSerializer(typeof(Answer)); serializer.Serialize(writer, record);
Vytvoříme si zapisovací nástroj StreamWriter, kterému jako parametr předáme jméno souboru a nastavíme kódování. Konstruktoru třídy XmlSerialize je nutné jako vstupní parametr předat datový typ ukládaného objektu. Výstup programu pak vypadá takto. 1 1 odpověď
Nyní tento soubor znovu načteme. StreamReader reader = new StreamReader("example2.xml"); XmlSerializer serializer = new XmlSerializer(typeof(Answer)); Answer record = (Answer)serializer.Deserialize(reader);
Vytvoříme si snímací nástroj StreamReader, a jako parametr mu předáme název načítaného souboru. Dále se vytvoříme instanci třídy Answer, a pomocí metody Deserialize, která vrací odkaz na object ji přiřadíme odkaz. Návratový typ metody Deserialize musí být přetypován na Answer.
6.2 Třída XMLModerator Veškerou práci s XML soubory zprostředkovává třída XMLModerator. Tato třída je implementována podle návrhového vzoru singleton (stejně jako třída ConnectionD). Třída při spuštění aplikace a při každé změně nastavení načítá data z konfiguračního souboru. Tyto data jsou pak uložena do statických členských proměnných třídy. Tato data jsou využívána na více místech v aplikaci, a proto je třeba mít k nim přímý přístup. Před samotným načtením je každý XML soubor překontrolován. Ke kontrole slouží atribut control_string v kořenovém uzlu. Pokud tento atribut není nalezen, nebo
31
jeho hodnota neodpovídá požadované hodnotě, je vygenerována vlastní výjimka WrongStringException. Vlastní výjimka. public class WrongStringException : Exception { }
6.3 Nastavení aplikace Aplikace má uloženo nastavení v jednoduchém XML souboru. Soubor je pojmenován config, a nachází se v kořenovém adresáři spolu s aplikací. Soubor vždy obsahuje tři elementy plus kořenový element. První element v sobě uchovává informaci o cestě k databázi. Druhý element uchovává informaci o cestě ke složce, do které se ukládá vše, co aplikace vygeneruje (testy, řešení, zálohy), a poslední element říká, zda se mají zobrazovat výstražná a varovná okna při práci s databází. Příklad konfiguračního XML souboru. <path_db>C:\Users\root\Documents\Database.mdb <path_file>C:\Users\root\Documents <show_warning show="False"/>
Pro práci s konfiguračním souborem třída obsahuje tři metody. První je metoda MakeConfig(). Tato metoda vytvoří konfigurační soubor, jako vstupní parametry očekává řetězec string, který reprezentuje cestu k databázi. Druhý řetězec string, reprezentuje cestu ke složce, do které se ukládá vše, co aplikace vygeneruje. Poslední je datový typ bool, ten říká, jestli se mají zobrazovat upozornění při práci s databází. Metoda UpdateConfig() upravuje záznamy v již vytvořeném souboru. Vstupní parametry jsou stejné jako u předchozí metody. Poslední metoda ReadConfig() čte data ze souboru a ukládá je do členských proměnných třídy. Do této metody nevstupují žádné parametry. Všechny metody pracující s konfiguračním souborem mají návratový typ void.
32
6.4 Uložení vygenerovaných testů Aplikace dokáže testy, řešení a oblasti ukládat do XML souboru a z těchto souborů je zpětně načíst. Veškerá práce se soubory je realizována pomocí rozhraní DOM. Všechna data, která jsou exportována do souborů, jsou realizována třídami, které jsou uložené v kolekcích. Rozhraní DOM je pro práci s kolekcemi mnohem pohodlnější než SAX. Do XML se ukládá instance třídy MyTest. Tato třída obsahuje veškeré informace týkající se testu. Tj. holý test, kolekci témat, kolekci obtížností, předmět, cestu pro uložení testu, hlavičku testu, informaci jestli se jedná o řešení testu, počet otázek, počet otázek se slovní odpovědí a počet odpovědí. Třída obsahuje tři konstruktory, první pro vytvoření testu, druhý a třetí pro načtení testu, řešení nebo oblasti z XML souboru. Pro práci s testy a řešeními má třída implementované dvě metody. Test a řešení je vlastně to samé, liší se pouze uložená správná odpověď (je zvýrazněná) a hodnota proměnné rated. Metoda, která ukládá test do XML, se jmenuje TestToXml(). Tato metoda má jeden vstupní parametr a to instanci třídy MyTest. Do kořenového elementu jsou vnořeny tři atributy. První atribut říká, jestli se jedná o test nebo řešení, v druhém je uložen název předmětu a ve třetím je control_string. Ten říká aplikaci, co je v souboru uloženo (test, řešení, oblast, databáze). Ukládání samotného testu probíhá ve dvou vnořených cyklech. První cyklus vytvoří uzel, který reprezentuje otázku a druhý cyklus k tomuto uzlu připojí další uzly, které reprezentují odpověď. Výstup může vypadat například takto. a) 1 + 3 = 3 b) 1 + 3 = 4 a) 1 + 4 = 5 b) 1 + 4 = 11
33
Pro čtení souboru je implementována metoda ReadTest(string
path).
Vstupním parametrem této metody je cesta k souboru. Z tohoto souboru je nečten kořenový element. A stejným, postupem je celý test načten a uložen do instance třídy MyTest. Uložení a čtení oblasti zajišťují metody OptionToXml() a ReadOption(). Ukládání probíhá velmi podobným způsobem jako u předešlých metod. Do kořenového uzlu se vloží elementy control_string, předmět a datum. Ke kořenovému uzlu se připojí uzel Topic, a k tomuto uzlu jsou připojeny uzly s názvem obtížnosti a id. Další uzel, který se připojuje ke kořenovému uzlu, obsahuje uzly s názvem obtížností. Poslední uzel s názvem figure obsahuje tři atributy. První je počet otázek se slovní odpovědí, druhý je počet odpovědí a třetí je počet otázek.
6.5 Záloha databáze Záloha databáze v aplikaci je realizována pomocí XML. Při implementaci se nabízel způsob načíst všechna data z databáze uložit do XML za pomocí rozhraní SAX. Data by byla v XML uložena jako objekty, a jejich načtení by bylo velmi snadné. Ovšem v databázi jsou všechny záznamy na sebe vázány primárním klíčem, který se automaticky inkrementuje a je uložen v členské proměnné objektu. To znamená, že při obnově databáze by neodpovídali primární klíče cizím klíčům. Navíc hodnotu primárního klíče nezadává uživatel, ale stará se o ni databáze. Dalším problémem by bylo v použití již implementovaných metod. Ty vracejí záznamy v kolekcích. S kolekcemi nedokáže serializer efektivně pracovat. Proto jsem se rozhodl navrhnout vlastní strukturu XML dokumentu. Pro čtení a ukládání jsem použil rozhraní DOM. Struktura dokumentu začíná kořenovým elementem, který je pojmenován database a obsahuje jeden parametr control_string. Ke kořenovému elementu jsou dále připojeny záznamy všech obtížností. A poslední elementy, které jsou připnuty ke kořenu, jsou předměty. Element předmět obsahuje parametr id a název předmětu. Id uváděná ve všech předmětech májí pouze informativní charakter. K předmětu jsou připojena všechna témata. Element téma obsahuje parametry id, jméno tématu, id předmětu a kapitolu. K tématu jsou připojeny všechny otázky, spadající pod téma. Element otázka obsahuje parametry: id, znění otázky, slovní odpověď, id předmětu a id obtížnosti. Uzel otázka obsahuje dva typy elementů a to obtížnost a odpověď. 34
Element obtížnost se v otázce vyskytuje jen jednou. Tento element se v dokumentu opakuje, protože kdyby nebyly na začátku uvedeny všechny obtížnosti a všechny otázky by byly ohodnoceny například pouze jednou obtížností. Do tabulky obtížnost by byl vložen pouze jeden záznam, a otázky by nešlo ohodnotit více obtížnostmi. Element odpověď obsahuje atributy id, označení správné odpovědi, id otázky a znění odpovědi. Příklad zálohy databáze do XML. Databáze obsahuje jeden předmět, jedno téma, jednu otázku a dvě odpovědi. <subject id="124" subject_name="Matematika"> dva tři
Pro vytvoření zálohy a obnovu zálohy jsou ve třídě XMLModerator implementovány metody SaveDbToXml() a LoadDbFromXml(). Tyto metody očekávají jako vstupní parametr cestu k souboru. Aplikace se pokusí z kořenového elementu získat atribut control_string. Pokud není atribut nalezen, nebo je chybný, metoda generuje výjimku WrongStringException. Pokud atribut odpovídá požadavku, metoda pokračuje. Při ukládání se prvně uloží všechny záznamy z tabulky obtížnost. Pak probíhá ukládání ve čtyřech v sobě vnořených cyklech. Každý cyklus prochází jednu tabulku, a v jeho těle jsou vkládány záznamy. Prvně je vytvořen element předmět, k tomuto elementu jsou připojeny elementy reprezentující téma. K elementu téma jsou připojeny elementy otázka a k elementu otázka je připojen jeden element obtížnost a elementy odpověď. Celý postup je znázorněn na obr. č. 6.
35
Obrázek 6: Algoritmus vytváření zálohy databáze do XML souboru
Načítání probíhá ve stejném pořadí jako ukládání. Při načítání je nejprve načten celý dokument jako celek. Pak jsou postupně procházeny uzly, které jsou přetypovávány na elementy. Z elementu jsou vytvořeny objekty, a ty jsou následně vkládány do databáze. Po vložení záznamu do databáze je ihned zjištěno jeho nové id, a to je použito pro všechny související záznamy. Při načítání jsou nejprve načteny všechny obtížnosti. Pak je vyhledáván uzel předmět, ten je přetypován na element. Z tohoto elementu je vytvořen objekt a vložen do databáze. Po vložení metoda vrátí id vloženého záznamu. Id je používáno se všemi souvisejícími záznamy. Z uzlu předmět je načten uzel téma, přetypován, uložen do databáze a je uloženo jeho id. Z uzlu téma je načten uzel otázka. Uzel je přetypován na element a z elementu je vytvořen objekt s defaultní obtížností, který je vložen do databáze a je uloženo jeho id. Tento objekt je nahrazen novým objektem, který je 36
doplněný o id. Kdyby nebyl objekt nahrazen, došlo by ke kolizi primárních klíčů. Nyní je z uzlu otázka načten uzel obtížnost a u záznamu otázka je upravena obtížnost. Poslední načítaný uzel je odpověď načítaný z uzlu otázka. Uzel je přetypován na element, je z něj vytvořen objekt a je vložen do databáze. Tento postup je opakován, dokud nedojdeme na konec XML dokumentu. Jak je znázorněno na obr č. 7.
Obrázek 7: Algoritmus obnovy databáze z XML souboru.
37
7 Práce s PDF 7.1 Knihovna PDFsharp V současné době Microsoft nenabízí žádnou knihovnu, která by programátorům umožňovala pracovat s PDF soubory. Naštěstí existuje široká podpora z třetích stran. Je možné najít mnoho knihoven placených, ale i zdarma. Pro svou aplikaci, jsem si vybral neplacenou knihovnu. U neplacených knihoven bývá práce s dokumenty poněkud složitější, ale jejich možnosti jsou široké a převyšují nároky mé aplikace. Po svou aplikaci jsem si vybral knihovnu PDFsharp. Tato knihovna umožňuje práci s PDF soubory v prostředí .NET. Jedná se o open source knihovnu, která může být využita k vytvoření PDF dokumentů, kreslení nebo posílání výstupů do tiskárny. Pro správnou funkci je nutné knihovnu přidat do referencí projektu. Součástí balíčku je knihovna Migradoc, která je určená k vytváření tabulek a složitějších textových prvků.
7.2 Třída PdfBuilder V celém projektu se nachází pouze jedna třída, která zprostředkovává práci s PDF soubory. Tato třída se jmenuje PdfBuilder. Třída obsahuje jeden konstruktor, který očekává instanci třídy MyTest. Pro práci s pdf soubory jsou ve třídě implementovány tři metody. Metoda MakePdfTest() vytváří test nebo řešení testu, metoda MakePdfOption() vytváří oblast testu. Metoda GetRect() vrací velikost textového pole. Poslední metoda, která se ve třídě nachází, se jmenuje IsTest(), tato metoda vrací hodnotu true, pokud se jedná o test nebo řešení a hodnotu false, pokud se jedná o oblast testu. Proměnné a hlavičky metod třídy PdfBuilder. class PdfBuilder { public PdfBuilder(MyTest source){…} private MyTest source; private string path; public void MakePdfTest(){…} public XRect GetRect(double y, string str){…} public void MakePdfOption(){…} public bool IsTest(){…} }
38
7.3 Zásady vkládání textu do dokumentu Pro správné vložení textového řetězce musí být vložena dostatečně velká textová oblast a použita třída, která dokáže zalamovat text. Pro vypočítání textové oblasti je implementována metoda GetRect(). Metoda očekává jako vstupní parametr souřadnici y v dokumentu a vkládaný řetězec. Vrací textovou oblast (XRect). Podle fontu jsem vypočítal, že optimální počet znaků na řádek je 95. Pokud je řetězec kratší nebo roven 95 znakům, je vytvořena textová oblast odpovídající jednomu řádku. Pokud je řetězec delší, vydělí se délka řetězce 95, zaokrouhlí nahoru a vynásobí výškou fontu. Pro úplnou jistotu se ještě přičte jeden řádek, aby nedošlo k oříznutí textu. Příklad vládání textové oblasti a textu. rect = GetRect(y, i.text_question); graph.DrawRectangle(XBrushes.Beige, rect); tf.DrawString(i.text_question, font_q, XBrushes.Black, rect, XStringFormats.TopLeft);
Na prvním řádku je inicializována proměnná rect (textová oblast) metodou GetRect(). Na druhém řádku je metodou DrawRectangle() třídy XGraphic vložena
textová
oblast
do
dokumentu.
Na
posledním
řádku
je
metodou
DrawString() třídy XTextFormatter vložen text. První parametr metody je vkládaný string dále následuje font písma, barva písma, textová oblast a zarovnání textu. Pro vkládání dlouhého textu musí být požita třída XTextFormatter, protože třída XGraphic neobsahuje metodu, která by dokázala zalamovat text v textové oblasti. Dále pro správnou vizuální podobu dokumentu musí být provedeno odřádkování. Tím je myšleno přičtením nějaké hodnoty k souřadnici y v dokumentu. Přičítaná hodnota nemůže být konstantní, každá textová oblast má jiný rozměr, a mohlo by dojít k překrývání těchto oblastí. Proto k ose y je přičítána aktuální souřadnice y (souřadnice začátku textové oblasti) plus výška textové oblasti. To zaručí, že textové oblasti se nebudou nikdy překrývat. Odřádkování probíhá po každém vložení textové oblasti do dokumentu. Na následujícím příkladu odřádkování je krom zmíněného vidět ještě přičtení dvou milimetrů, které vytvoří malý prostor mezi textovými oblastmi. K přepočtu na centimetry/milimetry je použita statická metoda FromCentimeter() struktury 39
XUnit. Tato metoda očekává jako vstupní parametr hodnotu double, kterou vrací přepočítanou na centimetry. y = rect.Y + rect.Height + XUnit.FromCentimeter(0.2).Point ;
7.4 Export testu a řešení do PDF Vytvoření testu a řeší je vlastně úplně to samé. Řešení je rozdílné v tom, že má vyznačenou
správnou
MakePdfTest().
odpověď.
Prvně
K tomuto
z instance
třídy
je
implementována
MyTest
za
pomocí
metoda metody
GetStockTest() je uložen holý test. Poté je vytvořen PDF dokument a jsou v něm nastaveny rozměry odpovídající formátu A4 a kódování. V dokumentu se nachází 5 rozdílných typů písma pro: nadpis, hlavičku, otázku, odpověď a správnou odpověď v řešení (kurzíva). Prvně je do testu vložen nadpis definovaný uživatelem. Pak jsou vloženy tři vedle sebe černě ohraničené textové oblasti. Oblasti se dají popsat jako tabulka o jednom řádku a třech sloupcích. Každá buňka má v levém horním rohu umístěn text (jméno a příjmení, obor, datum). Tato tabulka slouží k identifikaci studenta. Poté následuje vkládání otázek a opovědí ve dvou v sobě vnořených cyklech. Ve vnějším cyklu jsou vkládány otázky a ve vnitřním jsou vkládány odpovědi. Vždy na začátku průchodu vnějším cyklem se metoda ptá, jestli už stránka není „celá zaplněná“. Zaplněnost je rozpoznána podle osy y (plná stránka = 680 bodů). Pokud se jedná o první zápis na novou stranu, je vloženo číslo strany. Nyní přichází na řadu vložení otázky. Prvně je vytvořena a vložena do dokumentu textová oblast. A následně do této oblasti je vložena otázka. Po vložení otázky následuje druhý vnitřní cyklus, který vloží všechny odpovědi k otázce. Na začátku cyklu je podmínka, která testuje, jestli se jedná o otázku se slovní odpovědí. Jestliže ano, vloží textovou oblast o velikosti (délce) odpovědi plus dva řádky navíc. Pokud ne, je vložena klasická odpověď a), b), c)… stejným způsobem jako u otázky, pouze s odlišným fontem. Při vytváření řešení testu je u slovní odpovědi na místo prázdné oblasti vloženo znění odpovědi a u klasické otázky s možnostmi je správná odpověď zvýrazněna kurzívou. 40
Po skončení hlavního cyklu je dokument uložen na místo v proměnné path prostřednictvím metody Save() třídy PdfDocument. Tato metoda očekává jeden vstupní parametr, a to string, který obsahuje název souboru. Pokud nechceme, aby soubor byl uložen do kořenového adresáře aplikace, musí být před název přidána požadovaná cesta. Postup exportu do PDF souboru je znázorněn na obr č. 8.
Obrázek 8: Export testu nebo řešení do PDF souboru
41
7.5 Export oblasti do PDF Export
oblasti
do
PDF
souboru
probíhá
v metodě
MakePdfOption().
Začátek metody je velmi podobný metodě MakePdfTest(). Prvně je vytvořen dokument, přidána stránka a nastaven rozměr odpovídající formátu A4. Pak je nastaveno kódování a vytvořeny dva typy písma. První písmo je normální pro text a druhé je tučné pro nadpis. Následuje vložení názvu předmětu a poté data. Je vložen nadpis „Témata“ a spuštěn cyklus, v kterém jsou všechny použitá témata vypsány. Cyklus prochází kolekci témat, kterou vrací metoda GetTopic() ve třídě MyTest(). Dále je vložen nadpis „Obtížnost“ a následuje další cyklus, který projde kolekci obtížností, kterou vrací metoda GetDifficult() ve třídě MyTest. Jako poslední je vložen počet: otázek, maximální počet odpovědí a počet otázek se slovní odpovědí. Na konci metody je dokument uložen na místo, které je uloženo v proměnné path. Průchod metodou je znázorněn na obr. č. 9.
42
Obrázek 9: Export řešení do PDF
43
8 Prostředí aplikace 8.1 Menu, hlavní okno aplikace a dětská okna V hlavním okně aplikace na horní straně se nachází menu, které je ve všech oknech totožné. Toto menu není běžná komponenta, ale je ručně naprogramováno. Klasické menu z výběru komponent má okázalejší vzhled a je překrýváno dětskými okny. Toto menu je vytvářeno v metodě CreateMyMainMenu(). V této metodě se nachází instance třídy MainMenu, která představuje naše menu. Při vytváření menu je nutné vytvořit instanci třídy MenuItem, která představuje položku menu. Vlastnost Text představuje název položky. Dále je nutné přidat událost Click, která vyvolá nějakou část kódu po kliknutí na položku. Pomocí metody Add třídy MainMenu je položka přidána do menu. Do položky pomocí stejné metody může být vnořena další položka. Nakonec je nutné menu připojit k formuláři. Příklad jednoduchého menu, výsledek je vidět na obr. č. 10. MainMenu MenuItem MenuItem MenuItem MenuItem
mainMenu1 = new MainMenu(); menuItemS = new MenuItem(); item_end = new MenuItem(); item_refresh = new MenuItem(); item_back_db = new MenuItem();
menuItemS.Text = "Soubor"; item_end.Text = "Konec"; item_refresh.Text = "Aktualizuj stav"; item_back_db.Text = "Správa databáze"; menuItemS.MenuItems.Add(item_refresh); menuItemS.MenuItems.Add(item_back_db); menuItemS.MenuItems.Add(item_end); mainMenu1.MenuItems.Add(menuItemS); this.Menu = mainMenu1;
Obrázek 10: Ukázka menu
44
Hierarchie menu aplikace:
Soubor o Aktualizuj stav – načte konfigurační soubor a obnoví spojení s databází o Správa databáze – otevře okno pro správu databáze o Konec – ukončí aplikaci
Agendy o Agenda Otázky a Odpovědi – otevře okno pro správu otázek a odpovědí o Agenda Předměty – otevře okno pro správu předmětů o Agenda Témata – otevře okno pro správu témat
Test o Generovat test – otevře okno pro generování testu o Vytvořit test nebo oblast z xml – otevře okno pro export xml do pdf
Nastavení – otevře okno pro nastavení aplikace
O aplikaci – otevře okno o aplikaci
Hlavní okno aplikace se jmenuje Main_form. V levém horním rohu se nacházejí groupboxy s textem, které informují uživatele, jestli byl načten konfigurační soubor a jestli bylo navázáno spojení s databází. Na první pohled je formulář dosti strohý, ale jsou v něm implementovány všechny události obsluhující menu a obsahuje metodu, která obsluhuje čtení konfigurace a spojení s databází. Na spodní liště je datum a čas, kdy byla tato metoda naposledy zavolána. Horní titulková lišta obsahuje dynamicky měnící se text podle právě otevřeného okna. Okno je vidět na obr. č. 11.
45
Obrázek 11: Hlavní formulář
Všechna nově otevřená okna jsou zobrazovány v hlavním okně) se skrytou titulkovou lištou (s výjimkou oken O aplikaci a Náhled testu). Takto otevřenému oknu se říká dětské okno (je zobrazováno ve svém rodiči). Před samotným zobrazením dětského okna jsou zkontrolována všechna otevřená okna, je zbytečné, aby byla otevřena dvě stejná okna současně. Jestli je nalezeno otevřené stejné okno, zobrazení okna neproběhne. Pokud okno otevřeno není, jsou zavřena všechna dětská okna. Zase je zbytečné, aby bylo otevřeno více oken najednou. Více otevřených oken zvyšuje možnost výskytu chyby a zhoršuje orientaci v aplikaci. Veškerý kód, v kterém může vzniknout nějaká výjimka je uzavřen do bloků try-catch. Při vzniku chyby je uživatel informován prostřednictvím chybového hlášení, ve kterém je popsána příčina chyby, a důvod proč daná chyba vznikla.
46
Na následujícím příkladu ukážu, jakým způsobem lze zabránit otevření dvou oken stejného typu najednou.
foreach (Form form in Application.OpenForms) { if (form.GetType() == typeof(GenTest_Form)) { return; } }
Na začátek metody, v které je zobrazováno okno GenTest_Form vložíme tento kousek kódu. Cyklus prochází všechna otevřená okna v aplikaci a narazí-li na okno, které má datový typ GenTest_Form, zavolá příkaz return a metoda je ukončena ještě před zobrazením okna. Následující příklad zavírá všechna dětská okna. for (int i = Application.OpenForms.Count - 1; i >= 0; i--) { if (Application.OpenForms[i].GetType() != typeof(Main_form)) Application.OpenForms[i].Close(); }
Cyklus prochází všechna otevřená okna v aplikaci. V podmínce se testuje datový typ otevřeného okna, pokud je rozdílný od okna Main_Form (rodičovské okno), okno je zavřeno. Dětské okno zobrazováno ve svém rodiči, musí mít nastavenou vlastnost TopLevel na false. Dále mu musí být přiřazen rodič. Aby okno zakrylo veškerý prostor svého rodiče, musí být otevřeno v maximalizované podobě a přeneseno do popředí. Problém znázorňuje ukázka kódu. První zvýraznění řádek kódu přiřazuje potomkovi rodiče a druhý otevírá dětské okno maximalizované ve svém rodiči. GenTest_Form window = new GenTest_Form(); window.TopLevel = false; window.Parent = this; window.Show(); window.WindowState = FormWindowState.Maximized; window.BringToFront();
47
8.2 Formulář pro zálohu databáze Okno pro zálohu databáze je zobrazeno po kliknutí na položku menu Správa databáze v položce Soubor. V levém horním rohu se nachází panel se čtyřmi tlačítky, pod ním se nachází už pouze jeden textbox, do kterého se zadává jméno pro zálohu. Tlačítko Vytvoř zálohu databáze vyvolá okno pro výběr cesty, kam bude záloha uložena. Po kliknutí na tlačítko OK je záloha vytvořena. Pokud není textbox vyplněn, neotevře se dialogové okno pro výběr cesty. Tlačítko Obnovit databázi otevře dialogové okno pro výběr souboru s přednastaveným filtrem pro soubory s příponou xml. Po vybrání souboru je celá databáze smazána a nahrazena zálohou. Tlačítko Vymazat obsah databáze vymaže celou databázi a vloží defaultní záznamy do tabulky obtížnost. Hlavní ovládací prvky formuláře jsou vidět na obr. č. 12.
Obrázek 12: Hlavní ovládací prvky formuláře pro správu databáze
8.3 Agendy aplikace Pro správu databáze je implementováno okno Summary_Form. Na tomto okně se nachází komponenta TabControl se třemi záložkami. Okno se otevírá pouze s jednou zobrazenou záložkou. Záložka se zobrazuje podle výběru v menu Agendy (záložka1 = Agenda otázky a odpovědi, záložka2 = Agenda Předměty, záložka3 = Agenda Témata). Pro zobrazení záložky slouží trojici metod, které vyčistí kolekci TabPages třídy TabControl a přidají pouze jednu požadovanou záložku (TabPage), tyto metody jsou volány v hlavním okně v události Click dané položky menu. Každá záložka obsahuje komponenty pro zobrazení a editaci části databáze a svá vlastní tlačítka pro ukládání, mazání, vkládání a další operace. Dále je okno vybaveno automatickým ukládáním indexů vybraných komponent. V praxi po vložení nebo úpravě záznamu dojde k aktualizaci komponent a všechny SelectedIndex 48
aktualizovaných komponent se nastaví na nulu. To může být při vkládání nebo editaci většího množství záznamů velmi nepohodlné. Proto před každým aktualizováním komponent (vložení, mazání nebo editace záznamu) dojde k uložení vybraných indexů, teprve poté se provede akce a následně se provede obnova indexů. Po obnově indexů zůstanou v komponentách vybrány stejné položky jako před aktualizací. Výjimku tvoří mazání položek, po aktualizaci nemůže být smazaná položka vybrána, protože ji komponenta již neobsahuje. V takové komponentě je pak nastaven vybraný index na hodnotu 0. Tlačítka ve všech záložkách plní stejnou funkci. Tlačítko Uložit slouží pro editaci, uloží změny záznamu. Pokud je hodnota tlačítka Nový true a poté je kliknuto na tlačítko Uložit, je vložen nový záznam. Tlačítko Smazat smaže vybraný záznam. Tlačítko Vyčistit nastaví komponentám pro editaci základní hodnoty. Tlačítko Zavřít zavře okno. Záložka Agenda Otázky a Odpovědi Po kliknutí na položku menu Agenda Otázky a Odpovědi je zavolána metoda ShowQst() která zobrazí v TabControlu pouze tuto záložku. Tato záložka obsahuje komponenty pro práci s tabulkou Otazky a Odpovedi. Komponenty pro práci s těmito tabulkami jsou umístěny na jednom místě, protože po vložení otázky většinou následuje ihned vložení odpovědi a proces vkládání je tímto značně urychlen. Na obr. č. 13 je vidět okno Summary_form s vybranou záložkou „Agenda Otázky a Odpovědi“.
49
Obrázek 13: Agenda Otázky a Odpovědi
V levé části se nachází komponenty pro výběr otázky. Komponenty jsou mezi sebou provázány, takže po výběru předmětu v první roletce se naplní druhá roletka tématy patřící k předmětu. Podle vybrané položky v roletce s tématy se naplní první listbox otázkami. V levé straně se nacházejí komponenty pro editaci a vkládání nových záznamů. S výběrem položky v listboxu se naplní příslušná skupina komponent pro editaci a úpravu na pravé straně. Tlačítka Spravovat otázky a Spravovat Odpovědi určují, jestli první trojice tlačítek bude pracovat s otázkami nebo s odpověďmi. To je zajištěné vlastností Checked. Vždy jen jedno z těchto tlačítek může mít hodnotu true. Zároveň tyto tlačítka zakazují práci s komponentami pro editaci, které nejsou zrovna využívány. Záložka Agenda předměty Tato záložka je zobrazena po kliknutí na položku v menu Agenda předměty a zavoláním metody ShowSb(). Na záložce se nachází komponenty pro práci s tabulkou předměty jak je vidět na obr č. 14. Pro výběr předmětu slouží listbox u levého okraje a pro editaci textbox na pravé straně.
50
Obrázek 14: Agenda Předměty
Agenda Témata Záložka Agenda Témata se zobrazí po zavolání metody ShowTp(). Na této záložce se nacházejí komponenty pro editaci a procházení tabulky Tema. Záložka je zobrazena na obr č. 15.
51
Obrázek 15: Agenda Témata
8.4 Vytváření testů Test je možné vygenerovat, nebo obnovit ze zálohy v XML souboru. Okno pro generování testů se zobrazí po kliknutí na položku menu Generovat test. Po kliku se zobrazí formulář GenTest_Form. Na tomto formuláři se nachází komponenty, které slouží k zadávání parametrů, které určují výslednou podobu testu. Pro vygenerování testu je nutné vybrat jeden předmět a jedno nebo více témat. Po provedení výběru aplikace uživatele informuje, kolik je dostupných otázek odpovědí ve vybrané oblasti. Dále je nutné zvolit počet otázek a maximální počet odpovědí. V testu mohou být zahrnuty všechny obtížnosti otázek nebo jen některé. Vybráním položky v roletce se v testu objeví otázky s maximální obtížností vybrané položky. Poslední parametrem testu jsou otázky se slovní odpovědí. Okno pro vygenerování testu je vidět na obr. 16.
52
Obrázek 16: Okno Generovat test
Po vyplnění parametrů a kliknutí na tlačítko generovat test se zobrazí okno (TestPreview_Form). Po zobrazení okna má uživatel k dispozici náhled testu, řešení a oblasti. Pokud mu vygenerovaný test z jakéhokoliv důvodu nebude vyhotovovat, může okno jednoduše zavřít a ihned vygenerovat další test. Z tohoto důvodu toto okno není zobrazováno jako dětské, ale je zobrazeno modálně. V pravém dolním rohu se nachází skupina komponent, které určují parametry uložení. Uživatel může uložit test, řešení testu a oblast generování testu. Tyto dokumenty mohou být uloženy v PDF nebo XML formátu. Vytvořené soubory mají stejné jméno, které je doplněno příponou, která složí k rozpoznání souborů. Test má příponu „_test“, řešení má příponu „_řešení“ a oblast má příponu „_oblast“. Cesta, kam se soubory ukládají, je uložena v konfiguračním souboru aplikace. V tomto okně může být tato cesta jednorázově změněna, kliknutím na tlačítko Změň cestu. Kliknutím na tlačítko Uložit se
uloží
vybrané
dokumenty (test,
řešení,
oblast)
do
vybraných
formátů
(pdf, xml). Okno je vidět na obr. č. 17.
53
Obrázek 17: Náhled testu
Další možností je již vygenerovaný test v xml formátu převést do pdf formátu. K tomu slouží formulář XmlToPdf_Form. Po kliknutí na položku Vytvořit test nebo oblast z xml se zobrazí toto okno. V okně uživatel musí zadat jméno souboru a vybrat xml soubor, aplikace automaticky rozpozná, jestli se jedná o test nebo oblast. Po kliknutí na tlačítko uložit se zobrazí dialogové okno pro vybrání cesty. Kliknutím na tlačítko OK se soubor uloží. Hlavní ovládací prvky formuláře jsou vidět na obr. č. 18.
Obrázek 18: Hlavní ovládací prvky formuláře pro export pdf do xml
54
8.5 Nastavení aplikace Okno pro nastavení aplikace se zobrazí po kliknutí na položku Nastavení. V tomto okně lze nastavit cestu k databázi, cestu, kam se budou ukládat všechny soubory vytvořené aplikací a povolit nebo zakázat upozornění, která se zobrazují při práci s databází. Ve spodní liště je zobrazeno datum a čas, kdy bylo nastavení naposledy pozměněno. Výřez hlavní části okna pro nastavení je vidět na obr. č. 19.
Obrázek 19: Nastavení aplikace
55
9 Závěr Vytvořil jsem aplikaci podle zadání. Aplikace je schopná automaticky generovat testy, efektivně pracovat s pdf soubory, xml soubory a databází. Při vývoji jsem si vyzkoušel postup návrhu aplikace, který zahrnoval náčrt GUI aplikace, návrh algoritmů, návrh datové struktury, používání knihoven třetích stran, studium dokumentace a ladění chyb. Při samotném programování se opakovaně stávalo, že již implementované řešení se přestalo jevit jako ideální a musel jsem jej nahradit. Například když jsem zjistil, že implementace algoritmu pro náhodné generování pozic odpovědí většinu správných odpovědí umisťuje na poslední pozici, nebo že připojení k databázi je po aktualizaci ztraceno. Díky dodržení architektury MVC tyto úpravy aplikace nebyli rozsáhlé. Dodržením tohoto moderního postupu jsem získal cenné zkušenosti, jak postupovat pří vývoji aplikace a jak přísné oddělení vrstev zjednodušuje pozdější úpravy programu. Dále si práce kladla za cíl předvést a popsat možnosti práce s XML. Z dostupných zdrojů jsem si osvojil všechny publikované způsoby a demonstroval jednoduché příklady užití. Z těchto implementací, jsem se vynasnažil vybrat vždy tu nejvhodnější, a pokusit se objasnit, proč jsem ji použil. Při tom jsem navrhl vlastní strukturu XML dokumentu, která co nejvíce kopíruje databázovou strukturu. Uvědomil jsem si, jak je důležité umět používat debugovací nástroje, tj. nástroje pro ladění chyb a jak je důležité chápat princip ukládání data do paměti. Upevnil jsem si řešení problémů, jako je práce s databází, tvorba uživatelského prostředí a popis algoritmů pomocí vývojových diagramů. Pro tvorbu diagramu jsem použil nástroj Microsoft Visio 2013, který jsem mohl díky škole bezplatně využívat. Nástroj obsahuje mnoho šablon pro vývojové diagramy a práce s ním je velice efektivní. Pro vlastní vývoj aplikace jsem použil vývojové prostředí Microsoft Visual Studio 2013, které uživateli díky velkému množství nástrojů usnadňuje programování. Při návrhu uživatelského prostředí jsem se snažil, aby bylo atraktivní, přehledné a funkce komponent vyplynula z popisků. Vždy jsem informoval uživatele o výskytu chyby, a pokud to bylo možné, tak jsem sdělil, kde chyba vznikla a navrhl její řešení.
56
V dalších krocích může být aplikace doplněna následujícím vylepšením. Odesílat vygenerované testy přímo na e-mail, naprogramovat rozhraní pro komunikaci s e-learningem a vytvořit jiný pravděpodobnostní model pro generování testů. Z již implementovaných tříd pro generování testu vytvořit knihovnu (*. dll), která by usnadnila vývoj aplikací zabývajících se touto problematikou. Další modifikací může být rozšíření parametrů pro generování testu o procentuální pokrytí testu otázkami každého vybraného tématu.
57
Seznam použité literatury [1] MAREŠ, Amadeo. 1001 tipů a triků pro C# 2010. Brno: ComputerPress, 2011, 416 s. ISBN 978-80-251-3250-0. [2] PETZOLD, Charles. Programování Microsoft Windows Forms v jazyce C#. Vyd. 1. Překlad Karel Voráček. Brno: Computer Press, 2006, 356 s. ISBN 80-251-1058-3. [3] SHARP, John. Microsoft Visual C# 2010: krok za krokem. Vyd. 1. Brno: Computer Press, 2010, 696 s. ISBN 978-80-251-3147-3. [4] VIRIUS, Miroslav. C#: hotová řešení. Vyd. 1. Brno: ComputerPress, 2006, 341 s. ISBN 80-251-1084-2. [5] VIRIUS, Miroslav. C# 2010: hotová řešení. 1. vyd. Brno: ComputerPress, 2012, 424 s. K okamžitému použití (ComputerPress). ISBN 978-80-251-3730-7. [6] Devbook.cz - Programátorská sociální síť a materiálová základna pro C#, Java, PHP, HTML, CSS, JavaScript a další. [online]. 2014 [cit. 2014-05-12]. Dostupné z: http://www.devbook.cz/ [7] Microsoft
Developer
Network [online].
2014
[cit.
2014-05-12].
Dostupné
z: http://msdn.microsoft.com [8] W3Schools Online Web Tutorials [online]. 1999, 2014 [cit. 2014-05-01]. Dostupné z: http://www.w3schools.com/
58
Seznam obrázků Obrázek 1: Ukázka maximalizovaného okna aplikace ................................................... 11 Obrázek 2: Softwarová architektura model-view-controller .......................................... 13 Obrázek 3: Pole představující odpověď a správnost odpovědi ....................................... 16 Obrázek 4: Vývojový diagram znázorňující algoritmus generování testu ..................... 18 Obrázek 5: ERA model databáze .................................................................................... 23 Obrázek 6: Algoritmus vytváření zálohy databáze do XML souboru ............................ 36 Obrázek 7: Algoritmus obnovy databáze z XML souboru. ............................................ 37 Obrázek 8: Export testu nebo řešení do PDF souboru .................................................... 41 Obrázek 9: Export řešení do PDF ................................................................................... 43 Obrázek 10: Ukázka menu .............................................................................................. 44 Obrázek 11: Hlavní formulář .......................................................................................... 46 Obrázek 12: Hlavní ovládací prvky formuláře pro správu databáze .............................. 48 Obrázek 13: Agenda Otázky a Odpovědi ....................................................................... 50 Obrázek 14: Agenda Předměty ....................................................................................... 51 Obrázek 15: Agenda Témata .......................................................................................... 52 Obrázek 16: Okno Generovat test ................................................................................... 53 Obrázek 17: Náhled testu ................................................................................................ 54 Obrázek 18: Hlavní ovládací prvky formuláře pro export pdf do xml ........................... 54 Obrázek 19: Nastavení aplikace ..................................................................................... 55
59
Seznam použitých zkratek API
Application Programming Interface
ASCII
American Standard Code for Information Interchange
DOM
Document Object Model
GUI
Graphical User Interface
HTML
Hyper Text Markup Language
INT
Integer
MVC
Model View Controller
OLEDB
Object Linking Embedding Database
OOP
Objektově Orientované Programování
PC
Personal Computer
PDF
Portable Document Format
SAX
Simple API for XML
XML
Extensible Markup Language
60
Přílohy 1 Obsah přiloženého DVD Na přiloženém DVD se v kořenovém adresáři nachází tato bakalářská práce ve formátu bakalarska_prace.pdf s jednoduchým návodem navod.txt pro spuštění a obsluhu aplikace. V adresáři Generátor testů se nachází spustitelná verze vyvinuté aplikace. V adresáři Aplikace se nachází kompletní projekt se všemi zdrojovými kódy. V adresáři Ukázka xml se nachází zdrojové kódy demonstrující práci s XML.
61
2 Uživatelský manuál 2.1 První spuštění a nastavení aplikace Při prvním spuštění je nutné pro správnou funkci aplikace zadat cestu k databázi a vybrat adresář pro ukládání souborů. Postup nastavení je popsán níže. Formulář pro nastavení aplikace se zobrazí po kliknutí na položku menu Nastavení. V nastavení můžeme nastavit cestu k databázi a adresáři, do kterého se budou ukládat všechny vygenerované soubory a zapnout nebo vypnout upozornění při práci se záznamy v menu Agendy. Po kliknutí na tlačítko Cesta pro soubor se zobrazí dialogové okno, z kterého vybereme adresář pro ukládání souborů. Potvrdíme kliknutím na tlačítko OK. Po kliknutí na tlačítko Cesta pro DB se zobrazí dialogové okno, z kterého vybereme databázi (*.mdb). Potvrdíme kliknutím na tlačítko OK. Po každé změně nastavení je nutné kliknout na tlačítko Uložit nastavení. Ve spodní liště nás aplikace informuje, kdy bylo nastavení naposledy změněno.
2.2 Hlavní formulář Po spuštění aplikace se zobrazí hlavní formulář. V levém horním rohu formuláře jsou dva nápisy informující uživatele o načtení nastavení a spojení s databází. Kliknutím na položku Aktualizuj stav v záložce menu Soubor je spojení obnoveno. Ve spodní liště je uživatel informován, kdy bylo spojení naposledy navázáno.
2.3 Záloha a obnova databáze Formulář pro zálohu a obnovu databáze se zobrazí po kliknutí na položku Správa databáze v menu Soubor. Pro vytvoření zálohy musíme nejdříve zadat název zálohy, a poté kliknout na tlačítko Vytvořit zálohu databáze. Po kliknutí se zobrazí dialogové okno, ve kterém vybereme adresář pro uložení souboru. Po kliknutí na tlačítko OK je záloha vytvořena a uložena.
62
Pro obnovení zálohy klikneme na tlačítko Obnovit databázi a v zobrazeném dialogovém okně vybereme *. xml soubor obsahující zálohu. Po kliknutí na tlačítko OK je databáze ze souboru obnovena. Tlačítko Vymazat obsah databáze smaže všechny záznamy v databázi.
2.4 Vkládání, mazání a editace záznamů 2.4.1 Otázky a odpovědí Po kliknutí na položku Agenda otázky a odpovědi v menu Agendy se zobrazí formulář pro práci s otázkami a odpověďmi. Formulář má společné komponenty pro výběr a oddělené komponenty pro vkládání a editaci záznamů. Pokud chceme procovat s otázkami, klikneme na tlačítko Spravovat otázky, pokud chceme pracovat s odpověďmi, tak klikneme na tlačítko Spravovat odpovědi. Pro vložení nového záznamu vyplníme příslušné komponenty na pravé straně formuláře, klikneme na tlačítko Nový, a poté na tlačítko Uložit. Pokud je tlačítko Nový stisknuto (tučný popisek), vkládá tlačítko Uložit nové záznamy. Pro editaci vybereme záznam z komponent pro výběr na levé straně a upravíme jeho parametry. Ujistíme se, že tlačítko Nový není stisknuto (normální popisek) a klikneme na tlačítko Uložit. Pokud chceme záznam smazat, vybereme záznam z nabídky a klikneme na tlačítko Smazat. Pozor!: smazáním otázky dojde k smazání všech odpovědí spadajících pod příslušnou otázku.
2.4.2 Předměty Po kliknutí na položku Agenda Předměty v menu Agendy se zobrazí formulář pro práci s předměty. Pro vložení předmětu zadáme do textového pole název předmětu, klikneme na tlačítko Nový (tučný popisek), a poté na tlačítko Uložit.
63
Pro
editaci
záznamu
vybereme
z nabídky
požadovaný
předmět,
upravíme
název, ujistíme se, že tlačítko Nový není stisknuto (normální popisek) a klikneme na tlačítko Uložit. Pokud chceme záznam smazat, vybereme ho z nabídky a klikneme na tlačítko Smazat. Pozor!: Smazáním předmětu dojde k smazání všech témat, otázek a odpovědí spadajících do tohoto předmětu.
2.4.3 Témata Po kliknutí na položku Agenda Témata v menu Agendy se zobrazí formulář pro práci s tématy. Pro vložení tématu vyplníme příslušné komponenty na pravé straně formuláře, klikneme na tlačítko Nový (tučný popisek), a poté klikneme na tlačítko Uložit. Pro úpravu tématu vybereme téma z nabídky, upravíme parametry, ujistíme se, že tlačítko Nový není stisknuto (normální titulek) a klikneme na tlačítko Uložit. Pokud chceme téma smazat, vybereme ho z nabídky a klikneme na tlačítko Smazat. Pozor!: Smazáním tématu dojde k smazání všech otázek a odpovědí spadajících do tohoto tématu.
2.5 Vytváření testů 2.5.1 Vytvoření nového testu Formulář pro vytváření testů se zobrazí po kliknutí na položku Generovat test v menu Test. Na levé straně vybereme jeden předmět, z kterého chceme vygenerovat test. Pomocí zaškrtávacího pole vybereme jedno nebo více témat, které budou zahrnuty v testu. Na pravé straně zadáme parametry: počet otázek, počet odpovědí, obtížnost testu a počet otázek se slovní odpovědí. O dostupnosti otázek nás informuje spodní lišta.
64
Po kliknutí na tlačítko Generovat test se nám zobrazí jednoduchý náhled testu. Pokud se nám tento test nelíbí, jednoduše okno zavřeme a klikneme na tlačítko Generovat test znovu. V sekci Parametry pro uložení vybereme, co všechno chceme uložit a formáty, do kterých chceme soubor/y uložit. Formát PDF je určen k okamžitému tisku a formát XML
slouží
jako
záloha.
Pokud
nám
nevyhovuje
přednastavená
hlavička
testu, změníme ji v textovém poli. Pokud chceme soubor/y uložit do jiného, než přednastaveného, klikneme na tlačítko Změň cestu a vybereme jiný adresář. Nakonec klikneme na tlačítko Uložit.
2.5.2 Obnova z xml souboru Formulář pro obnovu z xml souboru se zobrazí po kliknutí na položku Vytvořit test nebo oblast z xml v menu Test. Prvně zadáme do textového pole název souboru, poté klikneme na tlačítko Vybrat xml soubor. Zobrazí se dialogové okno, ve kterém vybereme soubor a klikneme na tlačítko OK. Nakonec klikneme na tlačítko Uložit jako Pdf.
65