-
Notifications
You must be signed in to change notification settings - Fork 61
Expand file tree
/
Copy pathuser_domain.py
More file actions
377 lines (314 loc) · 12.1 KB
/
user_domain.py
File metadata and controls
377 lines (314 loc) · 12.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
"""
Module working as a facade to all business rules from the entire system.
It must interact only with app's internal facades and can be used by views, CLI and other interfaces
"""
from logging import Logger
import requests
from celery import shared_task
from django.conf import settings
from pythonpro.cohorts import facade as _cohorts_facade
from pythonpro.core import facade as _core_facade
from pythonpro.core.models import User as _User
from pythonpro.discourse.facade import MissingDiscourseAPICredentials, generate_sso_payload_and_signature
from pythonpro.email_marketing import facade as _email_marketing_facade
from pythonpro.email_marketing.facade import create_or_update_with_no_role
_logger = Logger(__file__)
UserCreationException = _core_facade.UserCreationException # exposing exception on Facade
__all__ = [
'register_lead', 'activate_user', 'find_user_interactions',
'visit_member_landing_page', 'promote_member', 'promote_bootcamper', 'promote_webdev', 'promote_data_scientist',
'find_user_by_email', 'find_user_by_id', 'force_register_lead', 'subscribe_to_waiting_list',
'force_register_member', 'click_member_checkout', 'subscribe_anonymous_user_to_waiting_list'
]
def register_lead(first_name: str, email: str, source: str = 'unknown', phone: str = '', tags: list = []) -> _User:
"""
Create a new user on the system generation a random password.
An Welcome email is sent to the user informing his password with the link to change it.
User is also registered on Email Marketing and subscribed to LeadWorkflow and is not registered on system in case
api call fails
:param first_name: User's first name
:param email: User's email
:param source: source of User traffic
:return: User
"""
if not source:
source = 'unknown'
_core_facade.validate_user(first_name, email, source)
lead = _core_facade.register_lead(first_name, email, source)
_email_marketing_facade.create_or_update_lead.delay(
first_name, email, phone=phone, *tags, id=lead.id, utm_source=source
)
return lead
def force_register_lead(first_name: str, email: str, phone: str, source: str = 'unknown') -> _User:
"""
Create a new user on the system generation a random password.
An Welcome email is sent to the user informing his password with the link to change it.
User is also registered on Email Marketing. But she will be registered even if api call fails
:param first_name: User's first name
:param email: User's email
:param phone: User's phone
:param source: source of User traffic
:return: User
"""
user = _core_facade.register_lead(first_name, email, source)
_email_marketing_facade.create_or_update_lead.delay(first_name, email, id=user.id, phone=phone)
return user
def force_register_member(first_name, email, source='unknown'):
"""
Create a new user on the system generation a random password or update existing one based on email.
An Welcome email is sent to the user informing his password with the link to change it.
User is also registered on Email Marketing. But she will be registered even if api call fails
:param first_name: User's first name
:param email: User's email
:param source: source of User traffic
:return: User
"""
user = _core_facade.register_member(first_name, email, source)
_cohorts_facade.subscribe_to_last_cohort(user)
cohort = _cohorts_facade.find_most_recent_cohort()
sync_user_on_discourse.delay(user.id)
_email_marketing_facade.create_or_update_member.delay(first_name, email, f'turma-{cohort.slug}', id=user.id)
return user
def promote_member(user: _User, source: str) -> _User:
"""
Promote a user to Member 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_member(user, source)
_cohorts_facade.subscribe_to_last_cohort(user)
cohort = _cohorts_facade.find_most_recent_cohort()
sync_user_on_discourse.delay(user.id)
_email_marketing_facade.create_or_update_member.delay(
user.first_name, user.email, f'turma-{cohort.slug}', id=user.id
)
return user
def promote_bootcamper(user: _User, source: str) -> _User:
"""
Promote a user to Bootcamper 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_bootcamper(user, source)
_cohorts_facade.subscribe_to_last_cohort(user)
cohort = _cohorts_facade.find_most_recent_cohort()
sync_user_on_discourse.delay(user.id)
_email_marketing_facade.create_or_update_bootcamper.delay(
user.first_name, user.email, f'turma-{cohort.slug}', id=user.id
)
return user
def promote_webdev(user: _User, source: str) -> _User:
"""
Promote a user to Webdev 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_webdev(user, source)
sync_user_on_discourse.delay(user.id)
_email_marketing_facade.create_or_update_webdev.delay(user.first_name, user.email, id=user.id)
return user
def promote_fellow(user: _User, source: str) -> _User:
"""
Promote a user to Fellow role and change it's role on Email Marketing. Will not fail in case API call fails.
:param source: source of traffic
:param user:
:return:
"""
try:
_core_facade.promote_to_fellow(user, source)
except _core_facade.UserRoleException:
pass
_email_marketing_facade.create_or_update_fellow.delay(user.first_name, user.email, id=user.id)
return user
def promote_data_scientist(user: _User, source: str) -> _User:
"""
Promote a user to DataScientist 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_data_scientist(user, source)
sync_user_on_discourse.delay(user.id)
_email_marketing_facade.create_or_update_data_scientist.delay(
user.first_name, user.email, id=user.id)
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
:param user_email:
:return: User
"""
return _core_facade.find_user_by_email(user_email)
def find_user_by_id(user_id: int) -> _User:
"""
Find user by her id
:param user_id:
:return:
"""
return _core_facade.find_user_by_id(user_id)
def find_user_interactions(user: _User):
"""
Find all user interactions ordered by creation date desc
:param user:
:return: list of user interactions
"""
return _core_facade.find_user_interactions(user)
def visit_member_landing_page(user, source):
"""
Mark user as visited member landing page
:param source: string containing source of traffic
:param user:
:return:
"""
_core_facade.visit_member_landing_page(user, source)
_email_marketing_facade.tag_as.delay(user.email, user.id, 'potential-member')
def visit_launch_landing_page(user, source):
"""
Mark user as visited launch landing page
:param source: string containing source of traffic
:param user:
:return:
"""
_core_facade.visit_launch_landing_page(user, source)
def subscribe_launch_landing_page(user, source):
"""
Mark user as subscribed to launch
:param source: string containing source of traffic
:param user:
:return:
"""
_core_facade.subscribe_to_launch(user, source)
def click_member_checkout(user):
"""
Mark user as visited member landing page
:param user:
:return:
"""
_core_facade.member_checkout(user, None)
_email_marketing_facade.tag_as.delay(user.email, user.id, 'member-checkout')
def member_generated_boleto(user):
_core_facade.member_generated_boleto(user, None)
def subscribe_to_waiting_list(session_id, user: _User, phone: str, source: str) -> None:
"""
Subscribe user to waiting list
:param session_id:
:param user:
:param phone:
:param source:
:return:
"""
_core_facade.subscribe_to_waiting_list(user, source)
create_or_update_with_no_role.delay(
user.first_name, user.email, 'lista-de-espera', id=user.id, phone=phone
)
def subscribe_anonymous_user_to_waiting_list(session_id, email: str, name: str, phone: str, source: str) -> None:
"""
Subscribe anonymous user to waiting list
:param session_id:
:param email:
:param name:
:param phone:
:param source:
:return:
"""
try:
user = _core_facade.find_user_by_email(email)
except _User.DoesNotExist:
create_or_update_with_no_role.delay(name, email, 'lista-de-espera', phone=phone)
else:
subscribe_to_waiting_list(None, user, phone, source)
def activate_user(user: _User, source: str) -> None:
"""
Activate user
:param user:
:param source:
:return:
"""
_core_facade.activate_user(user, source)
_email_marketing_facade.remove_tags.delay(user.email, user.id, 'never-watched-video')
def visit_cpl1(user: _User, source: str) -> None:
"""
User visit CPL1
:param user:
:param source:
:return:
"""
_core_facade.visit_cpl1(user, source)
_email_marketing_facade.tag_as.delay(user.email, user.id, 'cpl1')
def visit_cpl2(user: _User, source: str) -> None:
"""
User visit CPL2
:param user:
:param source:
:return:
"""
_core_facade.visit_cpl2(user, source)
_email_marketing_facade.tag_as.delay(user.email, user.id, 'cpl2')
def visit_cpl3(user: _User, source: str) -> None:
"""
User visit CPL2
:param user:
:param source:
:return:
"""
_core_facade.visit_cpl3(user, source)
_email_marketing_facade.tag_as.delay(user.email, user.id, 'cpl3')
@shared_task
def sync_user_on_discourse(user_or_id, *groups):
"""
Synchronize user data on forum if API is configured
:param user_or_id: Django user or his id
:return: returns result of hitting Discourse api
"""
can_make_api_call = bool(settings.DISCOURSE_API_KEY and settings.DISCOURSE_API_USER)
can_work_without_sync = not (settings.DISCOURSE_BASE_URL or can_make_api_call)
if can_work_without_sync:
_logger.info('Discourse Integration not available')
return
elif not can_make_api_call:
raise MissingDiscourseAPICredentials('Must define both DISCOURSE_API_KEY and DISCOURSE_API_USER configs')
user = _core_facade.find_user_by_id(user_or_id)
# https://meta.discourse.org/t/sync-sso-user-data-with-the-sync-sso-route/84398
params = {
'email': user.email,
'name': user.first_name,
'external_id': user.id,
'require_activation': 'false',
'groups': ','.join(groups)
}
sso_payload, signature = generate_sso_payload_and_signature(params)
url = f'{settings.DISCOURSE_BASE_URL}/admin/users/sync_sso'
headers = {
'content-type': 'multipart/form-data',
'Api-Key': settings.DISCOURSE_API_KEY,
'Api-Username': settings.DISCOURSE_API_USER,
}
requests.post(url, data={'sso': sso_payload, 'sig': signature}, headers=headers)
def register_user(first_name, email, source='', *args, **kwargs):
user, _ = _User.objects.get_or_create(email=email)
user.first_name = first_name
user.email = email
user.source = source
user.save()
_email_marketing_facade.create_or_update_with_no_role.delay(first_name, email)
return user