pearson_transfer.py 7.43 KB
Newer Older
1
from optparse import make_option
2
import os
3
from stat import S_ISDIR
4

5
import boto
6 7 8
from dogapi import dog_http_api, dog_stats_api
import paramiko

9 10 11 12 13 14 15 16
from django.conf import settings
from django.core.management import call_command
from django.core.management.base import BaseCommand, CommandError

import django_startup


django_startup.autostartup()
17 18 19


class Command(BaseCommand):
Ashley Penney committed
20 21 22
    help = """
    This command handles the importing and exporting of student records for
    Pearson.  It uses some other Django commands to export and import the
23
    files and then uploads over SFTP to Pearson and stuffs the entry in an
Ashley Penney committed
24 25
    S3 bucket for archive purposes.

26
    Usage: ./manage.py pearson-transfer --mode [import|export|both]
Ashley Penney committed
27
    """
28

29 30
    option_list = BaseCommand.option_list + (
        make_option('--mode',
31
                    action='store',
32 33
                    dest='mode',
                    default='both',
34
                    choices=('import', 'export', 'both'),
35 36 37 38 39
                    help='mode is import, export, or both'),
    )

    def handle(self, **options):

40
        if not hasattr(settings, 'PEARSON'):
41 42
            raise CommandError('No PEARSON entries in auth/env.json.')

43 44
        # check settings needed for either import or export:
        for value in ['SFTP_HOSTNAME', 'SFTP_USERNAME', 'SFTP_PASSWORD', 'S3_BUCKET']:
45 46 47 48
            if value not in settings.PEARSON:
                raise CommandError('No entry in the PEARSON settings'
                                   '(env/auth.json) for {0}'.format(value))

49 50 51 52
        for value in ['AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY']:
            if not hasattr(settings, value):
                raise CommandError('No entry in the AWS settings'
                                   '(env/auth.json) for {0}'.format(value))
Calen Pennington committed
53

54 55
        # check additional required settings for import and export:
        if options['mode'] in ('export', 'both'):
Calen Pennington committed
56
            for value in ['LOCAL_EXPORT', 'SFTP_EXPORT']:
57 58 59 60 61 62 63
                if value not in settings.PEARSON:
                    raise CommandError('No entry in the PEARSON settings'
                                       '(env/auth.json) for {0}'.format(value))
            # make sure that the import directory exists or can be created:
            source_dir = settings.PEARSON['LOCAL_EXPORT']
            if not os.path.isdir(source_dir):
                os.makedirs(source_dir)
Calen Pennington committed
64

65
        if options['mode'] in ('import', 'both'):
Calen Pennington committed
66
            for value in ['LOCAL_IMPORT', 'SFTP_IMPORT']:
67 68 69 70 71 72 73
                if value not in settings.PEARSON:
                    raise CommandError('No entry in the PEARSON settings'
                                       '(env/auth.json) for {0}'.format(value))
            # make sure that the import directory exists or can be created:
            dest_dir = settings.PEARSON['LOCAL_IMPORT']
            if not os.path.isdir(dest_dir):
                os.makedirs(dest_dir)
74 75


76
        def sftp(files_from, files_to, mode, deleteAfterCopy=False):
77 78
            with dog_stats_api.timer('pearson.{0}'.format(mode), tags='sftp'):
                try:
79 80 81
                    t = paramiko.Transport((settings.PEARSON['SFTP_HOSTNAME'], 22))
                    t.connect(username=settings.PEARSON['SFTP_USERNAME'],
                              password=settings.PEARSON['SFTP_PASSWORD'])
82
                    sftp = paramiko.SFTPClient.from_transport(t)
Calen Pennington committed
83

84 85 86 87 88
                    if mode == 'export':
                        try:
                            sftp.chdir(files_to)
                        except IOError:
                            raise CommandError('SFTP destination path does not exist: {}'.format(files_to))
89
                        for filename in os.listdir(files_from):
90 91 92
                            sftp.put(files_from + '/' + filename, filename)
                            if deleteAfterCopy:
                                os.remove(os.path.join(files_from, filename))
93
                    else:
94 95 96 97 98
                        try:
                            sftp.chdir(files_from)
                        except IOError:
                            raise CommandError('SFTP source path does not exist: {}'.format(files_from))
                        for filename in sftp.listdir('.'):
Calen Pennington committed
99
                            # skip subdirectories
100 101 102 103 104
                            if not S_ISDIR(sftp.stat(filename).st_mode):
                                sftp.get(filename, files_to + '/' + filename)
                                # delete files from sftp server once they are successfully pulled off:
                                if deleteAfterCopy:
                                    sftp.remove(filename)
105 106
                except:
                    dog_http_api.event('pearson {0}'.format(mode),
107 108
                                       'sftp uploading failed',
                                       alert_type='error')
109
                    raise
110 111 112
                finally:
                    sftp.close()
                    t.close()
113

114
        def s3(files_from, bucket, mode, deleteAfterCopy=False):
115 116
            with dog_stats_api.timer('pearson.{0}'.format(mode), tags='s3'):
                try:
117
                    for filename in os.listdir(files_from):
118
                        source_file = os.path.join(files_from, filename)
Calen Pennington committed
119
                        # use mode as name of directory into which to write files
120 121
                        dest_file = os.path.join(mode, filename)
                        upload_file_to_s3(bucket, source_file, dest_file)
122 123
                        if deleteAfterCopy:
                            os.remove(files_from + '/' + filename)
124
                except:
125 126
                    dog_http_api.event('pearson {0}'.format(mode),
                                       's3 archiving failed')
127 128
                    raise

129
        def upload_file_to_s3(bucket, source_file, dest_file):
130 131 132
            """
            Upload file to S3
            """
133
            s3 = boto.connect_s3(settings.AWS_ACCESS_KEY_ID,
134
                                 settings.AWS_SECRET_ACCESS_KEY)
135 136 137
            from boto.s3.key import Key
            b = s3.get_bucket(bucket)
            k = Key(b)
138 139
            k.key = "{filename}".format(filename=dest_file)
            k.set_contents_from_filename(source_file)
140 141

        def export_pearson():
Calen Pennington committed
142
            options = {'dest-from-settings': True}
143 144 145
            call_command('pearson_export_cdd', **options)
            call_command('pearson_export_ead', **options)
            mode = 'export'
Calen Pennington committed
146
            sftp(settings.PEARSON['LOCAL_EXPORT'], settings.PEARSON['SFTP_EXPORT'], mode, deleteAfterCopy=False)
147 148 149 150 151
            s3(settings.PEARSON['LOCAL_EXPORT'], settings.PEARSON['S3_BUCKET'], mode, deleteAfterCopy=True)

        def import_pearson():
            mode = 'import'
            try:
Calen Pennington committed
152
                sftp(settings.PEARSON['SFTP_IMPORT'], settings.PEARSON['LOCAL_IMPORT'], mode, deleteAfterCopy=True)
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
                s3(settings.PEARSON['LOCAL_IMPORT'], settings.PEARSON['S3_BUCKET'], mode, deleteAfterCopy=False)
            except Exception as e:
                dog_http_api.event('Pearson Import failure', str(e))
                raise e
            else:
                for filename in os.listdir(settings.PEARSON['LOCAL_IMPORT']):
                    filepath = os.path.join(settings.PEARSON['LOCAL_IMPORT'], filename)
                    call_command('pearson_import_conf_zip', filepath)
                    os.remove(filepath)

        # actually do the work!
        if options['mode'] in ('export', 'both'):
            export_pearson()
        if options['mode'] in ('import', 'both'):
            import_pearson()