aboutsummaryrefslogblamecommitdiff
path: root/tools/trac2md.py
blob: 628f0c0d4e96efe87bfc738ce7ebb8edf5365d13 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
















                                                                                               
                                              



                                                                                         


































                                                                               
                                                    


                                                
                                                                            



                                                              













































                                                                        
                    

                    
                                      
                    
 
                      

                                          






                                                 

                          

















                                                                         



                                                                
                                             




                                                                       
                                                                

                                        

             

                                                                      
                                                          
             
                                     
                               
                                            

                               

                                       

                                                                   
                                            














                                                             
                                                             



                                                        
                                            



                                


                                                     












                                                                 
                                
                                
 
                               







                                     
#!/usr/bin/python

# Untested code from https://www.snip2code.com/Snippet/1704331/Convert-trac-markup-to-Markdown/

# This code mostly taken from patches to pagure_importer by mreynolds

import sys
import re
import time
import requests
import shutil
import os
from base64 import b64decode
from datetime import datetime

wikilink_pattern = re.compile('\[http(.*)\]')
wikilink_extract = re.compile('\[(.*)\]')
strikethrough_pattern = re.compile('~~(.*)~~')
camelcase_pattern = re.compile("!(\w+)")

wikiheading_patterns = tuple((level, re.compile("^{} (.*)[ \t]*=*$".format("=" * level)))
                             for level in xrange(1, 7))

def to_timestamp(tm):
    ''' Convert to timestamp which can be jsonified '''

    tm = tm.replace('+00:00', '')
    date = datetime.strptime(tm, '%Y-%m-%dT%H:%M:%S')
    ts = str(time.mktime(date.timetuple()))[:-2]  # Strip the .0
    return ts


def strip_wikilink(content):
    ''' Need to remove wiki link format from custom fields.  They come in a
    variety of forms that can be comma or whitespace separated.  They can also
    include link names which must also be removed.

        [https://bugzilla.redhat.com/show_bug.cgi?id=772777]
        [https://bugzilla.com/123456789], [http://bugzilla.com/7777777 7777777]
        [https://bugzilla.com/6666666 6666666]
    '''

    links = []
    if wikilink_pattern.search(content):
        # Looks like we have a link in here
        links = []
        mylist = re.findall(r'\[([^]]*)\]', content)
        for i in mylist:
            links.append(i.split(' ', 1)[0])
        return ', '.join(links)
    else:
        return content


def convert_headers(line):
    ''' Convert wikiformat headers
    '''
    for level_count, header in wikiheading_patterns:
        try:
            level = header.search(line).group(1)
            if level:
                line = "%s %s" % ('#' * level_count, level.rstrip("= \r\t"))
                break  # No need to check other heading levels
        except:
            # Try the next heading level
            pass

    return line


def convert_wikilinks(line):
    ''' Convert wikiformat links
    '''
    if wikilink_pattern.search(line):
        try:
            result = wikilink_extract.search(line).group(1)
            if result:
                parts = result.split(' ', 1)
                if len(parts) == 1:
                    mdlink = '[%s](%s)' % (parts[0], parts[0])
                elif len(parts) == 2:
                    mdlink = '[%s](%s)' % (parts[1], parts[0])
                line = line.replace('[' + result + ']', mdlink)
        except:
            # Not a link, not a problem
            pass

    return line


def convert_strike(line):
    ''' Convert wikiformat striked text
    '''
    striked_result = strikethrough_pattern.search(line)
    if striked_result:
        try:
            striked_text = striked_result.group(1)
            if striked_text:
                orig_text = '~~%s~~' % striked_text
                new_text = '<s>%s</s>' % striked_text
                line = line.replace(orig_text, new_text)
        except:
            # Not striked
            pass
    return line

def WikiToMD(content):
    ''' Convert wiki/RST format to Markdown.  Code blocks, bold/italics,
    wiki links, lists, striked text, and headers. '''

    code_block = False
    in_list = False
    in_table = False
    nested_level = 0
    prev_indent = 0
    old_content = content.splitlines()
    new_content = []

    while old_content:
        line = old_content.pop(0).rstrip()
        tail = ["\n"]
        while "{{{" in line or "}}}" in line:
            if "{{{" in line:
                code_block = True
                line = line.replace("{{{", "```")
            if "}}}" in line:
                code_block = False
                line = line.replace("}}}", "```")
        if not code_block:
            #
            # Want to convert tables.  References:
            #   https://github.github.com/gfm/#tables-extension-
            #   https://permatrac.noc.ietf.org/wiki/WikiFormatting#Tables
            #
            # Table start: line containing "||"
            # Table end: blank line?
            #
            # Figuring out whether there's a real header line is fun,
            # trac doesn't require one, markdown does.  Guess we can
            # add a dummy header if no better idea.  Markdown requires
            # delimiter line, which we add immediately after the
            # header, both appear to be mandatory.  Trac can have
            # label cells anywhere, not just in header, might need to
            # add "*" to those or just ignore the issue.
            # Justification we can sort of figure out from the header,
            # if the rows do anything different, ouch, because
            # markdown specifies in delimiter line.
            #
            # Might do something clever with the "=" markers and
            # alignment, start with just getting the basic table
            # structure to something markdown will believe.
            #
            if line.strip().startswith("||"):
                line = line.replace("=|", "|").replace("|=", "|")
                line = line.replace("||", "|")
                if not in_table:
                    tail.append("|---" * (line.count("|") - 1) + "|\n")
                in_table = True
            elif in_table and not line.strip().startswith("||"):
                new_content.append("\n")
                in_table = False

            #
            # Convert bullet lists.  The start and end of a list needs
            # an empty line.  wikiformat uses both '*' and '-' for its
            # lists.  However, markdown only supports '*'.
            #
            if line.startswith('- '):
                if not in_list:
                    new_content.append("\n")
                in_list = True
                line = line[1:]
                line = '*%s' % (line)
            elif line.startswith('* '):
                # No need to modify the line, just add the new line
                if not in_list:
                    new_content.append("\n")
                in_list = True
            elif line.startswith(' '):
                # Check for nested lists
                nested_line = line.lstrip(' ')
                if nested_line.startswith('* ') or \
                   nested_line.startswith('- '):
                    # Adjust the nested list level as needed
                    indent = len(line) - len(nested_line)
                    if indent > prev_indent:
                        nested_level += 1
                    elif indent < prev_indent:
                        nested_level -= 1
                    prev_indent = indent

                    # Set the proper indentation for markdown
                    line = ('%s*%s' % ('    ' * nested_level,
                                       nested_line[1:]))
            else:
                if in_list:
                    # Add the closing empty line
                    new_content.append("\n")
                in_list = False
                nested_level = 0
                prev_indent = 0

            # Convert CamelCase
            line = camelcase_pattern.sub("\\1", line)

            # Convert headers
            line = convert_headers(line)

            # Convert wiki links
            line = convert_wikilinks(line)

            # Convert striked through text
            line = convert_strike(line)

            # Convert bold and italic text (do this last)
            line = line.replace("'''", "**")  # Convert bold text
            line = line.replace("''", "*")  # Convert italic text

        new_content.append(line)
        new_content.extend(tail)

    return "".join(new_content)

for f in sys.argv[1:]:
    d = WikiToMD(open(f, "r").read())
    newf = f.replace(".trac", ".md")
    with open(newf, "w") as fp:
        fp.write(d)
        pass
    pass