def test_edxnotes_harvard_notes_enabled(self): """ Tests that edxnotes are disabled when Harvard Annotation Tool is enabled. """ self.course.advanced_modules = ["foo", "imageannotation", "boo"] self.assertFalse(helpers.is_feature_enabled(self.course)) self.course.advanced_modules = ["foo", "boo", "videoannotation"] self.assertFalse(helpers.is_feature_enabled(self.course)) self.course.advanced_modules = ["textannotation", "foo", "boo"] self.assertFalse(helpers.is_feature_enabled(self.course)) self.course.advanced_modules = ["textannotation", "videoannotation", "imageannotation"] self.assertFalse(helpers.is_feature_enabled(self.course))
def get_html(self, *args, **kwargs): """ Returns raw html for the component. """ is_studio = getattr(self.system, "is_author_mode", False) course = self.descriptor.runtime.modulestore.get_course(self.runtime.course_id) # Must be disabled: # - in Studio; # - when Harvard Annotation Tool is enabled for the course; # - when the feature flag or `edxnotes` setting of the course is set to False. if is_studio or not is_feature_enabled(course): return original_get_html(self, *args, **kwargs) else: return render_to_string("edxnotes_wrapper.html", { "content": original_get_html(self, *args, **kwargs), "uid": generate_uid(), "edxnotes_visibility": json.dumps( getattr(self, 'edxnotes_visibility', course.edxnotes_visibility) ), "params": { # Use camelCase to name keys. "usageId": unicode(self.scope_ids.usage_id).encode("utf-8"), "courseId": unicode(self.runtime.course_id).encode("utf-8"), "token": get_id_token(self.runtime.get_real_user(self.runtime.anonymous_student_id)), "tokenUrl": get_token_url(self.runtime.course_id), "endpoint": get_endpoint(), "debug": settings.DEBUG, "eventStringLimit": settings.TRACK_MAX_EVENT / 6, }, })
def test_is_feature_enabled(self, enabled): """ Tests that is_feature_enabled shows correct behavior. """ course = CourseFactory(edxnotes=enabled) enrollment = CourseEnrollmentFactory(course_id=course.id) assert helpers.is_feature_enabled(course, enrollment.user) == enabled
def edxnotes(request, course_id): """ Displays the EdxNotes page. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, "load", course_key) if not is_feature_enabled(course): raise Http404 try: notes = get_notes(request.user, course) except EdxNotesServiceUnavailable: raise Http404 context = { "course": course, "search_endpoint": reverse("search_notes", kwargs={"course_id": course_id}), "notes": notes, "debug": json.dumps(settings.DEBUG), 'position': None, } if not notes: field_data_cache = FieldDataCache.cache_for_descriptor_descendents( course.id, request.user, course, depth=2 ) course_module = get_module_for_descriptor(request.user, request, course, field_data_cache, course_key) position = get_course_position(course_module) if position: context.update({ 'position': position, }) return render_to_response("edxnotes/edxnotes.html", context)
def edxnotes_visibility(request, course_id): """ Handle ajax call from "Show notes" checkbox. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, "load", course_key) field_data_cache = FieldDataCache([course], course_key, request.user) course_module = get_module_for_descriptor(request.user, request, course, field_data_cache, course_key, course=course) if not is_feature_enabled(course, request.user): raise Http404 try: visibility = json.loads(request.body)["visibility"] course_module.edxnotes_visibility = visibility course_module.save() return JsonResponse(status=200) except (ValueError, KeyError): log.warning( "Could not decode request body as JSON and find a boolean visibility field: '%s'", request.body) return JsonResponseBadRequest()
def test_edxnotes_enabled(self): """ Tests that edxnotes are enabled when the course tab configuration contains a tab with type "edxnotes." """ self.course.tabs = [{"type": "foo"}, {"name": "Notes", "type": "edxnotes"}, {"type": "bar"}] self.assertTrue(helpers.is_feature_enabled(self.course))
def test_edxnotes_not_enabled(self): """ Tests that edxnotes are disabled when the course tab configuration does NOT contain a tab with type "edxnotes." """ self.course.tabs = [] self.assertFalse(helpers.is_feature_enabled(self.course))
def get_html(self, *args, **kwargs): """ Returns raw html for the component. """ # Import is placed here to avoid model import at project startup. from edxnotes.helpers import generate_uid, get_edxnotes_id_token, get_public_endpoint, get_token_url, is_feature_enabled is_studio = getattr(self.system, "is_author_mode", False) course = self.descriptor.runtime.modulestore.get_course(self.runtime.course_id) # Must be disabled: # - in Studio; # - when Harvard Annotation Tool is enabled for the course; # - when the feature flag or `edxnotes` setting of the course is set to False. if is_studio or not is_feature_enabled(course): return original_get_html(self, *args, **kwargs) else: return render_to_string("edxnotes_wrapper.html", { "content": original_get_html(self, *args, **kwargs), "uid": generate_uid(), "edxnotes_visibility": json.dumps( getattr(self, 'edxnotes_visibility', course.edxnotes_visibility) ), "params": { # Use camelCase to name keys. "usageId": unicode(self.scope_ids.usage_id).encode("utf-8"), "courseId": unicode(self.runtime.course_id).encode("utf-8"), "token": get_edxnotes_id_token(self.runtime.get_real_user(self.runtime.anonymous_student_id)), "tokenUrl": get_token_url(self.runtime.course_id), "endpoint": get_public_endpoint(), "debug": settings.DEBUG, "eventStringLimit": settings.TRACK_MAX_EVENT / 6, }, })
def notes(self): """ Return whether edxnotes is enabled and visible. """ return { 'enabled': is_feature_enabled(self.overview, self.effective_user), 'visible': self.overview.edxnotes_visibility, }
def get_notes(self, course_overview): """ Return whether edxnotes is enabled and visible. """ return { 'enabled': is_feature_enabled(course_overview, course_overview.effective_user), 'visible': course_overview.edxnotes_visibility, }
def get_html(self, *args, **kwargs): """ Returns raw html for the component. """ # Import is placed here to avoid model import at project startup. from edxnotes.helpers import generate_uid, get_edxnotes_id_token, get_public_endpoint, get_token_url, is_feature_enabled is_studio = getattr(self.system, "is_author_mode", False) course = getattr(self, 'descriptor', self).runtime.modulestore.get_course( self.runtime.course_id) # Must be disabled when: # - in Studio # - Harvard Annotation Tool is enabled for the course # - the feature flag or `edxnotes` setting of the course is set to False # - the user is not authenticated user = self.runtime.get_real_user(self.runtime.anonymous_student_id) if is_studio or not is_feature_enabled(course, user): return original_get_html(self, *args, **kwargs) else: return render_to_string( "edxnotes_wrapper.html", { "content": original_get_html(self, *args, **kwargs), "uid": generate_uid(), "edxnotes_visibility": json.dumps( getattr(self, 'edxnotes_visibility', course.edxnotes_visibility)), "params": { # Use camelCase to name keys. "usageId": six.text_type(self.scope_ids.usage_id).encode("utf-8"), "courseId": six.text_type(self.runtime.course_id).encode("utf-8"), "token": get_edxnotes_id_token(user), "tokenUrl": get_token_url(self.runtime.course_id), "endpoint": get_public_endpoint(), "debug": settings.DEBUG, "eventStringLimit": settings.TRACK_MAX_EVENT / 6, }, })
def edxnotes(request, course_id): """ Displays the EdxNotes page. Arguments: request: HTTP request object course_id: course id Returns: Rendered HTTP response. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, "load", course_key) if not is_feature_enabled(course, request.user): raise Http404 notes_info = get_notes(request, course) has_notes = (len(notes_info.get('results')) > 0) context = { "course": course, "notes_endpoint": reverse("notes", kwargs={"course_id": course_id}), "notes": notes_info, "page_size": DEFAULT_PAGE_SIZE, "debug": settings.DEBUG, 'position': None, 'disabled_tabs': settings.NOTES_DISABLED_TABS, 'has_notes': has_notes, } if not has_notes: field_data_cache = FieldDataCache.cache_for_descriptor_descendents( course.id, request.user, course, depth=2) course_module = get_module_for_descriptor(request.user, request, course, field_data_cache, course_key, course=course) position = get_course_position(course_module) if position: context.update({ 'position': position, }) return render_to_response("edxnotes/edxnotes.html", context)
def edxnotes(request, course_id): """ Displays the EdxNotes page. Arguments: request: HTTP request object course_id: course id Returns: Rendered HTTP response. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, "load", course_key) if not is_feature_enabled(course, request.user): raise Http404 notes_info = get_notes(request, course) has_notes = (len(notes_info.get('results')) > 0) context = { "course": course, "notes_endpoint": reverse("notes", kwargs={"course_id": course_id}), "notes": notes_info, "page_size": DEFAULT_PAGE_SIZE, "debug": settings.DEBUG, 'position': None, 'disabled_tabs': settings.NOTES_DISABLED_TABS, 'has_notes': has_notes, } if not has_notes: field_data_cache = FieldDataCache.cache_for_descriptor_descendents( course.id, request.user, course, depth=2 ) course_module = get_module_for_descriptor( request.user, request, course, field_data_cache, course_key, course=course ) position = get_course_position(course_module) if position: context.update({ 'position': position, }) return render_to_response("edxnotes/edxnotes.html", context)
def search_notes(request, course_id): """ Handles search requests. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, "load", course_key) if not is_feature_enabled(course): raise Http404 if "text" not in request.GET: return HttpResponseBadRequest() query_string = request.GET["text"] try: search_results = search(request.user, course, query_string) except (EdxNotesParseError, EdxNotesServiceUnavailable) as err: return JsonResponseBadRequest({"error": err.message}, status=500) return HttpResponse(search_results)
def edxnotes_visibility(request, course_id): """ Handle ajax call from "Show notes" checkbox. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, "load", course_key) field_data_cache = FieldDataCache([course], course_key, request.user) course_module = get_module_for_descriptor( request.user, request, course, field_data_cache, course_key, course=course ) if not is_feature_enabled(course): raise Http404 try: visibility = json.loads(request.body)["visibility"] course_module.edxnotes_visibility = visibility course_module.save() return JsonResponse(status=200) except (ValueError, KeyError): log.warning("Could not decode request body as JSON and find a boolean visibility field: '%s'", request.body) return JsonResponseBadRequest()
def test_is_feature_enabled(self, _edxnotes): """ Tests that is_feature_enabled shows correct behavior. """ self.course.edxnotes = _edxnotes self.assertEqual(helpers.is_feature_enabled(self.course), _edxnotes)
def notes(request, course_id): """ Notes view to handle list and search requests. Query parameters: page: page number to get page_size: number of items in the page text: text string to search. If `text` param is missing then get all the notes for the current user for this course else get only those notes which contain the `text` value. Arguments: request: HTTP request object course_id: course id Returns: Paginated response as JSON. A sample response is below. { "count": 101, "num_pages": 11, "current_page": 1, "results": [ { "chapter": { "index": 4, "display_name": "About Exams and Certificates", "location": "i4x://org/course/category/name@revision", "children": [ "i4x://org/course/category/name@revision" ] }, "updated": "Dec 09, 2015 at 09:31 UTC", "tags": ["shadow","oil"], "quote": "foo bar baz", "section": { "display_name": "edX Exams", "location": "i4x://org/course/category/name@revision", "children": [ "i4x://org/course/category/name@revision", "i4x://org/course/category/name@revision", ] }, "created": "2015-12-09T09:31:17.338305Z", "ranges": [ { "start": "/div[1]/p[1]", "end": "/div[1]/p[1]", "startOffset": 0, "endOffset": 6 } ], "user": "******", "text": "first angry height hungry structure", "course_id": "edx/DemoX/Demo", "id": "1231", "unit": { "url": "/courses/edx%2FDemoX%2FDemo/courseware/1414ffd5143b4b508f739b563ab468b7/workflow/1", "display_name": "EdX Exams", "location": "i4x://org/course/category/name@revision" }, "usage_id": "i4x://org/course/category/name@revision" } ], "next": "http://0.0.0.0:8000/courses/edx%2FDemoX%2FDemo/edxnotes/notes/?page=2&page_size=10", "start": 0, "previous": null } """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, "load", course_key) if not is_feature_enabled(course): raise Http404 page = request.GET.get("page") or DEFAULT_PAGE page_size = request.GET.get("page_size") or DEFAULT_PAGE_SIZE text = request.GET.get("text") try: notes_info = get_notes(request, course, page=page, page_size=page_size, text=text) except (EdxNotesParseError, EdxNotesServiceUnavailable) as err: return JsonResponseBadRequest({"error": err.message}, status=500) return HttpResponse(json.dumps(notes_info, cls=NoteJSONEncoder), content_type="application/json")
def notes(request, course_id): """ Notes view to handle list and search requests. Query parameters: page: page number to get page_size: number of items in the page text: text string to search. If `text` param is missing then get all the notes for the current user for this course else get only those notes which contain the `text` value. Arguments: request: HTTP request object course_id: course id Returns: Paginated response as JSON. A sample response is below. { "count": 101, "num_pages": 11, "current_page": 1, "results": [ { "chapter": { "index": 4, "display_name": "About Exams and Certificates", "location": "i4x://org/course/category/name@revision", "children": [ "i4x://org/course/category/name@revision" ] }, "updated": "Dec 09, 2015 at 09:31 UTC", "tags": ["shadow","oil"], "quote": "foo bar baz", "section": { "display_name": "edX Exams", "location": "i4x://org/course/category/name@revision", "children": [ "i4x://org/course/category/name@revision", "i4x://org/course/category/name@revision", ] }, "created": "2015-12-09T09:31:17.338305Z", "ranges": [ { "start": "/div[1]/p[1]", "end": "/div[1]/p[1]", "startOffset": 0, "endOffset": 6 } ], "user": "******", "text": "first angry height hungry structure", "course_id": "edx/DemoX/Demo", "id": "1231", "unit": { "url": "/courses/edx%2FDemoX%2FDemo/courseware/1414ffd5143b4b508f739b563ab468b7/workflow/1", "display_name": "EdX Exams", "location": "i4x://org/course/category/name@revision" }, "usage_id": "i4x://org/course/category/name@revision" } ], "next": "http://0.0.0.0:8000/courses/edx%2FDemoX%2FDemo/edxnotes/notes/?page=2&page_size=10", "start": 0, "previous": null } """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, 'load', course_key) if not is_feature_enabled(course, request.user): raise Http404 page = request.GET.get('page') or DEFAULT_PAGE page_size = request.GET.get('page_size') or DEFAULT_PAGE_SIZE text = request.GET.get('text') try: notes_info = get_notes(request, course, page=page, page_size=page_size, text=text) except (EdxNotesParseError, EdxNotesServiceUnavailable) as err: return JsonResponseBadRequest({"error": text_type(err)}, status=500) return HttpResponse(json.dumps(notes_info, cls=NoteJSONEncoder), content_type="application/json")
def test_edxnotes_harvard_notes_enabled(self): """ Tests that edxnotes are disabled when Harvard Annotation Tool is enabled. """ self.course.advanced_modules = ['imageannotation', 'textannotation', 'videoannotation'] assert not helpers.is_feature_enabled(self.course, self.user)