Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ def client_with_user(client, logged_user):
return client


_all_roles = set('data_scientist lead client webdev bootcamper member'.split())
_advanced_roles = {'member'}
_all_roles = set('data_scientist lead client webdev bootcamper pythonista member'.split())
_advanced_roles = set('member pythonista'.split())
_level_one_roles = set('client webdev bootcamper member'.split())
_level_two_roles = set('webdev bootcamper member'.split())
_level_three_roles = set('bootcamper member'.split())
Expand Down
10 changes: 9 additions & 1 deletion pythonpro/core/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class UserAdmin(RolePermissionsUserAdminMixin, admin.ModelAdmin):
search_fields = ('first_name', 'email')
ordering = ('first_name',)
filter_horizontal = ('groups', 'user_permissions',)
actions = ['make_bootcamper', 'make_webdev', 'make_member', 'make_data_scientist']
actions = ['make_bootcamper', 'make_webdev', 'make_member', 'make_data_scientist', 'make_pythonista']

def make_webdev(self, request, queryset):
from pythonpro.domain import user_facade
Expand All @@ -71,6 +71,14 @@ def make_bootcamper(self, request, queryset):
except UserRoleException:
pass # No need to handle on admin

def make_pythonista(self, request, queryset):
from pythonpro.domain import user_facade
for user in queryset:
try:
user_facade.promote_pythonista(user, 'django_admin')
except UserRoleException:
pass # No need to handle on admin

def make_data_scientist(self, request, queryset):
from pythonpro.domain import user_facade
for user in queryset:
Expand Down
5 changes: 5 additions & 0 deletions pythonpro/core/facade.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ def promote_to_data_scientist(user, source):
assign_role(user, 'data_scientist')


def promote_to_pythonista(user, source):
UserInteraction(category=UserInteraction.BECOME_PYTHONISTA, source=source, user=user).save()
assign_role(user, 'pythonista')


def visit_launch_landing_page(user: User, source: str):
return UserInteraction(category=UserInteraction.LAUNCH_LP, source=source, user=user).save()

Expand Down
34 changes: 34 additions & 0 deletions pythonpro/core/migrations/0016_become_pythonista.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Generated by Django 3.0.8 on 2020-07-23 01:22

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
('core', '0015_become_bootcamper'),
]

operations = [
migrations.AlterField(
model_name='userinteraction',
name='category',
field=models.CharField(
choices=[('BECOME_LEAD', 'User become Lead'), ('ACTIVATED', 'User Watched first video class'),
('CLIENT_LP', 'User visited Client Landing Page'),
('CLIENT_CHECKOUT', 'User clicked on Client checkout button'),
('CLIENT_CHECKOUT_FORM', 'User Filled Client Checkout form'),
('CLIENT_BOLETO', 'User generated a Client Boleto'), ('BECOME_CLIENT', 'User become Client'),
('MEMBER_LP', 'User visited Member Landing Page'),
('MEMBER_CHECKOUT', 'User clicked on Member checkout Button'),
('MEMBER_CHECKOUT_FORM', 'User Filled Member Checkout form'),
('WEBDEV_CHECKOUT_FORM', 'User Filled Webdev Checkout form'),
('MEMBER_BOLETO', 'User generate Member Boleto'),
('WAITING_LIST', 'User subscribed to Waiting List'), ('BECOME_MEMBER', 'User Become Member'),
('BECOME_BOOTCAMPER', 'User Become Bootcamper'),
('BECOME_PYTHONISTA', 'User Become Pythonista'), ('BECOME_WEBDEV', 'User Become Webdev'),
('LAUNCH_LP', 'User visited Launch Landing Page'),
('LAUNCH_SUBSCRIPTION', 'User subscribed to launch'), ('CPL1', 'User Visited CPL1'),
('CPL2', 'User Visited CPL2'), ('CPL3', 'User Visited CPL3'),
('BECOME_DATA_SCIENTIST', 'User Become Data Scientist')], max_length=32),
),
]
2 changes: 2 additions & 0 deletions pythonpro/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ class Meta:
WAITING_LIST = 'WAITING_LIST'
BECOME_MEMBER = 'BECOME_MEMBER'
BECOME_BOOTCAMPER = 'BECOME_BOOTCAMPER'
BECOME_PYTHONISTA = 'BECOME_PYTHONISTA'
WEBDEV_CHECKOUT_FORM = 'WEBDEV_CHECKOUT_FORM'
BECOME_WEBDEV = 'BECOME_WEBDEV'
LAUNCH_LP = 'LAUNCH_LP'
Expand Down Expand Up @@ -121,6 +122,7 @@ class Meta:
(WAITING_LIST, 'User subscribed to Waiting List'),
(BECOME_MEMBER, 'User Become Member'),
(BECOME_BOOTCAMPER, 'User Become Bootcamper'),
(BECOME_PYTHONISTA, 'User Become Pythonista'),
(BECOME_WEBDEV, 'User Become Webdev'),
(LAUNCH_LP, 'User visited Launch Landing Page'),
(LAUNCH_SUBSCRIPTION, 'User subscribed to launch'),
Expand Down
11 changes: 11 additions & 0 deletions pythonpro/core/roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,22 @@ class Bootcamper(AbstractUserRole):
}


watch_pythonista_modules = 'watch_pythonista_modules'


class Pythonista(AbstractUserRole):
available_permissions = {
watch_pythonista_modules: True,
access_forum: True,
}


watch_member_modules = 'watch_member_modules'


class Member(AbstractUserRole):
available_permissions = {
watch_pythonista_modules: True,
watch_webdev_modules: True,
watch_lead_modules: True,
watch_client_modules: True,
Expand Down
15 changes: 15 additions & 0 deletions pythonpro/domain/user_facade.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,21 @@ def promote_data_scientist(user: _User, source: str) -> _User:
return user


def promote_pythonista(user: _User, source: str) -> _User:
"""
Promote a user to Pythonista role and change it's role on Email Marketing. Will not fail in case API call fails.
Email welcome email is sent to user
:param source: source of traffic
:param user:
:return:
"""
_core_facade.promote_to_pythonista(user, source)
sync_user_on_discourse.delay(user.id)
_email_marketing_facade.create_or_update_pythonista.delay(
user.first_name, user.email, id=user.id)
return user


def find_user_by_email(user_email: str) -> _User:
"""
Find user by her email
Expand Down
11 changes: 9 additions & 2 deletions pythonpro/email_marketing/facade.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@
WEBDEV = 'webdev'
BOOTCAMPER = 'bootcamper'
DATA_SCIENTIST = 'data-scientist'
PYTHONISTA = 'pythonista'

_PYTHON_PRO_ROLES = {LEAD, CLIENT, WEBDEV, BOOTCAMPER, MEMBER}

_ALL_ROLES = set(_PYTHON_PRO_ROLES)
_ALL_ROLES.add(DATA_SCIENTIST)
_ALL_ROLES.add(PYTHONISTA)

run_until_available = shared_task(autoretry_for=(JSONDecodeError,), retry_backoff=True, max_retries=None)

Expand All @@ -43,6 +45,11 @@ def create_or_update_data_scientist(name: str, email: str, *tags, id='0', phone=
return create_or_update_user(name, email, DATA_SCIENTIST, *tags, id=id, phone=phone)


@run_until_available
def create_or_update_pythonista(name: str, email: str, *tags, id='0', phone=None):
return create_or_update_user(name, email, PYTHONISTA, *tags, id=id, phone=phone)


@run_until_available
def create_or_update_client(name: str, email: str, *tags, id='0', phone=None):
return create_or_update_user(name, email, CLIENT, *tags, id=id, phone=phone)
Expand Down Expand Up @@ -120,8 +127,8 @@ def grant_role(email, id, role: str):
role = role.lower()

if role not in _ALL_ROLES:
raise ValueError(f'Role {role} must be one of {_PYTHON_PRO_ROLES}')
if role == DATA_SCIENTIST:
raise ValueError(f'Role {role} must be one of {_ALL_ROLES}')
if role in {DATA_SCIENTIST, PYTHONISTA}:
roles_to_remove = set()
role_to_grant = role.capitalize()
elif role in _PYTHON_PRO_ROLES:
Expand Down
5 changes: 4 additions & 1 deletion pythonpro/modules/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
Member,
watch_client_modules,
watch_lead_modules,
watch_webdev_modules, watch_bootcamp_modules
watch_webdev_modules, watch_bootcamp_modules, watch_pythonista_modules
)
from pythonpro.modules.models import Content

_LEAD_MODULES = {'python-birds'}
_CLIENT_MODULES = {'python-birds', 'pytools'}
_WEBDEV_MODULES = {'python-birds', 'pytools', 'django'}
_BOOTCAMPER_MODULES = {'python-birds', 'pytools', 'django', 'entrevistas-tecnicas'}
_PYTHONISTA_MODULES = {'python-birds', 'objetos-pythonicos', 'python-para-pythonistas', 'python-patterns'}


@register_object_checker()
Expand All @@ -24,6 +25,8 @@ def access_content(role, user, content: Content) -> bool:
return True
if module_slug in _WEBDEV_MODULES and has_permission(user, watch_webdev_modules):
return True
if module_slug in _PYTHONISTA_MODULES and has_permission(user, watch_pythonista_modules):
return True
if module_slug in _CLIENT_MODULES and has_permission(user, watch_client_modules):
return True
if module_slug in _LEAD_MODULES and has_permission(user, watch_lead_modules):
Expand Down