Edgewall Software

Ticket #5241: mediawiki2trac.3.py

File mediawiki2trac.3.py, 5.2 KB (added by jason.dusek@…, 4 years ago)

This one does links even a little more nicely.

Line 
1#!/usr/bin/python
2"""
3  This script is provided AS IS, without any warranty!
4  Copyright lio@lunesu.com, placed in the public domain
5  Modified by Koen Werdler @ 01-05-2007
6  Modified by Jason Dusek, 2008-07-14Z
7 
8  Requirements:
9 .  python 2.5
10 .  python-mysqldb
11
12  This script dumps every version of every page as an INSERT statement that
13  is compatible with the SQLite Trac schema (don't know about other
14  databases). It omits some functionality, like recognizing edits by IP
15  address users. It does not handle images. It separates `User:` and `Talk:`
16  pages from the rest of the bunch and handles them appropriately.
17
18  You may set the connection parameters towards the bottom of the script
19  (following "main program").
20                                                                           """
21
22import os
23import sys
24import time
25import string
26import _mysql
27
28
29
30
31def q(s):
32    return "'" + s.replace("'", "''") + "'"
33
34def time_fixer(mw_time):
35    """ convert from mediawiki dates to epoch seconds """
36    t = time.mktime(time.strptime(mw_time, "%Y%m%d%H%M%S"))
37    return int(t)
38
39pairs = [
40    ("\n***","\n   *"),
41    ("\n**", "\n  *"),
42    ("\n*",  "\n *"),
43    ("<br>","[[BR]]"),
44    ("\n:","\n "),
45    ]
46
47wiki_link_catcher = re.compile(r"""
48    \[\[
49      \s*
50        ([^|\[\]]+?)        ##  non-greedy `+?` to chop trailing space
51      \s*
52        (\|
53          \s*
54            ([^\[\]]+?)     ##  non-greedy `+?` to chop trailing space
55          \s*
56        )?
57    \]\]
58    """,
59    re.VERBOSE,
60    )
61
62def link_rewriter(match):
63    link = 'wiki:' + match.group(1).replace(' ', '_')
64    return match.group(3) and '[' + link + ' ' + match.group(3) + ']' or link
65
66def text_fixer(mw_text):
67    """ convert from mediawiki text to trac text """
68    for (mw, tw) in pairs:
69        mw_text = mw_text.replace(mw, tw)
70    return q(wiki_link_catcher.sub(link_rewriter, mw_text))
71
72def title_fixer(namespace, title):
73    if namespace is 0:
74        return q(title)
75    if namespace is 1:
76        return q('Talk:' + title)
77    if namespace is 2:
78        return q('User:' + title)
79
80def comment_fixer(mw_comment):
81    if mw_comment == '':
82        return 'NULL'
83    else:
84        return q(mw_comment)
85
86def row_writer(row):
87    tuple = ",\n\t".join([
88        title_fixer(int(row[-2]), row[0]),
89        str(row[1]),
90        str(time_fixer(row[2])),
91        q(row[3].lower()),
92        "'127.0.0.1'",
93        text_fixer(row[4]),
94        comment_fixer(row[5]),
95        'NULL'
96        ])
97    return "INSERT INTO wiki VALUES\n(\t" + tuple + "\n);"
98
99"""
100  Trac's "wiki" table is layed out as follows:
101
102CREATE TABLE wiki (
103    name text,
104    version integer,
105    time integer,
106    author text,
107    ipnr text,
108    text text,
109    comment text,
110    readonly integer,
111    UNIQUE (name,version)
112);
113                                                                           """
114
115
116
117
118"""
119  Failings of this query:
120 .  Does not handle "IP address" (anonymous) users.
121 .  Does not handle User_talk pages.
122                                                                           """
123query = string.Template("""
124    SELECT
125        ${p}page.page_title,
126        ${p}revision.rev_timestamp,
127        ${p}user.user_name,
128        ${p}text.old_text,
129        ${p}revision.rev_comment,
130        ${p}page.page_namespace,
131        ${p}revision.rev_page
132      FROM
133        ${p}page,
134        ${p}revision,
135        ${p}user,
136        ${p}text
137      WHERE
138        ${p}page.page_id = ${p}revision.rev_page
139       AND
140        ${p}revision.rev_user = ${p}user.user_id
141       AND
142        ${p}revision.rev_text_id = ${p}text.old_id
143       AND
144        ${p}page.page_namespace <3
145      ORDER BY
146        ${p}page.page_namespace,
147        ${p}revision.rev_page,
148        ${p}revision.rev_id
149      ;
150    """).safe_substitute({
151        "p" : ""
152        })
153"""
154  Peruse these MediaWiki config file variables:
155 .  $wgDBprefix
156                                                                           """
157
158
159
160
161
162
163
164
165"""
166                  _
167                 (_)
168    ____   _____  _  ____     ____    ____   ___    ____   ____  _____  ____
169   |    \ (____ || ||  _ \   |  _ \  / ___) / _ \  / _  | / ___)(____ ||    \
170   | | | |/ ___ || || | | |  | |_| || |    | |_| |( (_| || |    / ___ || | | |
171   |_|_|_|\_____||_||_| |_|  |  __/ |_|     \___/  \___ ||_|    \_____||_|_|_|
172                             |_|                  (_____|                     
173
174                                                                           """
175
176def the_end():
177    db.close()
178    sys.exit()
179
180res = ()
181row = ()
182def set_row():
183    global res
184    global row
185    res = rs.fetch_row()
186    if res == ():
187        the_end()        ## !! This is the point at which we exit the program.
188    row = res[0]
189
190cur = ()
191ver = ()
192def reset_versioning():
193    global cur
194    global ver
195    cur = row[-1]
196    ver = 0
197
198db = _mysql.connect(
199     "localhost",
200     "wikiuser",
201     "wikipass",
202     "wikidb",
203     )
204"""
205  Peruse these MediaWiki config file variables:
206 .  $wgDBserver
207 .  $wgDBname
208 .  $wgDBuser
209 .  $wgDBpassword
210 .  $wgDBport
211                                                                           """
212
213db.query(query)
214rs = db.use_result()
215set_row()
216reset_versioning()
217while True:
218    while row[-1] == cur:
219        ver += 1
220        print row_writer(row[0:1] + (ver,) + row[1:])
221        set_row()
222    reset_versioning()
223