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)
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)
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()
def __init__(self): self._accounts = CanvasAccounts(per_page=50) self._analytics = CanvasAnalytics() self._reports = CanvasReports()
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