User:Qwerfjkl/scripts/cleanup.py
Appearance
< User:Qwerfjkl | scripts
#!/usr/bin/env python # -*- coding: utf-8 -*- """ ¶ms; -test Test the routines used for regession testing -namespace:n Number or name of namespace to process. The parameter can be more than one to add additional namespaces commonfixes applied fixes which are general and specific to the English Wikipedia """ # TODO # TIP: use "%(dictname)s" % groupdict() a # better ref combining , combine urls and on ignoring a list of character (matching) # Seperate English from generic wikisyntax # Seperate enwiki sepefic # steel stuff from # http://en.wikipedia.org/wiki/User:Polbot/source/Reffix.pl # FIXME: # http://en.wikipedia.org/w/index.php?title=London&diff=prev&oldid=253531178 (infobox) # http://en.wikipedia.org/w/index.php?title=Hoover_Dam&diff=prev&oldid=253529821 # FIXME: # http://en.wikipedia.org/w/index.php?title=Rolls-Royce_RR300&diff=190562064&oldid=175311735 # http://www.nationaltrust.org/magazine/archives/arc_news_2007/010807.htm # http://scholarworks.umass.edu/cgi/viewcontent.cgi?article=1186&context=theses from __future__ import unicode_literals import re, urllib import wikipedia, pagegenerators from interwiki_map import interwiki_map try: import noreferences except ImportError: print("Unable to import noreferences.py") noreferences = None docuReplacements = { '¶ms;': pagegenerators.parameterHelp, } ignoreAsNames = ( 'january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' ) # NOT IMPLEMENTED PROPERLY # Will change work/publisher cite news and |agency="dictvalue" agencies = { "AP": "Associated Press", "The Associated Press": "Associated Press", "Associated Press": "Associated Press", "AP News": "Associated Press", # "DPA": "Deutsche Presse-Agentur", # "AFP": "Agence France-Presse", } # "The" will be stripped if it exist # So don't include Edge case e.g. "People" and "The People" commonPublishers = ( "American Journalism Review", "Associated Press", "BBC News", "BBC", "Boston Globe", "Chicago Tribune", "CNN", "Daily Telegraph", "Economist", "Guardian", "Huffington Post", "International Herald Tribune", "MTV", "New York Times", "NY Times", "Observer", "The Times", "The Register", "San Francisco Chronicle", "Scientific American", "Seattle Times", "Reuters", "Rolling Stone", "Wall Street Journal", "Washington Post", "Wired", # Web only sources "IGN", "GameStop", "Electronic Gaming Monthly", "Kotaku", "Ars Technica", "Joystiq", "Tom's Hardware", "Salon", "United Press International", # since 1907 ) # template choser # not implemented yet tpl_cite = ( # Match templates, replace template, regex condition ('cite web', 'cite encyclopedia', r'\|\s*url\s*=\s*https?://(www\.)?(encarta.com|encarta.msn.com|betanitca.com)'), ('cite web', 'cite news', r'\|\s*url\s*=\s*https?://(www\.)?(nytimes.com|ap.google.com|news\.bbc\.co\.uk|time\.com|economist\.com|timesonline\.co\.uk|channelonline\.tv|cnn\.com|independent\.co\.uk|cbc.ca|theglobeandmail.com)/'), ('cite web', 'cite paper', r'\|\s*url\s*=\s*https?://(www\.)?(havard.edu)'), ('cite web', 'cite news', r'\|\s*agency\s*='), ('cite web', 'cite book', r'\|\s*isbn\s*=\s*[^\s{|}[\]]'), ) htmltags = ( # Tags that must be closed 'b', 'del', 'i', 'ins', 'u', 'font', 'big', 'small', 'sub', 'sup', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'cite', 'code', 'em', 's', 'strike', 'strong', 'tt', 'var', 'div', 'center', 'blockquote', 'ol', 'ul', 'dl', 'table', 'caption', 'pre', 'ruby', 'rt' , 'rb' , 'rp', 'p', 'span', 'u', 'abbr', # Single 'br', 'hr', 'li', 'dt', 'dd', # Elements that cannot have close tags 'br', 'hr', # Tags that can be nested--?? 'table', 'tr', 'td', 'th', 'div', 'blockquote', 'ol', 'ul', 'dl', 'font', 'big', 'small', 'sub', 'sup', 'span', # Can only appear inside table, we will close them 'td', 'th', 'tr', # Tags used by list 'ul','ol', # Tags that can appear in a list 'li', ## pairs # "b", "i", "u", "font", "big", "small", "sub", "sup", "h1", # "h2", "h3", "h4", "h5", "h6", "cite", "code", "em", "s", "span", # "strike", "strong", "tt", "var", "div", "center", # "blockquote", "ol", "ul", "dl", "table", "caption", "pre", # "ruby", "rt" , "rb" , "rp", ## single # "br", "p", "hr", "li", "dt", "dd", ## nest # "table", "tr", "td", "th", "div", "blockquote", "ol", "ul", # "dl", "font", "big", "small", "sub", "sup", ## table tags # "td", "th", "tr", ) htmlattrs = ( "title", "align", "lang", "dir", "width", "height", "bgcolor", "clear", "noshade", "cite", "size", "face", "color", "type", "start", "value", "compact", #/* For various lists, mostly deprecated but safe */ "summary", "width", "border", "frame", "rules", "cellspacing", "cellpadding", "valign", "char", "charoff", "colgroup", "col", "span", "abbr", "axis", "headers", "scope", "rowspan", "colspan", "id", "class", "name", "style" ) # CSS HEX color values to named (<9 chars) color table namedColors = {'#00FFFF': 'aqua', '#F0FFFF': 'azure', '#F5F5DC': 'beige', '#FFE4C4': 'bisque', '#000000': 'black', '#0000FF': 'blue', '#A52A2A': 'brown', '#FF7F50': 'coral', '#FFF8DC': 'cornsilk', '#DC143C': 'crimson', '#00FFFF': 'cyan', '#00008B': 'darkBlue', '#008B8B': 'darkCyan', '#A9A9A9': 'darkGray', '#A9A9A9': 'darkGrey', '#8B0000': 'darkRed', '#FF1493': 'deepPink', '#696969': 'dimGray', '#696969': 'dimGrey', '#FF00FF': 'fuchsia', '#FFD700': 'gold', '#808080': 'gray', '#808080': 'grey', '#008000': 'green', '#F0FFF0': 'honeyDew', '#FF69B4': 'hotPink', '#4B0082': 'indigo', '#FFFFF0': 'ivory', '#F0E68C': 'khaki', '#E6E6FA': 'lavender', '#00FF00': 'lime', '#FAF0E6': 'linen', '#FF00FF': 'magenta', '#800000': 'maroon', '#FFE4B5': 'moccasin', '#000080': 'navy', '#FDF5E6': 'oldLace', '#808000': 'olive', '#FFA500': 'orange', '#DA70D6': 'orchid', '#CD853F': 'peru', '#FFC0CB': 'pink', '#DDA0DD': 'plum', '#800080': 'purple', '#FF0000': 'red', '#FA8072': 'salmon', '#2E8B57': 'seaGreen', '#FFF5EE': 'seaShell', '#A0522D': 'sienna', '#C0C0C0': 'silver', '#87CEEB': 'skyBlue', '#FFFAFA': 'snow', '#D2B48C': 'tan', '#008080': 'teal', '#D8BFD8': 'thistle', '#FF6347': 'tomato', '#EE82EE': 'violet', '#F5DEB3': 'wheat', '#FFFFFF': 'white', '#FFFF00': 'yellow', } def fixStyle(text): pass def fix(text="", page=None, verbose = True): if not page: page = wikipedia.Page(wikipedia.getSite(), 'Special:Snippet') if not text: text=page.get() if page.namespace() in (108,): return text if page.namespace() >= 0 and page.namespace() % 2 == 1: return text # ## Hacks # text = text.replace('http://www.news.bbc.co.uk', 'http://news.bbc.co.uk') # TODO: Fix accessyear/acessdate mismatch # Peer Reviewer script had for sometime time convert URL into the following bad form text = re.sub(r'\{\{[Cc]ite web\s*\|\s*url\s*=\s*http://(?P<title>[^{|}]+)\s*\|\s*title\s*=\s*(http://)?(?P=title)\s*(<!--[^<>]+-->)?\s*((\|format=(PDF|DOC))|(\|\s*accessdate *= *[^{|}]+))*\}\}', r'[http://\g<title>]', text) # a second time since we seem to hittings limits text = re.sub(r'\{\{[Cc]ite web\s*\|url=(https?://[^{|}]+)\s*\|title=([^{=}]+<!--[^<=>/]+-->)(\|format=(PDF|DOC))?\}\}', r'[\1 \2]', text) # Following the collapse of MiB preference PDFbot converts to the new format when saving text = re.sub(r'\{\{(PDF(?:link)?\|[^{|}]+\|[\d\.]+) \[\[[^|]+\|([KMG])iB\]\]<!--[^<>]+-->\}\}', r'{{\1 \2B}}', text) # EN MOS -- Format Retrieved \g<date>.</ref>', text) # deprecated date linking, remove in citations text = re.sub(r'\[\[(\d+ (?:January|February|March|April|May|June|July|August|September|October|November|December))\]\],? \[\[(\d{4})\]\](?=[^<>]*</ref>)', r'\1 \2', text) text = re.sub(r'\[\[((?:January|February|March|April|May|June|July|August|September|October|November|December) \d+)\]\],? \[\[(\d{4})\]\](?=[^<>]*</ref>)', r'\1, \2', text) # ## Comments # # Update {{NoMoreLinks}} text = re.sub(r'<!--=+\(\{\{No ?More ?Links\}\}\)=+([^<>]+|-->(\n*<!--.*?-->\n)+<!--)=+\(\{\{No ?More ?Links\}\}\)=+-->', '{{subst:NoMoreLinks}}', text) # Update {{Long comment}} text = re.sub(r'(?i)(\{\{Short pages monitor\}\}\s*|)<!--[^</">-]+long[ ]?comment[^</">-]+-->', r'{{subst:long comment}}', text) # Remove comment from the instroduction of footnotes text = re.sub(r"\n?<!--[^<>]*[Ss]ee +https?://en.wikipedia.org/wiki/Wikipedia:Footnotes +[^<>]+generate([^<>]|<(?=/?ref)[^<>]*>)+-->", '', text) # Remove outdated comments text = re.sub(r'\n?<!--\s*Categories\s*-->', '', text) # Now that we got all the stuff that deals with comments out the way we can hide them to prevent mismatching text = hideText(text) if page.site().sitename() == 'wikipedia:en' and page.namespace() in (0, 2, 6) and '{{disambig' not in text: wikipedia.output("Applying English Wikipedia commonfixes") text = formatEnglishWikipediaTemplate(page, text) # ## HTML ## # # <b> & <i> to ''' & '' text = re.sub(r"(?<!')<b>([^{|}<>\n']*?)</b>(?!')", r"'''\1'''", text) text = re.sub(r"(?<!')<i>([^{|}<>\n']*?)</i>(?!')", r"''\1''", text) # Standardize tables text = re.sub(r'\n\|-+(?=[^{|}\n]*\n)', r'\n|-', text) text = re.sub(r'\n\|-(?=\w)', r'\n|- ', text) text = re.sub(r'\n\|-[^{}|<>\n]*(?=\n\|-)', r'', text) text = re.sub(r'(\n\{\|[^][{}|<>\n]*)\n+(?=[|!][^+\-{}\n]+\n)', r'\1\n|-\n', text) text = re.sub(r'\n\|-[^{}|<>\n]*\n*(?=\n\|\})', r'', text) text = fixHTML(page,text) saved = text # saved state # Merge styles in a table for property in ['text-align', 'vertical-align', 'font-size', 'font-family', 'font-weight', 'font-style', 'color', 'background','background-color']: text = re.sub(r''' \|-([^\n{|}[\]]*?)( * \|[^{|}[\]]*style="[^"]*('''+property+r''':[^;"]+;)[^"]*"[^{|}[\]]*\|[^|\n]*?((?:\n\|(?!-)|\|\|)[^{|}[\]]*style="[^"]*\3[^"]*"[^{|}[\]]*\|[^|\n]*)+)(?= \|[-}])''', r'\n|-\1 style="\3" \2', text) p = re.compile(r'''( \|-[^\n{|}[\]]*? style="[^"]*?('''+property+r''':[^";]+;)[^"]*?"[^\n{|}[\]]*( \|(?!-)(?:[^[\]{|}]*\|[^\n]*?))*? \|(?!-)[^{|}[\]]*style="[^"]*)\2 *(?=[^"]*"[^[\]{|}]*\|[^\n])''') while p.search(text): text = p.sub(r'\1', text) if saved != text: text = fixHTML(page,text) # ## Hyperlinking ## # # Remove url junk (tracking, referrers, client info) for i in range(0,9): text = re.sub(r'(https?://[^][<>\s"|])(&client=firefox-a|<=)(?=[][<>\s"|&])', r'\1', text) text = text.replace('[{{SERVER}}{{localurl:', '[{{fullurl:') # Use magic words instead # text = re.sub(r'\[http://en.wikipedia.org/w/index.php\?title=([^][<>"\s&=?]+)&?([^][<>"\s]*)', r'[{{fullurl:\1|\2}}', text) # convert (see http://...) into <http://...>, which is better handled by software text = re.sub(r'(?i)[(](?:see|) *(https?://[^][<>"\s(|)]+[\w=/&])\s?[)]', r'<\1>', text) # From fixes.py # external link in double brackets text = re.sub(r'\[\[(?P<url>https?://[^\]\n]+?)\]\]', r'[\g<url>]', text) # external link starting with double bracket text = re.sub(r'\[\[(?P<url>https?://.+?)\]', r'[\g<url>]', text) # pipe in url (unlikely to go wrong) text = re.sub(r'\[(?P<url>https?://[^][<>\s"\|;?]+?\.(aspx?|doc|f?cgi|html?|jsp|pdf|php|pl|ppt|rtf|txt|xml)) *\| *(?P<label>[^\|\]]+?)\]', r'[\g<url> \g<label>]', text) # Use of Image: #if '[[Image:' in text: # text = re.sub(r'(?i)\[\[(:?)File:([^][{|}]+\.(djvu|jpe?g|png|gif|svg|tiff))(?=\||\]\])', r'[[\1Image:\2', text) text = re.sub(r'(?i)\[\[(:?)Image:([^][{|}]+\.(pdf|midi?|ogg|ogv|xcf))(?=\||\]\])', r'[[\1File:\2', text) # Commons fixes for URLs # TODO: remove domain name titles [http://example.com/aboutus.pdf example.com] # | url= http://www.statcan.ca/english/sdds/instrument/3901_Q2_V2_E.pdf] (fx by removing the invalid []) text = re.sub(ur'((https?):/* *){2,}(?=[a-z0-9:.\-]+/)', r'\2://', text) # Silently correct http://http:/ text = re.sub(ur"(\[\w+://[^][<>\"\s]+?)''", r"\1 ''", text) # corrects [http://''title''] (nospaces) -> [http:// ''title''] text = re.sub(ur'\[\n*(\w+://[^][<>"\s]+ *(?:(?<= )[^\n\]<>]*?|))\n([^[\]<>{}\n=@/]*?) *\n*\]', ur'[\1 \2]', text) # Fix some links which were broken with a line break text = re.sub(ur'\[(\w+://[^][<>"\s]+) +([Cc]lick here|[Hh]ere|\W|→|[ -/;-@]) *\]', ur'\2 [\1]', text) # remove unhelpful titles for screen readers # Embedded images with bad anchors text = re.sub(r'(?i)(\[\[(?:File|Image):[^][<>{|}]+)#(|filehistory|filelinks|file)(?=[\]|])', r'\1', text) text = ext2intLinks(page, text) try: text = simplifyLinks(page, text) except Exception, e: wikipedia.output("\03{lightred}ERROR\03{default}: simplifyLinks exception: %s" % e ) #if isinstance(e, UnicodeDecodeError):raise raise ## References ## # This is need because of <gallery>Image1.jpg|caption<ref>this is hidden</ref></gallery> text = fixReferences(page, text) text = showText(text) # Last part required for webreflinks if noreferences and page.namespace() != 10 and page.title() != 'Special:Snippet': norefbot = noreferences.NoReferencesBot(None, verbose=False, site=page.site()) if norefbot.lacksReferences(text): text = norefbot.addReferences(text) return text def formatEnglishWikipediaTemplate(page, text): # merge all variant of cite web # make into {'dictname':(t1, t2, t3),} text = re.sub(r'(?i)\{\{\s*(cite[_ \-]*(url|web|website)|Web[_ \-]*(citation|reference|reference[_ ]4))(?=\s*\|)', '{{cite web', text) # Aug2011 per request, detransclude URL (which are blacklisted anyway) text = re.sub(r'\{\{\s*((http|https|ftp)://[^{|}<\s">][^{}]+)\}\}', r'\1', text, flags=re.I) # XXX needs review, Jan 2011 ### Unlink ## Remove formatting on certian parameters #text = re.sub(r"(\|\s*(?:agency|author|first|format|language|last|location|month|publisher|work|year)\s*=\s*)(''|'''|''''')((?:\[\[[^][|]+|\[\[|)[][\w\s,.~!`\"]+)(''+)(?=\s*\|[\w\s]+=|\s*\}\})", r'\1\3', text) # Unlink well known publisher parameters (add work=?) text = re.sub(r'(?i)(\|\s*(?:publisher|newpaper)\s*=\s*)\[\[((?:[Tt]he |)(?:'+('|'.join(commonPublishers))+'))\]\]', r'\1\2', text) # Unlink PDF in format parameters text = re.sub(r'(?i)(\|\s*format\s*=\s*)\[\[(adobe|portable|document|file|format|pdf|\.|\s|\(|\)|\|)+\]\]', r'\1PDF', text) text = re.sub(r'(?i)(\|\s*format\s*=\s*)(\s*\.?(adobe|portable|document|file|format|pdf|\(|\)))+?(\s*[|}])', r'\1PDF\4', text) # No |format=HTML says {{cite web/doc}} text = re.sub(r'(?i)(\{\{cite[^{}]+)\|\s*format\s*=\s*(\[\[[^][|]+\||\[\[|)(\]\]| |html?|world|wide|web)+\s*(?=\||\}\})', r'\1', text) ## Fix parameters # Fix accessdate tags [[WP:AWB/FR#Fix accessdate tags]] text = re.sub(r'(\|\s*)a[ces]{3,8}date(\s*=\s*)(?=[^{|}]*20\d\d|\}\})', r'\1accessdate\2', text) text = re.sub(r'accessdate(\s*=\s*)\[*(200\d)[/_\-](\d{2})[/_\-](\d{2})\]*', r'accessdate\1\2-\3-\4', text) text = re.sub(r'(\|\s*)a[cs]*es*mou*nthday(\s*=\s*)', r'\1accessmonthday\2', text) text = re.sub(r'(\|\s*)a[cs]*es*daymou*nth(\s*=\s*)', r'\1accessdaymonth\2', text) text = re.sub(r'(\|\s*)accessdate(\s*=\s*[0-3]?[0-9] +(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\w*)([^][<>}{]*accessyear[\s=]+20\d\d)', r'\1accessdaymonth\2\3', text) text = re.sub(r'(\|\s*)accessdate(\s*=\s*(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\w* +[0-3]?[0-9])([^][<>}{]*accessyear[\s=]+20\d\d)', r'\1accessmonthday\2\3', text) text = re.sub(r'(\|\s*)accessdaymonth(\s*=\s*)\s*([^{|}<>]+?)\s*(\|[^][<>}{]*accessyear[\s=]+)(20\d\d)', r'\1accessdate\2\3 \5', text) text = re.sub(r'(\|\s*)accessmonthday(\s*=\s*)\s*([^{|}<>]+?)\s*(\|[^][<>}{]*accessyear[\s=]+)(20\d\d)', r'\1accessdate\2\3, \5', text) # Fix improper dates text = re.sub(r'(?i)(\{\{cit[ea][^{}]+\|\s*date\s*=\s*\d{2}[/\-.]\d{2}[/\-.])([5-9]\d)(?=\s*[|}])', r'\g<1>19\2', text) text = re.sub(r'(?i)(\{\{cit[ea][^{}]+\|\s*date\s*=\s*)(0[1-9]|1[012])[/\-.](1[3-9]|2\d|3[01])[/\-.](19\d\d|20\d\d)(?=\s*[|}])', r'\1\4-\2-\3', text) text = re.sub(r'(?i)(\{\{cit[ea][^{}]+\|\s*date\s*=\s*)(1[3-9]|2\d|3[01])[/\-.](0[1-9]|1[012])[/\-.](19\d\d|20\d\d)(?=\s*[|}])', r'\1\4-\3-\2', text) # Fix URLS lacking http:// text = re.sub(r'(\|\s*url\s*=\s*)([0-9a-z.\-]+\.[a-z]{2,4}/[^][{|}:\s"]\s*[|}])', r'\1http://\2', text) # Fix {{citation|title=[url title]}} text = re.sub(r'(?i)(\{\{cit[ea][^{}]*?)(\s*\|\s*)(?:url|title)(\s*=\s*)\[([^][<>\s"]*) +([^]\n]+)\](?=[|}])', r'\1\2url\3\4\2title\3\5', text) # Associated Press is usually the agency, not the work or publisher text = re.sub(r'(?i)\{\{\s*[Cc]ite\s*(?:web|news|newpaper|article)([^{}]+?)(\s*\|\s*)(?:publisher|work|author)(\s*=\s*)(\[\[[^[\]|]+\||\[\[|)(?P<agency>%s)(\]\])?(?=\s*\|[^][{}]+=|\s*\}\})' % '|'.join(agencies), r'{{cite news\1\2agency\3Associated Press', text) text = re.sub(r'(?i)(\{\{[^{}]+\|\s*url\s*=[^][{|}]+\.ap\.org/[^{}]+\|\s*)agency(\s*=\s*)Associated Press', r'\1work\2Associated Press', text) text = re.sub(r'(?i)(\{\{[^{}]+\|\s*)agency(\s*=\s*)Associated Press([^{}]+\|\s*url\s*=[^][{|}]+\.ap\.org/)', r'\1work\2Associated Press\3', text) # Fix pages=1 and page=20-44 and page=p. 22 , corner p. 23 section 5 # text = re.sub(r'(\{\{\s*(?:[Cc]ite (journal|news))[^{}]*\| *pages?\s*=\s*)(p[pg]?[. ]|pages?\b) *(?=[\d\-]+\s*[|}])', r'\1', text) text = re.sub(r'(?i)(\{\{\s*(?:cite (?:journal|news|book|web)|citation)[^{}]*?\|\s*)pages(?=\s*=\s*(p|pp|pg|page|pages|)\b[.:]?\s*\d+\s*(\||\}\}))', r'\1page', text) text = re.sub(r'(?i)(\{\{\s*(?:cite (?:journal|news|book|web)|citation)[^{}]*?\|\s*)page(?=\s*=\s*(p|pp|pg|page|pages|)\b[.:]?\s*\d+\s*[\-]\s*\d+\s*(\||\}\}))', r'\1pages', text) # \n in title causes links to break for m in re.finditer(r'\|\s*(?:title)\s*=\s*([^{|}]*?)\s*\|',text): text = text.replace(m.group(), m.group().replace(m.group(1), m.group(1).replace('\n', ' ').replace('\r', ' '))) # Change infoboxes from trailing pipes (likely stems from {{qif}} days) p = re.compile(r'(\{\{[\w\s_]*[Ii]nfobox([^{}]*?\{\{[^{}]+\}\})*[^{}]*?[^{|}](= )?) *\| *\n ?(?=[\s\w]+=)', re.U) while p.search(text): text = p.sub(r'\1\n| ', text) text = text.replace('|\n}}', '\n}}') # Fix web.archive.org links # TODO |url= web.archive -> url+archiveurl # Note: correct web.archive.org/2008/en.wikipedia.org/page format text = re.sub(ur''' (\{\{ (?:[Cc]ite web|[Cc]ite news|[Cc]ite|[Cc]itation) [^{}]*? ) (\|\s*) url (\s*=\s*) (?P<archiveurl>https?://(?:wayback.archive.org|web.archive.org)/web/(?P<y>\d{4})(?P<m>\d{2})(?P<d>\d{2})\d{6}/(?P<url>https?://[^[\]<>"\s]+?)) (\s*) (?=\||\}\}) ''', ur'\1\2url\3\g<url>\9\2archiveurl\3\g<archiveurl>\9\2archivedate\3\g<y>-\g<m>-\g<d>\9', text, flags=re.I | re.X) # Proper Capitilize ALL UPPERCASE names and titles for m in re.finditer(r'(\|\s*(?:title|last|first|author)\s*=\s)([A-Z"\'\s.:;\-+0-9]{10,})(?=[{|}])', text): s = m.group(2) s = s.capitalize() text=text.replace(m.group(), m.group(1)+s) # basic implemnt of tpl_cite for (find_template, replace_template, condition) in tpl_cite: text = re.sub(ur'(\{\{\s*)(?:%s)((?=\s*\|)[^{}]*(%s)[^{}]*\}\})' % (find_template, condition), r'\g<1>%s\g<2>' % replace_template, text) return text def fixHTML(page, text): ''' ''' # Remove old {{prettytable}} header row formatting text = re.sub(r'(?i)(\n\{\| class="wikitable[^\n]+\n\|-[^\n]*)(bgcolor\W+CCC+|background\W+ccc+)(?=\W+\n!)', r'\1', text) # <br/> has no visible effect on output next to a block level item text = re.sub(r'(\n([^<\n]|<(?!br[^>]*>))+\w+[^\w\s<>]*)<br[ /]*>(?=\n[*#:;]|\n?<div|\n?<blockquote)', r'\1', text) # Fix br text = re.sub(r'(?i)(<br[^</>]*>)\n?</br>', r'\1', text) text = re.sub(r'(?i)<[/]?br([^{/}<>]*?/?)>', r'<br\1>', text) #text = re.sub(r'(?i)<[/]?br([^{/}<>]*?)>', r'<br\1 />', text) # Arrg! people are using this is templated tables as a way to visually align items! See [[Battle of Stalingrad]] # text = re.sub(r'(<br[\s/]*>|\n *\n *){4,}', r'\n{{clear}}\n', text) text = re.sub(r'(?i)<br\s\S*clear\S*(all|both)\S*[\s/]*>', r'{{-}}', text) text = re.sub(r'<br\s\S*clear\S*(left|right)\S*[\s/]*>', r'{{clear\1}}', text) # class is not in all skins # # Use class="center" instead of <center> # text = re.sub(r'(?i)<center\b([^<>]*)>((?:[^<]|<(?!/?\s*center\s*>))*)</center>', r'<div class="center"\1>\2</div>', text) # combine font tags text = re.sub(r'(?i)(<font\b[^<>]*)> *\n?<font\b([^<>]*>)((?:[^<]|<(?!/?font))*?</font> *\n?)</font>', r'\1\2\3', text) # text = re.sub(r'(?i)<font ([^<>]*)>\[\[([^[\]{|}]+)\|([^[\]\n]*?)\]\]</font>', r'[[\2|<font \1>\3</font>]]', text) #TODO look for single character entiys such as ; \ in markup, but ignore / text = re.sub(r'(<(?P<tag>\w+)(?= +)|\n\{\||(?<=\n)\|-|(?P<cell>\n[!|]|!!|\|\|))(?P<attr>[^<>[\]{|}\n]+(?(tag)(?=>)|(?(cell)(?=[!|][^!|])|(?=\n))))', fixAttributes, text) # Convert simple <font> to <span> # NOTE: <font>[[link|text]]</font> transforms to [[link|<font>text</font>]] by tidy text = re.sub(r'<font(( +style="[^"]+")+)>(?!\[\[)((?:[^<]|<(?!/?font))*?)(?<!\]\])</font>', r'<span\1>\3</span>', text) # Removed elements in HTML5 spec HTML5_removed = ( "acronym", # Use <abbr> "dir", # Use <ul> "center", # Use <div style="text-align:center"> # Text styling "tt", # Use <code>, <kbd>, or <var> "strike", # Use <s> or <del> # Font modifier "font", "basefont", # Misc "center", "dir" ) removed_tags = {} for tag in re.finditer(r'(?<=<)\w+(?=[^<>]*>)', text): if tag in HTML5_removed: removed_tags[tag] = removed_tags.get(tag, 0) + 1 if removed_tags: wikipedia.output("\03{lightred}DEPRECATED TAG\03{default} : %s %s removed in the HTML5" % ( ' and '.join('<%s> (%d)' % t if t[1] > 1 else '<%s>'%t[0] for t in removed_tags.iteritems()), 'are' if len(removed_tags)>1 else 'is', )) return text def fixAttributes(node): tag = node.group('tag') attr = node.group('attr') if tag: tag = tag.lower() elif '{|' in node.group(1): tag = "table" elif '|-' in node.group(1): tag = "tr" if tag not in htmltags + (None, ): return node.group() # HACKS attr = re.sub(r'border="2" cellpadding="4" cellspacing="0" style="margin: *1em 1em 1em 0; background: *#f9f9f9; border: *1px #aaa+ solid; *border-collapse: *collapse(; *font-size: *[89]\d%)?', r'class="wikitable" style="', attr) # un-subst: {{prettytable}} and it dirvatives attr = re.sub(r'(?i)([^<>\n]*)border\W+2\W+cellpadding\W+4\W+cellspacing\W+0"?', r' class="wikitable" \1', attr) # p = re.compile(r'(class="wikitable[^<>\n]+ style="[^<>"\n]*?)(margin\W+1em\W+|1em\W+1em\W+0\W+|background\W+f9f9f9\W+|border\W+1px\W+#aa+a\W+solid\W+|border-collapse\W+collapse\W+|font-size\W+(100%|95%|1em)\W+)+(?=[^<>"\n]*")', re.I) # while p.search(text): # text = p.sub(r'\1', text) # WHERE DID I GET THIS!?!: ([^][{}<>|="\'\s]*[0-9a-zA-Z%._]+[^][{}<>|="\'\s]*) def quoteAttrib(m): # r' \g<attribute>="\g<value>"' if '"' in m.group('value'): return ' %s=\'%s\''%(m.group('attribute').lower(), m.group('value').strip()) else: return ' %s="%s"'%(m.group('attribute').lower(), m.group('value').strip()) # Quote attributes #FIXME: | param = ''italtic'' attr = re.sub(r"""(?x)[ ]* \b(?P<attribute>\w{2,}) [ ]*=[ ]* ["']?(?P<value> (?<=") [^"]*? (?=") | (?<=') [^']+? (?=') | [^<=>"' [\]{|}]+(?=[<> ]|$) )["']?""", quoteAttrib, attr) # Remove malformed attribute for m in re.finditer(r' [\w:;\-]+="[^"]+"[\w:;\-@.,_](?=[<> ]|$)', attr): attr = attr.replace(m.group(), '') wikipedia.output("\03{lightred}MALFORMED ATTRIBUTE\03{default} : Removing: %s" % (m.group(),)) # Deprecated classes attr = attr.replace(' class="prettytable', ' class="wikitable') # Repair broken HTML attr = re.sub(r'(?i) bgcolor="([A-Fa-f0-9]{6})"', r' bgcolor="#\1"', attr) # add hash to colors attr = re.sub(r'(?i) colspan="1"', r'', attr) attr = re.sub(r'(?i) rowspan="1"', r'', attr) # # move class= to the front # attr = re.sub(r'^(\s*)( [^][{|}<>]+)?( class="[^"]+"(?=\s|\Z))', r'\1\3\2', attr) if tag == 'table': # TODO move me # Tables attr = re.sub(r'(?i) align="(left|right)"', r' style="float:\1;" ', attr) attr = re.sub(r'(?i) align="center"', r' style="margin:auto;" ', attr) attr = re.sub(r'(?i) align="(\w+)"', '', attr) elif tag == 'div': attr = re.sub(r'(?i) align="(left|right)"', r' style="float:\1;"', attr) #attr = re.sub(r'(?i) align="center"', r' class="center"', attr) if tag == 'table': attr = re.sub(r'(col|row)span=("1"|1)(?=\D)', r'', attr) #attr = attr.replace('cellspacing="0"', 'style="border-collapse:collapse; "') if 'border=' not in attr: # See [[MediaWiki talk:Common.css# Wikitable borders without CSS]] attr = re.sub(r'class="wikitable([^"\'{|}]*)"( *border="?1"?)*', r'class="wikitable\1" border="1"', attr) if re.search('float: *right', attr) and 'toccolours' in attr and node.start() < 400: # floats right, and near the top, gotta be a infobox attr = re.sub(r'class="toc(colours|)', r'class="infobox', attr) attr = re.sub(r'float: *right;|margin[^:;="]*:[^:;="]+|border="1"', r'', attr) # border-collapse is not exactly the same but it's close enough #attr = re.sub(r' cellspacing="0"', r' style="border-collapse:collapse;"', attr) if 'class="wikitable' in attr: attr = re.sub(r'(?i)(border:)( 1px| #aaa+| solid)+',r'\1', attr) attr = re.sub(r'(?i) border="?([0-9])"?', r'', attr) attr = re.sub(r'(?i) cellspacing="?([0])"?', r'', attr) attr = re.sub(r'(?i) cellpadding="?([2-4])"?', r'', attr) attr = re.sub(r'(?i)margin: ?1em 1em 1em 0', r'', attr) attr = re.sub(r'(?i)background: ?#f9f9f9', r'', attr) attr = re.sub(r'(?i)border-collapse: ?collapse', r'', attr) attr = re.sub(r'font-size: ?(100%|1em)', r'', attr) #if # avoid float: position: etc.. #attr = re.sub(r'font-size: ?\.?9\d(%|em)', r'', attr) # replace with CSS attr = re.sub(r'(?i) align="(left|center|right|justify)"', r' style="text-align:\1;"', attr) attr = re.sub(r'(?i) bgcolor="([^"]+?)"', r' style="background-color:\1;"', attr) #attr = re.sub(r'(?i) border="?([1-9])"?', r' style="border:\1px;"', attr) attr = re.sub(r'(?i) color="([^"]+?)"', r' style="color:\1;"', attr) attr = re.sub(r'(?i) clear="(left|right|none)"', r' style="clear:\1;"', attr) attr = re.sub(r'(?i) clear="(all|both)"', r' style="clear:both;"', attr) attr = re.sub(r'(?i) clear="[^"]*"', r' ', attr) attr = re.sub(r'(?i) face="([^"]+?)"', r' style="font-family:\1;"', attr) attr = re.sub(r'(?i) height="([^"]+?)"', r' style="height:\1;"', attr) attr = re.sub(r'(?i) nowrap(="(nowrap|yes|true)"|(?= )|$)', r' style="white-space:nowrap;"', attr) attr = re.sub(r'(?i) size="(\d+(em|%|px|pt))"', r' style="font-size:\1;"', attr) attr = re.sub(r'(?i) valign="(top|middle|bottom|baseline)"', r' style="vertical-align:\1;"', attr) attr = re.sub(r'(?i) valign="[^"]*"', r' ', attr) attr = re.sub(r'(?i) width="([^"]+?)"', r' style="width:\1;"', attr) # font size="#" render browser dependent, W3C leaves it open fontSizeConvert = {'1':'0.8em','2':'1em','3':'1.2em','4':'1.4em','5':'1.9em','6':'2.4em','7':'3.7em', '-4':'50%','-3':'60%','-2':'70%','-1':'80%','0':'100%','+0':'100%', '+1':'120%','+2':'140%','+3':'160%','+4':'180%','+5':'200%','+6':'250%','+7':'300%',} for n in re.finditer(r' size="([1-7]|[+-][0-6])"', attr): attr = attr.replace(n.group(), r' style="font-size:%s;"'%fontSizeConvert[n.group(1)]) # merge style attributes together stylemerge = re.compile(r' style="([^"{|}\n]+?);* *" *(.*?) style="([^"{|}\n]+)"') while stylemerge.search(attr): attr = stylemerge.sub(r' \2 style="\1; \3"', attr) # Fix up style parameters for styleMatch in re.finditer(r' style="([^[\]{|}\n]*?)"', attr): styleText = fixCSS(styleMatch.group(1)) attr = attr.replace(styleMatch.group(), styleText and ' style="%s"'%styleText or '') if '=' in styleText: wikipedia.output("\03{lightyellow}WARNING\03{default} : U+003D EQUALS SIGN (=) character found in style attribute") # Remove all non approved attributes for m in re.finditer(r'(?<= )([\w:;\-]+)(="[^"]+"| +(?=\w)| *$| *>)', attr): if m.group(1).lower() not in htmlattrs and tag:# HACK remove when proper table support is in wikipedia.output("\03{lightred}INVALID ATTRIBUTE\03{default} : Removing: %s" % (m.group(),)) attr = attr.replace(m.group(), '') elif m.group(2) == '=""': wikipedia.output("Empty attribute") else: attr = attr.replace(m.group(), m.group(1).lower() + m.group(2)) # Alert user about deprecated html attributes # FIXME this should be split up into General, Table, Font if m.group(1).lower() in "align|alink|axis|background|bgcolor|border|cellspacing|cellpadding|char|charoff|clear|compact|color|face|frame|height|hspace|link|noshade|nowrap|rules|size|start|text|type|valign|vlink|width|vspace".split('|'): wikipedia.output("\03{lightred}DEPRECATED ATTRIBUTE\03{default} : %s"%''.join((node.group(1).lower(), attr.replace(m.group(), '\03{lightred}%s\03{default}'%m.group()))).strip()) # put back in if re.sub(r'[ ;"]', '', node.group('attr').lower()) != re.sub(r'[ ;"]', '', attr.lower()) and len(attr) < len(node.group('attr')) * 2: return ''.join((node.group(1).lower(), attr.rstrip() )) else: return node.group() def fixCSS(styleText): #TODO # add filter for value and dictionary units # Stylistics changes styleText += ';' # add then remove styleText = re.sub(r' *: *', ':', styleText) styleText = re.sub(r' *(; *)+', '; ', styleText) # Remove "float; ..." and "float:;" styleText = re.sub(r'(\A *|;)([^;:=]*:? *;)+', r'\1', styleText) styleText = re.sub(r'[\w\-\s]:; ', '', styleText) #styleText = re.sub(r'(background|color):([a-fA-F0-9]{6});', r'\1:#\2', styleText) # removed, we should check in the color list before trying HEX values if styleText.count('background') == 1: styleText = styleText.replace('background-color:', 'background:') # Background:none is shorter than background-color:transparent, but resets image related properties # We also assume that people will not set anything else since background-image: is filtered out # See: [[User:Chris Chittleborough/CSS-notes]] styleText = re.sub(r'background:[^:;]*transparent[^:;]*;', r'background:none;', styleText) # Assumed units styleText = re.sub(r'(width|height):(\d{2,});', r'\1:\2px;', styleText) styleText = re.sub(r'((?:background|border|border|color)(?:-color)?):([a-fA-F0-9]{3,6})(?=[ ;])', r'\1:#\2', styleText) # Fix units styleText = re.sub(r'\b(width|height|border|margin|padding):(\d{2,}|[1-9])(?=[; ])', r'\1:\2px;', styleText) styleText = re.sub(r'(?<=[ :]0)(em|%|px|pt)(?=[ ;])', "", styleText) # IE color compatiblity styleText = re.sub(r'(?i)\bgrey\b', r'gray', styleText) styleText = re.sub(r'(?i)(dark|dim|light|lightslate|slate)gr[ae]y', r'\1grey', styleText) # Shorten CSS color values for m in re.finditer(r'#(?:[0-9a-fA-F]{6}|[0-9a-fA-F]{3})(?=[ ;!])', styleText): if re.search(r'(?i)#(00|11|22|33|44|55|66|77|99|aa|bb|cc|dd|ee|ff){3}', m.group().lower() ): styleText = styleText.replace(m.group(), re.sub(r'(?ui)#([0-9a-f])[0-9a-f]([0-9a-f])[0-9a-f]([0-9a-f])[0-9a-f]', r'#\1\2\3', m.group().lower() )) elif m.group().upper() in namedColors: styleText = styleText.replace(m.group(), namedColors[m.group().upper()]) else: styleText = styleText.replace(m.group(), m.group().lower()) # use mirroring styleText = re.sub(r'(margin|padding):(?P<v>-?[\.0-9]+[a-zA-z]+|0)( (?P=v))+;', r'\1:\2;', styleText) styleText = re.sub(r'(margin|padding):(-?[\.0-9]+[a-zA-z]+|0) (-?[\.0-9]+[a-zA-z]+|0) \2 \3;', r'\1:\2 \3;', styleText) styleText = re.sub(r'(margin|padding):(-?[\.0-9]+[a-zA-z]+|0) (-?[\.0-9]+[a-zA-z]+|0) (-?[\.0-9]+[a-zA-z]+|0) \3;', r'\1:\2 \3 \4;', styleText) return styleText.strip() def ext2intLinks(page, text): text = re.sub(r'\[https?://upload.wikimedia.org/wikipedia/(?:commons|%s)/[0-9A-Fa-f]/[0-9A-Fa-f]{2}/([^[\]<>\s?]+) *((?<= )[^\n\]]+)\]' % (page.site().language()), r'[[Media:\1|\2]]', text) text = re.sub(r'\[https?://upload.wikimedia.org/wikipedia/(?:commons|%s)/[0-9A-Fa-f]/[0-9A-Fa-f]{2}/([^[\]<>\s?]+)\]' % (page.site().language()), r'<ref>[[Media:\1]]</ref>', text) text = re.sub(r'\[https?://(www\.toolserver\.org|toolserver\.org|tools\.wikimedia\.org|tools\.wikimedia\.de)/([^][<>"\s;?]*)\?? ([^]\n]+)\]', r'[[tools:\2|\3]]', text) if page.namespace() == 0: # [[WP:SELF]] states that we shouldn't cross link from the main namespace text = re.sub(r'''(?ix)\[https?://([a-z]{3}(?:-[a-z]+)*)\.(?: (wikt)ionary| wiki(n)ews| wiki(b)ooks| wiki(q)uote| wiki(s)ource| wiki(v)ersity)\.(?:com|net|org)/wiki/ (?![_ :]*(?:Talk|Help|User|Wikipedia|Wikinews|Wikibooks|wikiquote|wikisource|wikiversity|Portal|MediaWiki)(?:[ _]talk)?:) ([^][{|}\s"]*)[| ]+([^\n\]]+)\]''', r'[[\2\3\4\5\6\7:\1:\8|\9]]', text) text = re.sub(r'''(?ix)\[https?://(meta|commons|incubator|quality) \.wikimedia\.(?:com|net|org)/wiki/ (?![_:]*(?:Talk|Help|User|Meta|commons|incubator|quality|Portal|MediaWiki)(?:_talk)*:) ([^][{|}\s"]*)[| ]+([^\n\]]+)\]''', r'[[\1:\2|\3]]', text) else: text = re.sub(r'''(?ix)\[[https:]*//([a-z]{3}(?:-[a-z]+)*)\.(?: (wikt)ionary| wiki(n)ews| wiki(b)ooks| wiki(q)uote| wiki(s)ource| wiki(v)ersity)\.(?:com|net|org)/wiki/ ([^][{|}\s"]*)[| ]+([^\n\]]+)\]''', r'[[\2\3\4\5\6\7:\1:\8|\9]]', text) text = re.sub(r'''(?ix)\[https?://(meta|commons|incubator|quality) \.wikimedia\.(?:com|net|org)/wiki/ ([^][{|}\s"]*)[| ]+([^\n\]]+)\]''', r'[[\1:\2|\3]]', text) text = re.sub(r'''(?ix)\[https?://([a-z0-9\-]+)\.wikia\.(?:com|net|org)/wiki/ ([^][{|}\s"]*)[| ]+([^\n\]]+)\]''', r'[[wikia:\1:\2|\3]]', text) # Reverse interwiki map # [0-9A-Za-z\-.:_] not escaped # [;:@$!*(),/] are converted back in GlobalFunctions.php # [_#\'\\^`~] are assumed to be safe #conflict = {} for iw_prefix, iw_url in interwiki_map.iteritems(): # Expensive overlap test #if iw_url in conflict: # print("Collision in interwiki map [[%s:]] and [[%s:]] on %s<br/>" % (iw_prefix, conflict[iw_url], iw_url)) #else: # conflict[iw_url] = iw_prefix #for a,b in interwiki_map.iteritems(): # if b.find(iw_url) == 0 and a != iw_prefix: # print("Overlap between interwiki map [[%s:]] (%s) and [[%s:]] (%s)<br/>" % (iw_prefix, iw_url, a, b)) # re.escape(iw_url).replace(\\$1', r' text = re.sub(r'\[%s +([^\n\[\]]+)\]'%re.escape(iw_url).replace('\\$1', r'([0-9A-Za-z\-.;;:@$!*(),/_#\'\\^`~]*)'), r"[[%s:\1|\2]]"%iw_prefix, text) return text def canonicalTitle(title, firstupper=True, underscore=False): """ Converts unicode or bytes string to mw titles support: percent-encoded UTF-8, HTML character references """ # TODO namespace support, e.g. [[WP: Foo]] if isinstance(title, unicode): title = title.encode('utf-8') # Unpercent-encode title = wikipedia.urllib.unquote(title) try: title = unicode(title, 'utf-8') except:title = unicode(title, 'latin-1') # HTML character references title = wikipedia.html2unicode(title) # Remove ltr and rtl markers title = title.replace(u'\u200e', '').replace(u'\u200f', '') # Underscore to space and Strip space title = title.replace('_', ' ').strip().lstrip(':') # Merge multiple spaces while ' ' in title: title = title.replace(' ', ' ') # First uppercase if firstupper and title: title = title[0:1].upper() + title[1:] # Strip the section part if '#' in title: title = title[:title.index('#')] if underscore: title = title.replace(' ', '_') return title def wikilinkregex(t, firstupper=True): t = canonicalTitle(t, firstupper) # Workaround for titles with an escape char if firstupper: t = ur'[%s%s]%s' % (t[0].upper(), t[0].lower(), t[1:],) t = re.escape(t).replace('\\[', '[', 1).replace('\\]', ']', 1) return t.replace('\\ ', '[ _]+').replace('\\|', '|') def simplifyLinks(page, text): # import simplify_links text = simplify_links.simplifyAnchors(page.site(), text) # Prettify links, remove underscore and decode characters for m in re.finditer(ur'\[\[([^[\]{|}\n]+)\|([^\n|]*?)\]\]', text): link = m.group(1).replace(u'_', u' ').encode('utf-8') if b'#' in link: title, anchor = link.split(b'#', 1) anchor = re.sub(br''' # Single byte character (Printable ASCII) # we make that [0-9A-Za-z\-.:_] and [[\]{|}] are not included \.2[1-9A-CF] |\.3[BD-F] # Avoid encoding <tag and </tag |\.3C(?!\w|/|\.2F) |\.40 |\.5[CE] |\.60 |\.7E # skip .8-B\h # Two byte UTF-8 character U+0080-U+07FF |\.[CD][0-9A-F]\.[89AB][0-9A-F] # Three byte UTF-8 character U+0800-U+FFFF |\.E[0-9A-F]\.[89AB][0-9A-F]\.[89AB][0-9A-F] # Four byte UTF-8 character U+10000-U+10FFFF |\.F[0-7]\.[89AB][0-9A-F]\.[89AB][0-9A-F]\.[89AB][0-9A-F] ''', lambda m: m.group().replace(b'.', b'%'), anchor.replace(b'%', b'.25'), flags=re.X) link = b''.join((title, b'#', anchor)) link = urllib.unquote(link) # unescape %xx # Specific formating if link.startswith(b'tools:'): link = link.replace(b' ', b'_') link = link.replace(b'# ', b'#') # Remove copy & paste space link = link.decode('utf-8') #if m.group(2)[0:1].islower(): #if m.group(1) != link #if not any((s.isupper() for s in link[1:])) and not any((s.isupper() for s in m.group(2))): # if re.search(r'(?i)\[\[(\w{3,})\w{0,3}[()_ |[\]].*?\b\1', m.group()): # # Come up with better huristics # # issue: [[Internet|the internet]] # link = link[0].lower() + link[1:] text = text.replace(m.group(), '[[%s|%s]]'%(link, m.group(2))) # Simplify links # FIXME use canonicalTitle # [[A|AB]] -> [[A]]B text = re.sub(ur'\[\[([^{|}[\]]+)\|\1(\w*)\]\]', ur'[[\1]]\2', text) ## A[[ABC|B]]C -> [[ABC]] #text = re.sub(ur'(?u)([^{|}[\]]* *) *\[\[ *\1([^{|}[\]]+ *)( *[^{|}[\]]*) *\| *\2\]\]\3', ur'[[\1\2\3]]', text) # disabled: "by [[experimental music|experimental]] musician" # TODO # unbypass redirect change [[Light_cycle#Light_cycles]] and [[Tron_(film)#Light_cycles]] to the redirect [[Light cycle]] # find redirect such that A [[Article |B]] C to [[A B C]] return text def fixReferences(page, text): # Standardize to lowercase reference name, makes things easier for everyone text = re.sub(r'(?i)<(/?)REF\b([^>]*)>', r'<\1ref\2>', text) # it should be name = " or name=" NOT name =" text = re.sub(r'<ref +name(= *| *=)"', r'<ref name="', text) # Remove puncutation between start/end of ref/templates (}}.</ref>) text =re.sub(r'(<ref[^/>]*>\s*)[,.?:;~!]+\s*(?=\{\{)', r'\1', text) text = re.sub(r'(\{\{[^{}]{40,}\}\})\s*[,.?:;~!]+(?=\s*</ref>)', r'\1', text) # Leaving out the http:// text = re.sub(r'(?<=<ref>)\s*([a-z0-9\-\.]*?[a-z0-9\-]+\.[a-z\.]{2,6}/[^][<>\s"]+)\s*(?=</ref>)', r'http://\1', text) text = re.sub(r'(?<=<ref>)\s*\[?(?:http://)?([a-z0-9\-\.]*?[a-z0-9\-]+\.[a-z\.]{2,6}/[^][<>\s"|]+) +([^][{|}<>\n/]+?)\]?\s*(?=</ref>)', r'[http://\1 \2]', text) # TODO: Fix the below [ref] to <ref>[url]</ref> conversion text = re.sub(r'(?is)<ref\s*>\s*(\[\w+://[^][<>"\s]+\s*\])\s*(\[\w+://[^][<>"\s]+\s*\])\s*</ref\s*>', r'<ref>\1</ref><ref>\2</ref>', text) ## Badly formed references # Fake reference (<sup>[url]</sup>) text = re.sub(r'(?i)<sup *>\s*\[(\w+://[^][<>"\s]+) *\]\s*</sup>', r'<ref>\1</ref>', text) # Bracket to reference conversion # BUG matches <!-- [http://link/index ] --> def extToRef(m): try: referencesTemplates = noreferences.referencesTemplates[ wikipedia.getSite().family.name][wikipedia.getSite().lang] except KeyError: referencesTemplates = [] if referencesTemplates: reflist = '</?references|\{\{(' + '|'.join(referencesTemplates) + ')' else: reflist = '</?references' reflistM = re.compile(reflist).search(text, m.end()) if reflistM and m.end() < reflistM.end(): return m.expand(r'\1<ref>\2</ref>') return m.group() for i in range(8): #text = re.sub(r'(?miu)(^[^*#;:= ]{1,4}.{4,}?)(?<![*#]{3})(?<!PDFlink\|)(?<!PDF\|)(?<![(])\[((?:http|https|ftp)://[0-9a-z\-\.:]+/[^[\]<>\s"]{8,})\s*\](?![^-]*-->)(?!([^<]|<(?!ref))*</ref>)', extToRef, text) # testing text = re.sub(r'(?mi)(^[^#;:= ]{1,4}.{4,}?)(?<=[^*#]{15})(?<!PDFlink\|)(?<!PDF\|)(?<![(])\[((?:http|https|ftp)://[0-9a-z\-\.:]+/[^[\]<>\s"]{8,})\s*\](?![^-]*-->)(?!([^<]|<(?!ref))*</ref>)', extToRef, text) # remove invalid references text = re.sub(r'(?i)<ref> *</ref>', '', text) ## Format Punctuation # Applied if "[,.;:]<ref/>" is dominate if len(re.findall(r'[.,;:] *\s?<ref', text)) > len(re.findall(r'(?:</ref>|<ref [^</>]+/>) *\s?[.,;:]', text)): # Move punctuation left and space right but before \n text = re.sub(r'(?s)(?<=[\w")\]])( *)((?: *\s??<ref [^>]+?/>| *\s??<ref[^>]*?>(?:[^<]|<(?!/?ref))*?</ref>)+)( *)\n?([.,]|(?<!\n)[;:])(?![.,;:])(\s??)( *)', r'\4\2\1\6\5\3', text) # Move space to the right, if there's text to the right #u text = re.sub(r'(?s)(?<=[.,;:"])( +)((?: *\s??<ref [^>]+?/>| *\s??<ref[^>]*?>(?:[^<]|<(?!/?ref))*?</ref>)+)(?= *\s?[^\s<>])', r'\2\1', text) # Remove duplicate punctuation text = re.sub(r'(?s)(?P<punc>[.,;:])(["]?(?:<ref [^>]+?/> *\s?|<ref[^>]*?>([^<]|<(?!/?ref))*?</ref> *\s?)+)(?!(?<=\n)[:;]|[.,;:]{2,})(?P=punc)', r'\1\2', text) # Remove spaces between references text = re.sub(r'(</ref>|<ref [^>]+?/>) +(<ref)', r'\1\2', text) # Add two space if none, reduce to two if more # trim or add whitespace after <ref /> text = re.sub(r'(</ref>|<ref [^>]+?/>)()((\'{2,5}|)[\w"(\[])', r'\1 \3', text) text = re.sub(r'(</ref>|<ref [^>]+?/>)( {3,})([\w(\[])', r'\1 \3', text) # October 2010-[[WP:REFPUNCT]] now states to always place before # elif len(re.findall(r'(?:</ref>|<ref [^</>]+/>) *\s?[.,;:]', text)) > 10: # wikipedia.output('\03{lightyellow}ALERT\03{default}: Punctuation after the references is the dominate format!') # wikipedia.output('\03{lightyellow}ALERT\03{default}: The majority of references have commas and periods after the reference entry.\n When editing you should preserve this formatting.') # Merge duplicate refs # TODO seperate reference group from naming for m in re.finditer(r'(?si)(<ref>)(.*?)(</ref>)', text): # Skip single references if text.count(m.group()) <= 1: continue # Get a meaningful word part for p in (r'\|\s*last\s*=(\w+)', # Reference template: | last = LASTNAME r'[Bb][Yy] +[A-Z][a-z]+ +([A-Z][a-z]+)[.,\'"]', r'^((?:Mc|)[A-Z][a-z])[,.]', # First word, must be capitalized and followed by punctuation r'(?s)\w+://[a-z0-9\-\.]*?([a-z0-9\-]+)\.[a-z\.]{2,6}[ /|=!]', # Website DOMAIN r'(?s)^(?:\[\[[^][]+\|)?((?<![{])(?<=\W)\b\w+)[,. ].*?(\d{2,4}\b)', # [[Author|McAuthor]] p. 25 r'(?si)\{\{.*?\|(\w*?)\|.*\}\}', # EXPERIMENTAL: {{Harvnb|McCann|1999|p=247}} ): match = re.search(p, re.sub(r'accessdate\s*=[^{|}]*|Retrieved [\s\w\[\],]+', ' ', m.group(2)), re.UNICODE) if match and len(match.group(1)) > 4 and match.group(1).lower() not in ignoreAsNames: refname = match.group(1) break else: refname = 'autogenerated' # Default name # try for the longest Capitalized word for n in re.findall(r'\b(?:Mc)?[A-Z][a-z]+\b', re.sub(r'\|[^{|}=]+=|\{\{[^{|}]+\||\[\[^][|]+\|', ' ', m.group(2) )): if len(n) > len(refname): refname = n # Remove non-letters to avoid names like "rescue007" refname = refname.strip('\t\r\n 0123456789-').lower() # Get a number for p in (r'\|\s*(?:pages|page|p|pp)\s*=\s*(\d+)', r'\b(?:pages|page|p|pp|pg)[.:= ]*(\d{1,4})\b[\w\s\.\-<&\]]*', r'\|\s*year\s*=\s*(\d{4})', r'\b(19\d\d|200[0-7])\b', r'\b([mclxvi]*[clxvi]{2,6})(?:\b|\.)' ): match = re.search(p, re.sub(r'accessdate\s*=[^{|}]*|Retrieved [\s\w\[\],]+', ' ', m.group(2)) ) if match and refname+match.group(1) not in text: refname = refname+match.group(1) break else: i = 1 while refname+str(i) in text: i+=1 else: refname += str(i) # the replacement name should be 50% smaller if len(m.group(2)) * 0.50 > len(refname) + 8: text = text.replace(m.group(), '<ref name="%s">%s</ref>' % (refname, m.group(2)), 1) text = text.replace(m.group(), '<ref name="%s"/>' % refname) # remove formatting wrappers (adapted from AWB) m = re.search(r'(?i)(<(span|div)( class="(references-small|small|references-2column)"|)>\s*){1,2}\s*<references[\s]?/>(\s*</(span|div)>){1,2}', text) if m and m.group().count('<div') == m.group().count('</div'): cols = re.search(r'((?!-)column-count|-moz-column-count):\s*?(\d+)', m.group()) if "references-2column" in m.group(): text = text.replace(m.group(), '{{reflist|2}}') elif cols: text = text.replace(m.group(), '{{reflist|%s}}' % cols.group(2)) else: text = text.replace(m.group(), '{{reflist}}') # Multicolumn {{Reflist}} # If more than 30 refs, make sure the reference section is multi column if text.count('</ref>') > 30: text = re.sub(r'(?is)(=\s+(<!--.*?-->)*\s*)(\{\{Cleanup-link rot[^{}]*\}\}\s*)?(<references />|\{\{(?:Listaref|Reference|Refs|Reflist|Refs)\|?[134]?\}\})', r'\1{{reflist|colwidth=30em}}', text) elif text.count('</ref>') < 8: text = re.sub(r'(?is)(=\s+)\{\{reflist\|(\d+|colwidth=\d+\w+)\}\}', r'\1{{reflist}}', text) else: pass return text def correctdate(s): pass def wiki_table(match): return match.group() def html_attrib(match): return match.group() ## hideTokens = {} hideRegex = re.compile('|'.join([ r'<!--.*?-->', r'<includeonly>.*?</includeonly>', r'<math>.*?</math>', r'<nowiki>.*?</nowiki>', r'<source .*?</source>', r'<pre.*?</pre>', r'<timeline>.*?</timeline>', r'<gallery.*?>.*?</gallery>', ]), re.I | re.S) def hideText(text): global hideTokens n=111 for m in hideRegex.finditer(text): n+=1 hideTokens[n] = m.group() text = text.replace(m.group(), u'⌊⌊⌊⌊%06d⌋⌋⌋⌋'%n) return text def showText(text): global hideTokens for (key, value) in hideTokens.items(): text = text.replace(u'⌊⌊⌊⌊%06d⌋⌋⌋⌋'%key, value) if re.search(ur'⌊⌊⌊⌊\d{6,}⌋⌋⌋⌋', text): wikipedia.output("WARNING: Unable to replace all hidden tokens") raise "Please report this problem at [[User talk:Dispenser]]" hideTokens = {} # Empty return text def main(): gen = None namespaces = [] genFactory = pagegenerators.GeneratorFactory() summary = "Applying general fixes for links, HTML, and/or references" for arg in wikipedia.handleArgs(): if arg == '-test' or arg.startswith('-test:'): f = open('../cgi-bin/text/%s'%(arg[6:].replace('/', '|') or 'Tests.html')) test = unicode(f.read(), 'utf-8') site = wikipedia.getSite() page = wikipedia.Page(site, 'Special:Snippet') page._namespace=0 # Disable cgitb disk loggging import cgitb; cgitb.enable() wikipedia.output("Default site: %s"%site.sitename()) result = fix(text=test, page=page) wikipedia.showDiff(test, result) import parser print(b''' <table style="table-layout:fixed; width:100%%;"> <tr style="vertical-align:top;"> <td>%s</td> <td>%s</td> </tr> </table>''' % (parser.parser(test).encode('utf-8'), parser.parser(result).encode('utf-8'))) wikipedia.output('\n----\n== Double pass text ==') wikipedia.showDiff(result, fix(text=result, page=page)) return else: genFactory.handleArg(arg) if not gen: gen = genFactory.getCombinedGenerator() if not gen: wikipedia.showHelp('commonfixes') return for page in gen: try: page.get() except wikipedia.NoPage: wikipedia.output('%s does not exist!' % page.aslink()) continue except wikipedia.IsRedirectPage: wikipedia.output(u'Page %s is a redirect' % page.aslink()) continue text = fix(page=page) if text != page.get(): wikipedia.showDiff(page.get(), text) wikipedia.setAction(summary) page.put(text) else: print(b'No changes necessary') if __name__ == "__main__" and wikipedia.handleUrlAndHeader(): try: wikipedia.startContent() main() finally: wikipedia.endContent() wikipedia.stopme() #