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 = '<filename filename ...>'
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()