O código fonte deste projecto pode ser obtido aqui.
Para utilizar a aplicação basta instalar a framework Play (tem que ser uma versão anterior à 2.0, qualquer uma das versões 1.x.x serve sendo que a aplicação foi desenvolvida na versão 1.2.4), ir ao directório da aplicação e correr o comando play run
. Se nada tiver sido alterado nos ficheiros de configuração a aplicação pode nesse momento ser acedida pelo endereço http://localhost:9000 .

# Objetivos do Projeto
Os objetivos deste projeto podem-se separar em três categorias: navegação, procura e recomendação. Ao abordarmos cada um destes requisitos base foram criados requisitos mais específicos que correspondem a funcionalidades bem definidas.
## Navegação
1.1 Dado um id válido de ator mostrar toda a informação disponível sobre o mesmo numa página web dedicada.
1.2 Dado um id válido de criador mostrar toda a informação disponível sobre o mesmo numa página web dedicada.
1.3 Dado um id válido de personagem mostrar toda a informação disponível sobre a mesma numa página web dedicada.
1.4 Dado um id válido de série mostrar toda a informação disponível sobre a mesma numa página web dedicada.
1.5 Fazer a listagem completa de séries, atores, criadores, anos, géneros e tags.
1.6 Permitir a navegação via clique em referências a anos, géneros, tags, criadores, atores, séries e personagens.
## Procura
2.1 Pesquisa sintática baseada em número de ocorrências das palavras na query.
2.2 Funcionalidade por defeito da pesquisa semântica: tentar fazer matching da query a um nome de uma instância de série, ator, personagem ou criador.
2.3 Estender 2.2 permitindo definir a classe que se pretende.
2.4 Estender 2.3 permitindo definir um filtro por predicado.
2.5 Estender 2.4 permitindo definir um filtro num intervalo (para os campos em que se aplique)
2.6 Estender 2.3 permitindo especificar uma outra classe (que terá de estar relacionada com a primeira). Os resultados da procura serão todas as instâncias da segunda classe que se relacionem com as instâncias da primeira. Por exemplo todos os atores da série XYZ.
2.7 Tornar o matching de nomes de classes e predicados mais user-friendly não considerando a capitalização e permitindo palavras semelhantes (por exemplo: series ser interpretado como TVSeries).
2.8 Permitir internacionalização com base em anotações definidas na ontologia.
## Recomendação
3.1 Fazer recomendações com base no histórico de navegação do utilizador.
3.2 Ter uma estratégia de fall-back para o caso de utilizadores sem histórico.
3.3 Permitir ver diretamente (sem abrir outra página) informação relevante sobre as séries recomendadas (por exemplo: título e rating)
3.4 Ter uma vista dedicada que permita navegar visualmente todas as séries existentes na ontologia.
3.5 Estender 3.4 permitindo filtrar por ano e rating.
# Abordagem e Arquitetura
Nesta secção é descrita a abordagem seguida, o desenho das soluções, o design da arquitetura e são explicadas as decisões tomadas.
## Tecnologias

## Ontologia
A ontologia apresentada foi implementada de raiz uma vez que não encontrámos nenhuma que considerássemos adequada para reutilização. Por exemplo, a Programmes Ontology (BBC, 2009) está relacionada com o nosso tema, mas aprofunda muitos conceitos sem interesse neste contexto como meio (canal de comunicação) e acabaria por ser muito mais complexa que o necessário.
O namespace escolhido para a ontologia foi: http://www.semanticweb.org/ontologies/2011/9/WSTV.owl#. A ontologia foi criada no protégé versão 4.1.0 e foi extraída no formato RDF/XML.

Esta ontologia tem como classe mais importante TVSeries que contém seasons que por sua vez contêm episodes. TVSeries pode ainda ter uma ou mais tags criadas por users, um ou mais géneros e uma ou mais reviews criadas também por users. Esta classe tem ainda criadores, atores e personagens representadas por atores.
A classe Genre tem como subclasses todos os géneros que são contemplados nas séries guardadas no nosso sistema. Já as instâncias de Tag têm a propriedade hasTagName, que é uma data property com range string.
A razão desta distinção baseia-se na consideração de que o número de géneros possíveis que faz sentido contemplar é finito (ao contrário do número de tags) e como tal parece-nos mais correto contemplá-los como classes na ontologia, uma vez que não só fica explícito e automaticamente visível o que consideramos géneros, mas fica também controlado quando se acrescentam géneros. Por outro lado, podem existir várias tags escritas de várias formas diferentes que na realidade são equivalentes (devido à existência de sinónimos, diferentes perceções de questões muito subjetivas e mesmo devido a erros ortográficos) o que causa uma fragmentação de entidades que na realidade pertencem ao mesmo grupo lógico dentro do contexto. Este sistema de classificação por tags é uma folksonomy e como tal tem as mesmas vantagens e desvantagens destes métodos colaborativos. O mesmo não acontece com géneros que são melhor controlados e são finitos. Assim, há uma maior ponderação no conjunto de géneros que o mantém controlado e finito. Isto é, evita-se que estes cresçam sem controlo e que sejam dados vários nomes diferentes a algo que seja o mesmo género, uma vez que este tem que existir na ontologia para ser atribuído.
Uma Person pode ser User, Actor, Creator e Character. Consideramos que User é disjunta de todos. Especificamente é importante apontar que consideramos um User disjunta de Actor e Creator porque as entidades ator e criador são mantidas pela aplicação e não pelas próprias pessoas.
Contudo, um Creator pode ser também Actor logo estas classes não são disjuntas entre si. Adicionalmente, consideramos que Character pode ser interpretado por mais do que um Actor uma vez que tal é possível.
Em alguns casos são utilizadas anotações na ontologia para esclarecer alguns detalhes. São usadas ainda anotações com tag de língua (por exemplo “”@pt) para auxiliar a implementação de pesquisas multilíngue utilizando as funcionalidades específicas do SPARQL para queries desta natureza.
Utilizámos ainda o design pattern Value Partition para refinar a descrição da classe TVSeries. A nossa RatingsValuePartition restringe os possíveis valores de quão boa é uma série em: Good, Medium ou Bad de acordo com a sua nota. Uma TVSeries vai ter um destes valores e, como já se deve ter tornado aparente, a escolha é mutuamente exclusiva.
Dando uso a esta RatingsValuePartition criámos uma subclasse de TVSeries denominada de GoodTVSeries, que tem séries que têm boa nota.
Criámos ainda uma subclasse de TVSeries, InterestingTVSeries, que contém séries que satisfazem as condições:
+ Ser subclasse de TVSeries.
+ Possuir mais de 30.000 avaliações (nota atribuída por utilizadores).
+ Possuir mais de 100 revisões.
É importante notar, que apesar de termos as restrições definidas na ontologia criada no protégé, estas são na realidade mantidas pela aplicação aquando a população da ontologia. Refere-se ainda que seria possível obter esta mesma informação por queries SPARQL, mas considerámos que assim ficaria mais explícito o que consideramos séries boas e séries interessantes e permitiu-nos experimentar algumas funcionalidades do protégé e ainda simplificar algumas queries SPARQL que sabíamos que pretendíamos utilizar bastante no futuro.
O tutorial de ontologia de pizzas do protégé (Horridge, 2011) desaconselha, de forma geral, a especificação de domains e ranges de propriedades, uma vez que estas condições não se comportam como restrições e podem causar resultados de classificação “inesperados”. No entanto, a nossa ontologia não é muito grande logo não seria difícil descobrir efeitos secundários desta escolha e, desta forma, consideramos que compensa ter esta informação valiosa para alguém que queira perceber a ontologia. Adicionalmente, o facto de estarmos a trabalhar numa área que está na vanguarda tecnológica leva-nos a considerar que é possível a evolução e futura criação de novas features que permitam tirar valor de escolhas que neste momento não são aproveitadas pelas ferramentas em uso.
Adicionalmente, apesar de a ontologia ter sido exportada para RFD/XML, e por isso perder qualquer característica que não seja fosse possível representar neste formato, decidimos definir características que só existem em OWL. Claro que algumas destas características podem cair em domínios extremamente complexos e até indecidíveis mas, mais uma vez, têm valor para que o domínio de uso da ontologia esteja explícito, tanto para nós como para outros.
De entre as características relevantes que definimos que acabam por mais tarde ser garantidas por nós destacam-se as características das propriedades dos objetos (como: functional, inverse functional, transitive, symmetric, etc.) que explicitámos exaustivamente quando considerámos adequado.
Assim, a nossa ontologia apresenta várias relações e características que facilitam a sua leitura e, ainda que muitas delas tenham que ser mantidas pela aplicação, é útil tê-las explicitadas na ontologia.
## Arquitetura de Alto Nível
A nossa solução é constituída por 4 aplicações:
1. WSTV Repository Builder – Constrói o repositório (neste caso um ficheiro)
2. WSTV Ontology Builder – Constrói a triple-store com a ontologia com base no ficheiro criado pelo Repository Builder.
3. WSTV Index Builder – Constrói o índice utilizado na pesquisa sintática.
4. WSTV Web – O portal web. Utiliza a triple-store e o índice.
O WSTV Repository Builder constrói o repositório com base em duas fontes de dados: http://www.imdb.com/ (através de screen-scraping) e http://imdbapi.com/ (através de uma API em JSON).

## Componentes e Módulos da Aplicação Web
Dada a utilização da framework Play, à partida a aplicação web ficou estruturada segundo o modelo MVC:
+ Views – Templates baseados em HTML utilizados para mostrar a informação que lhes é passada pelo Controller.
+ Controller – Neste caso só existe um Controller. Este é composto por várias ações, sendo que cada uma corresponde a uma View com o mesmo nome.
+ Models – É aqui que está toda a lógica de Web Semântica: representações em memória de séries, atores, personagens e criadores; queries SPARQL; acesso aos índices do Lucene; pós-processamento dos resultados das pesquisas; e algoritmo de recomendação.
### Lista das Views/Controllers
+ *index* - Home-page. Contém a listagem completa de séries, atores, criadores, géneros, anos e tags bem como algumas recomendações.
+ *name* - Apresenta todas as informações disponíveis sobre um ator ou criador.
+ *title* - Apresenta todas as informações disponíveis sobre uma série.
+ *char* - Apresenta todas as informações disponíveis sobre um personagem.
+ *find* - Apresenta os resultados de uma pesquisa.
+ *visual* - Página do Visual Navigator.
### Classes que fazem parte dos Models
+ *JSONAssembler* - Traduz um objeto Java para uma String JSON. Utilizado para converter listas de séries.
+ *Pair* - Classe genérica que representa um Par. Muito útil para implementar rankings.
+ *Person* - Representa uma pessoa que neste caso pode ser um ator, criador ou personagem.
+ *Recomendations* - Implementa o algoritmo de recomendação.
+ *SearchModel* - Classe base para as classes de procura.
+ *SearchResult* - Representa um resultado de uma pesquisa.
+ *SemanticSearch* - Implementa a pesquisa semântica.
+ *SemanticSearchUtils* - Implementa as funções que lidam diretamente com o motor SPARQL.
+ *Series* - Represente uma série de televisão.
+ *SyntaticSearch* - Implementa a pesquisa sintática.
+ *TripleStoreManager* - Gere a ligação à triple-store.
+ *TSModel* - Classe base para Models baseados em dados na triple-store. Classe mãe de Person e Series.
+ *Utils* - Contém funções utilitárias.
### Pesquisa Sintática
A pesquisa sintática utiliza um índice construído pela ferramenta Lucene. O processo de construção é executado na aplicação WSTV Index Builder e é constituído pelas seguintes fases:
1. A página inicial (index) é adicionada à fila de páginas para analisar.
2. Se a fila estiver vazia termina. Se não passa a 3.
3. É tirada uma página da fila e é verificado se o seu url já foi analisado. Se não, passa a 4 se sim volta a 2.
4. Caso não tenha sido processada, a página é analisada tanto a nível de ocorrências de palavras como a nível de links existentes. Todos os urls dos links são adicionados à fila. Volta a 2.
Há que notar que os links para pesquisas não são considerados e que links diferentes que apontem para o mesmo recurso são filtrados.
Quando o utilizador insere uma query e escolhe o modo de pesquisa “Statistic” na aplicação web, a query é passada ao Lucene que devolve um conjunto de resultados ordenado pelo número de hits das keywords inseridas. Por uma questão de simplicidade apenas são exibidos no máximo 20 resultados.
### Pesquisa Semântica
Nesta subsecção é descrita a implementação da procura semântica.
#### i. Parsing de Queries
Dado que não era objetivo deste trabalho utilizar técnicas elaboradas de Natural Language Processing (NLP) foram feitas algumas simplificações a nível do parsing de queries de procura semântica, nomeadamente:
+ As queries têm de seguir uma estrutura pré-definida (ver tabela). É de notar que existe um mecanismo de reserva caso a query não siga a estrutura. Nesse caso é assumida a estrutura #1, apresentada na tabela, que se baseia em assumir que todas as keywords da pesquisa são uma instância a pesquisar de uma qualquer classe.
+ Classes, predicados e separadores têm de ser obrigatoriamente representados por palavras singulares, isto é, uma classe não pode ser representada por “Series de TV”. Esta restrição de nomenclatura só se aplica às palavras utilizadas nas queries e não às outras componentes do projeto.
##### Estrutura válida das queries. Estar dentro de [] significa que podem ser colocadas várias instâncias separadas por "and".