EntryMakerBot

De Wikipast
Aller à la navigation Aller à la recherche

Description

EntryMakerBot se base sur des archives de texte afin de déterminer quelles informations pourraient être ajoutées à la page wikipast associée à la recherche. En téléchargeant le texte brut depuis les archives, il est possible de déterminer quelles lignes correspondent au personnage recherché, la date de publication du journal, puis d'effectuer un simple triage en fonction de leur pertinence. L'important réside dans le but fixé d'être capable de savoir quel objet sémantique est le plus important.

Le fonctionnement du bot se divise en 3 étapes: Premièrement, un scrapping d'informations sur le site de référence (wikipédia ou letempsarchives) est effectué. L'algorithme détermine, grâce aux locations du nom de la personne recherchée, les phrases du texte importantes pour la recherche d'informations. Deuxièmement, les phrases passent par un premier test d'importance puis les dix plus importantes sont sélectionnées afin de passer par la troisième étape: Une sélection basée sur le lexique382.

Scrapping d'informations

En utilisant une copie des articles wikipédia ou les archives du temps, il est possible d'itérer sur chaque mot et trouver les locations du nom du personnage recherché. En interpolant sur ces locations, il est ensuite possible de reconnaître l'emplacement de l'article concernant la cible. Si le texte contient du bruit, il est ensuite nettoyé, c'est à dire que les phrases illisibles sont supprimées, ainsi que les objets purement typographiques, afin de rendre le texte plus facile à analyser pour un ordinateur.

Test d'importance 1

Le premier test d'importance se fait en utilisant simplement les phrases analysées. Durant ce test, le nombre d'occurrences (le nombre de fois qu'il apparait dans le texte) de chaque mot est compté et avec cela on détermine leur fréquence dans le texte. Si la phrase contient le nom du personnage, on lui attribuera des points (ce qui va la faire monter dans le classement) puis les phrases avant et après celles-ci gagnent des points elles aussi sous forme d'une fonction en inverse d'exponentielle. Grâce à ces informations, il est possible de classer les phrases et de déterminer celles ayant le plus de chances d'avoir des informations importantes.

Test d'importance 2

Le deuxième test prend en input les dix premières phrases dans le classement du premier puis refait un test d'occurence, mais cette fois-ci en utilisant la fréquence reportée sur la table du lexique382. Grâce à cela il est possible d'effectuer un nouveau classement et d'obtenir le résultat final.

Interface

Le bot possède une interface utilisant la librairie tkinter. Elle permet de choisir le nom à chercher ainsi que le fichier dans lequel chercher. Elle affiche ensuite les informations trouvées, classées par ordre d'importance. Le code est montré ci-dessous:

from tkinter import *
import EventStructure as es
import PhraseFinder as pf
import numpy as np
import matplotlib.pyplot as mpl

def open_fichier(nom_fichier):
	fichier=open(nom_fichier,'r')
	text=fichier.read()
	text = text.split('\n')
	fichier.close()
	return text;

def close_words(text):
	for i in range(0,len(text)-1):
		if len(text[i]) > 0 and text[i][len(text[i])-1]=='-':
			if ' ' in text[i+1]:
				a = text[i+1].split(' ')
				text[i] = text[i][:len(text[i])-1]+a[0]
				text[i+1] = text[i+1][len(a[0])+1:len(text[i+1])-1]
			else:
				a = text[i+1]
				text[i] = text[i][:len(text[i])-1]+a
				text[i+1]=''
	return text

def find_name(n,f,l, text):
	wo = []
	fo = []
	lo = []
	for i in range(0,len(text)):
		if n in text[i]:
			wo.append(i)
		elif f in text[i]:
			fo.append(i)
		elif l in text[i]:
			lo.append(i)
	return [wo,fo,lo]

def define_sentence(i,j,text):
	sen = ''
	for i in range(i,j):
		sen += text[i]
	new_sen = []
	z=0
	for i in range(1,len(sen)):
	 if sen[i] == '.' and sen[i-1].islower():
		 new_sen.append(sen[z+1:i])
		 z=i
	return new_sen

def clean_text_pre(text):
	text = close_words(text)
	for i in range(len(text)):
		text[i]+=' '
	text=define_sentence(0,len(text)-1,text)
	return text
def clean_text_post(text):
	#text = sc.check_text(text)
	text = list(filter(lambda x : x != '', text))		
	return text

def check_name(name):
	name = name.split(' ')
	if len(name) > 1:
		return True
	return False

def split_name(name):
	return name.split(' ');

def get_closest_lr(r,m):
	rl = list(filter(lambda x: x<m,r))
	rr = list(filter(lambda x: x>m,r))
	rl=np.array(rl)
	rr=np.array(rr)
	l=np.abs(rl-m).argmin()
	r=np.abs(rr-m).argmin()
	return [int(abs(rl[l])), int(abs(rr[r]))]

def plot_fit(x,y,p):
	xx=np.linspace(0,x[-1],300)
	plot = mpl.plot(x,y,'.',xx,p(xx),'-')
	mpl.show()
	
def name_occ_fit(wo,fo,lo,text):
	
	a=[0]*len(text)
	x=range(len(text))
	we=[0]*len(text)
	for i in x:
		if i in wo:
			a[i]+=1
		if i in fo:
			a[i]+=.5
		if i in lo:
			a[i]+=.8
		we[i] = a[i] + len(text[i])/1000
	b=[.3]*len(lo)
	c=[1]*len(wo)
	y = np.array(a+b+c)
	z = np.polyfit(x,a,10,w=we)
	p=np.poly1d(z)
	
	return [x,a,p]
	
def make_article_bounds(wo,p):
	r=np.round(np.roots(p))
	lr=[]
	for i in wo:
		lr.append(get_closest_lr(r,i))
	nlr=[]
	for i in lr:
		if i not in nlr:
			nlr.append(i)
	return nlr

def format_name(name):
	if check_name(name):
		[firstname,lastname] = split_name(name)
	else:
		[firstname,lastname] = ['_None_','_None_']
	return [firstname,lastname]

def get_text_for_name(fichier, name):
	
	text = open_fichier(fichier)
	# text is an array of newspaper lines
	text = clean_text_pre(text)
	# text is now an array of sentences
	[firstname,lastname]=format_name(name)
	[wo,fo,lo] = find_name(name, firstname,lastname,text)
	[x,y,p] = name_occ_fit(wo,fo,lo,text)
	lr=make_article_bounds(wo,p)
	sen=[]
	for i in lr:
		#print(i)
		sen+=text[i[0]:i[-1]]
	sen = clean_text_post(sen)
	return sen

class base_gui():
	def __init__(self):
		
		self.app = Tk()
		self.frame = Frame(self.app)
		
		self.frame.grid()
		self.title = Label(self.frame, fg='RED', text="ENTRYMAKER BOT v0.1 alphatest")
		self.title.grid(row=0)
		
		self.file_label = Label(self.frame, text="Fichier")
		self.file_label.grid(row=3)
		
		self.name_label = Label(self.frame, text="Nom à checher")
		self.name_label.grid(row=1)
		
		self.name_entry = Entry(self.frame)
		self.name_entry.grid(row=1,column=1)

		self.name_entry.delete(0, END)
		self.name_entry.insert(0, "Henri Dunant")
		
		self.file_entry = Entry(self.frame)
		self.file_entry.grid(row=3,column=1)

		self.file_entry.delete(0, END)
		self.file_entry.insert(0, "JDG19101101.txt")
		
		
		self.button = Button(self.frame, text="RUN", fg="blue", command=self.run_search)
		self.button.grid(row=4,column=0)
        
		self.button = Button(self.frame, text="PLOT", fg="red", command=self.plot_fit_gui)
		self.button.grid(row=4,column=1)
        
		self.app.mainloop()
		
	def make_new_text_window(self,text):
		h=600
		w=500
		new_tl=Toplevel(self.app,height=h,width=w)
		new_tl.geometry("%dx%d%+d%+d" % (w, h, 100, 10))
		scrollbar=Scrollbar(new_tl,orient=VERTICAL)
		scrollbar.pack(fill=Y,side=LEFT)
		new_cv=Canvas(new_tl,width=w,height=h)
		new_cv.pack(fill=BOTH)
		
		line = Text(new_cv,height=h,yscrollcommand=scrollbar.set)
		line.pack(fill=BOTH,side=LEFT)
		for i in range(0,len(text)):
			line.insert('1.0',text[len(text)-1-i])
			#line.insert('1.0','\n')
			#line.insert('1.0','-----------')
			#line.insert('1.0','\n')
			
		scrollbar.config(command=line.yview)
	def run_search(self):
		fichier = self.file_entry.get()
		text = open_fichier(fichier)
		name = self.name_entry.get()
		sen = get_text_for_name(fichier,name)
		prof = es.fiche(name)
		evs = pf.find_evenement(sen,text)
		for e in range(len(evs)):
			ev = es.evenement(evs[e][0],evs[e][1],evs[e][2])
			prof.add_evenement(ev)
			#prof.get_evenement(e)
		
		t2p=prof.str_fiche()
		self.make_new_text_window(t2p)
	def plot_fit_gui(self):
		name = self.name_entry.get()
		fichier = self.file_entry.get()
		text = open_fichier(fichier)
		[firstname,lastname]=format_name(name)
		[wo,fo,lo] = find_name(name, firstname,lastname,text)
		[x,y,p] = name_occ_fit(wo,fo,lo,text)
		plot_fit(x,y,p)
		

u = base_gui()

On notera que ce fichier contient en sus les fonctions nécessaires à l'ouverture et au formattage des textes lus.

Structure des Informations

Une classe evenement est créée pour contenir les événements: elle possède une DATE, un LIEU, une ACTION, qui contient la phrase retenue, ainsi qu'un RANG, qui traduit son score. Ces évenements sont incorporés dans la classe fiche qui contient le nom de la personne que la recherche concerne, ainsi que tous les évenements trouvés qui sont pertinents. Le code est visible ci-dessous:

class evenement:
	def __init__(self,d,l,a):
		self.date=d
		self.lieu=l
		self.action=a
class fiche:
	def __init__(self, n):
		self.name=n
		self.evts=[]
	def add_evenement(self, e):
		self.evts.append(e)
	def get_evenement(self,i):
		if i < len(self.evts):
			print(self.evts[i])
	def str_fiche(self):
		s = [self.name]
		s.append('\n')
		rang=0
		if len(self.evts)>0:
			for i in self.evts:
				s.append('\n')
				s.append('DATE:')
				s.append('\n')
				s.append(i.date)
				s.append('\n')
				s.append('LIEU:')
				s.append('\n')
				s.append(i.lieu)
				s.append('\n')
				s.append('ACTION:')
				s.append('\n')
				s.append(i.action)
				s.append('\n')
				s.append('RANG:')
				s.append('\n')
				s.append(str(rang))
				rang+=1
		return s