Gestão e Tratamento de Informação

Loading.... (view fulltext now)

Loading....

Loading....

Loading....

Loading....

Texto

(1)








1º
semestre






Mini‐Projecto
3
–
Entrega
a
25/11/2010


Os
 dois
 URLs
 que
 se
 listam
 de
 seguida
 correspondem
 a
 dois
 documentos
 XML


codificando
 informação
 sobre
 programas
 de
 televisão.
 Note‐se
 que
 os
 dois


documentos
XML
codificam
informação
semelhante,
embora
de
formas
diferentes.


1.

http://gibson.tagus.ist.utl.pt/~bmartins/tv‐shows‐info.xml

2.

http://gibson.tagus.ist.utl.pt/~bmartins/new‐tv‐shows‐info.xml

Utilizando
o
software
para
processamento
de
documentos
XML,
introduzido
nas
aulas


de
laboratório,
resolva
os
seguintes
exercícios
práticos.


Exercício
1


Usando
 os
 documentos
 XML
 de
 nome
 tv‐shows‐info.xml
 e
 new‐tv‐shows‐info.xml,


resolva
as
seguintes
alíneas:


1.1
 –
 Construa
 um
 dicionário
 com
 base
 nos
 nomes
 de
 actores
 mencionados
 no


documento
tv‐shows‐info.xml,
que
entraram
em
mais
de
um
episódio
de
alguma
série.


1.2
 ‐
 Usando
 o
 dicionário
 da
 alínea
 anterior,
 escreva
 uma
 XQuery
 que,
 através
 da


função
 de
 extensão
 de
 nome
 java:gti.dictionarychunk(),
 permita
 extrair


todos
 os
 nomes
 de
 actores
 mencionados
 nos
 conteúdos
 textuais
 das
 reviews
 do


documento
new‐tv‐shows‐info.xml.


1.3
 ‐
 A
 função
 de
 extensão
 java:gti.dictionarychunk(),
 descrita
 nas
 aulas


de
laboratório
e
construída
com
base
nas
funcionalidades
disponibilizadas
pela
API
do


LingPipe,
corresponde
ao
método
Java
cujo
código
se
apresenta
na
Figura
1.






public static String[] dictionarychunk(String text, String ent) throws Exception { MapDictionary<String> dictionary = new MapDictionary<String>();

for (int i=0; i<ent.length; i++)

dictionary.addEntry(new DictionaryEntry<String>(ent[i],”chunk”,1.0)); ExactDictionaryChunker dictionaryChunkerTT = new

ExactDictionaryChunker(dictionary,

IndoEuropeanTokenizerFactory.INSTANCE, true, true); Chunking chunking = dictionaryChunkerTT.chunk(text);

List<String> results = new ArrayList<String>(); for (Chunk chunk : chunking.chunkSet()) { int start = chunk.start();

int end = chunk.end(); String type = chunk.type(); double score = chunk.score();

String txt = text.substring(start,end); results.add(txt);

}

return results.toArray(new String[0]); }

Figura
1
–
Método
Java
correspondente
à
função
java:gti.dictionarychunk().


Crie
 uma
 nova
 função
 de
 extensão,
 denominada
 approxdictionarychunk(),
 que
 em


lugar
 de
 fazer
 o
 emparelhamento
 exacto
 com
 todas
 as
 entidades
 mencionadas
 no


dicionário,
 permita
 reconhecer
 todas
 as
 entidades
 mencionadas
 que
 tenham
 uma


(2)

distância
de
edição
inferior
a
dois
em
relação
às
entidades
mencionadas
no
dicionário.


O
código
Java
da
função
deverá
ser
apresentado
como
resolução
deste
exercício.


Sugestão:
 Deve
 consultar
 a
 documentação
 (i.e.,
 os
 tutoriais
 e
 a
 documentação


Javadoc)
 da
 API
 do
 LingPipe,
 que
 se
 encontra
 disponível
 através
 do
 site


http://alias‐

i.com/lingpipe/

.
 Em
 particular,
 deverá
 ser
 analisada
 em
 detalhe
 a
 documentação


disponível
para
a
classe
com.aliasi.dict.ApproxDictionaryChunker.


1.4
 ‐
 Usando
 o
 dicionário
 da
 alínea
 anterior,
 escreva
 uma
 XQuery
 que,
 através
 da


função
 java:gti.approxdictionarychunk()
 criada
 na
 alínea
 anterior,


permita
extrair
todos
os
nomes
de
actores
mencionados
nos
conteúdos
textuais
das


reviews
do
documento
new‐tv‐shows‐info.xml.


Exercício
2


Usando
 os
 documentos
 XML
 de
 nome
 tv‐shows‐info.xml
 e
 new‐tv‐shows‐info.xml,


resolva
as
seguintes
alíneas:


2.1
–
Escreva
uma
XQuery
que,
através
do
uso
de
um
Hidden
Markov
Model,
permita


extrair
 das
 reviews
 do
 documento
 new‐tv‐shows‐info.xml
 os
 nomes
 de
 criativos
 lá


mencionados.
 Para
 treinar
 o
 modelo
 de
 extracção,
 deve
 usar
 os
 exemplos
 anotados


que
se
encontram
no
URL


http://gibson.tagus.ist.utl.pt/~bmartins/persons.xml

.


2.2
–
Com
base
nos
criativos
associados
aos
programas
de
televisão
que
se
encontram


descritos
 nos
 documentos
 tv‐shows‐info.xml
 e
 new‐tv‐shows‐info.xml,
 escreva
 uma


XQuery
 que
 permita
 associar,
 a
 cada
 um
 dos
 nomes
 de
 criativos
 extraídos
 na
 alínea


anterior,
 um
 URL
 para
 uma
 página
 Web
 contendo
 informação
 detalhada
 sobre
 a


carreira
da
pessoa.
Os
criativos
extraídos
que
não
tenham
um
URL
associado
deverão


ser
removidos
da
resposta
produzida
neste
exercício.


Exercício
3


Usando
 os
 documentos
 XML
 tv‐shows‐info.xml
 e
 new‐tv‐shows‐info.xml,
 resolva
 as


seguintes
alíneas:


3.1
–
Utilizando
as
funções
de
extensão
relacionadas
com
classificação
de
documentos,


introduzidas
nas
aulas
de
laboratório,
treine
um
modelo
de
classificação
que
aprenda
a


classificar
reviews
de
programas
de
televisão
como
positivas
ou
negativas.
Como
dados


de
treino,
deverá
utilizar
as
reviews
que
se
encontram
já
classificadas
desta
forma
no


documento
tv‐shows‐info.xml.


3.2
–
Utilizando
o
modelo
de
classificação
desenvolvido
na
alínea
anterior,
classifique


as
reviews
que
se
encontram
no
documento
new‐tv‐shows‐info.xml.


3.3
–
Avalie
a
qualidade
dos
resultados
do
classificador
produzido
na
alínea
anterior


com
base
nas
métricas
de
Precisão
e
Cobertura.
Considere
que
todas
as
reviews
com


(3)

pontuação
de
6
estrelas
ou
mais
deveriam
ser
classificadas
como
positivas,
e
que
as


restantes
reviews
deveriam
ser
classificadas
como
negativas.


Exercício
4


Considere
 o
 seguinte
 Hidden
 Markov
 Model,
 M=(A,B,p),
 desenhado
 para
 identificar


ocorrências
de
nomes
de
programas
de
televisão
em
sequências
de
palavras.



Os
 estados
 considerados
 no
 modelo
 correspondem
 a
 etiquetas
 do
 tipo
 Show
 (i.e.,


Show_INICIO,
 Show_MEIO,
 Show_FIM)
 ou
 NÃO_Show,
 em
 que
 os
 vários
 estados


associados
à
etiqueta
Filme
pretendem
capturar
o
início
de
um
nome
de
um
filme,
o


meio
de
um
nome
de
filme,
e
o
fim
de
um
nome
de
filme.


MATRIZ
A


Show_INICIO
 Show_MEIO


Show_FIM


NÃO_Show


Show_INICIO
(I)


0,15


0,25


0,30


0,30


Show_MEIO
(M)


0,00


0,30


0,70


0,00


Show_FIM
(F)


0,20


0,00


0,00


0,80


NÃO_Show
(N)


0,30


0,00


0,00


0,70


MATRIZ
B


Show_INICIO
 Show_MEIO


Show_FIM


NÃO_Show


Laurie
(1)


0,05


0,01


0,05


0,20


Stars
(2)


0,08


0,01


0,02


0,11


Mr.
(3)


0,10


0,05


0,00


0,13


House
(4)


0,32


0,15


0,25


0,01


Adder
(5)


0,10


0,25


0,45


0,04


Black
(6)


0,23


0,22


0,10


0,02


<other_words>
(7)
 0,12


0,26


0,13


0,39


<pontuation>
(8)


0,00


0,05


0,00


0,10


PROBABILIDADES
INICIAIS
(p)


Show_INICIO


Show_MEIO


Show_FIM


NÃO_Show


0,20


0,00


0,00


0,80


No
modelo
apresentado,
A
é
a
matriz
de
transição
entre
estados,
B
é
a
probabilidade


de
gerar
cada
símbolo
em
cada
estado
e
p
é
a
probabilidade
inicial
de
cada
estado.


4.1
‐
Represente
graficamente
a
máquina
de
estados
correspondente
ao
modelo
M.


4.2
‐
Calcule
a
sequência
de
estados
mais
provável
de
ter
gerado
a
frase:
“Mr.
Laurie


stars
in
Black
Adder
and
in
House.”.
Apresente
os
cálculos
efectuados
em
cada
passo.


4.3
‐
Sugira
duas
modificações
ao
modelo
que
pudessem
melhorar
o
seu
desempenho.


Justifique
as
suas
respostas.


(4)

Exercício
5


Utilizando
 os
 documentos
 XML
 de
 nome
 tv‐shows‐info.xml
 e
 new‐tv‐shows‐info.xml,


resolva
as
seguintes
alíneas:


5.1
–
Defina
um
formato
de
representação
comum,
através
de
um
XML
Schema,
para


as
duas
fontes
de
dados
correspondentes
aos
documentos
tv‐shows‐info.xml
e
new‐tv‐

shows‐info.xml.
 O
 formato
 de
 representação
 deve
 tentar
 preservar,
 ao
 máximo,
 a


informação
que
se
encontra
em
cada
uma
das
fontes
de
dados
distintas.


5.2
 –
 Escreva
 duas
 funções
 em
 XQuery
 (i.e.,
 dois
 wrappers)
 que
 permitam,


respectivamente,
 converter
 os
 dados
 dos
 documentos
 tv‐shows‐info.xml
 e
 new‐tv‐

shows‐info.xml
para
o
formato
de
representação
comum.
Cada
um
dos
wrappers
deve


produzir
um
documento
XML
válido
de
acordo
com
o
XML
Schema
da
alínea
anterior.


5.3
 –
 Escreva
 uma
 função
 em
 XQuery
 (i.e.,
 um
 mediador)
 que
 permita
 integrar
 a


informação
 retornada
 pelos
 dois
 wrappers
 desenvolvidos
 na
 alínea
 anterior.
 Neste


exercício,
 não
 é
 necessário
 levar
 em
 consideração
 a
 eliminação
 de
 programas
 de


televisão
 potencialmente
 duplicados
 nas
 duas
 fontes.
 O
 mediador
 deve
 produzir
 um


documento
XML
válido
de
acordo
com
o
XML
Schema
da
primeira
alínea.


Entrega
do
terceiro
mini‐projecto


A
resolução
do
mini‐projecto
deve
ser
entregue
via
Fénix
sob
a
forma
de
um
ficheiro


com
a
extensão
.zip,
até
às
08:00
do
dia
estipulado
para
a
entrega
(i.e.,
antes
da
aula


teórica
da
disciplina
de
Gestão
e
Tratamento
de
Informação).



O
 ficheiro
 .zip
 deve
 conter
 os
 ficheiros
 de
 texto
 com
 as
 soluções
 (e.g.,
 documentos


XML,
XSD,
XSLT
ou
ficheiros
de
texto
com
as
interrogações
XPath/XQuery)
para
cada


uma
das
perguntas/alíneas
individuais
deste
enunciado.


Na
 aula
 teórica
 da
 data
 de
 entrega,
 deve
 ainda
 ser
 entregue
 um
 relatório
 impresso


com
a
resolução
das
várias
perguntas,
segundo
o
template
fornecido
na
página
da


disciplina.
 O
 relatório
 deve
 ter
 uma
 folha
 de
 rosto
 ou
 um
 cabeçalho
 identificando


claramente
qual
o
número
do
grupo
e
quais
os
números
de
aluno
dos
3
elementos.


Para
cada
pergunta,
e
além
de
listar
a
respectiva
solução
para
o
problema,
o
relatório


deve
ainda
indicar
claramente
qual
o
nome
do
ficheiro
que
contém
a
sua
resolução.


Não
 serão
 aceites
 trabalhos
 entregues
 via
 e‐mail,
 nem
 trabalhos
 onde
 o
 relatório


não
obedece
às
regras
estipuladas.


Boa
sorte
na
realização
do
mini‐projecto!


(5)

Resolução
do
mini‐projecto
3


Pergunta
1.1


declare namespace ns="http://gibson.tagus.ist.utl.pt/~bmartins/namespace-tvshows"

let $doc := doc("http://gibson.tagus.ist.utl.pt/~bmartins/tv-shows-info.xml") let $dic := distinct-values($doc//ns:actor[@episodes>1]/@name)

return $dic

Pergunta
1.2


declare namespace ns = "http://gibson.tagus.ist.utl.pt/~bmartins/namespace-tvshows"; declare namespace gti = "java:gti";

let $doc := doc("http://gibson.tagus.ist.utl.pt/~bmartins/tv-shows-info.xml") let $dict := distinct-values($doc//ns:actor[@episodes>1]/@name)

let $doc2 := doc("http://gibson.tagus.ist.utl.pt/~bmartins/new-tv-shows-info.xml") let $revs := string-join(data($doc2//show-reviews) , " ")

return distinct-values(gti:dictionarychunk($revs,$dict))

Pergunta
1.3


import com.aliasi.tokenizer.IndoEuropeanTokenizerFactory; import com.aliasi.chunk.*; import com.aliasi.dict.*; import com.aliasi.spell.*; import java.util.*;

public class ApproximateChunk {

public static String[] approxdictionarychunk(String text,

String ent[]) throws Exception { TrieDictionary<String> dictionary = new TrieDictionary<String>();

for (int i = 0; i < ent.length; i++) {

dictionary.addEntry(new DictionaryEntry<String>(ent[i], "chunk", 1.0)); }

ApproxDictionaryChunker dChunkerTT = new ApproxDictionaryChunker(dictionary, IndoEuropeanTokenizerFactory.INSTANCE,

new FixedWeightEditDistance(0,-1,-1,-1,Double.NaN), 1.0);

Chunking chunking = dChunkerTT.chunk(text); List<String> results = new ArrayList<String>(); for (Chunk chunk : chunking.chunkSet()) { int start = chunk.start();

int end = chunk.end(); String type = chunk.type(); double score = chunk.score();

String txt = text.substring(start, end); results.add(txt);

}

return results.toArray(new String[0]); }

}

Pergunta
1.4


declare namespace ns = "http://gibson.tagus.ist.utl.pt/~bmartins/namespace-tvshows"; declare namespace appx = "java:ApproximateChunk";

let $doc := doc("http://gibson.tagus.ist.utl.pt/~bmartins/tv-shows-info.xml") let $dict := distinct-values($doc//ns:actor[@episodes>1]/@name)

let $doc2 := doc("http://gibson.tagus.ist.utl.pt/~bmartins/new-tv-shows-info.xml") let $revs := string-join(data($doc2//show-reviews) , " ")

return distinct-values(appx:approxdictionarychunk($revs,$dict))

(6)

Pergunta
2.1


declare namespace gti = "java:gti";

let $doc := doc("http://gibson.tagus.ist.utl.pt/~bmartins/new-tv-shows-info.xml") let $train := doc("http://gibson.tagus.ist.utl.pt/~bmartins/persons.xml")

let $revs := string-join( data($doc//show-reviews) , " ") return distinct-values(gti:mlchunk($revs,$train))

Pergunta
2.2


declare namespace ns = "http://gibson.tagus.ist.utl.pt/~bmartins/namespace-tvshows"; declare namespace gti = "java:gti";

let $doc := doc("http://gibson.tagus.ist.utl.pt/~bmartins/new-tv-shows-info.xml") let $train := doc("http://gibson.tagus.ist.utl.pt/~bmartins/persons.xml")

let $revs := string-join( data($doc//show-reviews) , " ")

let $doc2 := doc("http://gibson.tagus.ist.utl.pt/~bmartins/tv-shows-info.xml") for $creat in distinct-values(gti:mlchunk($revs,$train))

let $ids := distinct-values(($doc2//ns:actor[data(@name)=$creat]/data(@id) , $doc2//ns:writer[data(@name)=$creat]/data(@id) , $doc2//ns:director[data(@name)=$creat]/data(@id) , $doc//creative[data(@name)=$creat]/data(@id))) where string-length($ids[1])>0

return <creative name="{$creat}" id="{$ids[1]}"/> 


Pergunta
3.1


declare namespace gti = "java:gti";

declare namespace ns2 = "http://gibson.tagus.ist.utl.pt/~bmartins/namespace-reviews"; let $doc := doc("http://gibson.tagus.ist.utl.pt/~bmartins/tv-shows-info.xml") let $model := gti:classify_train( $doc//ns2:review/ns2:text/text(),

$doc//ns2:review/string(@sentiment-analysis) ) return $model

Pergunta
3.2


declare namespace gti = "java:gti";

declare namespace ns2 = "http://gibson.tagus.ist.utl.pt/~bmartins/namespace-reviews"; let $doc := doc("http://gibson.tagus.ist.utl.pt/~bmartins/tv-shows-info.xml") let $model := gti:classify_train( $doc//ns2:review/ns2:text/text(),

$doc//ns2:review/string(@sentiment-analysis) ) let $doc2 := doc("http://gibson.tagus.ist.utl.pt/~bmartins/new-tv-shows-info.xml") return

<reviews> {

for $review in $doc2//review return <review show="{$review/../../@title}" sentiment-analysis="{gti:classify($review/text(),$model)}" score="{$review/@score}">{$review/text()}</review> } </reviews>

(7)

Pergunta
3.3


declare namespace gti = "java:gti";

declare namespace ns2 = "http://gibson.tagus.ist.utl.pt/~bmartins/namespace-reviews"; let $doc := doc("http://gibson.tagus.ist.utl.pt/~bmartins/tv-shows-info.xml") let $model := gti:classify_train( $doc//ns2:review/ns2:text/text(),

$doc//ns2:review/string(@sentiment-analysis) ) let $doc2 := doc("http://gibson.tagus.ist.utl.pt/~bmartins/new-tv-shows-info.xml") let $resuls := <reviews>{

for $review in $doc2//review return <review show="{$review/../../@title}"

sentiment-analysis="{gti:classify($review/text(),$model)}" score="{$review/@score}">{$review/text()}</review>

}</reviews>

let $crr_pos := count($resuls//review[data(@score) >= 6 and

data(@sentiment-analysis)="pos"]) let $crr_neg := count($resuls//review[data(@score) < 6 and

data(@sentiment-analysis)="neg"]) let $cnt_pos := count($resuls//review[data(@sentiment-analysis)="pos"]) let $cnt_neg := count($resuls//review[data(@sentiment-analysis)="neg"]) let $tot_pos := count($resuls//review[data(@score) >= 6])

let $tot_neg := count($resuls//review[data(@score) < 6]) let $pre_pos:= $crr_pos div $cnt_pos

let $pre_neg:= $crr_neg div $cnt_neg let $rec_pos:= $crr_pos div $tot_pos let $rec_neg:= $crr_neg div $tot_neg

return <results precision_pos="{$pre_pos}" recall_pos="{$rec_pos}" precision_neg="{$pre_neg}" recall_neg="{$rec_neg}"/> 


Pergunta
4.1


(8)

Pergunta
4.2


Em
casa
posição
k
da
sequência
de
tokens,
o
algoritmo
de
Viterbi
calcula
a
probabilidade
do
estado
q

k

assumir
um
dado
valor
si,
a
probabilidade
de
termos
atingido
um
estado
qk
seguindo
um
caminho
desde


q1
...
qk‐1,
e
a
probabilidade
de
termos
gerado
uma
sequência
de
observações
o1
...
ok.
O
algoritmo
de


Viterbi
calcula
as
probabilidades
de
uma
forma
recursiva.


δ1(i)
=
πi
*
bi(o1)


δ

k+1

(j)
=
max

1
≤
i
≤
n

k

(i)
*
a

ij

)
*
b

j

(o

t+1

)



O
 algoritmo
 também
 nos
 permite
 descobrir
 a
 sequencia
 de
 estados
 mais
 provável,
 através
 de
 uma


matriz
I
tal
como
definida
abaixo:


I[j,1]
=
0


I[j,k]
=
argmax1
≤
i
≤
n
δk‐1(i)
*
aij



A
matriz
I
permite‐nos
reconstruir
a
sequência
de
estados
mais
provável
através
de
backtracking.


Pergunta
4.3


Várias
alterações
seriam
possíveis:


Alterar
a
matriz
com
as
probabilidades
de
geração
de
símbolos,
por
forma
a
considerar
um
número


de
 símbolos
 maior
 (e.g.,
 decompondo
 o
 símbolo
 other_words,
 uma
 vez
 que
 estas
 palavras
 podem


pertencer
tanto
a
locais
como
a
não
locais).
As
próprias
probabilidades
desta
matriz
poderiam
ser
re‐

estimadas
 com
 base
 em
 dados
 de
 treino,
 possibilitando‐se
 desta
 forma
 uma
 melhor
 adequação
 a


sequencias
de
símbolos
reais.

Considerar
um
número
de
estados
maior
no
modelo
(e.g.,
decompondo
o
estado NAO_SHOW em

NAO_SHOW_INICIO, NAO_SHOW_MEIO e NAO_SHOW_FIM),
possibilitando
assim
uma
melhor


modelação
das
transições
entre
os
diferentes
estados.

Pergunta
5.1


Um
possível
formato
de
representação
comum
para
as
duas
fontes
de
dados
seria
o
formato
XML
que


utilizado
no
documento
tv-shows-info.xml,
uma
vez
que
este
já
considera
todos
os
campos
de


informação
usados
nas
duas
fontes
de
dados.
Um
XML
Schema
para
este
formato
de
representação
foi


já
definido
na
pergunta
1
do
primeiro
mini-projecto
da
disciplina
de
GTI.

(9)

Perguntas
5.2
e
5.3


declare namespace gti = "java:gti";

declare namespace ns1 = "http://gibson.tagus.ist.utl.pt/~bmartins/namespace-tvshows"; declare namespace ns2 = "http://gibson.tagus.ist.utl.pt/~bmartins/namespace-reviews"; declare function local:wrapper1 () {

let $doc := doc("http://gibson.tagus.ist.utl.pt/~bmartins/tv-shows-info.xml") return $doc

};

declare function local:wrapper2 () { <ns1:tvshows>

{

for $d in doc("http://gibson.tagus.ist.utl.pt/~bmartins/new-tv-shows-info.xml")//tvshow return <ns1:tvshow>

<ns1:show-title>{data($d/@title)}</ns1:show-title> <ns1:companies>{for $a in $d//company return

<ns1:company name="{data($a)}" id="{$a/@id}"/>} </ns1:companies>

<ns1:creators>{for $a in $d//creative[@title="creator"] return <ns1:creator name="{$a/@name}" id="{$a/@id}" />} </ns1:creators>

<ns1:directors>{for $a in $d//creative[@title="director"] return <ns1:director name="{$a/@name}" id="{$a/@id}" />} </ns1:directors>

<ns1:writers>{for $a in $d//creative[@title="writer"] return <ns1:writer name="{$a/@name}" id="{$a/@id}"/>} </ns1:writers>

<ns1:actors>{for $a in $d//creative[@title="actor"] return <ns1:actor name="{$a/@name}" id="{$a/@id}" />} </ns1:actors>

<ns1:sinopsis>{$d/summary/text()}</ns1:sinopsis> <ns1:genres>{for $a in $d//show-genre return <ns1:genre name="{$a/@name}" />} </ns1:genres>

<ns2:reviews>{for $a in $d//review return <ns2:review score="{$a/@score}"

sentiment-analysis="{if($a/@score > 5) then ("pos") else ("neg")}"> <ns2:author reviewer-id="{$a/@reviewer-id}"> {data($a/@reviewer-name)} </ns2:author> <ns2:text>{data($a)}</ns2:text> </ns2:review>} </ns2:reviews> </ns1:tvshow> } </ns1:tvshows> };

declare function local:mediador () {

<ns1:tvshows>{ local:wrapper1()//ns1:tvshow union local:wrapper2()//ns1:tvshow}</ns1:tvshows>

};

local:mediador()

Imagem

Referências

temas relacionados :