Beispiel #1
0
def edit_group(request, group_id):
    """Edit a group."""
    group = get_object_or_404(
        model.Group.objects.select_related('owner'), pk=group_id)
    if group.owner != request.user.profile:
        raise http.Http404

    if request.method == 'POST':
        if request.POST.get('remove', False):
            with xact.xact():
                group.delete()
            messages.success(request, "Group '%s' removed." % group)
            return redirect(home.redirect_home(request.user))
        form = forms.GroupForm(request.POST, instance=group)
        if form.is_valid():
            with xact.xact():
                group = form.save()
            tracking.track(request, 'edited group')
            return redirect('group', group_id=group.id)
    else:
        form = forms.GroupForm(instance=group)

    return TemplateResponse(
        request,
        'village/group_form/edit_group.html',
        {
            'form': form,
            'group': group,
            },
        )
Beispiel #2
0
def edit_student(request, student_id):
    """Edit a student."""
    rel = get_relationship_or_404(student_id, request.user.profile)
    group = get_querystring_group(request, rel.student)

    if request.method == 'POST':
        if request.POST.get('remove', False):
            with xact.xact():
                rel.delete()
            messages.success(request, "Student '%s' removed." % rel.student)
            if group:
                return redirect('group', group_id=group.id)
            else:
                return redirect('all_students')
        form = forms.StudentForm(
            request.POST, instance=rel.student, elder=rel.elder)
        if form.is_valid():
            with xact.xact():
                student = form.save()
            tracking.track(request, 'edited student')
            return redirect_to_village(student, group)
    else:
        form = forms.StudentForm(instance=rel.student, elder=rel.elder)

    return TemplateResponse(
        request,
        'village/student_form/edit_student.html',
        {
            'form': form,
            'student': rel.student,
            'group': group,
            },
        )
def test_savepoint_rollback(mock_transaction_methods):
    with pytest.raises(IntegrityError):
        with xact.xact():
            with xact.xact():
                raise IntegrityError()

    assert mock_transaction_methods['savepoint_rollback'].call_count == 1
    assert mock_transaction_methods['savepoint_commit'].call_count == 0
Beispiel #4
0
def edit_elder(request, elder_id, student_id=None, group_id=None):
    """Edit a village elder."""
    elder = get_object_or_404(
        model.Profile.objects.select_related('user'), id=elder_id)
    # can't edit the profile of another school staff
    if elder.school_staff:
        raise http.Http404
    if student_id is not None:
        teacher_rel = get_relationship_or_404(student_id, request.user.profile)
        editor = model.elder_in_context(teacher_rel)
        elder_rel = get_relationship_or_404(student_id, elder)
        elder = model.elder_in_context(elder_rel)
        all_elders = elder_rel.student.elder_relationships
        group = get_querystring_group(request, elder_rel.student)
    else:
        elder_rel = None
        editor = request.user.profile
        if group_id is not None:
            group = get_object_or_404(model.Group.objects.filter(
                    owner=request.user.profile), pk=group_id)
        else:
            group = model.AllStudentsGroup(request.user.profile)
        all_elders = group.all_elders

    if request.method == 'POST':
        form = forms.EditFamilyForm(request.POST, instance=elder, rel=elder_rel)
        success = False
        if elder_rel and request.POST.get('remove', False):
            with xact.xact():
                elder_rel.delete()
            success = True
        elif form.is_valid():
            with xact.xact():
                form.save(editor=editor)
            messages.success(request, u"Changes saved!")
            success = True
        if success:
            if elder_rel:
                return redirect('village', student_id=student_id)
            elif group and not group.is_all:
                return redirect('group', group_id=group.id)
            return redirect('all_students')
    else:
        form = forms.EditFamilyForm(instance=elder, rel=elder_rel)

    return TemplateResponse(
        request,
        'village/elder_form/edit_elder.html',
        {
            'form': form,
            'group': group,
            'student': elder_rel.student if elder_rel else None,
            'inviter': editor,
            'elder': elder,
            'elders': model.contextualized_elders(
                all_elders).order_by('school_staff', 'name'),
            },
        )
def test_nested_xact_uses_savepoints(mock_transaction_methods):
    mock_sp_commit = mock_transaction_methods['savepoint_commit']
    mock_sp = mock_transaction_methods['savepoint']
    mock_sp.return_value = 3

    with xact.xact():
        with xact.xact():
            pass

    assert mock_sp.call_count == 1
    assert mock_sp_commit.call_count == 1
    mock_sp_commit.assert_called_with(3, 'default')
def test_error_in_savepoint_commit_causes_rollback(mock_transaction_methods):
    mock_sp_commit = mock_transaction_methods['savepoint_commit']
    mock_sp_rollback = mock_transaction_methods['savepoint_rollback']
    mock_sp = mock_transaction_methods['savepoint']
    mock_sp.return_value = 4
    mock_sp_commit.side_effect = IntegrityError()

    with pytest.raises(IntegrityError):
        with xact.xact():
            with xact.xact():
                pass

    assert mock_sp_commit.call_count == 1
    assert mock_sp_rollback.call_count == 1
    mock_sp_rollback.assert_called_with(4, 'default')
Beispiel #7
0
def invite_family(request, student_id):
    """Invite family member to a student's village."""
    rel = get_relationship_or_404(student_id, request.user.profile)
    group = get_querystring_group(request, rel.student)

    if request.method == 'POST':
        form = forms.InviteFamilyForm(request.POST, rel=rel)
        if form.is_valid():
            with xact.xact():
                form.save()
            tracking.track(request, 'invited family')
            return redirect_to_village(rel.student, group)
    else:
        phone = request.GET.get('phone', None)
        initial = {}
        if phone is not None:
            initial['phone'] = phone
        form = forms.InviteFamilyForm(initial=initial, rel=rel)

    return TemplateResponse(
        request,
        'village/invite_elder/family.html',
        {
            'group': group,
            'student': rel.student,
            'inviter': model.elder_in_context(rel),
            'elders': model.contextualized_elders(
                rel.student.elder_relationships).order_by(
                'school_staff', 'name'),
            'form': form,
            },
        )
Beispiel #8
0
def add_student(request, group_id=None):
    """Add a student."""
    group = get_object_or_404(model.Group, id=group_id) if group_id else None

    if request.method == 'POST':
        form = forms.AddStudentForm(
            request.POST, elder=request.user.profile, group=group)
        if form.is_valid():
            with xact.xact():
                student = form.save()
            tracking.track(request, 'added student')
            return redirect_to_village(student, group)
    else:
        form = forms.AddStudentForm(elder=request.user.profile, group=group)

    return TemplateResponse(
        request,
        'village/student_form/add_student.html',
        {
            'form': form,
            'group': group,
            'code': group.code if group else request.user.profile.code,
            'default_lang_code': settings.LANGUAGE_CODE,
            'pyo_phone': formats.display_phone(
                request.user.profile.source_phone),
            'group_just_created': group and request.GET.get('created', None),
            },
        )
Beispiel #9
0
def invite_teacher_to_group(request, group_id):
    """Invite teacher to a group."""
    group = get_object_or_404(
        model.Group.objects.filter(owner=request.user.profile), id=group_id)

    if request.method == 'POST':
        form = forms.InviteTeacherForm(request.POST, group=group)
        if form.is_valid():
            with xact.xact():
                teacher = form.save()
            tracking.track(
                request,
                'invited teacher',
                invitedEmail=teacher.user.email,
                groupId=group_id,
                )
            return redirect('group', group_id=group.id)
    else:
        form = forms.InviteTeacherForm(group=group)

    return TemplateResponse(
        request,
        'village/invite_elder/teacher_to_group.html',
        {
            'group': group,
            'form': form,
            'elders': model.contextualized_elders(
                group.all_elders).order_by('school_staff', 'name'),
            },
        )
Beispiel #10
0
def invite_teacher(request, student_id):
    """Invite teacher to a student's village."""
    rel = get_relationship_or_404(student_id, request.user.profile)
    group = get_querystring_group(request, rel.student)

    if request.method == 'POST':
        form = forms.InviteTeacherForm(request.POST, rel=rel)
        if form.is_valid():
            with xact.xact():
                teacher = form.save()
            tracking.track(
                request,
                'invited teacher',
                invitedEmail=teacher.user.email,
                studentId=student_id,
                )
            return redirect_to_village(rel.student, group)
    else:
        form = forms.InviteTeacherForm(rel=rel)

    return TemplateResponse(
        request,
        'village/invite_elder/teacher.html',
        {
            'group': group,
            'student': rel.student,
            'form': form,
            'elders': model.contextualized_elders(
                rel.student.elder_relationships).order_by(
                'school_staff', 'name'),
            },
        )
def test_error_in_commit(mock_transaction_methods):
    """Error in commit causes rollback and re-raises error."""
    mock_transaction_methods['commit'].side_effect = IntegrityError()
    with pytest.raises(IntegrityError):
        with xact.xact():
            pass

    assert mock_transaction_methods['rollback'].call_count == 1
Beispiel #12
0
    def test_tasks_run_after_transaction(self, sms):
        """Task is not applied until after transaction-in-progress commits."""
        # If we actually touched the database in this test, we would need the
        # `transactional_db` fixture; but we don't, so we avoid the slowdown.
        with xact.xact():
            tasks.send_sms.delay('+15555555555', '+15555555555', 'something')
            assert len(sms.outbox) == 0

        assert len(sms.outbox) == 1
Beispiel #13
0
    def test_tasks_run_after_transaction(self, sms):
        """Task is not applied until after transaction-in-progress commits."""
        # If we actually touched the database in this test, we would need the
        # `transactional_db` fixture; but we don't, so we avoid the slowdown.
        with xact.xact():
            tasks.send_sms.delay('+15555555555', '+15555555555', 'something')
            assert len(sms.outbox) == 0

        assert len(sms.outbox) == 1
def test_can_use_twice():
    """Can use same xact object as context manager twice."""
    cm = xact.xact()

    assert not transaction.is_managed()

    for i in range(2):
        with cm:
            assert transaction.is_managed()
        assert not transaction.is_managed()
Beispiel #15
0
def donate(request):
    if request.method == 'POST':
        form = forms.DonateForm(request.POST)
        if form.is_valid():
            with xact.xact():
                stripe.api_key = STRIPE_PRIVATE_KEY
                token = request.POST['stripeToken']
                try:
                    charge = stripe.Charge.create(
                        amount=int(form.cleaned_data['amount']) *
                        100,  # amount needs to be in cents
                        currency="usd",
                        card=token,
                        description=form.cleaned_data['email'])

                    school = form.cleaned_data['school']
                    if school.id is None:
                        # this could just set country_code and then school.save(), but that
                        # creates a race condition for two users creating same school at
                        # same time, resulting in IntegrityError
                        school, created = model.School.objects.get_or_create(
                            name=school.name,
                            postcode=school.postcode,
                            defaults={
                                'country_code':
                                form.cleaned_data['country_code'],
                                'auto': school.auto,
                            },
                        )

                    donation = model.Donation(
                        name=form.cleaned_data['name'],
                        email=form.cleaned_data['email'],
                        phone=form.cleaned_data['phone'],
                        country_code=form.cleaned_data['country_code'],
                        school=school,
                        amount=int(form.cleaned_data['amount']) * 100,
                        charge_data=json.dumps(charge),
                        # We could add a currency field if donating in CAD is necessary
                    )
                    donation.save()

                except stripe.AuthenticationError:
                    # The stripe key was incorrect
                    messages.failure(
                        request,
                        u"Sorry, there was an issue with the payment.")
                except stripe.CardError, e:
                    # The card has been declined
                    messages.failure(
                        request,
                        u"Sorry, there was an issue with the payment.")

            messages.success(request, u"Payment accepted!")
            return redirect(redirect_home(request.user))
Beispiel #16
0
def edit_profile(request):
    if request.method == 'POST':
        form = forms.EditProfileForm(
            request.POST, instance=request.user.profile)
        if form.is_valid():
            with xact.xact():
                form.save()
            messages.success(request, u"Profile changes saved!")
            return redirect(redirect_home(request.user))
    else:
        form = forms.EditProfileForm(instance=request.user.profile)

    return TemplateResponse(request, 'users/edit_profile.html', {'form': form})
Beispiel #17
0
def edit_profile(request):
    if request.method == 'POST':
        form = forms.EditProfileForm(request.POST,
                                     instance=request.user.profile)
        if form.is_valid():
            with xact.xact():
                form.save()
            messages.success(request, u"Profile changes saved!")
            return redirect(redirect_home(request.user))
    else:
        form = forms.EditProfileForm(instance=request.user.profile)

    return TemplateResponse(request, 'users/edit_profile.html', {'form': form})
def test_post_commit_listener_uses_db(transactional_db):
    """Test that a post_commit listener can safely query the database."""
    called = []

    def _my_listener(**kwargs):
        assert model.Profile.objects.count() == 1
        called.append(True)

    xact.post_commit.connect(_my_listener)

    with xact.xact():
        factories.ProfileFactory.create()

    assert called == [True]
Beispiel #19
0
def donate(request):
    if request.method == 'POST':
        form = forms.DonateForm(request.POST)
        if form.is_valid():
            with xact.xact():
                stripe.api_key = STRIPE_PRIVATE_KEY
                token = request.POST['stripeToken']
                try:
                    charge = stripe.Charge.create(
                        amount=int(form.cleaned_data['amount'])*100, # amount needs to be in cents
                        currency="usd",
                        card=token,
                        description=form.cleaned_data['email']
                    )

                    school = form.cleaned_data['school']
                    if school.id is None:
                        # this could just set country_code and then school.save(), but that
                        # creates a race condition for two users creating same school at
                        # same time, resulting in IntegrityError
                        school, created = model.School.objects.get_or_create(
                            name=school.name,
                            postcode=school.postcode,
                            defaults={
                                'country_code': form.cleaned_data['country_code'],
                                'auto': school.auto,
                                },
                            )

                    donation = model.Donation(
                        name=form.cleaned_data['name'],
                        email=form.cleaned_data['email'],
                        phone=form.cleaned_data['phone'],
                        country_code=form.cleaned_data['country_code'],
                        school=school,
                        amount = int(form.cleaned_data['amount'])*100,
                        charge_data = json.dumps(charge),
                        # We could add a currency field if donating in CAD is necessary
                    )
                    donation.save()

                except stripe.AuthenticationError:
                    # The stripe key was incorrect
                    messages.failure(request, u"Sorry, there was an issue with the payment.")
                except stripe.CardError, e:
                    # The card has been declined
                    messages.failure(request, u"Sorry, there was an issue with the payment.")

            messages.success(request, u"Payment accepted!")
            return redirect(redirect_home(request.user))
Beispiel #20
0
    def test_tasks_discarded_if_transaction_rolled_back(self, sms):
        """If transaction is rolled back, pending tasks are discarded."""
        class TestException(Exception):
            pass
        try:
            with xact.xact():
                tasks.send_sms.delay(
                    '+15555555555', '+15555555555', 'something')
                # exception causes transaction to be rolled back
                raise TestException()
        except TestException:
            pass

        # task was discarded because transaction was rolled back
        assert len(sms.outbox) == 0
Beispiel #21
0
def twilio_receive(request):
    """Receive an SMS via Twilio."""
    source = request.POST['From']
    to = request.POST['To']
    body = request.POST['Body']

    with xact.xact():
        reply = hook.receive_sms(source, to, body)

    response = twiml.Response()

    if reply:
        for chunk in split_sms(reply):
            response.sms(chunk)

    return response
Beispiel #22
0
def twilio_receive(request):
    """Receive an SMS via Twilio."""
    source = request.POST["From"]
    to = request.POST["To"]
    body = request.POST["Body"]

    with xact.xact():
        reply = hook.receive_sms(source, to, body)

    response = twiml.Response()

    if reply:
        for chunk in split_sms(reply):
            response.sms(chunk)

    return response
Beispiel #23
0
    def test_tasks_discarded_if_transaction_rolled_back(self, sms):
        """If transaction is rolled back, pending tasks are discarded."""
        class TestException(Exception):
            pass

        try:
            with xact.xact():
                tasks.send_sms.delay('+15555555555', '+15555555555',
                                     'something')
                # exception causes transaction to be rolled back
                raise TestException()
        except TestException:
            pass

        # task was discarded because transaction was rolled back
        assert len(sms.outbox) == 0
Beispiel #24
0
def register(request):
    if request.method == 'POST':
        form = forms.RegistrationForm(request.POST)
        if form.is_valid():
            with xact.xact():
                profile = form.save()
                user = profile.user
                user.backend = settings.AUTHENTICATION_BACKENDS[0]
                auth.login(request, user)
                token_generator = tokens.EmailConfirmTokenGenerator()
                invites.send_invite_email(
                    profile,
                    'emails/welcome',
                    token_generator=token_generator,
                    )
                messages.success(
                    request,
                    "Welcome to Portfoliyo! "
                    "Grab your phone and add yourself as a parent "
                    "to see how it works!"
                    )
                tracking.track(
                    request,
                    'registered',
                    email_notifications=(
                        'yes'
                        if form.cleaned_data.get('email_notifications')
                        else 'no'
                        ),
                    user_id=user.id,
                    )
            return redirect(redirect_home(user))
    else:
        form = forms.RegistrationForm()

    return TemplateResponse(
        request,
        'users/register.html',
        {'form': form},
        )
Beispiel #25
0
def confirm_email(request, uidb36, token):
    """Confirm an email address."""
    try:
        uid_int = base36_to_int(uidb36)
        user = model.User.objects.get(id=uid_int)
    except (ValueError, model.User.DoesNotExist):
        user = None

    token_generator = tokens.EmailConfirmTokenGenerator()

    if user is not None and token_generator.check_token(user, token):
        with xact.xact():
            user.is_active = True
            user.save(force_update=True)
            profile = user.profile
            profile.email_confirmed = True
            profile.save(force_update=True)
            messages.success(request, "Email address %s confirmed!" % user.email)
            tracking.track(request, 'confirmed email')
        return redirect(redirect_home(user))

    return TemplateResponse(request, 'users/confirmation_failed.html')
Beispiel #26
0
def add_group(request):
    """Add a group."""
    if request.method == 'POST':
        form = forms.AddGroupForm(request.POST, owner=request.user.profile)
        if form.is_valid():
            with xact.xact():
                group = form.save()
            tracking.track(request, 'added group')
            if not group.students.exists():
                return redirect(
                    reverse('add_student', kwargs={'group_id': group.id})
                    + '?created=1')
            return redirect('group', group_id=group.id)
    else:
        form = forms.AddGroupForm(owner=request.user.profile)

    return TemplateResponse(
        request,
        'village/group_form/add_group.html',
        {
            'form': form,
            },
        )
Beispiel #27
0
def confirm_email(request, uidb36, token):
    """Confirm an email address."""
    try:
        uid_int = base36_to_int(uidb36)
        user = model.User.objects.get(id=uid_int)
    except (ValueError, model.User.DoesNotExist):
        user = None

    token_generator = tokens.EmailConfirmTokenGenerator()

    if user is not None and token_generator.check_token(user, token):
        with xact.xact():
            user.is_active = True
            user.save(force_update=True)
            profile = user.profile
            profile.email_confirmed = True
            profile.save(force_update=True)
            messages.success(request,
                             "Email address %s confirmed!" % user.email)
            tracking.track(request, 'confirmed email')
        return redirect(redirect_home(user))

    return TemplateResponse(request, 'users/confirmation_failed.html')
Beispiel #28
0
def register(request):
    if request.method == 'POST':
        form = forms.RegistrationForm(request.POST)
        if form.is_valid():
            with xact.xact():
                profile = form.save()
                user = profile.user
                user.backend = settings.AUTHENTICATION_BACKENDS[0]
                auth.login(request, user)
                token_generator = tokens.EmailConfirmTokenGenerator()
                invites.send_invite_email(
                    profile,
                    'emails/welcome',
                    token_generator=token_generator,
                )
                messages.success(
                    request, "Welcome to Portfoliyo! "
                    "Grab your phone and add yourself as a parent "
                    "to see how it works!")
                tracking.track(
                    request,
                    'registered',
                    email_notifications=(
                        'yes' if form.cleaned_data.get('email_notifications')
                        else 'no'),
                    user_id=user.id,
                )
            return redirect(redirect_home(user))
    else:
        form = forms.RegistrationForm()

    return TemplateResponse(
        request,
        'users/register.html',
        {'form': form},
    )
Beispiel #29
0
 def wrap_view(self, view):
     """Wrap in transaction; undo csrf-exempt."""
     wrapper = xact.xact(super(PortfoliyoResource, self).wrap_view(view))
     wrapper.csrf_exempt = False
     return wrapper
Beispiel #30
0
 def admin_view(self, *args, **kwargs):
     """Wrap all admin views in a transaction."""
     wrapped = super(AdminSite, self).admin_view(*args, **kwargs)
     return xact.xact(wrapped)
Beispiel #31
0
def create_post(request, student_id=None, group_id=None):
    """
    Create a post.

    If ``student_id`` is provided in the URL, the post will be a single-village
    post. If ``group_id`` is provided, it will be a group bulk post. If neither
    is provided, it will be an all-students bulk post.

    POST parameters accepted:

    ``text``

        The text of the post to create. Must be few enough characters that,
        when the user's auto-signature is appended, the resulting full SMS
        message is <160 characters.

    ``type``

        The type of post to create: "message", "note", "call", or
        "meeting". This parameter is ignored for bulk posts; all bulk posts are
        of type "message".

    ``elder``

        A list of elder IDs connected with this post. For a "message" type
        post, these users will receive the post via SMS. For a "meeting" or
        "call" type post, these are the users who were present on the call or
        at the meeting.

    ``extra_name``

       A list of additional names connected with this post. (For instance, for
       a "meeting" or "call" type post, these are names of additional people
       present at the meeting or on the call, who are not actually elders in
       the village.)

    ``author_sequence_id``

       An increasing numeric ID for posts authored by this user in this browser
       session. This value is opaque to the server and not stored anywhere, but
       is round-tripped through Pusher back to the client, to simplify
       matching up post data and avoid creating duplicates on the client.

    For non-bulk posts, an ``attachment`` file-upload parameter is also
    optionally accepted.

    Returns JSON object with boolean key ``success``. If ``success`` is
    ``False``, a human-readable message will be provided in the ``error``
    key. If ``success`` is ``True``, the ``objects`` key will be a list
    containing one JSON-serialized post object. (Even though this view will
    only ever return one post, it still returns a list for better compatibility
    with other client-side JSON-handling code.)

    """
    if 'text' not in request.POST:
        return http.HttpResponseBadRequest(
            json.dumps(
                {
                    'error': "Must provide a 'text' querystring parameter.",
                    'success': False,
                    }
                ),
            content_type='application/json',
            )

    extra_kwargs = {}
    group = None
    rel = None
    post_model = model.BulkPost
    profile_ids = 'all'
    if student_id is not None:
        rel = get_relationship_or_404(student_id, request.user.profile)
        post_model = model.Post
        target = rel.student
        profile_ids = request.POST.getlist('elder')
        extra_kwargs['extra_names'] = request.POST.getlist('extra_name')
        extra_kwargs['post_type'] = request.POST.get('type')
        if 'attachment' in request.FILES:
            extra_kwargs['attachments'] = request.FILES.getlist('attachment')
        redirect_url = reverse('village', kwargs={'student_id': student_id})
        qs_group = get_querystring_group(request, rel.student)
        if qs_group:
            redirect_url += "?group=%s" % qs_group.id
    elif group_id is not None:
        group = get_object_or_404(
            model.Group.objects.filter(owner=request.user.profile), pk=group_id)
        target = group
        redirect_url = reverse('group', kwargs={'group_id': group_id})
    else:
        target = None
        redirect_url = reverse('all_students')

    text = request.POST['text']
    sequence_id = request.POST.get('author_sequence_id')
    limit = model.post_char_limit(rel or request.user.profile)
    if len(text) > limit:
        return http.HttpResponseBadRequest(
            json.dumps(
                {
                    'error': 'Posts are limited to %s characters.' % limit,
                    'success': False,
                    }
                ),
            content_type='application/json',
            )

    with xact.xact():
        post = post_model.create(
            request.user.profile,
            target,
            text,
            profile_ids=profile_ids,
            sequence_id=sequence_id,
            **extra_kwargs)

    if request.is_ajax():
        data = {
            'success': True,
            'objects': [
                serializers.post2dict(
                    post, author_sequence_id=sequence_id, unread=False, mine=True)
                ],
            }

        return http.HttpResponse(
            json.dumps(data), content_type='application/json')
    else:
        return http.HttpResponseRedirect(redirect_url)
Beispiel #32
0
 def admin_view(self, *args, **kwargs):
     """Wrap all admin views in a transaction."""
     wrapped = super(AdminSite, self).admin_view(*args, **kwargs)
     return xact.xact(wrapped)
Beispiel #33
0
 def wrap_view(self, view):
     """Wrap in transaction; undo csrf-exempt."""
     wrapper = xact.xact(super(PortfoliyoResource, self).wrap_view(view))
     wrapper.csrf_exempt = False
     return wrapper