Nonciclopedia:Bot/Makelist/makelist.py

Vai alla navigazione Vai alla ricerca
Versione 2.1
alcuni aggiustamenti, ora la ricerca funziona al 100%

Richiede anche makelist-preset.py

Attenzione!
Non copiare il testo qui sotto, apri la pagina e copia direttamente il sorgente da dopo <pre><nowiki> a prima della chiusura dei due tag, in fondo al testo.

#!/usr/bin/python
# -*- coding: utf-8  -*-
"""Bot per generare una lista di pagine contenenti le corrispondenze ad una regex.

Parametri obbligatori specifici:
	-dest	Specifica la pagina in cui salvare la lista.

	-start	Specifica la pagina da cui partire con la ricerca.

	-ns		Limita la ricerca ai namespace indicati, separati da virgole.
			Può essere usato assieme o in sostituzione di -start.

Parametri facoltativi specifici:
	-nocase		 Le regex non distinguono fra maiuscole e minuscole.

	-append		 Accoda la lista alla pagina indicata con -dest.

	-rewrite	 Sostituisce la pagina indicata con -dest con la lista.

	-update		 Aggiunge unicamente le voci nuove.

	-savetofile	 Indica su quale file salvare la lista.

	-always		 Salva senza chiedere.

	-and		 Deve esserci almeno un risultato per ogni regex -and
				 affinché la pagina venga elencata nella lista.
				 Può essere usato più volte.

	-not		 Nessuna delle regex -not deve risultare nella pagina
				 affinché questa venga elencata nella lista.
				 Può essere usato più volte.

	-or			 Almeno una delle regex -or deve risultare nella pagina
				 affinché questa venga elencata nella lista.
				 Può essere usata più volte. Si possono anche elencare
				 le regex senza specificare -or.

	-comment	 Fornisce un commento che compare sopra la lista.

	-summary	 Fornisce una descrizione della modifica nella cronologia.

	-debug		 Avvia il bot in modalità debug.
	
	-exc-page	Esclude una serie di pagine dalla ricerca.
				Vanno separate con |.
	
	-exc-regex	Esclude una regex.
	
	-exc		Esclude un termine generico.

Parametri facoltativi generici:
&params;
	
Le regex devono essere incluse tra virgolette.
Esempio:
    python makelist.py -start:! -ns:0,NonNotizie "\\bBoh\\b" -dest:Nonciclopedia:Bot/Ricerche/Boh -rewrite

* http://nonciclopedia.wikia.com/wiki/Nonciclopedia:Bot/Makelist
"""

__version__ = '$Id: MakeList.py,v2.1 2011/05/01 Sanjilops, MFH $'

import wikipedia, pagegenerators, config
import re, time, locale, sys
from string import Template

codec = ''

# This is required for the text that is shown when you run this script
# with the parameter -help.
docuReplacements = {
    '&params;': pagegenerators.parameterHelp
}
msg = {
	'de': u'Bot: Ändere ...',
	'en': u'Robot: changing ...',
	'pt': u'Bot: alterando...',
	'sv': u'Bot: Ändrar ...',
	'it': u'Bot: [[Nonciclopedia:Bot/Makelist|Makelist]]',
    }

flags = re.U

debug = False

ORs = []
ANDs = []
NOTs = []	

class MakeListBot:
	def __init__(self, generator):
		self.generator = generator
		self.list = []
		self.count = 0
		self.fd = 0
		if preset:
			self.tmpfile = preset.tmpfile
		else:
			self.tmpfile = 'makelist-tmp.txt'	

	def run(self):
		print "Inizio ricerca..."
		try:
			for page in self.generator:
				try:
					self.treat(page)
				except:
					raise
					continue
		except KeyboardInterrupt: # Control-C
			print "Interrotto."
		if self.fd:
			self.fd.close()
		return

	def treat(self, page):
		global ORs
		text = ''
		if debug:
			wikipedia.output(u"Apro pagina %s." % page.title(asLink=True))
		try:
			# Load the page
			text = page.get()
		except wikipedia.NoPage:
			wikipedia.output(u"Page %s does not exist; skipping." % page.title(asLink=True))
			return
		except wikipedia.IsRedirectPage:
			wikipedia.output(u"Page %s is a redirect; skipping." % page.title(asLink=True))
			return

	################################################################
	# NOTE: Here you can modify the text in whatever way you want. #
	################################################################
		
		res = []
		for item in NOTs:
			res = item.findall(text)
			if res:
				# corrispondenza NOT
				if debug:
					print "\tCorrispondenza con RE NOT #%d" %(NOTs.index(item) + 1)
				return
		for item in ANDs:
			res = item.findall(text)
			if not res:
				# mancata corrispondenza AND
				if debug:
					print "\tNessuna corrispondenza con RE AND #%d " %(ANDs.index(item) + 1)
				return       
		if ORs == []: # solo NOT e AND			
			self.add_match(page.title())
			return
		matches = []
		for item in ORs:
			res = item.finditer(text)
			if res:
				item.counter +=1
				matches.append([ORs.index(item) + 1, res])
		if matches:
			self.add_match(page.title(), matches)
		else:
			if debug:
				print "\tNessuna corrispondenza OR"
		return

	def add_match (self, title, matches = []):
		self.count += 1
		self.list.append(listitem(title, matches))
		matchlist = u''
		wikipedia.output(u"\n %d. \03{lightpurple}%s\03{default}" % (self.count, title))
		if matches:
			for item in matches: #genera un'unica stringa di tutte le corrispondenze
				matchlist += '\t- RE #%d (%d occorrenze): \"%s\"\n' % (item[0], len(item[1]), u'\" // \"'.join(item[1]))
			wikipedia.output(u"\tCorrispondenze:\n%s" % (matchlist))
		if self.count == 1 and self.tmpfile: # non apre il file finché non viene trovato un risultato
			try:
				self.fd = open(self.tmpfile, "w")
			except:
				print "Impossibile aprire il file"
		if self.fd and self.tmpfile:
			try:
				self.fd.write("\n%d. [[%s]]\n" % (self.count, title.encode(codec)))
				if matches:
					self.fd.write(matchlist.encode(codec))
				if self.count % 10 == 0:
					if debug:
						print "Salvataggio file temporaneo..."
					self.fd.close()						
					try:
						self.fd = open(self.tmpfile, "a")
					except:
						print "Apertura file non riuscita"
			except:
				print "Scrittura su file %s non riuscita" %(self.tmpfile)
		return

class searchitem:
	def __init__ (self, arg, min=False):
		tmp = re.split(',,(?=\d)', arg) # nota: per trovare un numero con una posizione identica senza considerarlo una soglia basta racchiuderlo tra parentesi quadre
		self.string = tmp[0]
		if not min: #  da riga di comando
			try:
				self.min = int(tmp[1]) 
			except:
				self.min = 1
		else: # da file dei preset
			self.min = min
		self.arg = self.string
		if min > 1:
			self.arg += u',%d' %(self.min)
		try:
			self.RE = re.compile(self.string, flags)
		except:
			print u'"%s" non è una regex valida' % self.string
			raise
		self.counter = 0
			
	def finditer (self, text):
		result = []
		for i in self.RE.finditer(text, flags):
			tmp = i.group()
			result.append(u"%s" %(tmp))
		if len(result) >= self.min:
			return result
		else:
			return False
	
	def findall (self, text):
		result = self.RE.findall(text, flags)
		if len(result) >= self.min:
			return True
		else:
			return False

class listitem:
	def __init__ (self, title, matches = []):
		self.title = title
		self.strings = []
		self.results = []
		for item in matches:
			self.strings.append(item[0])
			self.results.append(item[1])

def exclude_gen(gen):
	gen = pagegenerators.PreloadingGenerator(gen)
	list = []
	for page in gen:
		list.append(page.title())
	return list

def ExclusionFilterPageGenerator(gen, list):
	for page in gen:
		if page.title() not in list:
			yield page
		else:
			if debug:
				wikipedia.output(u"Salto pagina %s" %(page.title(asLink=True)))
			
def NamespaceFilterPageGenerator(generator, namespaces, site):
    NS = []
    for ns in namespaces:
        if (isinstance(ns, unicode) or isinstance(ns, str)):
            if ns != '0':
                index = site.getNamespaceIndex(ns)
                if index is None:
                    raise ValueError(u'Unknown namespace: %s' % ns)
                NS.append(index)
            else:
                NS.append(0)
    for page in generator:
        if page.namespace() in NS:
            yield page

def RegexFilterPageGenerator(generator, regex):
    reg = re.compile(regex, re.U)
    for page in generator:
        if reg.search(page.titleWithoutNamespace()):
            yield page

def ExclusionRegexFilterPageGenerator(generator, regex):
    reg = re.compile(regex, re.U)
    for page in generator:
        if reg.search(page.titleWithoutNamespace()):
            if debug:
                wikipedia.output(u"Salto pagina %s" %(page.title(asLink=True)))
        else:
            yield page

def save_page(dest, list, save_mode, always, parameters, comment='', summary=''):
	list_heading = """$comment
:{{Dimensione|80%|$Botmsg ($Datetime)}}
* ''Espressioni:''$or_list

* Espressioni AND: {{tt|<nowiki>$and_list}}
* Espressioni NOT: {{tt|$not_list}}
* Parametri: {{tt|$parameters}}
"""
	list_format = u"\n# '''[[:$title]]''' ([{{fullurl:$title|action=edit}} modifica])"
	list_match_format = u"\n#*RE #$string ($num): ''$words''"
	format_dict = dict(	Botmsg= 'Lista generata con il bot [http://nonciclopedia.wikia.com/wiki/Nonciclopedia:Bot/Makelist makelist]',
						Datetime= time.strftime("%d %b %y, %H:%M", time.localtime())
						)
	match_separator = ' // '
	text = ''
	tmp = ''
	
# preparazione pagina
	if preset:
		if preset.list_heading:
			list_heading = preset.list_heading
		if preset.list_format:
			list_format = preset.list_format
		if preset.list_match_format:
			list_match_format = preset.list_match_format
		if preset.match_separator:
			match_separator = preset.match_separator

	if save_mode != 'u':
		format_dict['comment'] = comment.decode(codec)
		format_dict['parameters'] = parameters
		format_dict['or_list'] = ''
		format_dict['and_list'] = ''
		format_dict['not_list'] = ''
		for i in ORs:
			format_dict['or_list'] += u'\n*#{{tt|"%s"}} (%d pagine)' %(i.arg, i.counter)
		tmp = []
		for i in ANDs:
			tmp.append(u'"%s"' %(i.arg))
		format_dict['and_list'] = ' , '.join(tmp)
		tmp = []
		for i in NOTs:
			tmp.append(u'"%s"' %(i.arg))
		format_dict['not_list'] = ' , '.join(tmp)
		he_template = Template(list_heading)
		text += he_template.safe_substitute(format_dict)
	
	li_template = Template(list_format)
	match_template = Template(list_match_format)
	for item in list:
		format_dict['title'] = item.title	
		text += li_template.safe_substitute(format_dict)	
		for n in range(len(item.strings)):
			text += match_template.safe_substitute(string=item.strings[n] , num=len(item.results[n]), words= match_separator.join(item.results[n]))

			
# salvataggio
	wikipedia.output(u'Preparazione al salvataggio della pagina %s...' % dest.title(asLink=True))
	try:
		tmp = dest.get()                        
		if save_mode in ['a', 'u']:
			text = tmp + text
		elif save_mode == 'w':
			pass
		else:
			choice = wikipedia.inputChoice("La pagina %s esiste già. Cosa vuoi fare?" % dest.title(asLink=True), ['Sovrascrivere', 'Accodare', 'Niente'],  ['s', 'a', 'n'], 'N')
			if choice in ['n', 'N']:
				print "Nessuna modifica effettuata"
				return
			elif choice in ['s', 'S']:
				save_mode = 'w'
			else:
				save_mode = 'a'
				text = tmp + text
	except wikipedia.NoPage:
	    pass

	if not summary:
		summary = wikipedia.setAction(wikipedia.translate(wikipedia.getSite(), msg))
	if not always:                          
		if save_mode != 'w':
			wikipedia.showDiff(tmp, text)
		choice = wikipedia.inputChoice(u'Vuoi salvare?', ['Yes', 'No'], ['y', 'N'], 'N')
		if choice in ['n', 'N']:
			print "Nessuna modifica effettuata"
			return
	try:
		dest.put(text,summary)
	except wikipedia.EditConflict:
		wikipedia.output(u'Conflitto di edizione su % s' % (dest.title()))
	except wikipedia.SpamfilterError, error:
		wikipedia.output(u'Cannot change %s because of spam blacklist entry %s' % (dest.title(), error.url))
	finally:
		return

def main(gen_name = None, namespaces = [], title_re = '', exc_gen = [], exc_pages = [], exc_title_re = '', dest_name = '', file_name = '', save_mode = False, comment = ''):
	tmp = ''
	genFactory = pagegenerators.GeneratorFactory()
	start = ''
	gen = ''
	always = False
	summary = ''
	global flags, debug, ORs, ANDs, NOTs, codec
	
    # Lettura argomenti da linea di comando
	if '-nocase' in wikipedia.handleArgs(): # dev'essere processato per primo perché serve per compilare le regex
		flags = flags | re.I
	for arg in wikipedia.handleArgs():
		if arg.startswith('-dest:'):
			dest_name = arg.split(':',1)[1]
		elif arg.startswith('-ns:'):
			namespaces = arg.split(':',1)[1].split(",")
		elif arg == '-append':
			save_mode = 'a'
		elif arg == '-rewrite':
			save_mode = 'w'
		elif arg == '-update':
			save_mode = 'u'
		elif arg.startswith('-savetofile'):
			save_mode = 'f'			
			if len(arg) > len('-savetofile:'):
				file_name = arg[len('-savetofile:'):]
		elif arg == '-always':
			always = True
		elif arg.startswith('-and:'):
			ANDs.append(searchitem(arg.split(':',1)[1]))
		elif arg.startswith('-not:'):
			NOTs.append(searchitem(arg.split(':',1)[1]))
		elif arg.startswith('-or:'): # giusto per sicurezza
			ORs.append(searchitem(arg.split(':',1)[1]))                 
		elif arg.startswith('-comment:'):
			comment = arg.split(':',1)[1]
		elif arg.startswith('-summary:'):
			summary = arg.split(':',1)[1]
		elif arg == '-debug':
			wikipedia.output(u"Debug mode on")
			debug = True
		elif arg == '-nocase':
			pass # è già stato processato
		elif arg.startswith('-exc'):
			tmp = arg.split('-exc')[1]
			if tmp.startswith('-page:'):
				tmp = tmp[len('-page:'):].split("|")
				for item in tmp:
					exc_pages.append(item.replace("_", " "))
			elif tmp.startswith('-regex:'):
				exc_title_re = tmp[len("-regex:"):]
			else:				
				exc_gen.append(tmp)
		else:
	    # check if a standard argument like
	    # -start:XYZ or -ref:Asdf was given.
			if arg.startswith("-regex"):
				if arg[6] == ':':
					title_re = arg.split(':',1)[1]
				else:
					title_re = wikipedia.input(u"Inserisci una regex per la ricerca sui titoli:")
			else:
				tmp = genFactory.handleArg(arg)
				if tmp:
					gen_name = arg
				else:
					try:
						ORs.append(searchitem(arg))
					except:
						return

	# Gestione parametri
	if not gen_name:
		wikipedia.output(u'Nessun generatore valido specificato')
		return
	site = wikipedia.getSite()
	if gen_name.startswith("-start:"):
		start = gen_name[len("-start:"):]
		if namespaces != []:
			gen = []
			for n in namespaces: # scarica solo le pagine dei namespace richiesti
				if n.isdigit():
					tmp = int(n)
				else:
					tmp = site.getNamespaceIndex(n)
				gen.append(pagegenerators.AllpagesPageGenerator(start, tmp, False, site))
			gen = pagegenerators.CombinedPageGenerator(gen)
		else:
			gen = pagegenerators.AllpagesPageGenerator(start, False, False, site)		
		gen = pagegenerators.PreloadingGenerator(gen)
	else:
		gen = genFactory.handleArg(gen_name)
		gen = pagegenerators.PreloadingGenerator(gen)		
		if namespaces: # gli altri generatori non hanno un parametro "namespace" (per ora)
			gen = NamespaceFilterPageGenerator(gen, namespaces, site)
					
	if title_re:
		gen = RegexFilterPageGenerator(gen, title_re)
	
	
	dest = None
	if save_mode == 'f':
		if not file_name:
			file_name = "makelist.txt"
		try:
			wikipedia.setLogfileStatus(True, file_name)
		except:
			print "Impossibile aprire il file %s" %(file_name)
			return
	else:
		while not dest:
			if not dest_name:
				dest_name = wikipedia.input(u'In che pagina vuoi salvare la ricerca?')
			dest = wikipedia.Page(site, dest_name)		
			if not dest.canBeEdited():
				wikipedia.output(u'Non puoi modificare la pagina %s!' % (dest.title(asLink=True)))
				dest = None
			if dest.isRedirectPage():			
				wikipedia.output(u"La pagina %s è un redirect. Specificare un'altra pagina" % (dest.title(asLink=True)))
				dest = None
			
	original_exc_pages = tuple(exc_pages)
	if save_mode == 'u':
		exc_gen.append(u"-links:%s" %(dest.title()))
	if exc_gen:
		for item in exc_gen:
			tmp = genFactory.handleArg(item)
			if tmp:
				exc_pages += exclude_gen(tmp)
			else:
				wikipedia.output(u"Generatore non valido: %s" %(item))			
	if exc_pages:
		gen = ExclusionFilterPageGenerator(gen, exc_pages)
					
	if exc_title_re:
		gen = ExclusionRegexFilterPageGenerator(gen, exc_title_re)
		
	if ORs == [] and NOTs == [] and ANDs == []:
		if preset:
			print 'Cerco espressioni nel file di configurazione'
			try:
				for item in preset.NOTs:
					NOTs.append(searchitem(item[0], item[1]))
				for item in preset.ANDs:
					ANDs.append(searchitem(item[0], item[1]))
				for item in preset.ORs:
					ORs.append(searchitem(item[0], item[1]))
			except:
				print 'Espressioni non valide'
				return
		else:
			print "Devi specificare almeno un'espressione per la ricerca!"
			return

	if not codec:
		if sys.platform=='win32':
			if config.console_encoding == 'cp850':
				codec = 'windows-1252'
			elif config.console_encoding == 'cp852':
				codec = 'windows-1250'
			else:
				codec = config.console_encoding
		else:
			codec = config.console_encoding

	# Partenza!
	parameters = ''
	if flags & re.I:
		parameters += "-nocase "
	parameters += gen_name
	if namespaces:
		parameters += u" -ns:%s" %(",".join(namespaces))
	if title_re:
		parameters += u" -regex:%s" %(title_re)
	for item in exc_gen:
		parameters += u" -exc%s" %(item)
	if exc_title_re:
		parameters += u" -exc-regex:%s" %(exc_title_re)
	if original_exc_pages:
		tmp = []
		for item in original_exc_pages:
			tmp.append(item.replace(' ','_'))
		parameters += u" -exc-page:%s" %("|".join(tmp))

	if save_mode == 'f' or debug:
		tmp = []
		for item in ORs:
			tmp.append(u'-or:"%s"' %item.arg)
		for item in NOTs:
			tmp.append(u'-not:"%s"' %item.arg)
		for item in ANDs:
			tmp.append(u'-and:"%s"' %item.arg)
		wikipedia.output(u"Parametri: %s %s" %(' '.join(tmp), parameters))
	if debug:
		print u"Pagine escluse: %s" %(' - '.join(exc_pages[len(original_exc_pages):]))
		if dest:
			print u"Destinazione: %s" %(dest_name)
		print u"Codec: %s" %codec

	bot = MakeListBot(gen)
	bot.run()
	
	# Salvataggio
	if not bot.list:
		print 'Nessun risultato'
		return
	wikipedia.output(u"Pagine trovate: %d" % bot.count)
	if dest:
		save_page(dest, bot.list, save_mode, always, parameters, comment, summary)
	return

if __name__ == "__main__":
	try:
		import makelist_preset
		preset = makelist_preset.preload()
		flags = preset.flags
		main(preset.gen_name, preset.namespaces, preset.title_re, preset.exc_gen, preset.exc_pages, preset.exc_title_re, preset.dest_name, preset.file_name, preset.save_mode, preset.comment)
	except ImportError:
		preset = False
		main()
	finally:
		wikipedia.stopme()
		sys.exit()
</nowiki>