395 lines
13 KiB
Python
Executable File
395 lines
13 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*- encoding: utf-8 -*-
|
|
|
|
import libRecomendacao
|
|
import sqlite3
|
|
import random
|
|
|
|
#############################################
|
|
## Utilitários ##
|
|
#############################################
|
|
|
|
def clearScr():
|
|
for i in range(10):
|
|
print()
|
|
|
|
def pressEnterToReturn(msg:str = 'Pressione ENTER para voltar.')->str:
|
|
return input(msg)
|
|
|
|
def avg(iter):
|
|
return sum(iter)/len(iter)
|
|
|
|
#############################################
|
|
## Funções de modelo ##
|
|
#############################################
|
|
|
|
def pickTotalFilmes(db):
|
|
cursor = db.cursor()
|
|
cursor.execute('select movieId from movies order by movieId desc limit 1')
|
|
totalFilmes = cursor.fetchone()[0]
|
|
cursor.close()
|
|
return totalFilmes
|
|
|
|
def pickRatingsUsuario(db,usuario):
|
|
cursor = db.cursor()
|
|
cursor.execute('select userId, movieId, rating from ratings where userId = ?',(usuario,))
|
|
ratingsUsuario = list(cursor.fetchall())
|
|
cursor.close()
|
|
return ratingsUsuario
|
|
|
|
def pickRatingsFilme(db,filme):
|
|
cursor = db.cursor()
|
|
cursor.execute('select userId, movieId, rating from ratings where movieId = ?',(filme,))
|
|
ratingsFilme = list(cursor.fetchall())
|
|
cursor.close()
|
|
return ratingsFilme
|
|
|
|
def pickFilme(db,filme):
|
|
cursor = db.cursor()
|
|
cursor.execute('select movieId, title, year from movies where movieId = ?',(filme,))
|
|
filme = cursor.fetchone()
|
|
cursor.close()
|
|
return filme
|
|
|
|
def pickGenresFilme(db,filme):
|
|
cursor = db.cursor()
|
|
cursor.execute('select movieId, genreId from moviegenre where movieId = ?',(filme,))
|
|
generos = cursor.fetchall()
|
|
cursor.close()
|
|
return [g[1] for g in generos]
|
|
|
|
def pickGenres(db):
|
|
cursor = db.cursor()
|
|
cursor.execute('select genreId, genre from genres')
|
|
generos = cursor.fetchall()
|
|
cursor.close()
|
|
return {g[0]:g[1] for g in generos}
|
|
|
|
|
|
#############################################
|
|
## Dado um usuário e um filme, retorna a ##
|
|
## classificação para o par ##
|
|
#############################################
|
|
|
|
def predizNota(db,usuario,filme):
|
|
totalFilmes = pickTotalFilmes(db)
|
|
a = [None for x in range(totalFilmes+1)]
|
|
for rt in pickRatingsUsuario(db,usuario):
|
|
a[rt[1]]=rt[2]
|
|
c = 0
|
|
ous = pickRatingsFilme(db,filme)
|
|
n = [[None for x in range(totalFilmes+1)] for y in range(len(ous))]
|
|
for ou in ous:
|
|
for rt in pickRatingsUsuario(db,ou[0]):
|
|
n[c][rt[1]]=rt[2]
|
|
c+=1
|
|
return libRecomendacao.predicao(a,n,filme,libRecomendacao.similaridade)
|
|
|
|
#############################################
|
|
## Dado um usuário e um filme, retorna a ##
|
|
## classificação para o par; rapidamente ##
|
|
#############################################
|
|
|
|
def predizNotaRapidamente(db,usuario,filme):
|
|
notas = [rt[2] for rt in pickRatingsFilme(db,filme)]
|
|
return avg(notas)
|
|
|
|
#############################################
|
|
## Pede para o usuário entrar com o ID de ##
|
|
## um usuário ##
|
|
#############################################
|
|
|
|
def getUsuario(db):
|
|
cursor = db.cursor()
|
|
cursor.execute('select userId from ratings')
|
|
allUsers = [i[0] for i in cursor.fetchall()]
|
|
print('Usuários de exemplo: '+', '.join(map(str,random.sample(allUsers,3))))
|
|
u = None
|
|
while u is None:
|
|
u = input('["x" para abortar] Insira ID do usuário: ')
|
|
if u == 'x' or u == '"x"':
|
|
return None
|
|
elif (not u.isdigit()) or (int(u) not in allUsers):
|
|
u = None
|
|
print('Usuário inexistente')
|
|
else:
|
|
return int(u)
|
|
|
|
|
|
#############################################
|
|
## Procura por filmes no banco de dados ##
|
|
#############################################
|
|
|
|
def searchMovie(db,u):
|
|
u = u.replace('"',' ').replace('\'',' ').replace('%',' ').replace('_',' ')
|
|
while ' ' in u:
|
|
u = u.replace(' ',' ')
|
|
terms = u.strip().split(' ')
|
|
terms = ['title like "%'+term+'%"' for term in terms]
|
|
cursor = db.cursor()
|
|
cursor.execute('select movieId, title, year from movies where '+(' and '.join(terms))+' limit 7')
|
|
r = list(cursor.fetchall())
|
|
cursor.close()
|
|
return r
|
|
|
|
#############################################
|
|
## Exibe o resultado duma busca por ##
|
|
## filmes ##
|
|
#############################################
|
|
|
|
def printMovieTable(searchResults):
|
|
tbl = '| {0:>8} | {1:<40} | {2:>4} |'
|
|
tblDiv = '+'+10*'-'+'+'+42*'-'+'+'+6*'-'+'+'
|
|
print(tblDiv)
|
|
print(tbl.format(*['ID','Título','Ano']))
|
|
print(tblDiv)
|
|
for searchResult in searchResults[:7]:
|
|
print(tbl.format(*searchResult))
|
|
print(tblDiv)
|
|
|
|
|
|
#############################################
|
|
## Pede para o usuário entrar com o ID de ##
|
|
## um filme ##
|
|
#############################################
|
|
|
|
def getFilme(db):
|
|
cursor = db.cursor()
|
|
cursor.execute('select movieId, title, year from movies')
|
|
dbrt = cursor.fetchall()
|
|
cursor.close()
|
|
allMovies = [i[0] for i in dbrt]
|
|
print('Filmes de exemplo: '+', '.join(map(str,random.sample(allMovies,3))))
|
|
u = None
|
|
while u is None:
|
|
u = input('["x" para abortar] Insira ID do filme ou pesquise: ')
|
|
if u == 'x' or u == '"x"':
|
|
return None
|
|
elif (not u.isdigit()) or (int(u) not in allMovies):
|
|
print('ID de filme inexistente: pesquisando pelo título...')
|
|
searchResults = searchMovie(db,u)
|
|
printMovieTable(searchResults)
|
|
u = None
|
|
else:
|
|
return int(u)
|
|
|
|
#############################################
|
|
## Garante que a nota estará no intervalo ##
|
|
## entre 0% e 100% ##
|
|
#############################################
|
|
|
|
def normalizaNota(pred):
|
|
return max(min((pred*20),100),0)
|
|
|
|
#############################################
|
|
## Exibe um menu genérico e aguarda uma ##
|
|
## seleção válida ##
|
|
#############################################
|
|
|
|
def menuzaoGenerico(titulo:str, itens:dict)->int:
|
|
o = ''
|
|
acc = list(map(str,range(0,10)))
|
|
while o not in acc:
|
|
print('=== %s ==='%(titulo))
|
|
for i in range(1,10):
|
|
print(' %d. %s'%(i,itens.get(i,'')))
|
|
print(' 0. Sair')
|
|
o = input('Selecione sua opção: ')
|
|
if (o not in acc) or (o.isdigit() and (str(itens.get(int(o),'')) == '')):
|
|
if o == '0':
|
|
print('Saindo...')
|
|
else:
|
|
print('Escolha não existente')
|
|
else:
|
|
print()
|
|
return int(o)
|
|
|
|
#############################################
|
|
## Classifica um filme para um usuário ##
|
|
## imprimindo o fitness do filme para o ##
|
|
## usuário ##
|
|
#############################################
|
|
|
|
def menuClassificacaoUsuarioFilme(db):
|
|
clearScr()
|
|
usuario = getUsuario(db)
|
|
if usuario is None:
|
|
return
|
|
filme = getFilme(db)
|
|
if filme is None:
|
|
return
|
|
cursor = db.cursor()
|
|
cursor.execute('select userId, movieId, rating from ratings where userId = ? and movieId = ?',(usuario,filme))
|
|
todos = cursor.fetchall()
|
|
cursor.close()
|
|
clearScr()
|
|
if len(todos)>0:
|
|
print('Usuário já deu nota para este filme.')
|
|
print('%d%%'%(todos[0][2]*20))
|
|
else:
|
|
print('Predizendo nota... aguarde.')
|
|
pred = predizNota(db,usuario,filme)
|
|
if pred is None:
|
|
print('Dados insuficientes')
|
|
else:
|
|
print('%d%%'%(normalizaNota(pred)))
|
|
pressEnterToReturn()
|
|
|
|
#############################################
|
|
## Recomenda até sete filmes para um ##
|
|
## usuário ##
|
|
#############################################
|
|
|
|
def menuRecomendacaoFilmeParaUsuario(db):
|
|
clearScr()
|
|
usuario = getUsuario(db)
|
|
if usuario is None:
|
|
return
|
|
clearScr()
|
|
ratingsUsuario = pickRatingsUsuario(db,usuario)
|
|
if len(ratingsUsuario)>0:
|
|
assistido = set([rt[1] for rt in ratingsUsuario])
|
|
aAssistir = []
|
|
for filme in assistido:
|
|
for telespectador in set([rt[0] for rt in pickRatingsFilme(db,filme)]):
|
|
for sugestao in set([rt[1] for rt in pickRatingsUsuario(db,telespectador)]):
|
|
if sugestao not in assistido:
|
|
aAssistir.append(sugestao)
|
|
aAssistir = list(set(aAssistir))
|
|
if len(aAssistir)>0:
|
|
print('Pessoas que assistiram os filmes que o usuário assistiu')
|
|
print('assistiram %d títulos a mais.'%len(aAssistir))
|
|
rank = []
|
|
c = 0
|
|
for filme in aAssistir:
|
|
if c%250 == 0:
|
|
print('Preparando TOP-7... %d%%'%((100*c)/len(aAssistir)))
|
|
pass
|
|
nota = predizNotaRapidamente(db,usuario,filme)
|
|
rank.append((nota, filme))
|
|
c+=1
|
|
print('Preparando TOP-7... 100%')
|
|
rank = list(reversed(list(sorted(rank))))
|
|
filmes = []
|
|
for _,filme in rank[:7]:
|
|
filmes.append(pickFilme(db,filme))
|
|
pass
|
|
printMovieTable(filmes)
|
|
else:
|
|
print('Usuário já assistiu todos os filmes de sua bolha social')
|
|
else:
|
|
print('Usuário não avaliou nada')
|
|
pressEnterToReturn()
|
|
|
|
#############################################
|
|
## Retorna o perfil do nicho de usuários ##
|
|
## que se interessaram ##
|
|
## (ou desinteressaram) pelo filme de ##
|
|
## acordo com seus gêneros ##
|
|
#############################################
|
|
|
|
def menuRecomendacaoPerfilUsuarioParaFilme(db):
|
|
clearScr()
|
|
filme = getFilme(db)
|
|
if filme is None:
|
|
return
|
|
ratingsFilme = pickRatingsFilme(db,filme)
|
|
mediaFilme = avg([rt[2] for rt in ratingsFilme])
|
|
outrosFilmes = []
|
|
for telespectador in set([rt[0] for rt in ratingsFilme]):
|
|
for sugestao in set([rt[1] for rt in pickRatingsUsuario(db,telespectador)]):
|
|
outrosFilmes.append(sugestao)
|
|
outrosFilmes = list(set(outrosFilmes))
|
|
generosEste = dict()
|
|
gens = pickGenresFilme(db,filme)
|
|
for gen in gens:
|
|
generosEste[gen] = generosEste.get(gen,mediaFilme)
|
|
generosTodos = dict()
|
|
print('As pessoas que avaliaram o filme selecionado,')
|
|
print('também avaliaram outros %d filmes'%len(outrosFilmes))
|
|
c = 0
|
|
for outroFilme in outrosFilmes:
|
|
if c%250 == 0:
|
|
print('Preparando perfil do nicho de espectadores... %d%%'%((100*c)/len(outrosFilmes)))
|
|
pass
|
|
rf = avg([rf[2] for rf in pickRatingsFilme(db,outroFilme)])
|
|
gens = pickGenresFilme(db,outroFilme)
|
|
for gen in gens:
|
|
generosTodos[gen] = generosTodos.get(gen,[])
|
|
generosTodos[gen].append(rf)
|
|
c+=1
|
|
for key in generosTodos:
|
|
generosTodos[key] = avg(generosTodos[key])
|
|
clearScr()
|
|
tbl = '| {0:<15} | {1:>6}% | {2:>7}% |'
|
|
tblDiv = '+'+17*'-'+'+'+9*'-'+'+'+10*'-'+'+'
|
|
print(tblDiv)
|
|
print(tbl.format(*['Genero','Nicho','\u0394filme']))
|
|
print(tblDiv)
|
|
generosDb = pickGenres(db)
|
|
for key in generosDb:
|
|
notaGlobal = None
|
|
if key in generosTodos: notaGlobal = normalizaNota(generosTodos[key])
|
|
notaFilme = None
|
|
if key in generosEste: notaFilme = normalizaNota(generosEste[key])
|
|
display = ['Genre','???','???']
|
|
display[0] = generosDb[key]
|
|
if not (notaGlobal is None):
|
|
display[1] = '%.2f'%notaGlobal
|
|
if not (notaFilme is None):
|
|
display[2] = '%.2f'%(notaGlobal-notaFilme)
|
|
print(tbl.format(*display))
|
|
print(tblDiv)
|
|
try:
|
|
gt = normalizaNota(avg(list(generosTodos.values())))
|
|
ge = normalizaNota(avg(list(generosEste.values())))
|
|
if gt is None or ge is None:
|
|
raise Exception('dont print')
|
|
print(tbl.format(*['Média','%.2f'%gt,'%.2f'%(gt-ge)]))
|
|
print(tblDiv)
|
|
except:
|
|
pass
|
|
pressEnterToReturn()
|
|
|
|
#############################################
|
|
## Menu principal ##
|
|
#############################################
|
|
|
|
def menuPrincipal(db):
|
|
selecao = 1
|
|
while selecao!=0:
|
|
selecao = menuzaoGenerico('Menu Principal',{
|
|
1: 'Classificação usuário\u00d7filme',
|
|
2: 'Recomendações de filmes para usuário',
|
|
3: 'Perfil de usuários para filme',
|
|
})
|
|
if selecao == 0:
|
|
break
|
|
elif selecao == 1:
|
|
menuClassificacaoUsuarioFilme(db)
|
|
elif selecao == 2:
|
|
menuRecomendacaoFilmeParaUsuario(db)
|
|
elif selecao == 3:
|
|
menuRecomendacaoPerfilUsuarioParaFilme(db)
|
|
else:
|
|
pass
|
|
return
|
|
|
|
#############################################
|
|
## Rotina principal; ##
|
|
## invoca o menu principal ##
|
|
#############################################
|
|
|
|
def main():
|
|
db = None
|
|
try:
|
|
db = sqlite3.connect('database.db')
|
|
except:
|
|
print('Erro ao conectar-se ao banco de dados')
|
|
if not (db is None):
|
|
menuPrincipal(db)
|
|
return
|
|
|
|
if __name__ == '__main__':
|
|
main()
|