def get_course_info(course): course_info = {} course_info['course_id'] = course.course_id course_info['name'] = course.name course_info['thumbnail'] = course.thumbnail course_info['course_num'] = course.course_num course_info['modified'] = naturalFormatDate(course.modified) course_info['enrollment'] = course_stat.stat.enrollment_total_count( SlashSeparatedCourseKey.from_deprecated_string(course.course_id)) course_info['comment'] = course_stat.stat.comment_total_count( SlashSeparatedCourseKey.from_deprecated_string(course.course_id)) course_info['subtitle'] = course.subtitle course_info['about'] = reverse('about_course', args=[course.course_id]) course_info['start_time'] = time_format(course.start) course_info['end_time'] = time_format(course.end) try: staff = course.staff.all()[0] course_info['staff_avatar'] = staff.avartar course_info['staff_name'] = staff.name course_info['staff_title'] = "%s %s %s" % ( staff.company, staff.department, staff.position) except: course_info['staff_avatar'] = '' course_info['staff_name'] = '' course_info['staff_title'] = '' return course_info
def _decorated(request, course_id, *args, **kwargs): try: SlashSeparatedCourseKey.from_deprecated_string(course_id) except InvalidKeyError: raise Http404 response = view_func(request, course_id, *args, **kwargs) return response
def post(self, request, course_id, format=None): """ Enroll in Course """ user = request.user err = {} try: course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course = course_from_key(course_key) except ItemNotFoundError: err['err_type'] = 'InvalidCourseId' err['err_msg'] = _("Course id is invalid") return Response(err, status=status.HTTP_400_BAD_REQUEST) if not has_access(user, 'enroll', course): err['err_type'] = 'InvalidEnrollment' err['err_msg'] = _("Enrollment is closed") return Response(err, status=status.HTTP_400_BAD_REQUEST) # see if we have already filled up all allowed enrollments is_course_full = CourseEnrollment.is_course_full(course) if is_course_full: err['err_type'] = 'InvalidEnrollment' err['err_msg'] = _("Course is full") return Response(err, status=status.HTTP_400_BAD_REQUEST) # If this course is available in multiple modes, redirect them to a page # where they can choose which mode they want. available_modes = CourseMode.modes_for_course(course_id) available_modes_dict = CourseMode.modes_for_course_dict(course_id, available_modes) if CourseMode.has_verified_mode(available_modes_dict): err['err_type'] = 'InvalidEnrollment' err['err_msg'] = _("Missing course mode") return Response(err, status=status.HTTP_400_BAD_REQUEST) current_mode = available_modes[0] course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) dog_stats_api.increment( "common.student.enrollment", tags=[u"org:{0}".format(course_key.org), u"course:{0}".format(course_key.course), u"run:{0}".format(course_key.run)] ) server_track(request, 'api.course.enrollment', { 'username': user.username, 'course_id': course_id, }) CourseEnrollment.enroll(user, course.id, mode=current_mode.slug) return Response()
def handle(self, *args, **options): if not options['course']: raise CommandError(Command.course_option.help) try: course_key = CourseKey.from_string(options['course']) except InvalidKeyError: course_key = SlashSeparatedCourseKey.from_deprecated_string(options['course']) course = get_course_by_id(course_key) print 'Warning: this command directly edits the list of course tabs in mongo.' print 'Tabs before any changes:' print_course(course) try: if options['delete']: if len(args) != 1: raise CommandError(Command.delete_option.help) num = int(args[0]) if query_yes_no('Deleting tab {0} Confirm?'.format(num), default='no'): primitive_delete(course, num - 1) # -1 for 0-based indexing elif options['insert']: if len(args) != 3: raise CommandError(Command.insert_option.help) num = int(args[0]) tab_type = args[1] name = args[2] if query_yes_no('Inserting tab {0} "{1}" "{2}" Confirm?'.format(num, tab_type, name), default='no'): primitive_insert(course, num - 1, tab_type, name) # -1 as above except ValueError as e: # Cute: translate to CommandError so the CLI error prints nicely. raise CommandError(e)
def index(request, course_id, book_index, page=None): """ Serve static image-based textbooks. """ course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course = get_course_with_access(request.user, 'load', course_key) staff_access = bool(has_access(request.user, 'staff', course)) book_index = int(book_index) if book_index < 0 or book_index >= len(course.textbooks): raise Http404("Invalid book index value: {0}".format(book_index)) textbook = course.textbooks[book_index] table_of_contents = textbook.table_of_contents if page is None: page = textbook.start_page return render_to_response( 'staticbook.html', { 'book_index': book_index, 'page': int(page), 'course': course, 'book_url': textbook.book_url, 'table_of_contents': table_of_contents, 'start_page': textbook.start_page, 'end_page': textbook.end_page, 'staff_access': staff_access, }, )
def add_cohort(request, course_key_string): """ Return json of dict: {'success': True, 'cohort': {'id': id, 'name': name}} or {'success': False, 'msg': error_msg} if there's an error """ # this is a string when we get it here course_key = SlashSeparatedCourseKey.from_deprecated_string(course_key_string) get_course_with_access(request.user, 'staff', course_key) name = request.POST.get("name") if not name: return json_http_response({'success': False, 'msg': "No name specified"}) try: cohort = cohorts.add_cohort(course_key, name) except ValueError as err: return json_http_response({'success': False, 'msg': str(err)}) return json_http_response({ 'success': 'True', 'cohort': { 'id': cohort.id, 'name': cohort.name } })
def request_certificate(request): """Request the on-demand creation of a certificate for some user, course. A request doesn't imply a guarantee that such a creation will take place. We intentionally use the same machinery as is used for doing certification at the end of a course run, so that we can be sure users get graded and then if and only if they pass, do they get a certificate issued. """ if request.method == "POST": if request.user.is_authenticated(): xqci = XQueueCertInterface() username = request.user.username student = User.objects.get(username=username) course_key = SlashSeparatedCourseKey.from_deprecated_string(request.POST.get("course_id")) course = modulestore().get_course(course_key, depth=2) status = certificate_status_for_student(student, course_key)["status"] if status in [CertificateStatuses.unavailable, CertificateStatuses.notpassing, CertificateStatuses.error]: logger.info( "Grading and certification requested for user {} in course {} via /request_certificate call".format( username, course_key ) ) status = xqci.add_cert(student, course_key, course=course) return HttpResponse(json.dumps({"add_status": status}), mimetype="application/json") return HttpResponse(json.dumps({"add_status": "ERRORANONYMOUSUSER"}), mimetype="application/json")
def set_course_mode_price(request, course_id): """ set the new course price and add new entry in the CourseModesArchive Table """ try: course_price = int(request.POST['course_price']) except ValueError: return JsonResponse( {'message': _("Please Enter the numeric value for the course price")}, status=400) # status code 400: Bad Request currency = request.POST['currency'] course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course_honor_mode = CourseMode.objects.filter(mode_slug='honor', course_id=course_key) if not course_honor_mode: return JsonResponse( {'message': _("CourseMode with the mode slug({mode_slug}) DoesNotExist").format(mode_slug='honor')}, status=400) # status code 400: Bad Request CourseModesArchive.objects.create( course_id=course_id, mode_slug='honor', mode_display_name='Honor Code Certificate', min_price=course_honor_mode[0].min_price, currency=course_honor_mode[0].currency, expiration_datetime=datetime.datetime.now(pytz.utc), expiration_date=datetime.date.today() ) course_honor_mode.update( min_price=course_price, currency=currency ) return JsonResponse({'message': _("CourseMode price updated successfully")})
def get_anon_ids(request, course_id): # pylint: disable=W0613 """ Respond with 2-column CSV output of user-id, anonymized-user-id """ # TODO: the User.objects query and CSV generation here could be # centralized into instructor_analytics. Currently instructor_analytics # has similar functionality but not quite what's needed. course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id) def csv_response(filename, header, rows): """Returns a CSV http response for the given header and rows (excel/utf-8).""" response = HttpResponse(mimetype='text/csv') response['Content-Disposition'] = 'attachment; filename={0}'.format(unicode(filename).encode('utf-8')) writer = csv.writer(response, dialect='excel', quotechar='"', quoting=csv.QUOTE_ALL) # In practice, there should not be non-ascii data in this query, # but trying to do the right thing anyway. encoded = [unicode(s).encode('utf-8') for s in header] writer.writerow(encoded) for row in rows: encoded = [unicode(s).encode('utf-8') for s in row] writer.writerow(encoded) return response students = User.objects.filter( courseenrollment__course_id=course_id, ).order_by('id') header = ['User ID', 'Anonymized User ID', 'Course Specific Anonymized User ID'] rows = [[s.id, unique_id_for_user(s, save=False), anonymous_id_for_user(s, course_id, save=False)] for s in students] return csv_response(course_id.to_deprecated_string().replace('/', '-') + '-anon-ids.csv', header, rows)
def get(self, request, course_id): """ Handle the case where we have a get request """ upgrade = request.GET.get('upgrade', False) course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id) if CourseEnrollment.enrollment_mode_for_user(request.user, course_id) == 'verified': return redirect(reverse('dashboard')) verify_mode = CourseMode.mode_for_course(course_id, "verified") if verify_mode is None: return redirect(reverse('dashboard')) chosen_price = request.session.get( "donation_for_course", {} ).get( course_id.to_deprecated_string(), verify_mode.min_price ) course = modulestore().get_course(course_id) context = { "course_id": course_id.to_deprecated_string(), "course_modes_choose_url": reverse('course_modes_choose', kwargs={'course_id': course_id.to_deprecated_string()}), "course_name": course.display_name_with_default, "course_org": course.display_org_with_default, "course_num": course.display_number_with_default, "purchase_endpoint": get_purchase_endpoint(), "currency": verify_mode.currency.upper(), "chosen_price": chosen_price, "create_order_url": reverse("verify_student_create_order"), "upgrade": upgrade, } return render_to_response('verify_student/verified.html', context)
def add_users_to_cohort(request, course_key_string, cohort_id): """ Return json dict of: {'success': True, 'added': [{'username': ..., 'name': ..., 'email': ...}, ...], 'changed': [{'username': ..., 'name': ..., 'email': ..., 'previous_cohort': ...}, ...], 'present': [str1, str2, ...], # already there 'unknown': [str1, str2, ...]} Raises Http404 if the cohort cannot be found for the given course. """ # this is a string when we get it here course_key = SlashSeparatedCourseKey.from_deprecated_string(course_key_string) get_course_with_access(request.user, 'staff', course_key) try: cohort = cohorts.get_cohort_by_id(course_key, cohort_id) except CourseUserGroup.DoesNotExist: raise Http404("Cohort (ID {cohort_id}) not found for {course_key_string}".format( cohort_id=cohort_id, course_key_string=course_key_string )) users = request.POST.get('users', '') added = [] changed = [] present = [] unknown = [] for username_or_email in split_by_comma_and_whitespace(users): if not username_or_email: continue try: (user, previous_cohort) = cohorts.add_user_to_cohort(cohort, username_or_email) info = { 'username': user.username, 'name': user.profile.name, 'email': user.email, } if previous_cohort: info['previous_cohort'] = previous_cohort changed.append(info) else: added.append(info) except ValueError: present.append(username_or_email) except User.DoesNotExist: unknown.append(username_or_email) return json_http_response({'success': True, 'added': added, 'changed': changed, 'present': present, 'unknown': unknown})
def course_info(request, course_id): """ Display the course's info.html, or 404 if there is no such course. Assumes the course_id is in a valid format. """ course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course = get_course_with_access(request.user, 'load', course_key) staff_access = has_access(request.user, 'staff', course) masq = setup_masquerade(request, staff_access) # allow staff to toggle masquerade on info page reverifications = fetch_reverify_banner_info(request, course_key) studio_url = get_studio_url(course_key, 'course_info') context = { 'request': request, 'course_id': course_key.to_deprecated_string(), 'cache': None, 'course': course, 'staff_access': staff_access, 'masquerade': masq, 'studio_url': studio_url, 'reverifications': reverifications, } return render_to_response('courseware/info.html', context)
def progress(request, course_id, student_id=None): """ Wraps "_progress" with the manual_transaction context manager just in case there are unanticipated errors. """ with grades.manual_transaction(): return _progress(request, SlashSeparatedCourseKey.from_deprecated_string(course_id), student_id)
def show_unit_extensions(request, course_id): """ Shows all of the students which have due date extensions for the given unit. """ course = get_course_by_id(SlashSeparatedCourseKey.from_deprecated_string(course_id)) unit = find_unit(course, request.GET.get('url')) return JsonResponse(dump_module_extensions(course, unit))
def clean_course_id(self): """Validate the course id""" cleaned_id = self.cleaned_data["course_id"] try: course_key = CourseKey.from_string(cleaned_id) except InvalidKeyError: try: course_key = SlashSeparatedCourseKey.from_deprecated_string(cleaned_id) except InvalidKeyError: msg = u'Course id invalid.' msg += u' --- Entered course id was: "{0}". '.format(cleaned_id) msg += 'Please recheck that you have supplied a valid course id.' raise forms.ValidationError(msg) if not modulestore().has_course(course_key): msg = u'COURSE NOT FOUND' msg += u' --- Entered course id was: "{0}". '.format(course_key.to_deprecated_string()) msg += 'Please recheck that you have supplied a valid course id.' raise forms.ValidationError(msg) # Now, try and discern if it is a Studio course - HTML editor doesn't work with XML courses is_studio_course = modulestore().get_modulestore_type(course_key) != ModuleStoreEnum.Type.xml if not is_studio_course: msg = "Course Email feature is only available for courses authored in Studio. " msg += '"{0}" appears to be an XML backed course.'.format(course_key.to_deprecated_string()) raise forms.ValidationError(msg) return course_key
def list_cohorts(request, course_key_string): """ Return json dump of dict: {'success': True, 'cohorts': [{'name': name, 'id': id}, ...]} """ # this is a string when we get it here course_key = SlashSeparatedCourseKey.from_deprecated_string(course_key_string) course = get_course_with_access(request.user, 'staff', course_key) all_cohorts = [ { 'name': c.name, 'id': c.id, 'user_count': c.users.count(), 'assignment_type': cohorts.CohortAssignmentType.get(c, course) } for c in cohorts.get_course_cohorts(course) ] return json_http_response({'success': True, 'cohorts': all_cohorts})
def all_sequential_open_distrib(request, course_id): """ Creates a json with the open distribution for all the subsections in the course. `request` django request `course_id` the course ID for the course interested in Returns the format in dashboard_data.get_d3_sequential_open_distrib """ data = {} # Only instructor for this particular course can request this information course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) if has_instructor_access_for_class(request.user, course_key): try: data = dashboard_data.get_d3_sequential_open_distrib(course_key) except Exception as ex: # pylint: disable=broad-except log.error('Generating metrics failed with exception: %s', ex) data = {'error': "error"} else: data = {'error': "Access Denied: User does not have access to this course's data"} return HttpResponse(json.dumps(data), content_type="application/json")
def remove_user_from_cohort(request, course_key_string, cohort_id): """ Expects 'username': username in POST data. Return json dict of: {'success': True} or {'success': False, 'msg': error_msg} """ # this is a string when we get it here course_key = SlashSeparatedCourseKey.from_deprecated_string(course_key_string) get_course_with_access(request.user, 'staff', course_key) username = request.POST.get('username') if username is None: return json_http_response({'success': False, 'msg': 'No username specified'}) cohort = cohorts.get_cohort_by_id(course_key, cohort_id) try: user = User.objects.get(username=username) cohort.users.remove(user) return json_http_response({'success': True}) except User.DoesNotExist: log.debug('no user') return json_http_response({'success': False, 'msg': "No user '{0}'".format(username)})
def get_visible_courses(): """ Return the set of CourseDescriptors that should be visible in this branded instance """ filtered_by_org = microsite.get_value('course_org_filter') _courses = modulestore().get_courses(org=filtered_by_org) courses = [c for c in _courses if isinstance(c, CourseDescriptor)] courses = sorted(courses, key=lambda course: course.number) subdomain = microsite.get_value('subdomain', 'default') # See if we have filtered course listings in this domain filtered_visible_ids = None # this is legacy format which is outside of the microsite feature -- also handle dev case, which should not filter if hasattr(settings, 'COURSE_LISTINGS') and subdomain in settings.COURSE_LISTINGS and not settings.DEBUG: filtered_visible_ids = frozenset([SlashSeparatedCourseKey.from_deprecated_string(c) for c in settings.COURSE_LISTINGS[subdomain]]) if filtered_by_org: return [course for course in courses if course.location.org == filtered_by_org] if filtered_visible_ids: return [course for course in courses if course.id in filtered_visible_ids] else: # Let's filter out any courses in an "org" that has been declared to be # in a Microsite org_filter_out_set = microsite.get_all_orgs() return [course for course in courses if course.location.org not in org_filter_out_set]
def send_email(request, course_id): """ Send an email to self, staff, or everyone involved in a course. Query Parameters: - 'send_to' specifies what group the email should be sent to Options are defined by the CourseEmail model in lms/djangoapps/bulk_email/models.py - 'subject' specifies email's subject - 'message' specifies email's content """ course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id) if not bulk_email_is_enabled_for_course(course_id): return HttpResponseForbidden("Email is not enabled for this course.") send_to = request.POST.get("send_to") subject = request.POST.get("subject") message = request.POST.get("message") # Create the CourseEmail object. This is saved immediately, so that # any transaction that has been pending up to this point will also be # committed. email = CourseEmail.create(course_id, request.user, send_to, subject, message) # Submit the task, so that the correct InstructorTask object gets created (for monitoring purposes) instructor_task.api.submit_bulk_course_email(request, course_id, email.id) # pylint: disable=E1101 response_payload = { 'course_id': course_id.to_deprecated_string(), 'success': True, } return JsonResponse(response_payload)
def jump_to(request, course_id, location): """ Show the page that contains a specific location. If the location is invalid or not in any class, return a 404. Otherwise, delegates to the index view to figure out whether this user has access, and what they should see. """ try: course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) usage_key = course_key.make_usage_key_from_deprecated_string(location) except InvalidKeyError: raise Http404(u"Invalid course_key or usage_key") try: (course_key, chapter, section, position) = path_to_location(modulestore(), usage_key) except ItemNotFoundError: raise Http404(u"No data at this location: {0}".format(usage_key)) except NoPathToItem: raise Http404(u"This location is not in any class: {0}".format(usage_key)) # choose the appropriate view (and provide the necessary args) based on the # args provided by the redirect. # Rely on index to do all error handling and access control. if chapter is None: return redirect('courseware', course_id=course_key.to_deprecated_string()) elif section is None: return redirect('courseware_chapter', course_id=course_key.to_deprecated_string(), chapter=chapter) elif position is None: return redirect('courseware_section', course_id=course_key.to_deprecated_string(), chapter=chapter, section=section) else: return redirect('courseware_position', course_id=course_key.to_deprecated_string(), chapter=chapter, section=section, position=position)
def spoc_gradebook(request, course_id): """ Show the gradebook for this course: - Only shown for courses with enrollment < settings.FEATURES.get("MAX_ENROLLMENT_INSTR_BUTTONS") - Only displayed to course staff """ course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course = get_course_with_access(request.user, 'staff', course_key, depth=None) enrolled_students = User.objects.filter( courseenrollment__course_id=course_key, courseenrollment__is_active=1 ).order_by('username').select_related("profile") # possible extension: implement pagination to show to large courses student_info = [ { 'username': student.username, 'id': student.id, 'email': student.email, 'grade_summary': student_grades(student, request, course), 'realname': student.profile.name, } for student in enrolled_students ] return render_to_response('courseware/gradebook.html', { 'students': student_info, 'course': course, 'course_id': course_key, # Checked above 'staff_access': True, 'ordered_grades': sorted(course.grade_cutoffs.items(), key=lambda i: i[1], reverse=True), })
def static_tab(request, course_id, tab_slug): """ Display the courses tab with the given name. Assumes the course_id is in a valid format. """ try: course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) except InvalidKeyError: raise Http404 course = get_course_with_access(request.user, 'load', course_key) tab = CourseTabList.get_tab_by_slug(course.tabs, tab_slug) if tab is None: raise Http404 contents = get_static_tab_contents( request, course, tab ) if contents is None: raise Http404 return render_to_response('courseware/static_tab.html', { 'course': course, 'tab': tab, 'tab_contents': contents, })
def __init__( self, contentstore, mappings, stores, i18n_service=None, fs_service=None, user_service=None, create_modulestore_instance=None, signal_handler=None, **kwargs ): """ Initialize a MixedModuleStore. Here we look into our passed in kwargs which should be a collection of other modulestore configuration information """ super(MixedModuleStore, self).__init__(contentstore, **kwargs) if create_modulestore_instance is None: raise ValueError('MixedModuleStore constructor must be passed a create_modulestore_instance function') self.modulestores = [] self.mappings = {} for course_id, store_name in mappings.iteritems(): try: self.mappings[CourseKey.from_string(course_id)] = store_name except InvalidKeyError: try: self.mappings[SlashSeparatedCourseKey.from_deprecated_string(course_id)] = store_name except InvalidKeyError: log.exception("Invalid MixedModuleStore configuration. Unable to parse course_id %r", course_id) continue for store_settings in stores: key = store_settings['NAME'] is_xml = 'XMLModuleStore' in store_settings['ENGINE'] if is_xml: # restrict xml to only load courses in mapping store_settings['OPTIONS']['course_ids'] = [ course_key.to_deprecated_string() for course_key, store_key in self.mappings.iteritems() if store_key == key ] store = create_modulestore_instance( store_settings['ENGINE'], self.contentstore, store_settings.get('DOC_STORE_CONFIG', {}), store_settings.get('OPTIONS', {}), i18n_service=i18n_service, fs_service=fs_service, user_service=user_service, signal_handler=signal_handler, ) # replace all named pointers to the store into actual pointers for course_key, store_name in self.mappings.iteritems(): if store_name == key: self.mappings[course_key] = store self.modulestores.append(store)
def setUp(self): self.course_name = 'edX/toy/2012_Fall' # Create student account student = UserFactory.create() CourseEnrollmentFactory.create( user=student, course_id=SlashSeparatedCourseKey.from_deprecated_string(self.course_name) ) self.client.login(username=student.username, password="******") try: # URL for dashboard self.url = reverse('dashboard') except NoReverseMatch: raise SkipTest("Skip this test if url cannot be found (ie running from CMS tests)") # URL for email settings modal self.email_modal_link = ( ('<a href="#email-settings-modal" class="email-settings" rel="leanModal" ' 'data-course-id="{0}/{1}/{2}" data-course-number="{1}" ' 'data-optout="False">Email Settings</a>').format( 'edX', 'toy', '2012_Fall' ) )
def update_thread(request, course_id, thread_id): """ Given a course id and thread id, update a existing thread, used for both static and ajax submissions """ if 'title' not in request.POST or not request.POST['title'].strip(): return JsonError(_("Title can't be empty")) if 'body' not in request.POST or not request.POST['body'].strip(): return JsonError(_("Body can't be empty")) course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) thread = cc.Thread.find(thread_id) thread.body = request.POST["body"] thread.title = request.POST["title"] # The following checks should avoid issues we've seen during deploys, where end users are hitting an updated server # while their browser still has the old client code. This will avoid erasing present values in those cases. if "thread_type" in request.POST: thread.thread_type = request.POST["thread_type"] if "commentable_id" in request.POST: course = get_course_with_access(request.user, 'load', course_key) commentable_ids = get_discussion_categories_ids(course) if request.POST.get("commentable_id") in commentable_ids: thread.commentable_id = request.POST["commentable_id"] else: return JsonError(_("Topic doesn't exist")) thread.save() if request.is_ajax(): return ajax_content_response(request, course_key, thread.to_dict()) else: return JsonResponse(prepare_content(thread.to_dict(), course_key))
def users(request, course_id): """ Given a `username` query parameter, find matches for users in the forum for this course. Only exact matches are supported here, so the length of the result set will either be 0 or 1. """ course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) try: course = get_course_with_access(request.user, 'load_forum', course_key) except Http404: # course didn't exist, or requesting user does not have access to it. return JsonError(status=404) try: username = request.GET['username'] except KeyError: # 400 is default status for JsonError return JsonError(["username parameter is required"]) user_objs = [] try: matched_user = User.objects.get(username=username) cc_user = cc.User.from_django_user(matched_user) cc_user.course_id=course_key cc_user.retrieve(complete=False) if (cc_user['threads_count'] + cc_user['comments_count']) > 0: user_objs.append({ 'id': matched_user.id, 'username': matched_user.username, }) except User.DoesNotExist: pass return JsonResponse({"users": user_objs})
def __init__( self, data_dir, default_class=None, course_dirs=None, course_ids=None, load_error_modules=True, i18n_service=None, **kwargs ): """ Initialize an XMLModuleStore from data_dir Args: data_dir (str): path to data directory containing the course directories default_class (str): dot-separated string defining the default descriptor class to use if none is specified in entry_points course_dirs or course_ids (list of str): If specified, the list of course_dirs or course_ids to load. Otherwise, load all courses. Note, providing both """ super(XMLModuleStore, self).__init__(**kwargs) self.data_dir = path(data_dir) self.modules = defaultdict(dict) # course_id -> dict(location -> XBlock) self.courses = {} # course_dir -> XBlock for the course self.errored_courses = {} # course_dir -> errorlog, for dirs that failed to load if course_ids is not None: course_ids = [SlashSeparatedCourseKey.from_deprecated_string(course_id) for course_id in course_ids] self.load_error_modules = load_error_modules if default_class is None: self.default_class = None else: module_path, _, class_name = default_class.rpartition(".") class_ = getattr(import_module(module_path), class_name) self.default_class = class_ self.parent_trackers = defaultdict(ParentTracker) self.reference_type = Location # All field data will be stored in an inheriting field data. self.field_data = inheriting_field_data(kvs=DictKeyValueStore()) self.i18n_service = i18n_service # If we are specifically asked for missing courses, that should # be an error. If we are asked for "all" courses, find the ones # that have a course.xml. We sort the dirs in alpha order so we always # read things in the same order (OS differences in load order have # bitten us in the past.) if course_dirs is None: course_dirs = sorted( [d for d in os.listdir(self.data_dir) if os.path.exists(self.data_dir / d / "course.xml")] ) for course_dir in course_dirs: self.try_load_course(course_dir, course_ids)
def section_problem_grade_distrib(request, course_id, section): """ Creates a json with the grade distribution for the problems in the specified section. `request` django request `course_id` the course ID for the course interested in `section` The zero-based index of the section for the course Returns the format in dashboard_data.get_d3_section_grade_distrib If this is requested multiple times quickly for the same course, it is better to call all_problem_grade_distribution and pick out the sections of interest. """ data = {} # Only instructor for this particular course can request this information course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) if has_instructor_access_for_class(request.user, course_key): try: data = dashboard_data.get_d3_section_grade_distrib(course_key, section) except Exception as ex: # pylint: disable=broad-except log.error('Generating metrics failed with exception: %s', ex) data = {'error': "error"} else: data = {'error': "Access Denied: User does not have access to this course's data"} return HttpResponse(json.dumps(data), content_type="application/json")
def update_thread(request, course_id, thread_id): """ Given a course id and thread id, update a existing thread, used for both static and ajax submissions """ if 'title' not in request.POST or not request.POST['title'].strip(): return JsonError(_("Title can't be empty")) if 'body' not in request.POST or not request.POST['body'].strip(): return JsonError(_("Body can't be empty")) course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) thread = cc.Thread.find(thread_id) thread.body = request.POST["body"] thread.title = request.POST["title"] if "commentable_id" in request.POST: course = get_course_with_access(request.user, 'load', course_key) id_map = get_discussion_id_map(course) if request.POST.get("commentable_id") in id_map: thread.commentable_id = request.POST["commentable_id"] else: return JsonError(_("Topic doesn't exist")) thread.save() if request.is_ajax(): return ajax_content_response(request, course_key, thread.to_dict()) else: return JsonResponse(prepare_content(thread.to_dict(), course_key))
def get(self, request, course_id): """ display this view """ course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id) course = modulestore().get_course(course_id) course_enrollment = CourseEnrollment.get_or_create_enrollment(request.user, course_id) course_enrollment.update_enrollment(mode="verified") course_enrollment.emit_event(EVENT_NAME_USER_ENTERED_MIDCOURSE_REVERIFY_VIEW) context = { "user_full_name": request.user.profile.name, "error": False, "course_id": course_id.to_deprecated_string(), "course_name": course.display_name_with_default, "course_org": course.display_org_with_default, "course_num": course.display_number_with_default, "reverify": True, } return render_to_response("verify_student/midcourse_photo_reverification.html", context)
def _check_rights(course_id, user, rolename): """Check if user has correct rights.""" course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id) course = get_course_by_id(course_id) has_instructor_access = has_access(user, 'instructor', course) has_forum_admin = has_forum_access(user, course_id, FORUM_ROLE_ADMINISTRATOR) # default roles require either (staff & forum admin) or (instructor) if not (has_forum_admin or has_instructor_access): raise UnauthorizedAccessError( "Operation requires staff & forum admin or instructor access" ) # filter out unsupported for roles if rolename not in CUSTOM_ROLES: raise UnauthorizedAccessError(strip_tags( "Unrecognized FUN special rolename '{}'.".format(rolename) )) return course_id
def course_context_from_url(url): """ Extracts the course_context from the given `url` and passes it on to `course_context_from_course_id()`. """ url = url or '' match = COURSE_REGEX.match(url) course_id = None if match: course_id_string = match.group('course_id') try: course_id = SlashSeparatedCourseKey.from_deprecated_string( course_id_string) except InvalidKeyError: log.warning('unable to parse course_id "{course_id}"'.format( course_id=course_id_string), exc_info=True) return course_context_from_course_id(course_id)
def find_target_student_module(request, user_id, course_id, mod_id): """ Retrieve target StudentModule """ course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id) usage_key = course_id.make_usage_key_from_deprecated_string(mod_id) user = User.objects.get(id=user_id) field_data_cache = FieldDataCache.cache_for_descriptor_descendents( course_id, user, modulestore().get_item(usage_key), depth=0, select_for_update=True ) instance = get_module(user, request, usage_key, field_data_cache, grade_bucket_type='xqueue') if instance is None: msg = "No module {0} for user {1}--access denied?".format(mod_id, user) log.debug(msg) raise Http404 return instance
def __init__(self, xml_import_data): self.course_id = SlashSeparatedCourseKey.from_deprecated_string(xml_import_data.course_id) self.default_class = xml_import_data.default_class self._descriptors = {} def get_policy(usage_id): """Return the policy data for the specified usage""" return xml_import_data.policy.get(policy_key(usage_id), {}) super(InMemorySystem, self).__init__( get_policy=get_policy, process_xml=self.process_xml, load_item=self.load_item, error_tracker=Mock(), resources_fs=xml_import_data.filesystem, mixins=xml_import_data.xblock_mixins, select=xml_import_data.xblock_select, render_template=lambda template, context: pprint.pformat((template, context)), field_data=KvsFieldData(DictKeyValueStore()), )
def jump_to_id(request, course_id, module_id): """ This entry point allows for a shorter version of a jump to where just the id of the element is passed in. This assumes that id is unique within the course_id namespace """ course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) items = modulestore().get_items(course_key, name=module_id) if len(items) == 0: raise Http404( u"Could not find id: {0} in course_id: {1}. Referer: {2}".format( module_id, course_id, request.META.get("HTTP_REFERER", ""))) if len(items) > 1: log.warning( u"Multiple items found with id: {0} in course_id: {1}. Referer: {2}. Using first: {3}" .format(module_id, course_id, request.META.get("HTTP_REFERER", ""), items[0].location.to_deprecated_string())) return jump_to(request, course_id, items[0].location.to_deprecated_string())
def peer_grading(request, course_id): ''' When a student clicks on the "peer grading" button in the open ended interface, link them to a peer grading xmodule in the course. ''' course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) #Get the current course course = get_course_with_access(request.user, 'load', course_key) found_module, problem_url = find_peer_grading_module(course) if not found_module: error_message = _(""" Error with initializing peer grading. There has not been a peer grading module created in the courseware that would allow you to grade others. Please check back later for this. """) log.exception(error_message + u"Current course is: {0}".format(course_id)) return HttpResponse(error_message) return HttpResponseRedirect(problem_url)
def course_specific_register(request, course_id): """ Dispatcher function for selecting the specific registration method required by the course """ course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course = modulestore().get_course(course_key) if not course: # couldn't find the course, will just return vanilla registration page return redirect_with_get('register_user', request.GET) # now the dispatching conditionals. Only shib for now if (settings.FEATURES.get('AUTH_USE_SHIB') and course.enrollment_domain and course.enrollment_domain.startswith(SHIBBOLETH_DOMAIN_PREFIX)): # shib-login takes care of both registration and login flows return redirect_with_get('shib-login', request.GET) # Default fallthrough to normal registration page return redirect_with_get('register_user', request.GET)
def get_student_progress_url(request, course_id): """ Get the progress url of a student. Limited to staff access. Takes query paremeter unique_student_identifier and if the student exists returns e.g. { 'progress_url': '/../...' } """ course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id) user = get_student_from_identifier(request.GET.get('unique_student_identifier')) progress_url = reverse('student_progress', kwargs={'course_id': course_id.to_deprecated_string(), 'student_id': user.id}) response_payload = { 'course_id': course_id.to_deprecated_string(), 'progress_url': progress_url, } return JsonResponse(response_payload)
def has_passed(request, course_id, section_url_name): """ Returns True if the student has higher or equeal grades in asssignment type. """ student = request.user # Get the course by ID course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course = get_course_with_access(student, 'load', course_key, depth=None) # Get the grade summary with outer_atomic(): field_data_cache = grades.field_data_cache_for_grading(course, student) scores_client = ScoresClient.from_field_data_cache(field_data_cache) grade_summary = grades.grade(student, request, course, field_data_cache=field_data_cache, scores_client=scores_client) # Get assignment type wise percent assignments = {} for section in grade_summary['section_breakdown']: if section.get('prominent', False): assignments.update({section['category']: section['percent']}) # Get the section assignment type section_assignment_type = '' for chapter in course.get_children(): for sequenctial in chapter.get_children(): if sequenctial.url_name == section_url_name: section_assignment_type = sequenctial.format break # Get section assignment percent percentage = assignments.get(section_assignment_type, 0.0) # Return passing status return percentage * 100 == 100
def api_request(request, course_id, **kwargs): ''' Routes API requests to the appropriate action method and returns JSON. Raises a 404 if the requested resource does not exist or notes are disabled for the course. ''' assert isinstance(course_id, basestring) course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) # Verify that the api should be accessible to this course if not api_enabled(request, course_key): log.debug('Notes are disabled for course: {0}'.format(course_id)) raise Http404 # Locate the requested resource resource_map = API_SETTINGS.get('RESOURCE_MAP', {}) resource_name = kwargs.pop('resource') resource_method = request.method resource = resource_map.get(resource_name) if resource is None: log.debug('Resource "{0}" does not exist'.format(resource_name)) raise Http404 if resource_method not in resource.keys(): log.debug('Resource "{0}" does not support method "{1}"'.format(resource_name, resource_method)) raise Http404 # Execute the action associated with the resource func = resource.get(resource_method) module = globals() if func not in module: log.debug('Function "{0}" does not exist for request {1} {2}'.format(func, resource_method, resource_name)) raise Http404 log.debug('API request: {0} {1}'.format(resource_method, resource_name)) api_response = module[func](request, course_key, **kwargs) http_response = api_format(api_response) return http_response
def look_up_registration_code(request, course_id): """ Look for the registration_code in the database. and check if it is still valid, allowed to redeem or not. """ course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) code = request.GET.get('registration_code') course = get_course_by_id(course_key, depth=0) try: registration_code = CourseRegistrationCode.objects.get(code=code) except CourseRegistrationCode.DoesNotExist: return JsonResponse( { 'is_registration_code_exists': False, 'is_registration_code_valid': False, 'is_registration_code_redeemed': False, 'message': _('The enrollment code ({code}) was not found for the {course_name} course.' ).format(code=code, course_name=course.display_name) }, status=400) # status code 200: OK by default reg_code_already_redeemed = RegistrationCodeRedemption.is_registration_code_redeemed( code) registration_code_detail_url = reverse( 'registration_code_details', kwargs={'course_id': unicode(course_id)}) return JsonResponse({ 'is_registration_code_exists': True, 'is_registration_code_valid': registration_code.is_valid, 'is_registration_code_redeemed': reg_code_already_redeemed, 'registration_code_detail_url': registration_code_detail_url }) # status code 200: OK by default
def xblock_view(request, course_id, usage_id, view_name): """ Returns the rendered view of a given XBlock, with related resources Returns a json object containing two keys: html: The rendered html of the view resources: A list of tuples where the first element is the resource hash, and the second is the resource description """ if not settings.FEATURES.get('ENABLE_XBLOCK_VIEW_ENDPOINT', False): log.warn("Attempt to use deactivated XBlock view endpoint -" " see FEATURES['ENABLE_XBLOCK_VIEW_ENDPOINT']") raise Http404 if not request.user.is_authenticated(): raise PermissionDenied try: course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) except InvalidKeyError: raise Http404("Invalid location") with modulestore().bulk_operations(course_key): course = modulestore().get_course(course_key) instance, _ = get_module_by_usage_id(request, course_id, usage_id, course=course) try: fragment = instance.render(view_name, context=request.GET) except NoSuchViewError: log.exception("Attempt to render missing view on %s: %s", instance, view_name) raise Http404 hashed_resources = OrderedDict() for resource in fragment.resources: hashed_resources[hash_resource(resource)] = resource return JsonResponse({ 'html': fragment.content, 'resources': hashed_resources.items(), 'csrf_token': unicode(csrf(request)['csrf_token']), })
def get_section_list(request,course_id,student_id): section_id_dict = {} commentable_id_list = [] try: course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) except Exception as e: return Resposne( data={ "Error": e.message }) course = get_course_by_id(course_key) with modulestore().bulk_operations(course.id): course_module = get_module_for_descriptor( request.user, request, course, None, course.id, course=course ) if course_module is None: return Response( data={ "Error":"User is not allowed to do the operation" }) student = User.objects.get(id=student_id) query_params = { "course_id":course_id, "user_id": student.id, } threads = Thread.search(query_params).collection for chapter in course_module.get_children(): for section in chapter.get_children(): for unit in section.get_children(): for vertical in unit.get_children(): if vertical.category == 'discussion': participated = False for thread in threads: if thread['commentable_id'] == vertical.discussion_id: participated = thread['username'] == student.username section_id_dict.update({chapter.url_name: participated}) return section_id_dict
def inline_discussion(request, course_id, discussion_id): """ Renders JSON for DiscussionModules """ nr_transaction = newrelic.agent.current_transaction() course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course = get_course_with_access(request.user, 'load_forum', course_key) cc_user = cc.User.from_django_user(request.user) user_info = cc_user.to_dict() try: threads, query_params = get_threads(request, course_key, discussion_id, per_page=INLINE_THREADS_PER_PAGE) except ValueError: return HttpResponseBadRequest("Invalid group_id") with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"): annotated_content_info = utils.get_metadata_for_threads( course_key, threads, request.user, user_info) is_staff = cached_has_permission(request.user, 'openclose_thread', course.id) threads = [ utils.prepare_content(thread, course_key, is_staff) for thread in threads ] with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"): add_courseware_context(threads, course) return utils.JsonResponse({ 'discussion_data': threads, 'user_info': user_info, 'annotated_content_info': annotated_content_info, 'page': query_params['page'], 'num_pages': query_params['num_pages'], 'roles': utils.get_role_ids(course_key), 'course_settings': make_course_settings(course) })
def handle(self, *args, **options): course_id = options['course_id'] if not course_id: raise CommandError("You must specify a course-id") def update_user_whitelist(username, add=True): """ Update the status of whitelist user(s) """ user = get_user_from_identifier(username) cert_whitelist, _created = CertificateWhitelist.objects.get_or_create( user=user, course_id=course) cert_whitelist.whitelist = add cert_whitelist.save() # try to parse the serialized course key into a CourseKey try: course = CourseKey.from_string(course_id) except InvalidKeyError: print(("Course id {} could not be parsed as a CourseKey; " "falling back to SSCK.from_dep_str").format(course_id)) course = SlashSeparatedCourseKey.from_deprecated_string(course_id) if options['add'] and options['del']: raise CommandError("Either remove or add a user, not both") if options['add'] or options['del']: user_str = options['add'] or options['del'] add_to_whitelist = True if options['add'] else False users_list = user_str.split(",") for username in users_list: if username.strip(): update_user_whitelist(username, add=add_to_whitelist) whitelist = CertificateWhitelist.objects.filter(course_id=course) print(u"User whitelist for course {0}:".format(course_id)) for whitelisted in whitelist: username = whitelisted.user.username email = whitelisted.user.email is_whitelisted = whitelisted.whitelist print(username, email, is_whitelisted)
def handle(self, *args, **options): if len(args) == 0: raise CommandError("Arguments missing: 'org/number/run commit'") if len(args) == 1: if args[0] == 'commit': raise CommandError( "Delete_course requires a course_key <org/number/run> argument." ) else: raise CommandError( "Delete_course requires a commit argument at the end") elif len(args) == 2: try: course_key = CourseKey.from_string(args[0]) except InvalidKeyError: try: course_key = SlashSeparatedCourseKey.from_deprecated_string( args[0]) except InvalidKeyError: raise CommandError( "Invalid course_key: '%s'. Proper syntax: 'org/number/run commit' " % args[0]) if args[1] != 'commit': raise CommandError( "Delete_course requires a commit argument at the end") elif len(args) > 2: raise CommandError( "Too many arguments! Expected <course_key> <commit>") if not modulestore().get_course(course_key): raise CommandError("Course with '%s' key not found." % args[0]) print 'Actually going to delete the %s course from DB....' % args[0] if query_yes_no("Deleting course {0}. Confirm?".format(course_key), default="no"): if query_yes_no("Are you sure. This action cannot be undone!", default="no"): delete_course_and_groups(course_key, ModuleStoreEnum.UserID.mgmt_command) print "Deleted course {}".format(course_key)
def course_id_from_url(url): """ Extracts the course_id from the given `url`. """ if not url: return None match = COURSE_REGEX.match(url) if match is None: return None course_id = match.group('course_id') if course_id is None: return None try: return SlashSeparatedCourseKey.from_deprecated_string(course_id) except InvalidKeyError: return None
def ensure_certif(request, course_id): user_id = request.user.id username = request.user.username course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course_tma = get_course_by_id(course_key) is_graded = course_tma.is_graded grade_cutoffs = modulestore().get_course( course_key, depth=0).grade_cutoffs['Pass'] * 100 grading_note = CourseGradeFactory().create(request.user, course_tma) passed = grading_note.passed percent = float(int(grading_note.percent * 1000) / 10) overall_progress = get_overall_progress(request.user.id, course_key) context = { 'passed': passed, 'percent': percent, 'is_graded': is_graded, 'grade_cutoffs': grade_cutoffs, 'overall_progress': overall_progress } return JsonResponse(context)
def notes(request, course_id): ''' Displays the student's notes. ''' course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course = get_course_with_access(request.user, 'load', course_key) if not notes_enabled_for_course(course): raise Http404 notes = Note.objects.filter(course_id=course_key, user=request.user).order_by('-created', 'uri') student = request.user storage = course.annotation_storage_url context = { 'course': course, 'notes': notes, 'student': student, 'storage': storage, 'token': retrieve_token(student.email, course.annotation_token_secret), 'default_tab': 'myNotes', } return render_to_response('notes.html', context)
def show_requirements(request, course_id): """ Show the requirements necessary for the verification flow. """ course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id) if CourseEnrollment.enrollment_mode_for_user(request.user, course_id) == ('verified', True): return redirect(reverse('dashboard')) upgrade = request.GET.get('upgrade', False) course = modulestore().get_course(course_id) context = { "course_id": course_id.to_deprecated_string(), "course_modes_choose_url": reverse("course_modes_choose", kwargs={'course_id': course_id.to_deprecated_string()}), "verify_student_url": reverse('verify_student_verify', kwargs={'course_id': course_id.to_deprecated_string()}), "course_name": course.display_name_with_default, "course_org": course.display_org_with_default, "course_num": course.display_number_with_default, "is_not_active": not request.user.is_active, "upgrade": upgrade == u'True', } return render_to_response("verify_student/show_requirements.html", context)
def get_overall_course_progress(request): """ Description: This view kept for fetching the overall course progress. Request Parameters: course_id: course ID for which progress needs to be calculated. student_id: Student for which progress needs to be calculated. Returns: json response Assumes the course_id is in a valid format. Author: Naresh Makwana """ course_id = request.GET.get('course_id') course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) overall_progress = get_overall_progress(request.user.id, course_key) return JsonResponse({'overall_progress': overall_progress})
def list_instructor_tasks(request, course_id): """ List instructor tasks. Takes optional query paremeters. - With no arguments, lists running tasks. - `problem_location_str` lists task history for problem - `problem_location_str` and `unique_student_identifier` lists task history for problem AND student (intersection) """ course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id) problem_location_str = strip_if_string(request.GET.get('problem_location_str', False)) student = request.GET.get('unique_student_identifier', None) if student is not None: student = get_student_from_identifier(student) if student and not problem_location_str: return HttpResponseBadRequest( "unique_student_identifier must accompany problem_location_str" ) if problem_location_str: try: module_state_key = course_id.make_usage_key_from_deprecated_string(problem_location_str) except InvalidKeyError: return HttpResponseBadRequest() if student: # Specifying for a single student's history on this problem tasks = instructor_task.api.get_instructor_task_history(course_id, module_state_key, student) else: # Specifying for single problem's history tasks = instructor_task.api.get_instructor_task_history(course_id, module_state_key) else: # If no problem or student, just get currently running tasks tasks = instructor_task.api.get_running_instructor_tasks(course_id) response_payload = { 'tasks': map(extract_task_features, tasks), } return JsonResponse(response_payload)
def remove_user_from_cohort(request, course_key_string, cohort_id): """ Expects 'username': username in POST data. Return json dict of: {'success': True} or {'success': False, 'msg': error_msg} """ # this is a string when we get it here course_key = SlashSeparatedCourseKey.from_deprecated_string( course_key_string) get_course_with_access(request.user, 'staff', course_key) username = request.POST.get('username') if username is None: return json_http_response({ 'success': False, 'msg': 'No username specified' }) try: user = User.objects.get(username=username) except User.DoesNotExist: log.debug('no user') return json_http_response({ 'success': False, 'msg': "No user '{0}'".format(username) }) try: membership = CohortMembership.objects.get(user=user, course_id=course_key) membership.delete() except CohortMembership.DoesNotExist: pass return json_http_response({'success': True})
def get_completion_status(request): """ Description: To check completion status of the section/chapter. Request Parameters: course_id: course ID string. Returns: json response Assumes the course_id is in a valid format. Author: Naresh Makwana """ # Set initial value to progress progress = {} completion_status = {} # Get course id and convert it to course key course_id = request.GET.get('course_id') course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) # Get the student course completion progress try: student_course_progress = StudentCourseProgress.objects.get( student=request.user.id, course_id=course_key) progress = student_course_progress.progress except StudentCourseProgress.DoesNotExist: pass # Prepare completion status dictionary sequential_id = request.GET.get('sequential_id') sequential_progress = progress.get(sequential_id, [])['progress'] log.info('sequential progress {}'.format(sequential_progress)) for block_id in progress.get(sequential_id, [])['children']: completion_status.update( {block_id: progress.get(block_id, [])['progress']}) # Return the JSON resposne return JsonResponse({'completion_status': completion_status})
def handle(self, *args, **options): if not options['course']: raise CommandError(Command.course_option.help) try: course_key = CourseKey.from_string(options['course']) except InvalidKeyError: course_key = SlashSeparatedCourseKey.from_deprecated_string( options['course']) course = get_course_by_id(course_key) print 'Warning: this command directly edits the list of course tabs in mongo.' print 'Tabs before any changes:' print_course(course) try: if options['delete']: if len(args) != 1: raise CommandError(Command.delete_option.help) num = int(args[0]) if query_yes_no('Deleting tab {0} Confirm?'.format(num), default='no'): tabs.primitive_delete(course, num - 1) # -1 for 0-based indexing elif options['insert']: if len(args) != 3: raise CommandError(Command.insert_option.help) num = int(args[0]) tab_type = args[1] name = args[2] if query_yes_no( 'Inserting tab {0} "{1}" "{2}" Confirm?'.format( num, tab_type, name), default='no'): tabs.primitive_insert(course, num - 1, tab_type, name) # -1 as above except ValueError as e: # Cute: translate to CommandError so the CLI error prints nicely. raise CommandError(e)
def user_software_license(request): if request.method != 'POST' or not request.is_ajax(): raise Http404 # get the course id from the referer url_path = urlparse(request.META.get('HTTP_REFERER', '')).path pattern = re.compile('^/courses/(?P<id>[^/]+/[^/]+/[^/]+)/.*/?$') match = re.match(pattern, url_path) if not match: raise Http404 course_id = match.groupdict().get('id', '') course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) user_id = request.session.get('_auth_user_id') software_name = request.POST.get('software') generate = request.POST.get('generate', False) == 'true' try: software = CourseSoftware.objects.get(name=software_name, course_id=course_key) except CourseSoftware.DoesNotExist: raise Http404 try: user = User.objects.get(id=user_id) except User.DoesNotExist: raise Http404 if generate: software_license = get_or_create_license(user, software) else: software_license = get_license(user, software) if software_license: response = {'serial': software_license.serial} else: response = {'error': 'No serial number found'} return HttpResponse(json.dumps(response), content_type='application/json')
def test_branching(self): """ Exercise branching code of import """ repo_dir = self.GIT_REPO_DIR # Test successful import from command if not os.path.isdir(repo_dir): os.mkdir(repo_dir) self.addCleanup(shutil.rmtree, repo_dir) # Checkout non existent branch with self.assertRaisesRegexp(GitImportError, GitImportError.REMOTE_BRANCH_MISSING): git_import.add_repo(self.TEST_REPO, repo_dir / 'edx4edx_lite', 'asdfasdfasdf') # Checkout new branch git_import.add_repo(self.TEST_REPO, repo_dir / 'edx4edx_lite', self.TEST_BRANCH) def_ms = modulestore() # Validate that it is different than master self.assertIsNotNone(def_ms.get_course(self.TEST_BRANCH_COURSE)) # Attempt to check out the same branch again to validate branch choosing # works git_import.add_repo(self.TEST_REPO, repo_dir / 'edx4edx_lite', self.TEST_BRANCH) # Delete to test branching back to master def_ms.delete_course(self.TEST_BRANCH_COURSE, ModuleStoreEnum.UserID.test) self.assertIsNone(def_ms.get_course(self.TEST_BRANCH_COURSE)) git_import.add_repo(self.TEST_REPO, repo_dir / 'edx4edx_lite', 'master') self.assertIsNone(def_ms.get_course(self.TEST_BRANCH_COURSE)) self.assertIsNotNone( def_ms.get_course( SlashSeparatedCourseKey.from_deprecated_string( self.TEST_COURSE)))
def set_course_mode_price(request, course_id): """ set the new course price and add new entry in the CourseModesArchive Table """ try: course_price = int(request.POST['course_price']) except ValueError: return JsonResponse( { 'message': _("Please Enter the numeric value for the course price") }, status=400) # status code 400: Bad Request currency = request.POST['currency'] course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course_honor_mode = CourseMode.objects.filter(mode_slug='honor', course_id=course_key) if not course_honor_mode: return JsonResponse( { 'message': _("CourseMode with the mode slug({mode_slug}) DoesNotExist"). format(mode_slug='honor') }, status=400) # status code 400: Bad Request CourseModesArchive.objects.create( course_id=course_id, mode_slug='honor', mode_display_name='Honor Code Certificate', min_price=course_honor_mode[0].min_price, currency=course_honor_mode[0].currency, expiration_datetime=datetime.datetime.now(pytz.utc), expiration_date=datetime.date.today()) course_honor_mode.update(min_price=course_price, currency=currency) return JsonResponse( {'message': _("CourseMode price updated successfully")})
def mktg_course_about(request, course_id): """ This is the button that gets put into an iframe on the Drupal site """ course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) try: course = get_course_with_access(request.user, 'see_exists', course_key) except (ValueError, Http404) as e: # if a course does not exist yet, display a coming # soon button return render_to_response( 'courseware/mktg_coming_soon.html', {'course_id': course_key.to_deprecated_string()}) registered = registered_for_course(course, request.user) if has_access(request.user, 'load', course): course_target = reverse('info', args=[course.id.to_deprecated_string()]) else: course_target = reverse('about_course', args=[course.id.to_deprecated_string()]) allow_registration = has_access(request.user, 'enroll', course) show_courseware_link = (has_access(request.user, 'load', course) or settings.FEATURES.get('ENABLE_LMS_MIGRATION')) course_modes = CourseMode.modes_for_course_dict(course.id) return render_to_response( 'courseware/mktg_course_about.html', { 'course': course, 'registered': registered, 'allow_registration': allow_registration, 'course_target': course_target, 'show_courseware_link': show_courseware_link, 'course_modes': course_modes, })