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') if re.match(r'^\d+$', options['account_id']): account = self._accounts.get_account(options['account_id']) else: account = self._accounts.get_account_by_sis_id( options['account_id']) try: self.report_external_tools(account) except DataFailureException as err: if err.status == 404: print('Unknown Sub-Account \"%s\"' % (options['account_id']), file=sys.stderr)
def update_discovery_url(self, discovery_url): account_id = settings.RESTCLIENTS_CANVAS_ACCOUNT_ID LiveDAO.pools = {} 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): 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_sub_account(self): canvas = Canvas() accounts = canvas.get_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 test_update_account(self, mock_update): mock_update.return_value = None canvas = Canvas() accounts = canvas.get_all_sub_accounts_by_sis_id( 'uwcourse:seattle:cse') account = accounts[0] account.name = "New Name" canvas.update_account(account) mock_update.assert_called_with('/api/v1/accounts/88888', {'account': { 'name': 'New Name' }})
def test_update_sis_id(self, mock_update): mock_update.return_value = None canvas = Canvas() canvas.update_sis_id(54321, 'NEW_SIS_ID') mock_update.assert_called_with( '/api/v1/accounts/54321', {'account': { 'sis_account_id': 'NEW_SIS_ID' }}) # Cannot update sis id for root account self.assertRaises(Exception, canvas.update_sis_id, getattr(settings, 'RESTCLIENTS_CANVAS_ACCOUNT_ID'), 'NEW_SIS_ID')
def test_update_auth_settings(self, mock_update): mock_update.return_value = None canvas = Canvas() auth_settings = CanvasSSOSettings( unknown_user_url='https://test.edu/unknown') canvas.update_auth_settings( getattr(settings, 'RESTCLIENTS_CANVAS_ACCOUNT_ID'), auth_settings) mock_update.assert_called_with( '/api/v1/accounts/12345/sso_settings', { 'sso_settings': { 'login_handle_name': None, 'change_password_url': None, 'auth_discovery_url': None, 'unknown_user_url': 'https://test.edu/unknown' } })
def find_grading_standard_for_account(self, account_id, grading_standard_id): """ Get a single grading standard in account context, searching ancestor accounts if necessary. """ if not self._canvas_account_id: raise MissingAccountID() acc = Accounts() while account_id is not None: try: return self.get_grading_standard_for_account( account_id, grading_standard_id) except DataFailureException as ex: if ex.status == 404 and account_id != self._canvas_account_id: account_id = acc.get_account(account_id).parent_account_id continue raise
def update_account_sis_id(account_id, sis_account_id): return Accounts().update_sis_id(account_id, sis_account_id)
def get_all_sub_accounts(account_id): return Accounts(per_page=100).get_all_sub_accounts(account_id)
def get_account_by_sis_id(sis_account_id): return Accounts().get_account_by_sis_id(sis_account_id)
def get_account_by_id(account_id): return Accounts().get_account(account_id)
class Command(BaseCommand): help = "Report externals tools in account" def add_arguments(self, parser): parser.add_argument( '-a', '--account', action='store', dest='account_id', default=default_account, help=('show external tools in account by id or ' 'sis_id (default: {})').format(default_account)) parser.add_argument('-r', '--recurse', action='store_true', dest='recurse', default=False, help='recurse through subaccounts') parser.add_argument('-c', '--courses', action='store_true', dest='courses', default=False, help='include account courses in report') parser.add_argument('-t', '--term', action='store', dest='term', default='', help='include only courses in given term') parser.add_argument( '-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') if re.match(r'^\d+$', options['account_id']): account = self._accounts.get_account(options['account_id']) else: account = self._accounts.get_account_by_sis_id( options['account_id']) try: self.report_external_tools(account) except DataFailureException as err: if err.status == 404: print('Unknown Sub-Account \"%s\"' % (options['account_id']), file=sys.stderr) 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("{}_navigation".format(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: tools = self._tools url = tools.get_sessionless_launch_url_from_account( tool['id'], account.account_id) line.append(url['url']) except DataFailureException as ex: line.append('') self._writer.writerow(line)
def handle(self, *args, **options): subaccount_id = options.get('subaccount_id') sis_term_id = options.get('term_id') accounts = Accounts() reports = Reports() pws = PWS() outpath = "{}/{}-{}-{}.csv".format( dirname(__file__), "active-instructors", subaccount_id, sis_term_id) outfile = open(outpath, "w") csv.register_dialect('unix_newline', lineterminator='\n') writer = csv.writer(outfile, dialect='unix_newline') writer.writerow(['email', 'first_name', 'last_name']) 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 = next(enrollment_csv_data) 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 = next(course_csv_data) 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 in active_instructors: continue try: person = pws.get_person_by_regid(sis_user_id) active_instructors[sis_user_id] = [ person.uwnetid + "@uw.edu", person.preferred_first_name or person.first_name, person.preferred_surname or person.surname] except InvalidRegID: continue except DataFailureException as err: if err.status == 404: continue else: raise for csv_data in active_instructors.values(): writer.writerow(csv_data) outfile.close() reports.delete_report(enrollment_report) reports.delete_report(course_report) print(outpath)