def update_discovery_url(self, discovery_url):
        account_id = settings.RESTCLIENTS_CANVAS_ACCOUNT_ID

        Live.pool = None
        auth = Accounts().get_auth_settings(account_id)

        if auth.auth_discovery_url != discovery_url:
            auth.auth_discovery_url = discovery_url
            Accounts().update_auth_settings(account_id, auth)
예제 #2
0
    def test_account(self):
        with self.settings(
                RESTCLIENTS_CANVAS_DAO_CLASS='restclients.dao_implementation.canvas.File'):
            canvas = Canvas()

            account = canvas.get_account_by_sis_id('uwcourse:seattle:cse:cse')
            self.assertEquals(account.account_id, 696969)
            self.assertEquals(account.name, "Computer Science & Engineering", "Has proper name")
            self.assertEquals(account.sis_account_id, 'uwcourse:seattle:cse:cse')
            self.assertEquals(account.parent_account_id, 987654)
예제 #3
0
    def test_all_sub_accounts(self):
        with self.settings(
                RESTCLIENTS_CANVAS_DAO_CLASS='restclients.dao_implementation.canvas.File'):
            canvas = Canvas()

            accounts = canvas.get_all_sub_accounts_by_sis_id('uwcourse:seattle:cse')

            account = accounts[1]

            self.assertEquals(len(accounts), 3, "Too few accounts")
            self.assertEquals(account.name, "Comp Sci & Engr Accelerated Masters Prg", "Has proper name")
            self.assertEquals(account.sis_account_id, 'uwcourse:seattle:cse:csem')
            self.assertEquals(account.parent_account_id, 54321)
    def handle(self, *args, **options):
        self._canvas = Canvas()
        self._tools = ExternalTools()
        self._accounts = Accounts()
        self._courses = Courses()
        self._options = options

        csv.register_dialect("unix_newline", lineterminator="\n")
        self._writer = csv.writer(sys.stdout, dialect="unix_newline")

        self._headers = ['tool_name', 'tool_id', 'tool_type', 'account_name', 'account_id']

        if self._options['courses']:
            self._headers.append('course_name')
            self._headers.append('course_id')
            self._headers.append('term')

        if options['sessionless']:
            self._headers.append('sessionless url')

        accounter = self._accounts.get_account if re.match(r'^\d+$', options['account_id']) \
                        else self._accounts.get_account_by_sis_id
        try:
            self.report_external_tools(accounter(options['account_id']))

        except DataFailureException as err:
            if err.status == 404:
                print >> sys.stderr, 'Unknown Sub-Account \"%s\"' % (options['account_id'])
    def handle(self, *args, **options):
        self._accounts = Accounts()
        self._tools = ExternalTools()

        account = self._accounts.get_account(
            getattr(settings, 'RESTCLIENTS_CANVAS_ACCOUNT_ID'))

        self.update_tools_in_account(account)
        self.update_job()
class Command(SISProvisionerCommand):
    help = "Sync LTI Manager app with actual external tools in Canvas"

    def handle(self, *args, **options):
        self._accounts = Accounts()
        self._tools = ExternalTools()

        account = self._accounts.get_account(
            getattr(settings, 'RESTCLIENTS_CANVAS_ACCOUNT_ID'))

        self.update_tools_in_account(account)
        self.update_job()

    def update_tools_in_account(self, account):
        for tool in self._tools.get_external_tools_in_account(
                account.account_id):
            self.update_model(account, tool)

        for subaccount in self._accounts.get_sub_accounts(account.account_id):
            self.update_tools_in_account(subaccount)

    def update_model(self, account, config):
        canvas_id = config['id']
        try:
            external_tool = ExternalTool.objects.get(canvas_id=canvas_id)
        except ExternalTool.DoesNotExist:
            external_tool = ExternalTool(canvas_id=canvas_id)

        try:
            et_account = ExternalToolAccount.objects.get(
                account_id=account.account_id)
        except ExternalToolAccount.DoesNotExist:
            et_account = ExternalToolAccount(account_id=account.account_id)
            et_account.sis_account_id = account.sis_account_id
            et_account.name = account.name
            et_account.save()

        external_tool.account = et_account
        external_tool.config = json.dumps(config)
        external_tool.changed_by = 'auto'
        external_tool.changed_date = datetime.utcnow().replace(tzinfo=utc)
        external_tool.save()
예제 #7
0
 def __init__(self):
     self._accounts = CanvasAccounts(per_page=50)
     self._analytics = CanvasAnalytics()
     self._reports = CanvasReports()
예제 #8
0
class ReportBuilder():
    def __init__(self):
        self._accounts = CanvasAccounts(per_page=50)
        self._analytics = CanvasAnalytics()
        self._reports = CanvasReports()

    def build_subaccount_activity_report(self, root_account_id, sis_term_id):
        report = Report(report_type=Report.SUBACCOUNT_ACTIVITY,
                        started_date=datetime.utcnow().replace(tzinfo=utc))
        report.save()

        accounts = []
        account_courses = {}

        root_account = self._accounts.get_account_by_sis_id(root_account_id)
        accounts.append(root_account)
        accounts.extend(self._accounts.get_all_sub_accounts_by_sis_id(root_account_id))

        for account in accounts:
            sis_account_id = account.sis_account_id
            if sis_account_id is None:
                continue

            account_courses[sis_account_id] = {
                "courses": 0,
                "active_courses": 0,
                "ind_study_courses": 0,
                "active_ind_study_courses": 0,
            }

            activity = SubaccountActivity(report=report,
                                          term_id=sis_term_id,
                                          subaccount_id=sis_account_id,
                                          subaccount_name=account.name)

            data = self._analytics.get_statistics_by_account(sis_account_id,
                                                             sis_term_id)

            for key, val in data.items():
                if key == "courses":
                    continue

                setattr(activity, key, val)

            data = self._analytics.get_activity_by_account(sis_account_id,
                                                           sis_term_id)

            for item in data["by_category"]:
                setattr(activity, "%s_views" % item["category"], item["views"])

            activity.save()

        # Generate course totals
        term = self._reports.get_term_by_sis_id(sis_term_id)
        course_prov_report = self._reports.create_course_provisioning_report(
            root_account.account_id, term.term_id,
            params={"include_deleted": True})

        course_data = self._reports.get_report_data(course_prov_report)
        header = course_data.pop(0)
        for row in csv.reader(course_data):
            if not len(row):
                continue

            sis_course_id = row[1]
            sis_account_id = row[5]
            if (sis_course_id is None or sis_account_id is None or
                sis_account_id not in account_courses):
                continue

            status = row[8]
            ind_study = True if len(sis_course_id.split("-")) == 6 else False
            is_active = True if status == "active" else False
            for sis_id in account_courses:
                if sis_account_id.find(sis_id) == 0:
                    account_courses[sis_id]["courses"] += 1
                    if is_active:
                        account_courses[sis_id]["active_courses"] += 1
                    if ind_study:
                        account_courses[sis_id]["ind_study_courses"] += 1
                        if is_active:
                            account_courses[sis_id]["active_ind_study_courses"] += 1

        # Save course totals
        for sis_account_id in account_courses:
            try:
                totals = account_courses[sis_account_id]
                activity = SubaccountActivity.objects.get(report=report,
                    term_id=sis_term_id, subaccount_id=sis_account_id)
                activity.courses = totals["courses"]
                activity.active_courses = totals["active_courses"]
                activity.ind_study_courses = totals["ind_study_courses"]
                activity.active_ind_study_courses = totals["active_ind_study_courses"]
                activity.save()
            except SubaccountActivity.DoesNotExist:
                continue

        report.finished_date = datetime.utcnow().replace(tzinfo=utc)
        report.save()
    def handle(self, *args, **options):
        if options['print'] or options['dry_run']:
            print "section_id,canvas_enrollments,sws_enrollments,delta"

        canvas_courses = CanvasCourses(per_page=50)
        canvas_sections = CanvasSections(per_page=50)
        canvas_accounts = CanvasAccounts(per_page=50)
        for account in canvas_accounts.get_all_sub_accounts_by_sis_id(options['root_account']):

            if options['print']:
                print '# Account: "%s" (%s, %s)' % (account.name,
                                                    account.sis_account_id,
                                                    account.account_id)

            if (account.sis_account_id is not None
                and re.match(r'^(([^:]+:){4}|curriculum-).*$', str(account.sis_account_id))):

                n_courses = 0
                n_sections = 0
                n_bad_sections = 0

                if options['all_courses']:
                    courses = canvas_courses.get_courses_in_account_by_sis_id(account.sis_account_id)
                else:
                    courses = canvas_courses.get_published_courses_in_account_by_sis_id(account.sis_account_id)

                for course in courses:
                    if (course.sis_course_id is not None
                        and re.match('^%s-' % options['term'], course.sis_course_id)
                        and not self._is_independent_study(course.sis_course_id)):
                        n_courses += 1
                        sections = canvas_sections.get_sections_with_students_in_course_by_sis_id(course.sis_course_id) 
                        for section in sections:
                            if section.sis_section_id is not None:
                                section_id = section.sis_section_id
                                n_sections += 1

                                try:
                                    s = self.get_section_by_id(section_id)
                                except DataFailureException, err:
                                    print '# BAD SECTION: %s' % err
                                    continue

                                enrollments = (s.current_enrollment + s.auditors)
                                delta = len(section.students) - enrollments
                                if delta >= options['threshold']:
                                    n_bad_sections += 1
                                    if options['print'] or options['dry_run']:
                                        print "%s,%s,%s,%s" % (section_id, len(section.students), enrollments, delta)

                                    if not options['dry_run']:
                                        try:
                                            section_model_id = re.sub(r'--$', '', section_id)
                                            section_model = Course.objects.get(course_id=section_model_id)
                                        except Course.DoesNotExist:
                                            section_model = Course(course_id=section_model_id)

                                        if not section_model.queue_id and section_model.priority < PRIORITY_HIGH:
                                            section_model.priority = PRIORITY_HIGH
                                            section_model.save()

                if options['print']:
                    if n_courses and n_sections and n_bad_sections:
                        print('# %s of %s (%.3s%%) sections in %s courses for %s'
                              % (n_bad_sections, n_sections,
                                 ((n_bad_sections/float(n_sections)) * 100),
                                 n_courses, options['term']))
    def handle(self, *args, **options):
        self._options = options
        self._verbosity = int(options.get('verbosity'))
        self._canvas_admins = CanvasAdmins()
        self._canvas_accounts = CanvasAccounts()
        self._pws = PWS()
        self._accounts = Accounts()

        self._canvas_role_mapping = {}
        for role in settings.ASTRA_ROLE_MAPPING:
            self._canvas_role_mapping[settings.ASTRA_ROLE_MAPPING[role]] = role
        if not self._options.get('commit'):
            logger.info('NOT commiting ASTRA admins.  Only logging what would change.')

        # Compare table to Canvas reality
        try:
            if self._verbosity > 0:
                logger.info('building admin table from ASTRA...')

            ASTRA({ 'verbosity': self._verbosity }).load_all_admins({ 'override': self._options.get('override_id') })

            if self._verbosity > 0:
                logger.info('building sub account list...')

            accounts = []
            root = options.get('root_account')
            root_account_id = root if self._canvas_accounts.valid_canvas_id(root) else self._canvas_accounts.sis_account_id(root)

            if self._verbosity > 1:
                logger.info('get account for id: %s...' % root_account_id)

            root_canvas_admins = []

            account = self.get_account(root_account_id)
            sub_accounts = self.get_all_sub_accounts(root_account_id)

            accounts.append(account)
            accounts.extend(sub_accounts)
            for account in accounts:
                canvas_id = account.account_id
                self._shown_canvas_id = canvas_id
                account_model = self._accounts.load_account(account)

                # reconcile admins against Admin table
                if account_model.is_sdb():
                    self._shown_id = account.sis_account_id
                else:
                    self._shown_id = 'canvas_%s' % canvas_id

                astra_admins = Admin.objects.filter(canvas_id=canvas_id)
                canvas_admins = self.get_admins(canvas_id)

                if account_model.is_root():
                    root_canvas_admins = canvas_admins

                if self._verbosity > 0 and (len(astra_admins) or len(canvas_admins)):
                    logger.info('%d ASTRA and %s Canvas admins in account %s (%s)' %
                                   (len(astra_admins), len(canvas_admins),
                                    account.name, account.account_id))

                for astra_admin in astra_admins:
                    user_role = {
                        'canvas_account_id': account.account_id,
                        'role': settings.ASTRA_ROLE_MAPPING[astra_admin.role],
                        'net_id': astra_admin.net_id
                    }

                    canvas_admin = None

                    for admin in canvas_admins:
                        if (user_role['net_id'] == admin.user.login_id and
                            user_role['role'] == admin.role):
                            canvas_admin = admin
                            canvas_admin.in_astra = True
                            break

                    if astra_admin.is_deleted:
                        if canvas_admin:
                            user_role['user_id'] = canvas_admin.user.user_id
                            self._remove_admin(**user_role)
                            try:
                                ancillary = settings.ANCILLARY_CANVAS_ROLES[astra_admin.role]
                                user_role['role'] = ancillary['canvas_role']
                                if ancillary['account'] == 'common':
                                    self._remove_admin(**user_role)
                                elif len(Admin.objects.filter(net_id=canvas_admin.user.login_id,
                                                              role=astra_admin.role,
                                                              is_deleted__isnull=True)) == 0:
                                    user_role['canvas_account_id'] = settings.RESTCLIENTS_CANVAS_ACCOUNT_ID
                                    self._remove_admin(**user_role)
                            except Admin.DoesNotExist:
                                pass
                            except KeyError:
                                pass

                            astra_admin.deleted_date = datetime.datetime.utcnow().replace(tzinfo=utc)
                            astra_admin.save()
                    elif not canvas_admin:
                        user_role['user_id'] = self._canvas_admins.sis_user_id(astra_admin.reg_id)
                        self._add_admin(**user_role)

                        if astra_admin.role in settings.ANCILLARY_CANVAS_ROLES:
                            ancillary = settings.ANCILLARY_CANVAS_ROLES[astra_admin.role]
                            user_role['role'] = ancillary['canvas_role']
                            if ancillary['account'] == 'root':
                                user_role['canvas_account_id'] = settings.RESTCLIENTS_CANVAS_ACCOUNT_ID

                            self._add_admin(**user_role)

                        astra_admin.provisioned_date = datetime.datetime.utcnow().replace(tzinfo=utc)
                        astra_admin.save()
                    else:
                        if self._verbosity > 0:
                            logger.info('  %s already in Canvas as %s' % (astra_admin.net_id, astra_admin.role))

                        if astra_admin.role in settings.ANCILLARY_CANVAS_ROLES:
                            ancillary = settings.ANCILLARY_CANVAS_ROLES[astra_admin.role]
                            user_role['role'] = ancillary['canvas_role']
                            add_ancillary = True
                            if ancillary['account'] == 'root':
                                user_role['canvas_account_id'] = settings.RESTCLIENTS_CANVAS_ACCOUNT_ID
                                for root_canvas_admin in root_canvas_admins:
                                    if (astra_admin.net_id == root_canvas_admin.user.login_id
                                        and root_canvas_admin.role == user_role['role']):
                                        add_ancillary = False
                                        break
                            elif  ancillary['account'] == 'common':
                                for canvas_admin in canvas_admins:
                                    if (astra_admin.net_id == canvas_admin.user.login_id
                                        and canvas_admin.role == user_role['role']):
                                        add_ancillary = False
                                        break

                            if add_ancillary:
                                user_role['user_id'] = self._canvas_admins.sis_user_id(astra_admin.reg_id)
                                self._add_admin(**user_role)
                            elif self._verbosity > 0:
                                logger.info('  %s ancillary role %s already in %s'
                                               % (user_role['net_id'],
                                                  user_role['role'],
                                                  user_role['canvas_account_id']))

                # remove unrecognized admins
                for canvas_admin in canvas_admins:
                    if (self._options['remove_non_astra']
                        and not (hasattr(canvas_admin, 'in_astra')
                                 and canvas_admin.in_astra)):
                        if self._is_ancillary(account, canvas_admin.role,
                                              canvas_admin.user.login_id, account_model.is_root()):
                            logger.info('preserving ancillary role: %s as %s'
                                           % (canvas_admin.user.login_id,
                                              canvas_admin.role))

                            continue

                        try:
                            self._remove_admin(canvas_account_id=canvas_id,
                                               net_id=canvas_admin.user.login_id,
                                               user_id=canvas_admin.user.user_id,
                                               role=canvas_admin.role)
                        except DataFailureException as err:
                            if err.args[1] == 404:
                                logger.info('Ancillary role NOT in Canvas: %s as %s'
                                               % (canvas_admin.user.login_id,
                                                  canvas_admin.role))
                            else:
                                raise

            if self._verbosity > 0:
                logger.info('Done.')

        except ASTRAException as err:
            logger.error('ASTRA ERROR: %s\nAborting.' % err)

        except DataFailureException as err:
            if err.status in self.retry_status_codes:
                logger.error('RETRIES EXCEEDED: %s\nAborting.' % err)
                Admin.objects.dequeue()
            else:
                logger.error('REST ERROR: %s\nAborting.' % err)

        self.update_job()
class Command(SISProvisionerCommand):
    help = "Reconcile ASTRA / Canvas Administrators"

    option_list = BaseCommand.option_list + (
        make_option('-r', '--root-account', action='store', dest='root_account', type="string",
                    default=settings.RESTCLIENTS_CANVAS_ACCOUNT_ID,
                    help='reconcile sections at and below root account (default: %s)'
                    % settings.RESTCLIENTS_CANVAS_ACCOUNT_ID),
        make_option('-c', '--commit', action='store_true', dest='commit',
                    default=False, help='update Canvas with ASTRA admins and roles'),
        make_option('-a', '--astra-is-authoritative', action='store_true', dest='remove_non_astra',
                    default=False, help='Remove Canvas admins not found in ASTRA'),
        make_option('-o', '--override', action='store', dest='override_id',
                    default=0, help='Override blocked Canvas admins import of given process id'),
        )

    max_retry = 5
    sleep_interval = 5
    retry_status_codes = [408, 500, 502, 503, 504]

    def handle(self, *args, **options):
        self._options = options
        self._verbosity = int(options.get('verbosity'))
        self._canvas_admins = CanvasAdmins()
        self._canvas_accounts = CanvasAccounts()
        self._pws = PWS()
        self._accounts = Accounts()

        self._canvas_role_mapping = {}
        for role in settings.ASTRA_ROLE_MAPPING:
            self._canvas_role_mapping[settings.ASTRA_ROLE_MAPPING[role]] = role
        if not self._options.get('commit'):
            logger.info('NOT commiting ASTRA admins.  Only logging what would change.')

        # Compare table to Canvas reality
        try:
            if self._verbosity > 0:
                logger.info('building admin table from ASTRA...')

            ASTRA({ 'verbosity': self._verbosity }).load_all_admins({ 'override': self._options.get('override_id') })

            if self._verbosity > 0:
                logger.info('building sub account list...')

            accounts = []
            root = options.get('root_account')
            root_account_id = root if self._canvas_accounts.valid_canvas_id(root) else self._canvas_accounts.sis_account_id(root)

            if self._verbosity > 1:
                logger.info('get account for id: %s...' % root_account_id)

            root_canvas_admins = []

            account = self.get_account(root_account_id)
            sub_accounts = self.get_all_sub_accounts(root_account_id)

            accounts.append(account)
            accounts.extend(sub_accounts)
            for account in accounts:
                canvas_id = account.account_id
                self._shown_canvas_id = canvas_id
                account_model = self._accounts.load_account(account)

                # reconcile admins against Admin table
                if account_model.is_sdb():
                    self._shown_id = account.sis_account_id
                else:
                    self._shown_id = 'canvas_%s' % canvas_id

                astra_admins = Admin.objects.filter(canvas_id=canvas_id)
                canvas_admins = self.get_admins(canvas_id)

                if account_model.is_root():
                    root_canvas_admins = canvas_admins

                if self._verbosity > 0 and (len(astra_admins) or len(canvas_admins)):
                    logger.info('%d ASTRA and %s Canvas admins in account %s (%s)' %
                                   (len(astra_admins), len(canvas_admins),
                                    account.name, account.account_id))

                for astra_admin in astra_admins:
                    user_role = {
                        'canvas_account_id': account.account_id,
                        'role': settings.ASTRA_ROLE_MAPPING[astra_admin.role],
                        'net_id': astra_admin.net_id
                    }

                    canvas_admin = None

                    for admin in canvas_admins:
                        if (user_role['net_id'] == admin.user.login_id and
                            user_role['role'] == admin.role):
                            canvas_admin = admin
                            canvas_admin.in_astra = True
                            break

                    if astra_admin.is_deleted:
                        if canvas_admin:
                            user_role['user_id'] = canvas_admin.user.user_id
                            self._remove_admin(**user_role)
                            try:
                                ancillary = settings.ANCILLARY_CANVAS_ROLES[astra_admin.role]
                                user_role['role'] = ancillary['canvas_role']
                                if ancillary['account'] == 'common':
                                    self._remove_admin(**user_role)
                                elif len(Admin.objects.filter(net_id=canvas_admin.user.login_id,
                                                              role=astra_admin.role,
                                                              is_deleted__isnull=True)) == 0:
                                    user_role['canvas_account_id'] = settings.RESTCLIENTS_CANVAS_ACCOUNT_ID
                                    self._remove_admin(**user_role)
                            except Admin.DoesNotExist:
                                pass
                            except KeyError:
                                pass

                            astra_admin.deleted_date = datetime.datetime.utcnow().replace(tzinfo=utc)
                            astra_admin.save()
                    elif not canvas_admin:
                        user_role['user_id'] = self._canvas_admins.sis_user_id(astra_admin.reg_id)
                        self._add_admin(**user_role)

                        if astra_admin.role in settings.ANCILLARY_CANVAS_ROLES:
                            ancillary = settings.ANCILLARY_CANVAS_ROLES[astra_admin.role]
                            user_role['role'] = ancillary['canvas_role']
                            if ancillary['account'] == 'root':
                                user_role['canvas_account_id'] = settings.RESTCLIENTS_CANVAS_ACCOUNT_ID

                            self._add_admin(**user_role)

                        astra_admin.provisioned_date = datetime.datetime.utcnow().replace(tzinfo=utc)
                        astra_admin.save()
                    else:
                        if self._verbosity > 0:
                            logger.info('  %s already in Canvas as %s' % (astra_admin.net_id, astra_admin.role))

                        if astra_admin.role in settings.ANCILLARY_CANVAS_ROLES:
                            ancillary = settings.ANCILLARY_CANVAS_ROLES[astra_admin.role]
                            user_role['role'] = ancillary['canvas_role']
                            add_ancillary = True
                            if ancillary['account'] == 'root':
                                user_role['canvas_account_id'] = settings.RESTCLIENTS_CANVAS_ACCOUNT_ID
                                for root_canvas_admin in root_canvas_admins:
                                    if (astra_admin.net_id == root_canvas_admin.user.login_id
                                        and root_canvas_admin.role == user_role['role']):
                                        add_ancillary = False
                                        break
                            elif  ancillary['account'] == 'common':
                                for canvas_admin in canvas_admins:
                                    if (astra_admin.net_id == canvas_admin.user.login_id
                                        and canvas_admin.role == user_role['role']):
                                        add_ancillary = False
                                        break

                            if add_ancillary:
                                user_role['user_id'] = self._canvas_admins.sis_user_id(astra_admin.reg_id)
                                self._add_admin(**user_role)
                            elif self._verbosity > 0:
                                logger.info('  %s ancillary role %s already in %s'
                                               % (user_role['net_id'],
                                                  user_role['role'],
                                                  user_role['canvas_account_id']))

                # remove unrecognized admins
                for canvas_admin in canvas_admins:
                    if (self._options['remove_non_astra']
                        and not (hasattr(canvas_admin, 'in_astra')
                                 and canvas_admin.in_astra)):
                        if self._is_ancillary(account, canvas_admin.role,
                                              canvas_admin.user.login_id, account_model.is_root()):
                            logger.info('preserving ancillary role: %s as %s'
                                           % (canvas_admin.user.login_id,
                                              canvas_admin.role))

                            continue

                        try:
                            self._remove_admin(canvas_account_id=canvas_id,
                                               net_id=canvas_admin.user.login_id,
                                               user_id=canvas_admin.user.user_id,
                                               role=canvas_admin.role)
                        except DataFailureException as err:
                            if err.args[1] == 404:
                                logger.info('Ancillary role NOT in Canvas: %s as %s'
                                               % (canvas_admin.user.login_id,
                                                  canvas_admin.role))
                            else:
                                raise

            if self._verbosity > 0:
                logger.info('Done.')

        except ASTRAException as err:
            logger.error('ASTRA ERROR: %s\nAborting.' % err)

        except DataFailureException as err:
            if err.status in self.retry_status_codes:
                logger.error('RETRIES EXCEEDED: %s\nAborting.' % err)
                Admin.objects.dequeue()
            else:
                logger.error('REST ERROR: %s\nAborting.' % err)

        self.update_job()

    @retry(DataFailureException, status_codes=retry_status_codes,
           tries=max_retry, delay=sleep_interval, logger=logger)
    def get_admins(self, canvas_id):
        return self._canvas_admins.get_admins(canvas_id)

    @retry(DataFailureException, status_codes=retry_status_codes,
           tries=max_retry, delay=sleep_interval, logger=logger)
    def get_account(self, root_account_id):
        return self._canvas_accounts.get_account(root_account_id)

    @retry(DataFailureException, status_codes=retry_status_codes,
           tries=max_retry, delay=sleep_interval, logger=logger)
    def get_all_sub_accounts(self, root_account_id):
        return self._canvas_accounts.get_all_sub_accounts(root_account_id)

    def _is_ancillary(self, account, canvas_role, canvas_login_id, is_root):
        ancillary = settings.ANCILLARY_CANVAS_ROLES
        for astra_role in ancillary.keys():
            if ancillary[astra_role]['canvas_role'] == canvas_role:
                if ancillary[astra_role]['account'] == 'root' and is_root:
                    if len(Admin.objects.filter(net_id=canvas_login_id,
                                                role=astra_role,
                                                is_deleted__isnull=True)) > 0:
                        return True
                elif ancillary[astra_role]['account'] == 'common':
                    try:
                        Admin.objects.get(account_id=account.account_id,
                                          net_id=canvas_login_id,
                                          role=astra_role,
                                          is_deleted__isnull=True)
                        return True
                    except Admin.DoesNotExist:
                        pass

        return False

    def _add_admin(self, **kwargs):
        prefix = 'WOULD ADD'

        if self._options.get('commit'):
            prefix = 'ADDING'
            try:
                self._canvas_admins.create_admin(
                    kwargs['canvas_account_id'],
                    kwargs['user_id'],
                    kwargs['role'])
            except DataFailureException as err:
                if err.status == 404:  # Non-personal regid?
                    prefix = "ADD FAIL: MISSING USER %s" % (kwargs['user_id'])
                else:
                    raise

        self._record('  %s: %s as %s' % (
            prefix, kwargs['net_id'], kwargs['role']))

    def _remove_admin(self, **kwargs):
        action = 'WOULD DELETE'

        if kwargs['net_id'] in getattr(settings, 'CANVAS_SERVICE_USER_ACCOUNTS', []):
            action = 'SERVICE USER'
        elif self._options.get('commit'):
            action = 'DELETING'
            try:
                self._canvas_admins.delete_admin(kwargs['canvas_account_id'],
                                                 kwargs['user_id'],
                                                 kwargs['role'])
            except DataFailureException as err:
                if err.status == 404:  # Non-personal regid?
                    action = "ALREADY DELETED"
                else:
                    raise

        self._record('  %s: %s (%s) as %s' % (action, kwargs['net_id'],
                                              kwargs['user_id'], kwargs['role']))

    def _record(self, msg):
        if self._shown_id:
            logger.info('reconciling %s (%s)' % (self._shown_id, self._shown_canvas_id))
            self._shown_id = None

        logger.info(msg)
class Command(BaseCommand):
    help = "Report externals tools in account"

    option_list = BaseCommand.option_list + (
        make_option('-a', '--account', action='store', dest='account_id', type="string",
                    default=default_account,
                    help='show external tools in account by id or sis_id (default: %s)' % default_account),
        make_option('-r', '--recurse', action='store_true', dest='recurse',
                    default=False, help='recurse through subaccounts'),
        make_option('-c', '--courses', action='store_true', dest='courses',
                    default=False, help='include account courses in report'),
        make_option('-t', '--term', action='store', dest='term', type="string",
                    default='', help='include only courses in given term'),
        make_option('-s', '--sessionless-url', action='store_true', dest='sessionless',
                    default=False, help='show sessionless url with each external tool'),
    )

    def handle(self, *args, **options):
        self._canvas = Canvas()
        self._tools = ExternalTools()
        self._accounts = Accounts()
        self._courses = Courses()
        self._options = options

        csv.register_dialect("unix_newline", lineterminator="\n")
        self._writer = csv.writer(sys.stdout, dialect="unix_newline")

        self._headers = ['tool_name', 'tool_id', 'tool_type', 'account_name', 'account_id']

        if self._options['courses']:
            self._headers.append('course_name')
            self._headers.append('course_id')
            self._headers.append('term')

        if options['sessionless']:
            self._headers.append('sessionless url')

        accounter = self._accounts.get_account if re.match(r'^\d+$', options['account_id']) \
                        else self._accounts.get_account_by_sis_id
        try:
            self.report_external_tools(accounter(options['account_id']))

        except DataFailureException as err:
            if err.status == 404:
                print >> sys.stderr, 'Unknown Sub-Account \"%s\"' % (options['account_id'])

    def report_external_tools(self, account):
        tools = self._tools.get_external_tools_in_account(account.account_id)
        self._print_tools(tools, account)

        if self._options['courses']:
            params = {
                "by_subaccounts":[account.account_id],
                "include": ["term"]
            }

            if self._options['term']:
                params['enrollment_term_id'] = self._canvas.get_term_by_sis_id(self._options['term']).term_id

            courses = self._courses.get_published_courses_in_account(account.account_id,
                                                                     params=params)
            for course in courses:
                tools = self._tools.get_external_tools_in_course(course.course_id)
                self._print_tools(tools, account, course)
                
        if self._options['recurse']:
            subaccounts = self._accounts.get_sub_accounts(account.account_id)
            for account in subaccounts:
                self.report_external_tools(account)

    def _print_tools(self, tools, account, course=None):
        if len(tools):
            if self._headers:
                self._writer.writerow(self._headers)
                self._headers = None
            
            for tool in tools:
                tool_types = []
                for tt in ['account', 'course', 'user']:
                    if tool.get("%s_navigation" % tt):
                        tool_types.append(tt)

                tool_type = ' & '.join(tool_types)
                line = [tool['name'], tool['id'], tool_type, account.name, account.account_id]

                if self._options['courses']:
                    if course:
                        line.extend([course.name, course.course_id, course.term.name if course.term else ''])
                    else:
                        line.extend(['','',''])

                if self._options['sessionless']:
                    try:
                        sessionless = self._tools.get_sessionless_launch_url_from_account(tool['id'], account.account_id)
                        line.append(sessionless['url'])
                    except DataFailureException as ex:
                        line.append('')

                self._writer.writerow(line)
    def handle(self, *args, **options):
        if len(args) == 2:
            subaccount_id = args[0]
            sis_term_id = args[1]
        else:
            raise CommandError("find_active_instructors <subaccount_id> <term_id>")

        accounts = Accounts()
        reports = Reports()
        pws = PWS()

        account = accounts.get_account(subaccount_id)
        term = reports.get_term_by_sis_id(sis_term_id)

        enrollment_report = reports.create_enrollments_provisioning_report(account.account_id, term.term_id)
        enrollment_data = reports.get_report_data(enrollment_report)

        all_instructors = {}

        enrollment_csv_data = csv.reader(enrollment_data)
        header = enrollment_csv_data.next()
        course_id_idx = header.index("course_id")
        sis_user_id_idx = header.index("user_id")
        role_idx = header.index("role")
        status_idx = header.index("status")

        for row in enrollment_csv_data:
            if not len(row):
                continue

            course_id = row[course_id_idx]
            sis_user_id = row[sis_user_id_idx]
            role = row[role_idx]
            status = row[status_idx]

            if sis_user_id != "" and role.lower() == "teacher" and status.lower() == "active":
                if course_id not in all_instructors:
                    all_instructors[course_id] = []

                all_instructors[course_id].append(sis_user_id)

        course_report = reports.create_course_provisioning_report(account.account_id, term.term_id)
        course_data = reports.get_report_data(course_report)

        course_csv_data = csv.reader(course_data)
        header = course_csv_data.next()
        course_id_idx = header.index("course_id")
        sis_account_id_idx = header.index("account_id")
        status_idx = header.index("status")

        active_instructors = {}
        for row in course_csv_data:
            if not len(row):
                continue

            course_id = row[course_id_idx]
            sis_account_id = row[sis_account_id_idx]
            status = row[status_idx]

            if sis_account_id != "" and status.lower() == "active" and course_id in all_instructors:
                for sis_user_id in all_instructors[course_id]:
                    if sis_user_id not in active_instructors:
                        try:
                            person = pws.get_person_by_regid(sis_user_id)
                            email = person.uwnetid + "@uw.edu"
                            active_instructors[sis_user_id] = email
                        except InvalidRegID:
                            continue
                        except DataFailureException as err:
                            if err.status == 404:
                                continue
                            else:
                                raise

        filename = "-".join(["active-instructors", subaccount_id, sis_term_id])
        outpath = dirname(__file__) + "/" + filename + ".txt"

        f = open(outpath, "w")
        data = active_instructors.values()
        data.sort()
        f.write("\n".join(data))
        f.close()

        reports.delete_report(enrollment_report)
        reports.delete_report(course_report)

        print outpath