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 .

WebSemanticTVSeries

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

Technologies

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.

Ontology

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).

Architecture

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:

Lista das Views/Controllers

Classes que fazem parte dos Models

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:

Estrutura v�lida das queries. Estar dentro de [] significa que podem ser colocadas v�rias inst�ncias separadas por "and".
# Estrutura Objectivo
1 �valor� 2.2
2 �classe� �valor� 2.3
3 �classe� [�predicado� �valor�] 2.4
4 �classe� [�predicado� from �valor� to �valor�] 2.5
5 �classe� �valor� �classe 2� 2.6

A nossa solu��o, apesar de restritiva, permite bastante flexibilidade. Destacam-se algumas caracter�sticas, por exemplo: + As procuras s�o case-insensitive. + S�o permitidos sin�nimos (por exemplo TVSeries ? Series). + Permite escolher entre pesquisa em Ingl�s e Portugu�s. Esta funcionalidade s� est� parcialmente implementada j� que era apenas uma prova de conceito. As propriedades que podem ser utilizadas em Portugu�s s�o t�tulo, da classe Series, e ano da mesma classe.

A cada uma das estruturas na tabela corresponde uma fun��o a que chamamos de primitiva. Cada uma destas fun��es recebe como par�metros os campos referidos na tabela, com a ressalva de que quando existem intersec��es estas s�o tratadas separadamente, ou seja, � feita uma chamada � primitiva por cada condi��o. Tal decis�o foi tomada para simplificar a implementa��o e teste das primitivas e aumentar a modularidade e potencial de reutiliza��o do c�digo.

A dete��o da estrutura � feita com base em fun��es que, conforme a l�ngua escolhida pelo utilizador, tentam encontrar correspond�ncias entre as palavras na query e a forma correta (igual � que est� na ontologia). Caso n�o exista nenhuma correspond�ncia � devolvido null. Existem fun��es para traduzir classes, predicados e predicados num�ricos (aos quais � poss�vel aplicar um intervalo). Por outras palavras, para descobrir a estrutura das keywords usadas na pesquisa s�o utilizadas as fun��es para determinar se determinada keyword � classe ou propriedade, por exemplo. A fun��o try2Resolve2Class(String w) tem o nome das classes e os sin�nimos e tenta fazer a correspond�ncia devolvendo o nome exato da classe (como apresentado na ontologia) ou null se n�o for feita correspond�ncia. Por exemplo podemos chamar try2Resolve2Class(�Series�) e obtemos como return �TVSeries�.

A fun��o try2Resolve2Prop(String classWord, String propWord, String language) � um pouco mais elaborada (uma vez que existe um grande n�mero de propriedades na ontologia, o que tornou necess�rio automatizar a tarefa de as percorrer). Com este objetivo � utilizada uma query para encontrar todas as subclasses da classWord que tenham determinada propriedade. Logo que seja encontrada uma um caso que satisfa�a a condi��o � devolvida a propriedade (uma vez que na ontologia n�o existem duas propriedades diferentes com nomes iguais).

N�o foi implementado registo de utilizadores, login e recomenda��es sem�nticas baseadas em hist�rico guardado por utilizador (foi sim implementada recomenda��o sem�ntica baseada em hist�rico guardado na sess�o do browser), uma vez que tal n�o ia contribuir para a complexidade ou interesse da vertente sem�ntica da nossa aplica��o. A gest�o de utilizadores neste contexto n�o est� relacionada com web sem�ntica e sugest�es por hist�rico de conta seriam uma simples extens�o de sugest�es baseadas no hist�rico da sess�o.

ii. Primitivas

Na estrutura #1 � assumido que as palavras de procura s�o um valor que pode corresponder � inst�ncia de qualquer classe cujos elementos tenham as suas pr�prias p�ginas, como tal realiza-se a procura comparando com todas as inst�ncias das classes Actor, Creator, Character e TVSeries, unindo-se todos os resultados de procura num conjunto. Por outras palavas, � chamada quatro vezes a fun��o find(String className, String instance), sendo que className vai assumir os valores �Actor�, �TVSeries�, �Character� e �Creator� e instance vai ter as keywords usadas na pesquisa. Como exemplo se procurarmos a atriz Emma Bell, as queries SPARQL utilizadas seriam semelhantes � seguinte:

PREFIX foaf:   <http://www.semanticweb.org/ontologies/2011/9/WSTV.owl#> PREFIX foaf2: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> SELECT ?s WHERE { ?s foaf2:type foaf:Actor . ?s foaf:hasName ?instanceName . FILTER regex(?instanceName, "Emma Bell", "i" ) }

A flag �i� torna as correspond�ncias �s express�es regulares case-insensitive. Para a estrutura #2 � novamente utilizada a fun��o find(String className, String instance) e quando o className corresponde a Actor, Creator, Character ou TVSeries a query SPARQL utilizada � semelhante � anterior. Contudo, existem ainda casos especiais como para as classes Tag ou Genre. Para Genre por exemplo a query seria a seguinte:

PREFIX foaf:   <http://www.semanticweb.org/ontologies/2011/9/WSTV.owl#> SELECT ?s WHERE { ?s foaf:hasGenre foaf:"+instance+"}

Na estrutura #3 � utilizada a fun��o findByProp(String className, String propName, String propValue, String language). Nesta fun��o mapeiam-se os par�metros para className, que tem propriedade propName com o valor propValue. A query SPARQL utilizada � a seguinte:

PREFIX foaf:   <http://www.semanticweb.org/ontologies/2011/9/WSTV.owl#>  PREFIX foaf2: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> PREFIX foaf3: <http://www.w3.org/2000/01/rdf-schema#> SELECT ?s WHERE { { {?s foaf2:type foaf:"+className+"} UNION {?s foaf3:subClassOf foaf:"+className+"} }  . ?s foaf:"+propName+" ?o . FILTER regex(?o, \""+propValue+"\", \"i\" )}

Se o par�metro language fosse Portugu�s, o mapeamento com propName seria usado em rela��o � anota��o em Portugu�s da propriedade. A query para este caso seria:

PREFIX foaf:   <http://www.semanticweb.org/ontologies/2011/9/WSTV.owl#>  PREFIX foaf2: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> PREFIX foaf3: <http://www.w3.org/2000/01/rdf-schema#> SELECT ?s WHERE { { {?s foaf2:type foaf:"+className+"} UNION {?s foaf3:subClassOf foaf:"+className+"} }  . ?propo foaf3:comment \""+propName+"\"@pt . ?s ?propo ?o . FILTER regex(?o, \""+propValue+"\", \"i\" )}

Note-se que por defeito a aplica��o lida com as queries em Ingl�s.

Na estrutura #4 � utilizada a fun��o findByRange(String className, String proposition, double start, double end). Por exemplo, se estivermos a fazer uma range query de s�ries entre dois anos, a query � a seguinte:

PREFIX foaf:   <http://www.semanticweb.org/ontologies/2011/9/WSTV.owl#> PREFIX foaf2: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> PREFIX foaf3: <http://www.w3.org/2000/01/rdf-schema#>  SELECT ?s WHERE { { {?s foaf2:type foaf:"+className+"} UNION {?s foaf3:subClassOf foaf:"+className+"} } . ?s foaf:"+proposition+" ?year . FILTER (?year > "+start+") . FILTER (?year < "+end+")}

Como se pode observar � poss�vel utilizar os filtros do SPARQL diretamente (de forma semelhante ao SQL) para restringir valores num�ricos.

Na estrutura #5 vai-se encontrar a inst�ncia (ou inst�ncias) da primeira classe e de seguida devolver as inst�ncias da segunda classe relacionadas com a primeira inst�ncia. Exemplos de procura seriam:

A fun��o utilizada � find(String className, String instance, String assocClassName). Esta fun��o come�a por chamar a fun��o find(String className, String instance) com os dois primeiros par�metros (className e instance). Um exemplo desta query para o segundo caso seria:

<http://www.semanticweb.org/ontologies/2011/9/WSTV.owl#> PREFIX foaf2: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> SELECT ?s WHERE { ?s foaf2:type foaf:Actor . ?s foaf:hasName ?instanceName . FILTER regex(?instanceName, "Summer Glau", "i" ) }

De seguida v�o-se buscar todas as propriedades poss�veis para unir a inst�ncia � segunda classe. Tomando novamente como exemplo a segunda pesquisa sem�ntica as propriedades poss�veis entre a inst�ncia �Summer Glau� da classe Actor e TVSeries s�o as Object Properties hasPerformedInTVSeries e isStartOf. Tendo esta informa��o v�o-se buscar as inst�ncias que est�o relacionadas com a Actor Summer Glau (com identificador �nico nm1132359) atrav�s destas propriedades. Um exemplo desta query � apresentado de seguida:

PREFIX foaf:   <http://www.semanticweb.org/ontologies/2011/9/WSTV.owl#> SELECT ?o WHERE {  { foaf:nm1132359 foaf:hasPerformedInTVSeries ?o }  UNION  { foaf:nm1132359 foaf:isStarOf ?o }  }

Algoritmo de Recomenda��es

As recomenda��es feitas ao utilizador t�m como base o seu hist�rico de visualiza��o de p�ginas de s�ries durante a sess�o. A visualiza��o de atores, criadores e personagens n�o tem qualquer influ�ncia na recomenda��o. Uma das raz�es desta decis�o � que essa informa��o j� pode ser consultada na sec��o de filmografia, ao aceder � p�gina da pessoa. Contudo, a raz�o principal para n�o fazermos sugest�es com base em eixos, como por exemplo ator, � que temos uma base de dados de s�ries relativamente pequena na qual � raro um ator ter participado em mais do que uma s�rie.

Quando o utilizador n�o tem hist�rico s�o recomendadas s�ries que fazem parte de um conjunto de s�ries de alta qualidade e/ou interessantes definido na ontologia. O prop�sito � que � prefer�vel recomendar s�ries de qualidade do que n�o recomendar nenhuma ou usar um crit�rio aleat�rio. Al�m disso, de todas as N s�ries neste conjunto s� s�o recomendadas no m�ximo 4, escolhidas aleatoriamente e n�o ordenadas (para evitar que se sugiram sempre as mesmas).

Com base no hist�rico do utilizador s�o constru�dos dois histogramas, um de g�neros e outro de criadores. Estes histogramas v�o contar todas as ocorr�ncias de um determinado g�nero ou criador. � de notar que n�o foram descartadas s�ries que aparecem no hist�rico mais que uma vez � se essas s�ries foram mais visitadas os seus atributos devem ter mais relev�ncia.

Com base nos dois histogramas � calculada a pontua��o de todas as s�ries no dataset. Esta pontua��o (ou P) � calculada da seguinte forma:

  1. Inicia P a 1.

  2. Para todos os g�neros, adiciona a P o valor desse g�nero no histograma.

  3. Para todos os criadores, adiciona a P o valor desse criador no histograma.

  4. Multiplica P pelo rating da s�rie dividido por 10.

O objetivo do passo 4 �, sobretudo, evitar que s�ries de muito baixa qualidade sejam recomendadas. P � iniciado a 1 para que, caso existam poucas correspond�ncias com g�neros ou criadores, as restantes s�ries sejam ordenadas pelo seu rating. Por fim, as s�ries s�o ordenadas por P e s�o recomendadas as 5 melhor classificadas de acordo com este sistema.

De notar que s�ries no hist�rico n�o s�o recomendadas (porque o utilizador j� as viu), nem a s�rie que o utilizador possa estar a ver no momento.

Visual Navigator

O Visual Navigator � um hibrido entre navega��o e recomenda��o. A ideia base � ver, num plano 2D, o qu�o pr�ximo est�o todas as s�ries no dataset de uma s�rie alvo. Quanto maior essa proximidade menos distantes do centro aparecer�o as s�ries. Para tornar a visualiza��o mais interessante cada s�rie � representada pela sua imagem sendo que o tamanho desta tamb�m reflete (de forma equivalente � dist�ncia ao centro) a afinidade com a s�rie alvo (maior - mais pr�xima).

A proximidade � baseada numa pontua��o � a mesma utilizada para as recomenda��es. O algoritmo e o c�digo s�o os mesmos, a �nica diferen�a � que neste caso a �nica s�rie no �hist�rico� � a s�rie alvo (de notar que a utiliza��o desta feature n�o altera de forma nenhuma a funcionalidade de recomenda��o).

Tamb�m � poss�vel filtrar as s�ries vis�veis por ano e rating. Esta funcionalidade � exemplo das vantagens da modularidade da aplica��o web. Em termos de back-end, implementar esta feature limitou-se � utiliza��o da componente de pesquisa sem�ntica por intervalo.

Al�m das referidas existe ainda mais uma s�rie de funcionalidades para enriquecer a experi�ncia do utilizador:

VisualNavigator


Pedro Gil Catr� & Lu�s Filipe Cardoso. Web site criado com http://strapdownjs.com/

Copyright � 2012