Poniżej jest umieszczana automatycznie treść podstrony czytnik_rss znajdującej się w namespace projektu. Proszę ją utworzyć i traktować jako stronę startową - tam umieszczamy linki do poszczególnych podstron, które też mają się znajdować w projekcie, np. analiza_wymagan. W namespace mogą też Państwo umieszczać pliki (obrazki, diagramy, archiwa) linkowane na stronie danego projektu. Proszę usunąć ten akapit po zapoznaniu się z jego treścią.
Przedmiotem projektu jest portal spełniający rolę czytnika kanałów RSS. Czytnik udostępniany użytkownikom zostanie w modelu Software As A Service.
Kanały RSS to dokumenty w formacie XML, zawierające informacje o wpisach na blogach, czy listach artykułów na stronach internetowych. W formie tej udostępniane mogą być wszelkiego rodzaju informacje mające charakter strumieniowego przekazywania danych, jak komentarze, wydarzenia.
Specyficznym elementem tego czytnika będzie jego aktywność. Posiadać on będzie dodatkową logikę pozwalającą na wykonywanie przez aplikacji akcji niezależnie od obecności na portalu użytkownika. Na podstawie filtrów zdefiniowanych przez użytkownika artykuły mogą być ignorowane z jego listy, lub użytkownik może być informowany o ich pojawieniu się kanałami komunikacji bezpośredniej (jak Email czy SMS).
Na rynku znajduje się wiele czytników kanałów RSS. Można je znaleźć w formie wtyczek do popularnych przeglądarek, czy klientów poczty elektronicznej. Działają również jako widgety na pulpit oraz aplikacje stand-alone. Istnieje również grupa czytników działających w postaci stron internetowych.
Wiele z tych czytników pozwala na okresowe sprawdzanie kanałów i powiadamianie o nowych wpisach. Nie znalazłem jednak czytnika, który mógłby pozwalać na zastosowanie powiadomienia kanałem aktywnym (jak mail czy SMS), oraz pozwalającego na definicję filtrów.
Interesującym aspektem może być również wykorzystanie w przyszłości powyższych mechanizmów w połączeniu z panującą aktualnie modą na tworzenie rozwiązań społecznościowych.
Funkcjonalności :
Funkcjami bazy danych muszą być :
Rejestracja
Logowanie
Modyfikacja danych użytkownika
Dodawanie nowego kanału RSS
Usuwanie istniejącego kanału RSS
Wyróżnione są następujące encje:
Kod SQL
Kod, który odpowiada za tworzenie bazy danych
SHOW WARNINGS| -- ----------------------------------------------------- -- Table `User` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `User` ( `idUser` INT NOT NULL AUTO_INCREMENT , `email` TINYTEXT NOT NULL , `password` CHAR(40) NOT NULL , `salt` CHAR(23) NOT NULL , `meta` TEXT NOT NULL , `created` DATETIME NOT NULL , `lastLogin` DATETIME NOT NULL , `emailHash` CHAR(40) NOT NULL PRIMARY KEY (`idUser`) ) ENGINE = InnoDB| SHOW WARNINGS| -- ----------------------------------------------------- -- Table `Channel` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `Channel` ( `idChannel` INT NOT NULL AUTO_INCREMENT , `url` VARCHAR(256) CHARACTER SET 'ascii' NOT NULL , `title` TEXT NOT NULL , `lastChecked` DATETIME NOT NULL , `nextCheck` DATETIME NOT NULL , `meta` TEXT NOT NULL , UNIQUE INDEX `url_UNIQUE` (`url` ASC) , PRIMARY KEY (`idChannel`) ) ENGINE = InnoDB| SHOW WARNINGS| -- ----------------------------------------------------- -- Table `ChannelUser` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `ChannelUser` ( `channel` INT NOT NULL , `user` INT NOT NULL , `added` DATETIME NOT NULL , PRIMARY KEY (`channel`, `user`) , INDEX `fk_Channel2User_User1` (`user` ASC) , INDEX `fk_Channel2User_Channel` (`channel` ASC) , CONSTRAINT `fk_Channel2User_Channel` FOREIGN KEY (`channel` ) REFERENCES `Channel` (`idChannel` ) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `fk_Channel2User_User1` FOREIGN KEY (`user` ) REFERENCES `User` (`idUser` ) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE = InnoDB| SHOW WARNINGS| -- ----------------------------------------------------- -- Table `Item` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `Item` ( `idItem` INT(11) NOT NULL AUTO INCREMENT, `channel` INT NOT NULL , `title` TEXT NOT NULL , `description` TEXT NOT NULL , `link` VARCHAR(256) CHARACTER SET 'ascii' NULL , `meta` TEXT NOT NULL , PRIMARY KEY (`idItem`) , INDEX `fk_Item_Channel1` (`channel` ASC) , CONSTRAINT `fk_Item_Channel1` FOREIGN KEY (`channel` ) REFERENCES `Channel` (`idChannel` ) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE = InnoDB| SHOW WARNINGS| -- ----------------------------------------------------- -- Table `ItemUser` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `ItemUser` ( `item` INT NOT NULL , `user` INT NOT NULL , `readed` DATETIME NOT NULL , `meta` TEXT NOT NULL, PRIMARY KEY (`item`, `user`) , INDEX `fk_Item2User_User1` (`user` ASC) , INDEX `fk_Item2User_Item1` (`item` ASC) , CONSTRAINT `fk_Item2User_Item1` FOREIGN KEY (`item` ) REFERENCES `Item` (`idItem` ) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `fk_Item2User_User1` FOREIGN KEY (`user` ) REFERENCES `User` (`idUser` ) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE = InnoDB| SHOW WARNINGS| CREATE TABLE IF NOT EXISTS `Filter` ( `idFilter` INT(11) NOT NULL , `user` INT(11) NOT NULL , `expression` TEXT NOT NULL , `counter` INT(11) NOT NULL DEFAULT 0 , PRIMARY KEY (`idFilter`) , INDEX `fk_Filter_User1` (`user` ASC) , CONSTRAINT `fk_Filter_User1` FOREIGN KEY (`user` ) REFERENCES `User` (`idUser` ) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE = InnoDB |
1NF - pierwsza postać normalna
Tabele w bazie spełniają warunek postaci normalnej, jeśli każda z tych tabel spełnia następujące warunki:
Baza danych spełnia warunki 1NF gdyż wszystkie wartości atrybutów są atomowe (przy założeniu, że ciąg string nie może być rozbijany na podstringi), nie zawiera powtarzających się grup informacji. Dodatkowo każda tabela posiada klucz główny(prosty bądź złożony)
2NF - druga postać normalna
Baza jest w drugiej postaci normalnej, wtedy gdy spełniona jest 1NF oraz gdy wartości nie-kluczowe są zależne od klucza głównego. Gdy występuje tylko klucz prosty nie ma z tym problemu, należy sprawdzic czy wartości nie-kluczowe kluczy złożonych są aby zależne od całego klucza, a nie np od jego części.
Następujące tabele posiadają klucz główny prosty:
Należy sprawdzic tabele, które mają klucze złożone:
W obu przypadkach wartości nie-kluczowe zależą od wartości składowych klucza głównego, zatem baza danych jest w postaci 2NF
3NF - trzecia postać normalna
Wszystkie wartości nie-kluczowe zależą bezpośrednio od wartości klucza głównego, dlatego baza jest w 3NF
Te zapytania wynikają bezpośrednio z działań użytkownika za pomocą interfejsu webowego aplikacji. Ich częstość występowania zależy wprost od aktywności użytkowników, a ich czasy wykonywania przekładają się na czasy odpowiedzi aplikacji.
Dodawanie Nowego kanału
INSERT INTO `Channel` (`url`, `title`, `lastChecked`, `nextCheck`, `meta`) VALUES ('#Url#', '#title#', NOW(), NOW(), '#meta#')
Subskrypcja kanału do konta
INSERT INTO `ChannelUser` (`channel`, `user`, `added`) VALUES (#channelId#, #userId#, NOW())
Cofnięcie subskrypcji
DELETE FROM `ChannelUser` WHERE `channel` = #channelId# AND `user` = #userId#
Pobieranie listy kanasłów subskrybowanych przez użytkownika
SELECT `ChannelUser`.*, `Channel`.* FROM `ChannelUser` JOIN `Channel` ON `ChannelUser`.channel = `Channel`.idChannel WHERE (USER=#UserId#)
Pobieranie listy wpisów dla jednego kanału wraz z jego metadanymi (przeglądanie kanału)
SELECT `Item`.*,`ItemUser`.* FROM `ItemUser` RIGHT JOIN `Item` ON (`ItemUser`.item = `Item`.idItem AND USER=#UserId# ) WHERE (idItem IN ( #ListOfChannellIds#)) ORDER BY `idItem` DESC LIMIT 10;
Logowanie użytkownika do systemu
SELECT `User`.* FROM `User` WHERE (emailHash='#HashOfEmail#') AND (email='#Email#') AND (password=sha(concat(salt,'#Password#')))
Zapytania te wykonywane są przez proces działający w tle. Wynikają one pośrednio z aktywności użytkowników, jak i monitorowanych zasobów i sygnałów (stron, filtrów, komend administratora). Zapytania te w mniejszy sposób związane są z interakcją użytkownika w systemie. Czasy ich wykonania mogą się wydłużać, nie powinny jednak blokować zapytań z poprzedniej grupy.
Sprawdzanie istniejnia pojedynczego wpisu
SELECT `Item`.* FROM `Item` WHERE (channel='#channelId#') AND (link='#EntryUrl#') LIMIT 1
Dodawanie nowego wpisu do kanału
INSERT INTO `Item` (`channel`, `title`, `description`, `link`, `meta`) VALUES (#ChannelId#, '#Title#', '#Description#', '#EntryUrl#', '#EntryMetadata#')
Dodawanie identyfikatora wpisu do kolejki analizy
INSERT INTO `ItemAnalyze` (`item`) VALUES (#EntryId#)
(utworzenie bazy, implementacja obiektów) (realizacja struktury pustej bazy w oparciu o diagramy ERD i SQL)
Projekt bazy
Baza została stworzona przy użyciu wcześniej zaprojektowanego diagramu ERD:
Tworzenie bazy
W projekcie zostało wykorzystane narzędzie do zautomatyzowanego procesu budowania aplikacji - Apache Ant. Dzięki niemu możliwe było wykorzystanie dbdeploy - narzędzia do zarządzania zmianami w bazie danych, dzięki któremu cały zespół mógł bez większych komplikacji update'ować bazy danych w lokalnych środkowiskach.
Skrypt dbdeploy : dbdeploy.xml
Skrypt SQL do utworzenia pustej bazy: dbdeploy.sql
Do rozpoczęcia pracy z aplikacją nie są potrzebne żadne dane początkowe, które należałoby wprowadzić do bazy danych.
(formularze; należy zaprojektować strukturę każdego formularza oraz powiązania między nimi w oparciu o diagramy FHF, DFD i STD).
Poniżej zostaną przedstawione formularze służące do wprowadzania danych
Rejestracja
Logowanie
Zmiana hasła
Dodawanie kanalu
Oznaczanie wpisu wg wybranego filtru
Aplikacja nie wymaga (na tym etapie rozwoju) panelu sterowania. Dlatego taka funkcja nie została zaimplementowana.
Makropolecenia wykonujące mapowanie obiektów do bazy danych oparte są o komponent Zend_Db_Table będący implementacją wzorca projektowego Brama danych tabeli(Table Data Gateway)
W celu uruchomienia aplikacji na skonfigurowanym serwerze HTTP należy :
W obrębie back-endu ( warstwa usług ) w celu testowania wykonane zostały testy jednostkowe. Użycie testów jednostkowych pozwoliło na :
Aby wykonać testy w skonfigurowanym środowisku produkcyjnym należy zmienić katalog roboczy na tests i wykonać komendę phpunit
Do działania aplikacji nie potrzebne są żadne początkowe dane do wprowadzenia. Wystarczy tylko stworzyć pustą bazę danych. Wprowadzanie danych odbywa sie ręcznie poprzez formularze do wypełniania.
Dodatkowo w tle działa cron, który update'uje wpisy na kanale co jakiś ustalony czas.
Do poprawnego działania aplikacji potrzebne będą:
Aplikacja jest zaprojektowana w sposób intuicyjny, dlatego też jakakolwiek dokumentacja dla użytkownika wydaje sie byc zbędna. Niech zostanie również podkreślone to, że aplikacja ma być prosta w obsłudze i ma służyć jednemu celowi - wygodnemu przeglądaniu wpisów RSS.
Niemniej jednak można byłoby napisać FAQ lub pomoc dla użytkownika do obsługi nieco bardziej skomplikowanych operacji, jak np. tworzenie zaawansownych filtrów.
Z punktu widzenia klienta aplikacja na aktualnym etapie nie potrzebuje administracji oraz zarządzania. Użytkownicy sami zarządzają aplikacją. Dokumentacje techniczną może stanowić ninejszy dokument.
Utrzymywanie systemu sprowadza się do utrzymania serwera www oraz serwera baz danych. Należy również zwrócić uwagę na jakość połączenia internetowego (cron sprawdzający aktualność kanałów).
Co do samej aplikacji błędy znalezione przez użytkowników można raportować do obsługi technicznej systemu (zwykle admin).
Aplikacja jest zaprojektowana w taki sposób,że wzbogacanie ją o kolejne moduły nie powinno stanowić problemu dla developerów. Wszystko jest ułożone w ustrukturyzowany sposób, sama aplikacja jest wykonana w modelu MVC.
W trakcie rozwijania aplikacji padło kilka pomysłów na funkcjonalności, jednak z braku czasu nie udało się ich zaimplementować.
Były to m.in. :
Przy realizacji aplikacji duży procent czasu został poświęcony na projektowanie systemu(bazy danych + model + kontrolery + akcje). Dzięki temu uniknięto niepotrzebnych błędów i nieporozumień w trakcie implementacji funkcjonalności systemu.
Jednym z głównych aspektów, który należy opisać jest czas który należało poświęcić na wdrożenie się w zagadnienia i technologie implementacyjne. Projekt został napisany w PHP przy użyciu Zend Framework, który posiada bogatą dokumentację, jednak dla początkującego może być lekko niezrozumiała. Kolejnym minusem wynikającym z użycia takiej a nie innej technologii były problemy podczas samej implementacji. PHP jest językiem z dynamiczną typizacją która to sprawiała problemy na etapie rozwijania aplikacji.
Niemniej jednak ZF dostarcza masę bibliotek dzięki którym można skrócić czas poświęcony pisaniu kodu o kilkadziesiąt procent.