def create(request): """Creates a collection.""" role_bucket = services.get_or_update_role_bucket(request) course_collections = LTIService(request).getCourseCollections() collection_filters = dict( collection_ids=course_collections, can_filter=not queries.is_superuser_or_staff(request.user)) collection_list = queries.getCollectionList(role_bucket, **collection_filters) if request.method == 'POST': lti_req = LTIService(request) published = not lti_req.isTeacher() request.POST = request.POST.copy() request.POST['published'] = published collection_form = CollectionForm(request.POST) card_template_id = collection_form.data['card_template'] if collection_form.is_valid(): collection = collection_form.save() lti_req.associateCourse(collection.id) services.add_user_to_collection( user=request.user, collection=collection, role=Users_Collections.ADMINISTRATOR) #update role_bucket to add admin permission to the user for this newly created collection services.get_or_update_role_bucket( request, collection.id, Users_Collections.role_map[Users_Collections.ADMINISTRATOR]) log.info('Collection %s created.' % collection.id, extra={'user': request.user}) analytics.track(actor=request.user, verb=analytics.VERBS.created, object=analytics.OBJECTS.collection, context={"custom": False}) return redirect(collection) else: rel_templates = CardTemplate.objects.filter( Q(owner__isnull=True) | Q(owner=request.user)) initial = {'card_template': '1'} card_template_id = initial['card_template'] collection_form = CollectionForm(query_set=rel_templates, initial=initial) # Pre-populate the "preview" of the card template # This view is also called via AJAX on the page. prev_request = HttpRequest() prev_request.method = 'GET' prev_request.GET['card_template_id'] = card_template_id prev_response = card_template.preview(prev_request) card_template_preview_html = prev_response.content context = { "nav_collections": collection_list, "active_collection": None, "collection_form": collection_form, "card_template_preview_html": card_template_preview_html } return render(request, 'collections/create.html', context)
def delete(request, deck_id=None): """Deletes a deck.""" d = {'user': request.user} collection_id = queries.getDeckCollectionId(deck_id) success = services.delete_deck(deck_id) if success: log.info('Deck %(d)s deleted from collection %(c)s' % { 'd': deck_id, 'c': collection_id }, extra=d) else: log.info('Deck %(d)s could not be deleted from collection %(c)s' % { 'd': deck_id, 'c': collection_id }, extra=d) response = redirect('collectionIndex', collection_id) response['Location'] += '?instructor=edit' analytics.track( actor=request.user, verb=analytics.VERBS.deleted, object=analytics.OBJECTS.deck, context={"deck_id": deck_id}, ) return response
def all_cards(request, collection_id): collection_id = int(collection_id) decks = queries.getDecksByCollection(collection_ids=[collection_id]) decks = decks[collection_id] current_collection = Collection.objects.get(id=collection_id) deck_cards = [] for deck in decks: deck_cards += Decks_Cards.objects.filter(deck=deck).order_by( 'sort_order').prefetch_related('card__cards_fields_set__field') [cards, collection_list, is_quiz_mode, is_deck_admin, card_id] = deck_view_helper(request, current_collection, deck_cards) context = { "collection": current_collection, "nav_collections": collection_list, "deck": { 'id': -collection_id, 'title': 'All Cards' }, "cards": cards, "is_quiz_mode": is_quiz_mode, "is_deck_admin": is_deck_admin, "card_id": card_id, } analytics.track( actor=request.user, verb=analytics.VERBS.viewed, object=analytics.OBJECTS.deck, context={ "collection_id": collection_id, 'type': 'All Cards Deck' }, ) return render(request, "deck_view.html", context)
def index(request, deck_id=None): """Displays the deck of cards for review/quiz.""" deck = Deck.objects.get(id=deck_id) deck_cards = Decks_Cards.objects.filter(deck=deck).order_by( 'sort_order').prefetch_related('card__cards_fields_set__field') current_collection = deck.collection [cards, collection_list, is_quiz_mode, is_deck_admin, card_id] = deck_view_helper(request, current_collection, deck_cards) context = { "collection": current_collection, "nav_collections": collection_list, "deck": deck, "cards": cards, "is_quiz_mode": is_quiz_mode, "is_deck_admin": is_deck_admin, "card_id": card_id, } analytics.track( actor=request.user, verb=analytics.VERBS.viewed, object=analytics.OBJECTS.deck, context={"deck_id": deck_id}, ) return render(request, "deck_view.html", context)
def add_deck(request, collection_id=None): """Adds a deck.""" deck_title = 'Untitled Deck' action = 1 if request.method == 'POST': deck_title_submitted = request.POST.get('deck_title', deck_title) deck_title = deck_title_submitted if deck_title_submitted != "" else deck_title action = int(request.POST.get('action', action)) deck = services.create_deck(collection_id=collection_id, deck_title=deck_title) log.info('Deck %(d)s added to the collection %(c)s.' %{'d': deck.id, 'c': str(collection_id)}, extra={'user': request.user}) analytics.track( actor=request.user, verb=analytics.VERBS.created, object=analytics.OBJECTS.deck, context={"collection_id": collection_id, "deck_id": deck.id} ) actions = {2: 'deckCreateCard', 3: 'deckUpload'} if action in actions.keys(): response = redirect(actions[action], deck.id) response['Location'] += '?deck_id='+str(deck.id) return response else: return redirect(deck)
def share_collection(request, collection_id=None): """ Share a collection with users by creating a temporary url for authorized users to use in order to add themselves into the collection's authorized users list """ role_bucket = services.get_or_update_role_bucket(request) course_collections = LTIService(request).getCourseCollections() collection_list = queries.getCollectionList( role_bucket, collection_ids=course_collections) collection = Collection.objects.get(id=collection_id) collection_share_form = CollectionShareForm() context = { 'share_form': collection_share_form, 'nav_collections': collection_list, 'collection': collection, } if request.POST: collection_share_form = CollectionShareForm(request.POST) if collection_share_form.is_valid(): expired_in = str(collection_share_form.cleaned_data['expired_in']) # This creates an array of the share data (collection id and expiration data # for the url). Random data is appended/prepended to "obfuscate" the url. # This isn't intended to be secure by any means, just make it harder to guess. # The assumption is that a share URL will only grant a user student-level # access, so they can't do any damage if they "guess" the URL. If that # assumption changes, then this implementation should be revisited to make it # more secure and tamper-proof. share_data = [collection_id, expired_in] share_data.insert(0, utils.generate_random_id(3)) share_data.append(utils.generate_random_id(3)) share_key = '!!'.join(share_data) # This pads the share key so that the base64 encoded string doesn't include # base64 padding, which is typically the equals sign ("="). The equals sign # must be percent encoded as %3D which looks bad. Since 3 ascii characters (8 # bits) are encoded with 4 base64 characters (6 bits each) this should ensure # that the share_key is always a multiple of 3. if (len(share_key) % 3) > 0: share_key = share_key + utils.generate_random_id( 3 - (len(share_key) % 3)) secret_share_key = base64.urlsafe_b64encode( share_key.encode("ascii", "ignore")) context['share_form'] = collection_share_form context['secret_share_key'] = secret_share_key log.info('URL generated to share collection %s.' % collection_id, extra={'user': request.user}) analytics.track(actor=request.user, verb=analytics.VERBS.shared, object=analytics.OBJECTS.collection, context={"collection_id": collection_id}) else: context['share_form'] = collection_share_form return render(request, 'collections/share.html', context)
def add_deck(request, collection_id=None): """Adds a deck.""" deck_title = 'Untitled Deck' action = 1 if request.method == 'POST': deck_title_submitted = request.POST.get('deck_title', deck_title) deck_title = deck_title_submitted if deck_title_submitted != "" else deck_title action = int(request.POST.get('action', action)) deck = services.create_deck(collection_id=collection_id, deck_title=deck_title) log.info('Deck %(d)s added to the collection %(c)s.' % { 'd': deck.id, 'c': str(collection_id) }, extra={'user': request.user}) analytics.track(actor=request.user, verb=analytics.VERBS.created, object=analytics.OBJECTS.deck, context={ "collection_id": collection_id, "deck_id": deck.id }) actions = {2: 'deckCreateCard', 3: 'deckUpload'} if action in actions.keys(): response = redirect(actions[action], deck.id) response['Location'] += '?deck_id=' + str(deck.id) return response else: return redirect(deck)
def download_template(request, collection_id=None): ''' Downloads an excel spreadsheet that may be used as a template for uploading a deck of cards. ''' collection = Collection.objects.get(id=collection_id) response = HttpResponse(content_type='application/vnd.ms-excel') response[ 'Content-Disposition'] = 'attachment; filename=flashcards_template.xls' file_output = utils.create_deck_template_file(collection.card_template) response.write(file_output) log.info('Template for the collection %(c)s downloaded by the user.' % {'c': str(collection_id)}, extra={'user': request.user}) analytics.track(actor=request.user, verb=analytics.VERBS.downloaded, object=analytics.OBJECTS.template, context={ "collection_id": collection_id, "custom": False }) return response
def index(request, deck_id=None): """Displays the deck of cards for review/quiz.""" deck = Deck.objects.get(id=deck_id) deck_cards = Decks_Cards.objects.filter(deck=deck).order_by('sort_order').prefetch_related('card__cards_fields_set__field') current_collection = deck.collection [cards, collection_list, is_quiz_mode, is_deck_admin, card_id] = deck_view_helper(request, current_collection, deck_cards) context = { "collection": current_collection, "nav_collections": collection_list, "deck": deck, "cards": cards, "is_quiz_mode": is_quiz_mode, "is_deck_admin": is_deck_admin, "card_id": card_id, } analytics.track( actor=request.user, verb=analytics.VERBS.viewed, object=analytics.OBJECTS.deck, context={"deck_id": deck_id}, ) return render(request, "deck_view.html", context)
def all_cards(request, collection_id): collection_id = int(collection_id) decks = queries.getDecksByCollection(collection_ids = [collection_id]) decks = decks[collection_id] current_collection = Collection.objects.get(id=collection_id) deck_cards = [] for deck in decks: deck_cards += Decks_Cards.objects.filter(deck=deck).order_by('sort_order').prefetch_related('card__cards_fields_set__field') [cards, collection_list, is_quiz_mode, is_deck_admin, card_id] = deck_view_helper(request, current_collection, deck_cards) context = { "collection": current_collection, "nav_collections": collection_list, "deck": {'id': -collection_id, 'title': 'All Cards'}, "cards": cards, "is_quiz_mode": is_quiz_mode, "is_deck_admin": is_deck_admin, "card_id": card_id, } analytics.track( actor=request.user, verb=analytics.VERBS.viewed, object=analytics.OBJECTS.deck, context={"collection_id": collection_id, 'type': 'All Cards Deck'}, ) return render(request, "deck_view.html", context)
def edit(request): """Add/edit card.""" result = {"success": False} card_id = request.POST.get('card_id', '') deck_id = request.POST.get('deck_id', '') add_another = int(request.POST.get('add_another_val', 0)) # fetch the fields being edited; new cards must be created from the card template if card_id == '': deck = Deck.objects.get(id=deck_id) card_fields = deck.collection.card_template.fields.all() else: card = Card.objects.get(id=card_id) card_fields = [cfield.field for cfield in card.cards_fields_set.all()] for k, v in request.POST.iteritems(): if k.endswith('_image_url') and v: field_name = k.replace('_image_url', '') request.FILES[field_name] = services.fetch_image_from_url(v) elif k.endswith('_audio_url') and v: field_name = k.replace('_audio_url', '') request.FILES[field_name] = services.fetch_audio_from_url(v) # attempted to validate and save the form data card_edit_form = CardEditForm(request.POST, request.FILES, card_fields=card_fields) if card_edit_form.is_valid(): card_edit_form.save() card = card_edit_form.get_card() deck = card_edit_form.get_deck() result['success'] = True is_all_cards = request.GET.get('is_all_cards', 0) if add_another: base_url = deck.get_add_card_url() goto_url = "{0}?deck_id={1}".format(base_url, deck.id) else: if int(is_all_cards): base_url = deck.collection.get_all_cards_url() else: base_url = deck.get_absolute_url() goto_url = "{0}?card_id={1}".format(base_url, card.id) result['data'] = {"card_id": card.id, "card_url": goto_url} else: card_edit_form.get_card().delete() result['errors'] = card_edit_form.errors analytics.track( actor=request.user, verb=analytics.VERBS.modified, object=analytics.OBJECTS.card, context={ "deck_id": deck_id, "card_id": card_id }, ) return HttpResponse(json.dumps(result), content_type="application/json")
def edit(request): """Add/edit card.""" result = {"success":False} card_id = request.POST.get('card_id', '') deck_id = request.POST.get('deck_id', '') add_another = int(request.POST.get('add_another_val', 0)) # fetch the fields being edited; new cards must be created from the card template if card_id == '': deck = Deck.objects.get(id=deck_id) card_fields = deck.collection.card_template.fields.all() else: card = Card.objects.get(id=card_id) card_fields = [cfield.field for cfield in card.cards_fields_set.all()] for k, v in request.POST.iteritems(): if k.endswith('_image_url') and v: field_name = k.replace('_image_url', '') request.FILES[field_name] = services.fetch_image_from_url(v) elif k.endswith('_audio_url') and v: field_name = k.replace('_audio_url', '') request.FILES[field_name] = services.fetch_audio_from_url(v) # attempted to validate and save the form data card_edit_form = CardEditForm(request.POST, request.FILES, card_fields=card_fields) if card_edit_form.is_valid(): card_edit_form.save() card = card_edit_form.get_card() deck = card_edit_form.get_deck() result['success'] = True is_all_cards = request.GET.get('is_all_cards', 0) if add_another: base_url = deck.get_add_card_url() goto_url = "{0}?deck_id={1}".format(base_url, deck.id) else: if int(is_all_cards): base_url = deck.collection.get_all_cards_url() else: base_url = deck.get_absolute_url() goto_url = "{0}?card_id={1}".format(base_url, card.id) result['data'] = { "card_id": card.id, "card_url": goto_url } else: card_edit_form.get_card().delete() result['errors'] = card_edit_form.errors analytics.track( actor=request.user, verb=analytics.VERBS.modified, object=analytics.OBJECTS.card, context={"deck_id": deck_id, "card_id": card_id}, ) return HttpResponse(json.dumps(result), content_type="application/json")
def share_collection(request, collection_id=None): """ Share a collection with users by creating a temporary url for authorized users to use in order to add themselves into the collection's authorized users list """ role_bucket = services.get_or_update_role_bucket(request) course_collections = LTIService(request).getCourseCollections() collection_list = queries.getCollectionList(role_bucket, collection_ids=course_collections) collection = Collection.objects.get(id=collection_id) collection_share_form = CollectionShareForm() context = { 'share_form': collection_share_form, 'nav_collections': collection_list, 'collection': collection, } if request.POST: collection_share_form = CollectionShareForm(request.POST) if collection_share_form.is_valid(): expired_in = str(collection_share_form.cleaned_data['expired_in']) # This creates an array of the share data (collection id and expiration data # for the url). Random data is appended/prepended to "obfuscate" the url. # This isn't intended to be secure by any means, just make it harder to guess. # The assumption is that a share URL will only grant a user student-level # access, so they can't do any damage if they "guess" the URL. If that # assumption changes, then this implementation should be revisited to make it # more secure and tamper-proof. share_data = [collection_id, expired_in] share_data.insert(0, utils.generate_random_id(3)) share_data.append(utils.generate_random_id(3)) share_key = '!!'.join(share_data) # This pads the share key so that the base64 encoded string doesn't include # base64 padding, which is typically the equals sign ("="). The equals sign # must be percent encoded as %3D which looks bad. Since 3 ascii characters (8 # bits) are encoded with 4 base64 characters (6 bits each) this should ensure # that the share_key is always a multiple of 3. if (len(share_key) % 3) > 0: share_key = share_key + utils.generate_random_id(3 - (len(share_key) % 3)) secret_share_key = base64.urlsafe_b64encode(share_key.encode("ascii", "ignore")) context['share_form'] = collection_share_form context['secret_share_key'] = secret_share_key log.info('URL generated to share collection %s.' %collection_id, extra={'user': request.user}) analytics.track( actor=request.user, verb=analytics.VERBS.shared, object=analytics.OBJECTS.collection, context={"collection_id": collection_id} ) else: context['share_form'] = collection_share_form return render(request, 'collections/share.html', context)
def index(request, collection_id=None): """Displays a set of collections to the user depending on whether or not the collections are private or public and whether or not the user has permission.""" if collection_id is not None: collection_id = int(collection_id) role_bucket = services.get_or_update_role_bucket(request) lti_req = LTIService(request) course_collections = lti_req.getCourseCollections() is_teacher = lti_req.isTeacher() or queries.is_superuser_or_staff( request.user) collection_admin_perms = [] for r in (Users_Collections.ADMINISTRATOR, Users_Collections.INSTRUCTOR): role_name = Users_Collections.role_map[r] collection_admin_perms.extend(role_bucket[role_name]) copy_collections = queries.getCopyCollectionList(request.user) collection_filters = dict( collection_ids=course_collections, can_filter=not queries.is_superuser_or_staff(request.user)) collection_list = queries.getCollectionList(role_bucket, **collection_filters) display_collections = queries.groupCollectionsByList(collection_list) context = { "nav_collections": collection_list, "display_collections": display_collections, "copy_collections": copy_collections, "active_collection": None, "user_collection_role": role_bucket, "collection_admin_perms": collection_admin_perms, "is_teacher": is_teacher, } if collection_id: try: cur_collection = Collection.objects.get(id=collection_id) except Collection.DoesNotExist: raise Http404 filtered_collection_list = [ c for c in collection_list if c['id'] == cur_collection.id ] if len(filtered_collection_list) == 0: raise Http404 context['active_collection'] = filtered_collection_list[0] analytics.track(actor=request.user, verb=analytics.VERBS.viewed, object=analytics.OBJECTS.collection, context={"collection_id": collection_id}) return render(request, 'collections/index.html', context)
def create(request): """Creates a collection.""" role_bucket = services.get_or_update_role_bucket(request) course_collections = LTIService(request).getCourseCollections() collection_filters = dict(collection_ids=course_collections, can_filter=not queries.is_superuser_or_staff(request.user)) collection_list = queries.getCollectionList(role_bucket, **collection_filters) if request.method == 'POST': lti_req = LTIService(request) published = not lti_req.isTeacher() request.POST = request.POST.copy() request.POST['published'] = published collection_form = CollectionForm(request.POST) card_template_id = collection_form.data['card_template'] if collection_form.is_valid(): collection = collection_form.save() lti_req.associateCourse(collection.id) services.add_user_to_collection(user=request.user, collection=collection, role=Users_Collections.ADMINISTRATOR) #update role_bucket to add admin permission to the user for this newly created collection services.get_or_update_role_bucket(request, collection.id, Users_Collections.role_map[Users_Collections.ADMINISTRATOR]) log.info('Collection %s created.' %collection.id, extra={'user': request.user}) analytics.track( actor=request.user, verb=analytics.VERBS.created, object=analytics.OBJECTS.collection, context={"custom": False} ) return redirect(collection) else: rel_templates = CardTemplate.objects.filter(Q(owner__isnull=True) | Q(owner=request.user)) initial = {'card_template': '1'} card_template_id = initial['card_template'] collection_form = CollectionForm(query_set=rel_templates,initial=initial) # Pre-populate the "preview" of the card template # This view is also called via AJAX on the page. prev_request = HttpRequest() prev_request.method = 'GET' prev_request.GET['card_template_id'] = card_template_id prev_response = card_template.preview(prev_request) card_template_preview_html = prev_response.content context = { "nav_collections": collection_list, "active_collection": None, "collection_form": collection_form, "card_template_preview_html": card_template_preview_html } return render(request, 'collections/create.html', context)
def delete(request, collection_id=None): """Deletes a collection.""" services.delete_collection(collection_id) response = redirect('collectionIndex') response['Location'] += '?instructor=edit' log.info('Collection %(c)s deleted.' % {'c': str(collection_id)}, extra={'user': request.user}) analytics.track(actor=request.user, verb=analytics.VERBS.deleted, object=analytics.OBJECTS.collection, context={"collection_id": collection_id}) return response
def log_analytics_delete(success, entity_type, entity_id, card_id, user): d = {'user': user} if success: log.info('Card deleted from the %(t) %(id)s' %{'t': entity_type, 'id': str(entity_id)}, extra=d) else: log.error('Card could not be deleted from the %(t) %(id)s' %{'t': entity_type, 'id': str(entity_id)}, extra=d) analytics.track( actor=user, verb=analytics.VERBS.deleted, object=analytics.OBJECTS.card, context={entity_type+"_id": entity_id, "card_id": card_id} )
def index(request, collection_id=None): """Displays a set of collections to the user depending on whether or not the collections are private or public and whether or not the user has permission.""" if collection_id is not None: collection_id = int(collection_id) role_bucket = services.get_or_update_role_bucket(request) lti_req = LTIService(request) course_collections = lti_req.getCourseCollections() is_teacher = lti_req.isTeacher() or queries.is_superuser_or_staff(request.user) collection_admin_perms = [] for r in (Users_Collections.ADMINISTRATOR, Users_Collections.INSTRUCTOR): role_name = Users_Collections.role_map[r] collection_admin_perms.extend(role_bucket[role_name]) copy_collections = queries.getCopyCollectionList(request.user) collection_filters = dict(collection_ids=course_collections, can_filter=not queries.is_superuser_or_staff(request.user)) collection_list = queries.getCollectionList(role_bucket, **collection_filters) display_collections = queries.groupCollectionsByList(collection_list) context = { "nav_collections": collection_list, "display_collections": display_collections, "copy_collections": copy_collections, "active_collection": None, "user_collection_role": role_bucket, "collection_admin_perms": collection_admin_perms, "is_teacher": is_teacher, } if collection_id: try: cur_collection = Collection.objects.get(id=collection_id) except Collection.DoesNotExist: raise Http404 filtered_collection_list = [c for c in collection_list if c['id'] == cur_collection.id] if len(filtered_collection_list) == 0: raise Http404 context['active_collection'] = filtered_collection_list[0] analytics.track( actor=request.user, verb=analytics.VERBS.viewed, object=analytics.OBJECTS.collection, context={"collection_id": collection_id} ) return render(request, 'collections/index.html', context)
def post(self, request, *args, **kwargs): ''' Handles the LTI launch request and redirects to the main page. ''' lti_launch_json = json.dumps(request.session['LTI_LAUNCH'], sort_keys=True, indent=4, separators=(',',': ')) log.debug("LTI launch parameters: %s" % lti_launch_json) LTIService(request).subscribeToCourseCollections() analytics.track( actor=request.user, verb=analytics.VERBS.launched, object=analytics.OBJECTS.application, context={"lti_launch": request.session['LTI_LAUNCH']} ) return redirect(self.url)
def delete(request, collection_id=None): """Deletes a collection.""" services.delete_collection(collection_id) response = redirect('collectionIndex') response['Location'] += '?instructor=edit' log.info('Collection %(c)s deleted.' %{'c': str(collection_id)}, extra={'user': request.user}) analytics.track( actor=request.user, verb=analytics.VERBS.deleted, object=analytics.OBJECTS.collection, context={"collection_id": collection_id} ) return response
def post(self, request, *args, **kwargs): ''' Handles the LTI launch request and redirects to the main page. ''' lti_launch_json = json.dumps(request.session['LTI_LAUNCH'], sort_keys=True, indent=4, separators=(',', ': ')) log.debug("LTI launch parameters: %s" % lti_launch_json) LTIService(request).subscribeToCourseCollections() analytics.track(actor=request.user, verb=analytics.VERBS.launched, object=analytics.OBJECTS.application, context={"lti_launch": request.session['LTI_LAUNCH']}) return redirect(self.url)
def track(request): actor = request.user statements_json = request.POST.get('statements', '') statements = json.loads(statements_json) num_statements = len(statements) results = [] for s in statements: verb = s.get('verb', '') object = s.get('object', '') context = s.get('context', '') if context == '': context = None timestamp = s.get('timestamp', '') if timestamp == '': timestamp = None result = {"success": False, "data": s} if verb != '' and object != '': statement = analytics.track( actor=actor, verb=verb, object=object, timestamp=timestamp, context=context, ) result['success'] = True result['data'] = statement.as_dict() results.append(result) return HttpResponse(json.dumps({"statements": results}), content_type="application/json")
def test_track_anonymous_user(self): stmt_args = { "actor": "anonymous", "verb": "did", "object": "that" } statement = analytics.track(**stmt_args) self.assertTrue(statement.model.stmt_actor_user is None) self.assertEqual(statement.model.stmt_actor_desc, stmt_args['actor'])
def edit(request, collection_id=None): """Edits a collection.""" collection = Collection.objects.get(id=collection_id) role_bucket = services.get_or_update_role_bucket(request) course_collections = LTIService(request).getCourseCollections() collection_filters = dict( collection_ids=course_collections, can_filter=not queries.is_superuser_or_staff(request.user)) collection_list = queries.getCollectionList(role_bucket, **collection_filters) if request.method == 'POST': collection_form = CollectionForm(request.POST, instance=collection) if collection_form.is_valid(): collection = collection_form.save() response = redirect(collection) response['Location'] += '?instructor=edit' analytics.track(actor=request.user, verb=analytics.VERBS.modified, object=analytics.OBJECTS.collection, context={"collection_id": collection_id}) return response else: collection_form = CollectionForm(instance=collection) collection_decks = [] for c in collection_list: if c['id'] == collection.id: collection_decks = c['decks'] break print collection_decks context = { "collection_form": collection_form, "nav_collections": collection_list, "collection": collection, "collection_decks": collection_decks, } return render(request, 'collections/edit.html', context)
def edit(request, collection_id=None): """Edits a collection.""" collection = Collection.objects.get(id=collection_id) role_bucket = services.get_or_update_role_bucket(request) course_collections = LTIService(request).getCourseCollections() collection_filters = dict(collection_ids=course_collections, can_filter=not queries.is_superuser_or_staff(request.user)) collection_list = queries.getCollectionList(role_bucket, **collection_filters) if request.method == 'POST': collection_form = CollectionForm(request.POST, instance=collection) if collection_form.is_valid(): collection = collection_form.save() response = redirect(collection) response['Location'] += '?instructor=edit' analytics.track( actor=request.user, verb=analytics.VERBS.modified, object=analytics.OBJECTS.collection, context={"collection_id": collection_id} ) return response else: collection_form = CollectionForm(instance=collection) collection_decks = [] for c in collection_list: if c['id'] == collection.id: collection_decks = c['decks'] break print collection_decks context = { "collection_form": collection_form, "nav_collections": collection_list, "collection": collection, "collection_decks": collection_decks, } return render(request, 'collections/edit.html', context)
def upload_deck(request, deck_id=None): ''' Imports a deck of cards from an excel spreadsheet. ''' upload_error = '' deck = Deck.objects.get(id=deck_id) current_collection = deck.collection role_bucket = services.get_or_update_role_bucket(request) canvas_course_collections = LTIService(request).getCourseCollections() collection_list = queries.getCollectionList( role_bucket, collection_ids=canvas_course_collections) if request.method == 'POST': d = {'user': request.user} log.info('The user is uploading a new deck.', extra=d) deck_form = DeckImportForm(request.POST, request.FILES) if deck_form.is_valid(): if 'file' in request.FILES: try: services.handle_uploaded_deck_file(deck, request.FILES['file']) log.info( 'New deck successfully added to the collection %(c)s.' % {'c': str(deck.collection.id)}, extra=d) analytics.track( actor=request.user, verb=analytics.VERBS.uploaded, object=analytics.OBJECTS.deck, context={"deck_id": deck_id}, ) return redirect(deck) except Exception, e: upload_error = str(e) msg = 'The following error occurred when the user tried uploading a deck: ' log.error(msg + upload_error, extra=d) else: log.info('No file selected.', extra=d) else: log.error('Deck Form is not valid.', extra=d)
def download_custom_template(request, collection_id=None): ''' Downloads an excel spreadsheet that may be used as a template for uploading a deck of cards. ''' response = HttpResponse(content_type='application/vnd.ms-excel') response['Content-Disposition'] = 'attachment; filename=flashcards_template.xls' file_output = utils.create_custom_template_file() response.write(file_output) log.info('Custom template downloaded.', extra={'user': request.user}) analytics.track( actor=request.user, verb=analytics.VERBS.downloaded, object=analytics.OBJECTS.template, context={"collection_id": collection_id, "custom": True} ) return response
def download_deck(request, deck_id=None): ''' Downloads a ZIP containing the excel spreadsheet of the deck of cards along with any associated media files like images or audio. ''' deck = Deck.objects.get(id=deck_id) zfile_output = services.create_zip_deck_file(deck) log.info('Deck %(d)s from the collection %(c)s downloaded by the user.' %{'d': str(deck.id), 'c': str(deck.collection.id)}, extra={'user': request.user}) response = HttpResponse(zfile_output, content_type='application/x-zip-compressed') response['Content-Disposition'] = 'attachment; filename=deck.zip' analytics.track( actor=request.user, verb=analytics.VERBS.downloaded, object=analytics.OBJECTS.deck, context={"deck_id": deck_id} ) return response
def delete(request, deck_id=None): """Deletes a deck.""" d = {'user': request.user} collection_id = queries.getDeckCollectionId(deck_id) success = services.delete_deck(deck_id) if success: log.info('Deck %(d)s deleted from collection %(c)s' %{'d':deck_id, 'c':collection_id}, extra=d) else: log.info('Deck %(d)s could not be deleted from collection %(c)s' %{'d':deck_id, 'c':collection_id}, extra=d) response = redirect('collectionIndex', collection_id) response['Location'] += '?instructor=edit' analytics.track( actor=request.user, verb=analytics.VERBS.deleted, object=analytics.OBJECTS.deck, context={"deck_id": deck_id}, ) return response
def log_analytics_delete(success, entity_type, entity_id, card_id, user): d = {'user': user} if success: log.info('Card deleted from the %(t) %(id)s' % { 't': entity_type, 'id': str(entity_id) }, extra=d) else: log.error('Card could not be deleted from the %(t) %(id)s' % { 't': entity_type, 'id': str(entity_id) }, extra=d) analytics.track(actor=user, verb=analytics.VERBS.deleted, object=analytics.OBJECTS.card, context={ entity_type + "_id": entity_id, "card_id": card_id })
def download_template(request, collection_id=None): ''' Downloads an excel spreadsheet that may be used as a template for uploading a deck of cards. ''' collection = Collection.objects.get(id=collection_id) response = HttpResponse(content_type='application/vnd.ms-excel') response['Content-Disposition'] = 'attachment; filename=flashcards_template.xls' file_output = utils.create_deck_template_file(collection.card_template) response.write(file_output) log.info('Template for the collection %(c)s downloaded by the user.' %{'c': str(collection_id)}, extra={'user': request.user}) analytics.track( actor=request.user, verb=analytics.VERBS.downloaded, object=analytics.OBJECTS.template, context={"collection_id": collection_id, "custom": False} ) return response
def download_custom_template(request, collection_id=None): ''' Downloads an excel spreadsheet that may be used as a template for uploading a deck of cards. ''' response = HttpResponse(content_type='application/vnd.ms-excel') response[ 'Content-Disposition'] = 'attachment; filename=flashcards_template.xls' file_output = utils.create_custom_template_file() response.write(file_output) log.info('Custom template downloaded.', extra={'user': request.user}) analytics.track(actor=request.user, verb=analytics.VERBS.downloaded, object=analytics.OBJECTS.template, context={ "collection_id": collection_id, "custom": True }) return response
def download_deck(request, deck_id=None): ''' Downloads a ZIP containing the excel spreadsheet of the deck of cards along with any associated media files like images or audio. ''' deck = Deck.objects.get(id=deck_id) zfile_output = services.create_zip_deck_file(deck) log.info('Deck %(d)s from the collection %(c)s downloaded by the user.' % { 'd': str(deck.id), 'c': str(deck.collection.id) }, extra={'user': request.user}) response = HttpResponse(zfile_output, content_type='application/x-zip-compressed') response['Content-Disposition'] = 'attachment; filename=deck.zip' analytics.track(actor=request.user, verb=analytics.VERBS.downloaded, object=analytics.OBJECTS.deck, context={"deck_id": deck_id}) return response
def upload_deck(request, deck_id=None): ''' Imports a deck of cards from an excel spreadsheet. ''' upload_error = '' deck = Deck.objects.get(id=deck_id) current_collection = deck.collection role_bucket = services.get_or_update_role_bucket(request) canvas_course_collections = LTIService(request).getCourseCollections() collection_list = queries.getCollectionList(role_bucket, collection_ids=canvas_course_collections) if request.method == 'POST': d = {'user': request.user} log.info('The user is uploading a new deck.', extra=d) deck_form = DeckImportForm(request.POST, request.FILES) if deck_form.is_valid(): if 'file' in request.FILES: try: services.handle_uploaded_deck_file(deck, request.FILES['file']) log.info('New deck successfully added to the collection %(c)s.' %{'c': str(deck.collection.id)}, extra=d) analytics.track( actor=request.user, verb=analytics.VERBS.uploaded, object=analytics.OBJECTS.deck, context={"deck_id": deck_id}, ) return redirect(deck) except Exception, e: upload_error = str(e) msg = 'The following error occurred when the user tried uploading a deck: ' log.error(msg + upload_error, extra=d) else: log.info('No file selected.', extra=d) else: log.error('Deck Form is not valid.', extra=d)
def test_track_anonymous_user(self): stmt_args = {"actor": "anonymous", "verb": "did", "object": "that"} statement = analytics.track(**stmt_args) self.assertTrue(statement.model.stmt_actor_user is None) self.assertEqual(statement.model.stmt_actor_desc, stmt_args['actor'])
def test_track_user(self): statement = analytics.track(**self.test_stmt_args) exists = Analytics.objects.filter(pk=statement.model.pk).exists() self.assertTrue(exists)
return redirect(deck) except Exception, e: upload_error = str(e) msg = 'The following error occurred when the user tried uploading a deck: ' log.error(msg + upload_error , extra=d) context = { "nav_collections": collection_list, "active_collection": None, 'upload_error': upload_error, "course_name": course_name or '' } analytics.track( actor=request.user, verb=analytics.VERBS.created, object=analytics.OBJECTS.collection, context={"custom": True} ) return render(request, 'collections/custom.html', context) #should only check on collections? allow any registered user to create their own? @login_required def create(request): """Creates a collection.""" role_bucket = services.get_or_update_role_bucket(request) course_collections = LTIService(request).getCourseCollections() collection_filters = dict(collection_ids=course_collections, can_filter=not queries.is_superuser_or_staff(request.user)) collection_list = queries.getCollectionList(role_bucket, **collection_filters) if request.method == 'POST':
extra=d) return redirect(deck) except Exception, e: upload_error = str(e) msg = 'The following error occurred when the user tried uploading a deck: ' log.error(msg + upload_error, extra=d) context = { "nav_collections": collection_list, "active_collection": None, 'upload_error': upload_error, "course_name": course_name or '' } analytics.track(actor=request.user, verb=analytics.VERBS.created, object=analytics.OBJECTS.collection, context={"custom": True}) return render(request, 'collections/custom.html', context) #should only check on collections? allow any registered user to create their own? @login_required def create(request): """Creates a collection.""" role_bucket = services.get_or_update_role_bucket(request) course_collections = LTIService(request).getCourseCollections() collection_filters = dict( collection_ids=course_collections, can_filter=not queries.is_superuser_or_staff(request.user)) collection_list = queries.getCollectionList(role_bucket,