User:QICbot/source

From Wikimedia Commons, the free media repository
Jump to: navigation, search

Might not be the up-to-date running code!

import sys, os
print os.environ['HOME']
sys.path.append(os.environ['HOME'] + '/qic_bot/pywikipedia')

import wikipedia
import time,datetime
import string, re
import codecs

pageName = 'Commons:Quality images candidates'
pageName2 = 'Commons:Quality images/'
promotedDump = pageName2 + 'Recently promoted'
unassessedCat = '[[Category:Unassessed QI candidates]]'

waitDays = 2
waitDaysNoDecission = 8

vDecline    = "{{/Decline|"
vWithdrawn  = "{{/Withdrawn|"
vNomination = "{{/Nomination|"
vPromotion  = "{{/Promotion|"
vDiscuss    = "{{/Discuss|"

# note the missing closing braces, they are added below!
QITag = "{{QualityImage"

nowTime = time.mktime( datetime.datetime.utcnow().timetuple() )

site = wikipedia.getSite()

timeRE = re.compile('(\d\d):(\d\d), (\d\d?) (January|February|March|April|May|June|July|August|September|October|November|December) (\d\d\d\d) \((UTC|GMT)\)')
userRE = re.compile('\[\[[Uu]ser:([^\|\]]+)[^\]]*\]\]')
headingRE = re.compile('===([^=]+)===')
imageRE = re.compile('\[\[([^\|\]]+)')
moveRE = re.compile('\{\{QICbotMove\|([^\|\}]+)\}\}')
galleryRE = re.compile('^\s*[Ii]mage:([^\|]+)')
parentRE = re.compile('^(.*)/[^/]*$')

#
# Tagging helperfunction
#
# The tag is split into two parts ( startTag, endTag )
# to check if an image on the list is already tagged the code looks
# only for startTag. That allows matching for tags with additional 
# template parameters for example.
#

def doTagging( imageList, startTag, endTag ) :
	global site
	for image in imageList :
		page = wikipedia.Page(site, image )

		if page.exists() :
			# already tagged maybe?
			text = page.get()
			if string.find(text, startTag ) < 0 :
				text += "\n" + startTag + endTag + "\n"
				page.put(text)
		else :
			print "Oops " + image.encode("utf-8") + " doesn't exist..."


#
# Insert image into the first gallery tags on ''text''
#

def galleryInsert( imageLines, text ) :
	newText = ''
	notInserted = True
	for line in string.split(text, "\n") :
		newText += line + "\n"
		if line[:8] == "<gallery" and notInserted :
			newText += imageLines
			notInserted = False
	
	if notInserted :
		newText = "<gallery>\n%s</gallery>\n%s" % ( imageLines, text )

	return newText.rstrip("\n")
			
#
# Return first ''imageLimit'' images (in galleries) from ''text''
#

def gallerySample( imageLimit, text ) :
	global galleryRE
	firstFour = ''
	got = 0
	for line in string.split(text, "\n") :
		image = galleryRE.search( line )
		if image != None and got < 4 :
			firstFour += line + "\n"
			got += 1

	return firstFour
			

#
# Toss out all images except the first ''imageLimit'' in ''text''
#

def galleryLimit( imageLimit, text ) :
	global galleryRE
	got = 0
	newText = ''
	for line in string.split(text, "\n") :
		image = galleryRE.search( line )
		if image != None :
			if got < imageLimit :
				newText += line + "\n"
				got += 1
		else :
			newText += line + "\n"

	return newText.rstrip("\n")
			

#
# Open QIC page and extract nominations
#

page = wikipedia.Page(site, pageName + "/candidate list" )
text = page.get()

# abort if the qicbot marker is missing from the page 
if string.find(text, "<!-- QICBOT_ON -->") < 0:
	print "the string <!-- QICBOT_ON --> was not found on page " + pageName + "/candidate list"
	sys.exit(0)

inGallery = False
inCRSection = False
inConsensual = 0

newText = ''
numChanges = 0
userNote = {}
galleryMove = {}
tagImages = []
unassessed = []
currentImage = ''
currentHeading = ''
currentStatus = 0
currentArchive = ''

bakArchiveFileName = os.environ['HOME'] + '/qic_bot/archive.tmp'

if os.path.isfile( bakArchiveFileName ) :
	f = codecs.open(bakArchiveFileName, "r", "utf-8")
	archive = f.read() 
	f.close()
else :
	archive = ''
	consensual = ''

archiveCR = ''

for line in string.split(text, "\n") :

	discardLine = False 
	archiveLine = False 

	if line[:8] == "<gallery" and inGallery == False :
		inGallery = True

	elif line == "</gallery>" and inGallery == True :
		inGallery = False

	elif line == "= Consensual review =" and inConsensual == 0 :
		inConsensual = 1

	elif line == "New images go below this line.  -->" and inConsensual == 1 :
		inConsensual = 2
		discardLine = True
		newText += line + "\n" + consensual

	elif inConsensual == 2 :

		heading = headingRE.search(line)
		if heading != None :
			if inCRSection :
				if currentStatus == 0 or currentImage == '':
					newText += currentArchive
				else :
					print ( "Closing old CR status=%d %s (%s by %s)" % ( currentStatus, currentHeading, currentImage, currentUser ) ).encode("utf-8"),
					archiveCR += currentArchive.replace('{{/', '{{../', 1)

					if currentStatus == 1 :
						tagImages.append(currentImage)

						if currentUser == '' :
							currentUser = 'Dschwen'
							print "Did not find a user to notify, using Dschwen!"
							
						try:
							userNote[currentUser] += "{{QICpromoted|" + currentImage + "}}\n" 
						except KeyError:
							userNote[currentUser] = "{{QICpromoted|" + currentImage + "}}\n" 
			
			currentHeading = heading.group(1)
			currentImage = ''
			currentUser = ''
			currentStatus = 0
			currentArchive = ''
			inCRSection = True

		user = userRE.search(line)
		if user != None and inCRSection and currentUser == '' :
			currentUser = user.group(1)

		image = imageRE.search(line)
		if image != None and inCRSection and currentImage == '' :
			currentImage = image.group(1)

		if ( string.find(line, vDecline) == 0 or string.find(line, vWithdrawn) == 0 ) and currentStatus == 0 :
			currentStatus = -1

		if string.find(line, vPromotion) == 0 and currentStatus == 0 :
			currentStatus = 1

		if inCRSection :
			discardLine = True
			currentArchive += line + "\n"

	elif inGallery == True and string.find(line, "|") > -1 :
		image, verdict = string.split(line, "|", 1)
		verdict = verdict.strip()

		#check date
		nomTime = 0
		for match in timeRE.findall(verdict) :
			sigTime = time.strptime( "%s:%s, %s %s %s" % match[:5] ,"%H:%M, %d %B %Y")
			nomTime = max(nomTime, time.mktime(sigTime))

		if nomTime == 0 :
			line = image + "|'''Please sign with name and date (four tildes)''' " + verdict; 

		if string.find(verdict, vDiscuss) == 0 :
			consensual += '\n===' + image + "===\n[[" + image + "|200px]]\n" + verdict + "\n"
			discardLine = True
			

		if (nowTime-nomTime)/(60.0*60.0*24.0) > waitDaysNoDecission and nomTime > 0 :

			if string.find(verdict, vNomination) == 0 :
				print (nowTime-nomTime)/(60.0*60.0*24.0), image.encode("utf-8"), " NO VERDICT!"
				unassessed.append(image)
				discardLine = True
				archiveLine = True

		if (nowTime-nomTime)/(60.0*60.0*24.0) > waitDays and nomTime > 0 :

			if string.find(verdict, vDecline) == 0 or string.find(verdict, vWithdrawn) == 0 :
				print (nowTime-nomTime)/(60.0*60.0*24.0), image.encode("utf-8")
				discardLine = True
				archiveLine = True

			if string.find(verdict, vPromotion) == 0 :
				comments = verdict[len(vPromotion):]

				try:
					userNote[userRE.search(line).group(1)] += "{{QICpromoted|" + image + '|' + comments + "\n" 
				except KeyError:
					userNote[userRE.search(line).group(1)] = "{{QICpromoted|" + image + '|' + comments + "\n" 

				tagImages.append(image)
				discardLine = True
				archiveLine = True

	if not discardLine :
		newText += line + "\n"
	else :
		numChanges += 1
		if archiveLine :
			archive += line.replace('|{{/', '|{{../', 1) + "\n"

if inConsensual < 2 :
	 newText +=  consensual

# take care of the last entry on the page (a copy from above. ToDo: refactor!)
if inCRSection :
	if currentStatus == 0 or currentImage == '' :
		newText += currentArchive
	else :
		print ( "Closing old CR status=%d %s (%s by %s)" % ( currentStatus, currentHeading, currentImage, currentUser ) ).encode("utf-8")
		archiveCR += currentArchive

		if currentStatus == 1 :
			tagImages.append(currentImage)

			if currentUser == '' :
				currentUser = 'Dschwen'
				print "Did not find a user to notify, using Dschwen!"
				
			try:
				userNote[currentUser] += "{{QICpromoted|" + currentImage + "}}\n" 
			except KeyError:
				userNote[currentUser] = "{{QICpromoted|" + currentImage + "}}\n" 



#
# Move Commons:Quality images/Recently promoted images
#

page = wikipedia.Page(site, promotedDump )

if page.exists() :
	text = page.get()
	newText = ''
	for line in string.split( text, "\n" ) :
		image = galleryRE.search( line )
		dest  = moveRE.search( line )
		if image != None and dest != None :
			try:
				galleryMove[dest.group(1)] += moveRE.sub( '', line ) + "\n" 
			except KeyError:
				page2 = wikipedia.Page(site, pageName2 + dest.group(1) )
				if page2.exists() :
					galleryMove[dest.group(1)] =  moveRE.sub( '', line ) + "\n" 
				else :
					newText += line + ", '''Subgallery does not exist'''\n"
		else :
			newText += line + "\n"

	wikipedia.setAction("moving categorized images")
	page.put( newText.rstrip("\n") )

for key in galleryMove.keys() :

	# Insert into subject/type gallery
	page = wikipedia.Page(site, pageName2 + key )
	if page.exists() :
		text = page.get()
	else :
		text = ''
		
	newText = galleryInsert( galleryMove[key], text )

	wikipedia.setAction("sorted into the appropriate category")
	page.put( newText )


	# Rebuild four image preview gallery
	pagePath = pageName2 + key
	while True:
		print "trying %s/Sample" % pagePath
		page = wikipedia.Page(site, pagePath + '/Sample' )
		if page.exists() :
			text = page.get()
		else :
			parent = parentRE.search( pagePath )
			if parent != None :
				print "Going to parent's /Sample"
				pagePath = parent.group(1)
				continue
			else :
				print "sample gallery search failed at " + pagePath
				break

		newText = galleryLimit( 4, galleryInsert( galleryMove[key], text ) )

		wikipedia.setAction("rebuilding preview")
		page.put( newText )
		break


#sys.exit(0)

if numChanges == 0 :
	print "No action taken"
	sys.exit(0)


f = codecs.open( bakArchiveFileName, "w", "utf-8")
f.write( archive )
f.close()

wikipedia.setAction( "extract processed nominations older than %d days" % waitDays )
page.put(newText)


#
# Archive removed Nominations
#

if archiveCR != '' :
	archiveCR = "\n" + archiveCR;

archivePage = datetime.datetime.utcnow().strftime("/Archives %B %Y")
page = wikipedia.Page(site, pageName + archivePage )

if page.exists() :
	text = page.get()
	galleryIndex = string.find(text, "<gallery>")

	if galleryIndex > -1 :
		text = text[:galleryIndex+9] + "\n" + archive + text[galleryIndex+9:] + archiveCR
	else:
		text = "<gallery>\n" + archive + "</gallery>\n" + text + archiveCR
else :
	text = "<gallery>\n" + archive + "</gallery>\n==Consensual review==\n" + archiveCR

wikipedia.setAction("archive old nominations")
page.put(text)
os.unlink( bakArchiveFileName )


#
# Tag unassessed images
#

wikipedia.setAction("Tag as unassessed Quality Image Candidate")
doTagging( unassessed, unassessedCat, '' )


if len(tagImages) == 0: 
	print "No images to promote"
	sys.exit(0)


#
# Dump all promoted images onto Commons:Quality images/Recently promoted
#

page = wikipedia.Page(site, promotedDump )

if page.exists() :
	text = page.get()
	galleryIndex = string.find(text, "<gallery>")

	if galleryIndex > -1 :
		text = text[:galleryIndex+9] + "\n" + string.join(tagImages, "\n") + text[galleryIndex+9:]
	else:
		text = "<gallery>\n" + string.join(tagImages, "\n") + "</gallery>\n" + text

	newText = ''
	#text = newText

else :
	text = "<gallery>\n" + string.join(tagImages, "\n") + "</gallery>"

wikipedia.setAction("please sort these into the appropriate categories")
page.put( text.rstrip("\n") )



#
# Tag images
#

wikipedia.setAction("Tag promoted Quality Image")
doTagging( tagImages, QITag, '}}' )


#
# User notifications
#

wikipedia.setAction("Notify user of promoted Quality Image(s)")
for key in userNote.keys() :
	page = wikipedia.Page(site, "User talk:" + key )

	if page.exists() :
		text = page.get()
	else :
		text = 'Welcome to commons ' + key + ". What better way than starting off with a Quality Image promotion could there be? :-) --~~~~\n\n"
	
	text = text + "\n==Quality Image Promotion==\n" + userNote[key]
	page.put(text)

# done!