1
import sqlalchemy as SA
2
from sqlalchemy import sql
3
from datetime import datetime
5
from optparse import make_option
7
from django.core.management.base import BaseCommand, CommandError
8
from django.contrib.auth.models import User
10
from pybb.models import Category, Forum, Topic, Post, Profile
11
from pybb.lib import phpserialize
13
class Command(BaseCommand):
15
option_list = BaseCommand.option_list + (
16
make_option('--user', help=u'Punbb DB username'),
17
make_option('--password', help=u'Punbb DB password'),
18
make_option('--host', default='localhost', help=u'Punbb DB host'),
19
make_option('--port', help=u'Punbb DB port'),
20
make_option('--encoding', default='cp1251', help=u'Punbb DB encoding'),
21
make_option('--mysql-encoding', help=u'Punbb DB encoding. I can\'t explain this yet'),
22
make_option('--engine', default='mysql', help=u'Punbb DB engine [postgres, mysql etc]'),
23
make_option('--prefix', default='punbb_', help=u'Punbb DB tables prefix'),
25
help = u'Imports Punbb database. Attention: old contents of pybb database will be removed'
28
def handle(self, *args, **options):
30
raise CommandError('Punbb database name required')
33
ENCODING = options['encoding']
34
MYSQL_ENCODING = options['mysql_encoding']
35
PREFIX = options['prefix']
37
uri = '%s://' % options['engine']
38
if options['user'] is not None:
39
uri += options['user']
40
if options['password'] is not None:
41
uri += ':%s' % options['password']
42
if options['host'] is not None:
43
uri += '@%s' % options['host']
44
if options['port'] is not None:
45
uri += ':%s' % options['port']
48
if options['engine'] == 'mysql' and not MYSQL_ENCODING:
49
uri += '?charset=%s' % ENCODING.replace('-', '')
51
engine = SA.create_engine(uri, convert_unicode=False)
52
conn = engine.connect()
57
users_table = SA.Table(PREFIX + 'users', meta, autoload=True)
58
cats_table = SA.Table(PREFIX + 'categories', meta, autoload=True)
59
forums_table = SA.Table(PREFIX + 'forums', meta, autoload=True)
60
topics_table = SA.Table(PREFIX + 'topics', meta, autoload=True)
61
posts_table = SA.Table(PREFIX + 'posts', meta, autoload=True)
62
groups_table = SA.Table(PREFIX + 'groups', meta, autoload=True)
63
config_table = SA.Table(PREFIX + 'config', meta, autoload=True)
64
subscriptions_table = SA.Table(PREFIX + 'subscriptions', meta, autoload=True)
69
if options['engine'] != 'mysql' or MYSQL_ENCODING:
70
return data.decode(ENCODING, 'replace')
75
print 'Searching admin group'
78
for count, row in enumerate(conn.execute(sql.select([groups_table]))):
79
if row['g_title'] == 'Administrators':
80
print 'Admin group was found'
81
ADMIN_GROUP = row['g_id']
83
if ADMIN_GROUP is None:
84
print 'Admin group was NOT FOUND'
87
print 'Importing users'
89
User.objects.all().delete()
92
for count, row in enumerate(conn.execute(sql.select([users_table]))):
93
joined = datetime.fromtimestamp(row['registered'])
94
last_login = datetime.fromtimestamp(row['last_visit'])
95
if len(row['password']) == 40:
96
hash = 'sha1$$' + row['password']
98
hash = 'md5$$' + row['password']
99
user = User(username=decode(row['username']),
101
first_name=decode((row['realname'] or '')[:30]),
103
last_login=last_login,
106
if row['group_id'] == ADMIN_GROUP:
107
print u'Admin was found: %s' % row['username']
108
user.is_superuser = True
113
except Exception, ex:
116
users[row['id']] = user
118
profile = user.pybb_profile
119
profile.jabber = decode(row['jabber'])
120
profile.icq = decode(row['icq'])
121
profile.yahoo = decode(row['yahoo'])
122
profile.msn = decode(row['msn'])
123
profile.aim = decode(row['aim'])
124
profile.location = decode(row['location'])
125
profile.signature = decode(row['signature'])
126
profile.show_signatures = bool(row['show_sig'])
127
profile.time_zone = row['timezone']
129
print 'Total: %d' % (count + 1)
130
print 'Imported: %d' % len(users)
133
print 'Importing categories'
135
Category.objects.all().delete()
138
for count, row in enumerate(conn.execute(sql.select([cats_table]))):
139
cat = Category(name=decode(row['cat_name']),
140
position=row['disp_position'])
142
cats[row['id']] = cat
144
print 'Total: %d' % (count + 1)
145
print 'Imported: %d' % len(cats)
148
print 'Importing forums'
151
Forum.objects.all().delete()
154
for count, row in enumerate(conn.execute(sql.select([forums_table]))):
156
updated = datetime.fromtimestamp(row['last_post'])
160
forum = Forum(name=decode(row['forum_name']),
161
position=row['disp_position'],
162
description=decode(row['forum_desc'] or ''),
163
category=cats[row['cat_id']])
165
forums[row['id']] = forum
167
forum._pybb_updated = updated
169
if row['moderators']:
170
for username in phpserialize.loads(row['moderators']).iterkeys():
171
user = User.objects.get(username=username)
172
forum.moderators.add(user)
173
moderators[user.id] = user
175
print 'Total: %d' % (count + 1)
176
print 'Imported: %d' % len(forums)
177
print 'Total number of moderators: %d' % len(moderators)
181
print 'Importing topics'
184
Topic.objects.all().delete()
187
for count, row in enumerate(conn.execute(sql.select([topics_table]))):
188
created = datetime.fromtimestamp(row['posted'])
189
updated = datetime.fromtimestamp(row['last_post'])
196
username = decode(row['poster'])
197
#testuser = users.values()[0]
198
for id, testuser in users.iteritems():
199
if testuser.username == username:
203
topic = Topic(name=decode(row['subject']),
204
forum=forums[row['forum_id']],
205
views=row['num_views'],
206
sticky=bool(row['sticky']),
207
closed=bool(row['closed']),
210
topic._pybb_updated = updated
211
topic._pybb_created = created
212
topic._pybb_punbb_id = row['id']
213
topic._pybb_posts = 0
214
#print topic._pybb_updated
215
topics[row['id']] = topic
217
print 'Total: %d' % (count + 1)
218
print 'Imported: %d' % len(topics)
219
print 'Moved: %d' % moved_count
223
print 'Importing posts'
225
Post.objects.all().delete()
229
for count, row in enumerate(conn.execute(sql.select([posts_table]))):
230
created = datetime.fromtimestamp(row['posted'])
231
updated = row['edited'] and datetime.fromtimestamp(row['edited']) or None
233
if not row['poster_id'] in users:
234
print 'post #%d, poster_id #%d does not exist' % (row['id'], row['poster_id'])
237
post = Post(topic=topics[row['topic_id']],
241
user=users[row['poster_id']],
242
user_ip=row['poster_ip'],
243
body=decode(row['message']))
247
topics[row['topic_id']]._pybb_posts += 1
250
## postmarkups feels bad on some posts :-/
253
#except Exception, ex:
255
#print decode(row['message'])
259
#topics[row['topic_id']]._pybb_posts += 1
260
##posts[row['id']] = topic
262
print 'Total: %d' % (count + 1)
263
print 'Imported: %d' % imported
267
print 'Importing subscriptions'
270
for count, row in enumerate(conn.execute(sql.select([subscriptions_table]))):
271
user = users[row['user_id']]
272
topic = topics[row['topic_id']]
273
topic.subscribers.add(user)
275
print 'Imported: %d' % count
278
print 'Restoring topics updated and created values'
279
for topic in topics.itervalues():
280
topic.updated = topic._pybb_updated
281
topic.created = topic._pybb_created
286
print 'Restoring forums updated and created values'
287
for forum in forums.itervalues():
288
forum.updated = forum._pybb_updated
293
print 'Importing config'
294
for row in conn.execute(sql.select([config_table])):
295
if row['conf_name'] == 'o_announcement_message':
296
value = decode(row['conf_value'])
298
open('pybb_announcement.txt', 'w').write(value.encode('utf-8'))
299
print 'Not empty announcement found and saved to pybb_announcement.txt'
300
print 'If you need announcement write PYBB_NOTICE = " ... text ... " to settings file'