bgneal@290: """ bgneal@290: import_old_users.py - For importing users from SG101 1.0 as csv files. bgneal@290: """ bgneal@290: from __future__ import with_statement bgneal@290: import csv bgneal@290: import optparse bgneal@290: import re bgneal@290: import sys bgneal@290: from datetime import datetime bgneal@290: bgneal@290: import postmarkup bgneal@290: bgneal@290: from django.core.management.base import LabelCommand, CommandError bgneal@290: from django.contrib.auth.models import User bgneal@290: bgneal@290: import bio.models bgneal@290: from legacy.phpbb import unphpbb bgneal@290: from legacy.html2md import MarkdownWriter bgneal@290: bgneal@290: TIME_ZONES = { bgneal@290: '-5': 'US/Eastern', bgneal@290: '-6': 'US/Central', bgneal@290: '-7': 'US/Mountain', bgneal@290: '-8': 'US/Pacific', bgneal@290: } bgneal@290: USERNAME_RE = re.compile(r'^[\w.@+-]+$') bgneal@290: USERNAME_LEN = (1, 30) # min & max length values bgneal@290: bgneal@290: bgneal@290: def _valid_username(username): bgneal@290: """ bgneal@290: Return true if the username is valid. bgneal@290: """ bgneal@290: return (USERNAME_LEN[0] <= len(username) <= USERNAME_LEN[1] and bgneal@290: USERNAME_RE.match(username)) bgneal@290: bgneal@290: bgneal@290: def _break_name(name): bgneal@290: """ bgneal@290: Break name into a first and last name. bgneal@290: Return a 2-tuple of first_name, last_name. bgneal@290: """ bgneal@290: parts = name.split() bgneal@290: n = len(parts) bgneal@290: if n == 0: bgneal@290: t = '', '' bgneal@290: elif n == 1: bgneal@290: t = parts[0], '' bgneal@290: else: bgneal@290: t = ' '.join(parts[:-1]), parts[-1] bgneal@290: return t[0][:USERNAME_LEN[1]], t[1][:USERNAME_LEN[1]] bgneal@290: bgneal@290: bgneal@290: class Command(LabelCommand): bgneal@290: args = '' bgneal@290: help = 'Imports users from the old database in CSV format' bgneal@290: option_list = LabelCommand.option_list + ( bgneal@290: optparse.make_option("-s", "--super-user", bgneal@290: help="Make the user with this name a superuser"), bgneal@290: optparse.make_option("-a", "--anon-user", bgneal@290: help="Make the user with this name the anonymous user " bgneal@290: "[default: Anonymous]"), bgneal@290: optparse.make_option("-p", "--progress", action="store_true", bgneal@290: help="Output a . after every 20 users to show progress"), bgneal@290: ) bgneal@290: bb_parser = postmarkup.create(use_pygments=False, annotate_links=False) bgneal@290: md_writer = MarkdownWriter() bgneal@290: bgneal@290: def handle_label(self, filename, **options): bgneal@290: """ bgneal@290: Process each line in the CSV file given by filename by bgneal@290: creating a new user and profile. bgneal@290: bgneal@290: """ bgneal@290: self.superuser = options.get('super_user') bgneal@290: self.anonymous = options.get('anon_user') bgneal@290: if self.anonymous is None: bgneal@290: self.anonymous = 'Anonymous' bgneal@290: self.show_progress = options.get('progress') bgneal@290: bgneal@290: if self.superuser == self.anonymous: bgneal@290: raise CommandError("super-user name should not match anon-user") bgneal@290: bgneal@290: try: bgneal@290: with open(filename, "rb") as f: bgneal@290: self.reader = csv.DictReader(f) bgneal@290: num_rows = 0 bgneal@290: try: bgneal@290: for row in self.reader: bgneal@290: self.process_row(row) bgneal@290: num_rows += 1 bgneal@291: if self.show_progress and num_rows % 20 == 0: bgneal@290: sys.stdout.write('.') bgneal@290: sys.stdout.flush() bgneal@290: except csv.Error, e: bgneal@290: raise CommandError("CSV error: %s %s %s" % ( bgneal@290: filename, self.reader.line_num, e)) bgneal@290: bgneal@290: print bgneal@290: bgneal@290: except IOError: bgneal@290: raise CommandError("Could not open file: %s" % filename) bgneal@290: bgneal@290: def process_row(self, row): bgneal@290: """ bgneal@290: Process one row from the CSV file: create a user and user profile for bgneal@290: the row and save it in the database. bgneal@290: bgneal@290: """ bgneal@290: row = dict((k, v if v != 'NULL' else '') for k, v in row.iteritems()) bgneal@290: bgneal@290: if not _valid_username(row['username']): bgneal@290: print "Skipping import of %s; invalid username" % row['username'] bgneal@290: return bgneal@290: bgneal@290: n = User.objects.filter(username=row['username']).count() bgneal@290: if n > 0: bgneal@290: print "Skipping import of %s; user already exists" % row['username'] bgneal@290: return bgneal@290: bgneal@290: first_name, last_name = _break_name(row['name']) bgneal@290: is_superuser = self.superuser == row['username'] bgneal@290: is_anonymous = self.anonymous == row['username'] bgneal@290: bgneal@290: u = User(id=int(row['user_id']), bgneal@290: username=row['username'], bgneal@290: first_name=first_name, bgneal@290: last_name=last_name, bgneal@290: email=row['user_email'], bgneal@290: password=row['user_password'] if row['user_password'] else None, bgneal@290: is_staff=is_superuser, bgneal@290: is_active=True if not is_anonymous else False, bgneal@290: is_superuser=is_superuser, bgneal@290: last_login=datetime.fromtimestamp(int(row['user_lastvisit'])), bgneal@290: date_joined=datetime.strptime(row['user_regdate'], "%b %d, %Y")) bgneal@290: bgneal@290: if is_anonymous: bgneal@290: u.set_unusable_password() bgneal@290: bgneal@290: u.save() bgneal@290: bgneal@290: p = u.get_profile() bgneal@294: p.location = row['user_from'].decode('latin-1') bgneal@294: p.occupation = row['user_occ'].decode('latin-1') bgneal@294: p.interests = row['user_interests'].decode('latin-1') bgneal@290: p.profile_text = u'' bgneal@290: p.hide_email = True if row['user_viewemail'] != '1' else False bgneal@290: p.signature = self.to_markdown(row['user_sig']) if row['user_sig'] else u'' bgneal@290: p.time_zone = TIME_ZONES.get(row['user_timezone'], 'US/Pacific') bgneal@290: p.use_24_time = False bgneal@290: p.forum_post_count = int(row['user_posts']) bgneal@290: p.status = bio.models.STA_ACTIVE if p.forum_post_count > 10 else bio.models.STA_STRANGER bgneal@290: p.status_date = datetime.now() bgneal@290: p.update_date = p.status_date bgneal@290: p.save() bgneal@290: bgneal@290: def to_html(self, s): bgneal@290: return self.bb_parser.render_to_html(unphpbb(s), cosmetic_replace=False) bgneal@290: bgneal@290: def to_markdown(self, s): bgneal@290: self.md_writer.reset() bgneal@290: self.md_writer.feed(self.to_html(s)) bgneal@290: return self.md_writer.markdown()