Donnerstag, 17. Mai 2007

Eine einfache Anwendung in Perl Catalyst

Hallo!

Wie ich gelesen habe, gibt es nur wenige deutschsprachige Einführungen ins Catalyst-Framework.

In diesem Beitrag werde ich Ihnen eine kleine Catalyst-Anwendung zeigen. Anhand dieser Anwendung können Catalyst-Anfänger besser verstehen, wie Catalyst funktioniert.

Die Anwendung heißt Time Tracking und ist mit Perl, Catalyst, JavaScript und MySQL entwickelt worden.

In dieser Anwendung kann der Benutzer Angaben über Zeitverbrauch speichern und Kategorien bzw. Zielen zuordnen.

Sehen wir uns das an einem Beispiel an:

Hier gibt es eine Aktivität Wrote documentation, die der Kategorie Documentation und dem Ziel Documentation (JavaScript spike solution) zugeordnet ist.

Ziele sind hierarchisch gegliedert:

In diesem Fall dient die Aktivität dem Erreichen des Zieles JavaScript spike solution.

Die Anwendung besteht aus drei Seiten:


Einstiegsseite
Auf der Einstiegsseite befinden sich nur Links zu den anderen Seiten:

Kategorien-Seite
Die Kategorien-Seite dient dem Erstellen und Löschen von Kategorien:

Neue Kategorie erstellen
Um eine neue Kategorie anzulegen, muss man zuerst den Link New category anklicken.


Danach erscheint ein neues Fenster, wo man die Daten der neuen Kategorie eingeben kann:


Wenn man in diesem neuen Fester den Cancel-Link anklickt, wird das Fenster geschlossen und es passiert sonst nichts.

Wenn man den OK-Link anklickt, ohne einen Text in das Textfield Name einzugeben, kommt eine Fehlermeldung:

Wenn man den Namen und das Kommentar eingegeben hat und dann auf OK klickt, wird
  1. das kleine Fenster geschlossen und

  2. die Liste im Hauptfenster aktualisiert.


Kategorien einzeln löschen
Neben jeder Kategorie steht ein Delete-Link.

Klickt man auf diesen, kommt eine Sicherheitsabfrage:


Klickt man auf No, wird das kleine Fenster geschlossen und es passiert sonst nichts.

Klickt man auf Yes, wird das kleine Fenster geschlossen, die Kategorie gelöscht und im Hauptfenster wird die Liste aktualisiert.

Kategorien an- und abwählen
Neben jeder Kategorie steht eine Checkbox. Über die Links Select all und Deselect all, kann man alle Kategorien an- bzw. abwählen:

Mehrere Kategorien auf einmal löschen
Wenn mehrere Kategorien ausgewählt sind, kann man sie alle auf einmal löschen, indem man auf Delete selected categories klickt (wenn keine Kategorien ausgewählt sind, führt ein Klick auf diesen Link zu einer alert-Fehlermeldung):

Es erscheint eine Sicherheitsabfrage:


Klickt man in diesem Fenster auf Yes, wird es geschlossen, die Kategorien gelöscht und die Liste im Hauptfenster aktualisiert.

Ziele-Seite
Über die Zielseite (http://localhost:3000/goals) kann man die Zielhierarchie editieren.

Der Baum wird von einem dtree-Skript von Geir Landrö (drop@destroydrop.com) gesteuert, man kann die Knoten auf- und zuklappen:



Erstellen von Zielen ohne Ober-Ziel
Will man ein Ziel ohne Oberziel erstellen (d. h. Ziel ohne Eltern-Ziel), muß man auf den Link Create new top-level goal klicken.


Wenn man den Namen und das Kommentar eingibt und auf OK klickt, wird das neue Ziel angelegt und im Baum dargestellt:

Anlegen von Unterzielen
Klickt man auf die Aufschrift des Ziels (rote Ellipse im letzten Screenshot), erscheint folgendes Fenster:

Um ein Unterziel von diesem Ziel zu erstellen, muß man auf den Link Create Sub-Goal klicken.

Danach verändert sich das Fenster:


Gibt man den Namen und Kommentar ein, wird das kleine Fenster geschlossen und der Baum aktualisiert:
Ziele löschen
Um ein Ziel zu löschen, muß man es anklicken und im kleinen Fenster Delete anklicken.

Zugeordnete Aktivitäten anzeigen
Jede Aktivität wird einem Ziel zugeordnet. Über den Zielbaum kann man sich ansehen, welche Aktivitäten einem Ziel zugeordnet sind.

Nehmen wir an, wir wollen ansehen, welche Aktivitäten dem Ziel Basic knowledge of Catalyst zugeordnet sind.

Es erscheint folgendes Fenster:

Klickt man auf den Link Show activities of this goal, erscheint im Hauptfenster folgende Tabelle:

Aktivitäten-Seite
Die Aktivitäten-Seite besteht aus mehreren Teilen.

Im blau hervorgehobenen Teil werden alle vorhandenen Aktivitäten aufgelistet. Über den Link New activity kann man eine neue Aktivitäten eingeben.

Neue Aktivität eingeben
Die Seite zum Eingeben neuer Aktivitäten sieht folgendermaßen aus:

Zunächst muß man das Datum eingeben. Das geschieht über einen Date-Picker, den über den rot hervorgehobenen Button aufgerufen wird (der Datepicker wurde von TengYong Ng, http://www.rainforestnet.com, contact@rainforestnet.com entwickelt).

Es erscheint folgendes Fenster:


Wenn man einen Tag im Kalender anklickt, wird das kleine Fenster geschlossen und das Textfeld aktualisiert:

Anschließend muß man die Kategorie aus der Liste auswählen und ein Ziel für diese Aktivität eingeben.

Das geschieht über Select goal-Link:

Wenn man einen Knoten im Baum anklickt (die Aufschrift), dann wird das kleine Fenster geschlossen und das Ziel in „input type=hidden“-Element gespeichert:

Schließlich muß man noch die Dauer der Aktivität eingeben und auf OK klicken.

Folgender Inhalt erscheint:


Klickt man auf Back to list, gelangt man zur Aktivitäten-Ansicht, wo die neue Aktivität zu sehen ist.
Summen nach Zielen und Kategorien
Im unteren Teil des Fensters sind folgende zwei Tabellen zu sehen:


In der linken sind Zeiten der Aktivitäten nach Zielen summiert, in der rechten – nach Kategorien.

Die zugrundeliegenden SQL-Abfragen sehen so aus:

SELECT SUM(duration), goals.name
FROM activities, goals
WHERE activities.goalId = goals.id
GROUP BY activities.goalId
ORDER BY goals.name

SELECT SUM(duration), categories.name
FROM activities, categories
WHERE activities.categoryId = categories.id
GROUP BY activities.categoryId
ORDER BY categories.name
Datenbank-Tabellen

Es gibt bei dieser Anwendung drei Datenbank-Tabellen – jeweils eine für Aktivitäten, Kategorien und Ziele:

Die Tabellen habe ich manuell in phpMyAdmin angelegt, ein SQL-Skript (von phpMyAdmin generiert) sieht so aus:


-- phpMyAdmin SQL Dump

-- version 2.6.0-pl3

-- http://www.phpmyadmin.net

--

-- Host: localhost

-- Generation Time: May 08, 2007 at 12:19 AM

-- Server version: 4.1.8

-- PHP Version: 5.0.3

--

-- Database: `timetracking`

--


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


--

-- Table structure for table `activities`

--


CREATE TABLE `activities` (

`id` int(11) NOT NULL auto_increment,

`date` date NOT NULL default '0000-00-00',

`categoryId` int(11) NOT NULL default '0',

`name` varchar(255) collate latin1_general_ci NOT NULL default '',

`comment` varchar(255) collate latin1_general_ci NOT NULL default '',

`goalId` int(11) NOT NULL default '0',

`duration` double NOT NULL default '0',

PRIMARY KEY (`id`)

) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci AUTO_INCREMENT=18 ;


--

-- Dumping data for table `activities`

--


INSERT INTO `activities` (`id`, `date`, `categoryId`, `name`, `comment`, `goalId`, `duration`) VALUES (11, 0x323030372d30352d3032, 26, 'Learned how to use Catalyst', 'Tutorial "Catalyst::Manual::Tutorial::Authentication - Catalyst Tutorial - Part 4: Authentication", "Catalyst::Manual::Tutorial::Authorization - Catalyst Tutorial - Part 5: Authorization"', 17, 0.75);

INSERT INTO `activities` (`id`, `date`, `categoryId`, `name`, `comment`, `goalId`, `duration`) VALUES (10, 0x323030372d30352d3031, 26, 'Learned how to use Catalyst', '', 17, 6.73);

INSERT INTO `activities` (`id`, `date`, `categoryId`, `name`, `comment`, `goalId`, `duration`) VALUES (12, 0x323030372d30352d3033, 23, 'Created skeleton for the demo app', '', 19, 0.78);

INSERT INTO `activities` (`id`, `date`, `categoryId`, `name`, `comment`, `goalId`, `duration`) VALUES (13, 0x323030372d30352d3035, 23, 'Demo app "time tracking"', '', 19, 2.23);

INSERT INTO `activities` (`id`, `date`, `categoryId`, `name`, `comment`, `goalId`, `duration`) VALUES (14, 0x323030372d30352d3036, 23, 'Demo app "time tracking"', '', 19, 7.56);

INSERT INTO `activities` (`id`, `date`, `categoryId`, `name`, `comment`, `goalId`, `duration`) VALUES (15, 0x323030372d30352d3037, 23, 'Demo app "time tracking", debugging', '', 19, 8.08);

INSERT INTO `activities` (`id`, `date`, `categoryId`, `name`, `comment`, `goalId`, `duration`) VALUES (16, 0x323030372d30352d3037, 24, 'Wrote documentation', '', 18, 2);

INSERT INTO `activities` (`id`, `date`, `categoryId`, `name`, `comment`, `goalId`, `duration`) VALUES (17, 0x323030372d30352d3037, 26, 'Some activity', '', 19, 1);


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


--

-- Table structure for table `categories`

--


CREATE TABLE `categories` (

`id` int(11) NOT NULL auto_increment,

`name` varchar(255) collate latin1_general_ci NOT NULL default '',

`comment` varchar(255) collate latin1_general_ci NOT NULL default '',

PRIMARY KEY (`id`)

) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci AUTO_INCREMENT=30 ;


--

-- Dumping data for table `categories`

--


INSERT INTO `categories` (`id`, `name`, `comment`) VALUES (26, 'Learning', 'Learning how to use some new (for me) technology');

INSERT INTO `categories` (`id`, `name`, `comment`) VALUES (25, 'Reading (tech)', 'Reading technical books');

INSERT INTO `categories` (`id`, `name`, `comment`) VALUES (24, 'Documentation', 'Writing user and developers'' manual');

INSERT INTO `categories` (`id`, `name`, `comment`) VALUES (23, 'Programming', 'All programming activities');


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


--

-- Table structure for table `goals`

--


CREATE TABLE `goals` (

`id` int(11) NOT NULL auto_increment,

`name` varchar(255) collate latin1_general_ci NOT NULL default '',

`comment` varchar(255) collate latin1_general_ci NOT NULL default '',

`superGoal` int(11) NOT NULL default '0',

`deadline` date NOT NULL default '0000-00-00',

`attained` binary(1) NOT NULL default '',

`whenAttained` date NOT NULL default '0000-00-00',

`cancelled` binary(1) NOT NULL default '',

`whenCancelled` date NOT NULL default '0000-00-00',

PRIMARY KEY (`id`)

) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci AUTO_INCREMENT=22 ;


--

-- Dumping data for table `goals`

--


INSERT INTO `goals` (`id`, `name`, `comment`, `superGoal`, `deadline`, `attained`, `whenAttained`, `cancelled`, `whenCancelled`) VALUES (12, 'Enterprise information system', 'ASP.NET project', 0, 0x303030302d30302d3030, '', 0x303030302d30302d3030, '', 0x303030302d30302d3030);

INSERT INTO `goals` (`id`, `name`, `comment`, `superGoal`, `deadline`, `attained`, `whenAttained`, `cancelled`, `whenCancelled`) VALUES (13, 'JavaScript spike solution', 'Program for testing my JavaScript skills', 0, 0x303030302d30302d3030, '', 0x303030302d30302d3030, '', 0x303030302d30302d3030);

INSERT INTO `goals` (`id`, `name`, `comment`, `superGoal`, `deadline`, `attained`, `whenAttained`, `cancelled`, `whenCancelled`) VALUES (14, 'User interface (EIS)', '', 12, 0x303030302d30302d3030, '', 0x303030302d30302d3030, '', 0x303030302d30302d3030);

INSERT INTO `goals` (`id`, `name`, `comment`, `superGoal`, `deadline`, `attained`, `whenAttained`, `cancelled`, `whenCancelled`) VALUES (15, 'Business logic (EIS)', '', 12, 0x303030302d30302d3030, '', 0x303030302d30302d3030, '', 0x303030302d30302d3030);

INSERT INTO `goals` (`id`, `name`, `comment`, `superGoal`, `deadline`, `attained`, `whenAttained`, `cancelled`, `whenCancelled`) VALUES (16, 'Documentation (EIS)', '', 12, 0x303030302d30302d3030, '', 0x303030302d30302d3030, '', 0x303030302d30302d3030);

INSERT INTO `goals` (`id`, `name`, `comment`, `superGoal`, `deadline`, `attained`, `whenAttained`, `cancelled`, `whenCancelled`) VALUES (17, 'Basic knowledge of Catalyst', '', 13, 0x303030302d30302d3030, '', 0x303030302d30302d3030, '', 0x303030302d30302d3030);

INSERT INTO `goals` (`id`, `name`, `comment`, `superGoal`, `deadline`, `attained`, `whenAttained`, `cancelled`, `whenCancelled`) VALUES (18, 'Documentation (JavaScript spike solution)', '', 13, 0x303030302d30302d3030, '', 0x303030302d30302d3030, '', 0x303030302d30302d3030);

INSERT INTO `goals` (`id`, `name`, `comment`, `superGoal`, `deadline`, `attained`, `whenAttained`, `cancelled`, `whenCancelled`) VALUES (19, 'Demo app works', '', 13, 0x303030302d30302d3030, '', 0x303030302d30302d3030, '', 0x303030302d30302d3030);

INSERT INTO `goals` (`id`, `name`, `comment`, `superGoal`, `deadline`, `attained`, `whenAttained`, `cancelled`, `whenCancelled`) VALUES (20, 'My new goal', 'My new goal comment', 0, 0x303030302d30302d3030, '', 0x303030302d30302d3030, '', 0x303030302d30302d3030);

Abschlußworte
Die Anwendung wird mit "script\timetrackingapp_server.pl" gestartet. Parallel dazu muß xampp laufen, denn die Anwendung benötig eine MySQL-Datenbank.

Den Source-Code der Anwendung können Sie hier herunterladen.

Und falls Sie nur an JavaScript-Teilen der Anwendung interessiert sind, können Sie hier eine "statische" Version der Anwendung herunterladen, also einfach die HTML-Dateien, die Sie in jedem Browser ansehen können.

In dieser statischen Version sind die serverseitigen Teile natürlich nicht funktionsfähig.

Viel Spaß beim Erforschen von Catalyst!

Dmitri Pissarenko

Kommentare:

Johannes hat gesagt…

Hi Dmiti,

nette Anwendung. Du musst mir noch mal sagen wie du catalyst mit XAMPP zum laufen gebracht hast. Ich finde die Installation wirklich fiess...

nja.
gruss johannes

Dmitri Pissarenko hat gesagt…

Meinst Du die Installation von XAMPP oder die Installation von Catalyst?