We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Выпилить интеграцию с mailjet (она давно не используется)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/app/settings.py`
Content:
```
1 import environ
2 import os
3 from celery.schedules import crontab
4 
5 root = environ.Path(__file__) - 2        # three folder back (/a/b/c/ - 3 = /)
6 env = environ.Env(DEBUG=(bool, False))  # set default values and casting
7 environ.Env.read_env()                   # reading .env file
8 SITE_ROOT = root()
9 
10 USE_L10N = True
11 USE_i18N = True
12 
13 LANGUAGE_CODE = 'ru'
14 LOCALE_PATHS = ['locale']
15 
16 INTERNAL_IPS = [
17     '127.0.0.1',
18 ]
19 FRONTEND_URL = 'https://education.borshev.com'
20 
21 USE_TZ = False
22 TIME_ZONE = env('TIME_ZONE', cast=str, default='Europe/Moscow')
23 
24 # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
25 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
26 TEST_RUNNER = 'app.test.disable_test_command_runner.DisableTestCommandRunner'
27 
28 
29 # Quick-start development settings - unsuitable for production
30 # See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
31 
32 # SECURITY WARNING: keep the secret key used in production secret!
33 SECRET_KEY = env('SECRET_KEY', cast=str, default='s3cr3t')
34 
35 # SECURITY WARNING: don't run with debug turned on in production!
36 DEBUG = env('DEBUG', cast=bool, default=False)
37 CI = env('CI', cast=bool, default=False)
38 ANONYMIZE_ENABLED = DEBUG
39 
40 ABSOLUTE_HOST = env('ABSOLUTE_HOST', cast=str, default='https://edu-app.borshev.com')
41 ALLOWED_HOSTS = [
42     'edu-app.borshev.com',
43     'localhost',
44     'localhost:8000',
45     'education.borshev.com',
46     ABSOLUTE_HOST.replace('https://', ''),
47 ]
48 
49 CORS_ORIGIN_WHITELIST = [
50     'https://pmdaily.ru',
51     'https://education.borshev.com',
52 ]
53 
54 CSRF_TRUSTED_ORIGINS = [
55     'pmdaily.ru',
56     'education.borshev.com',
57     'borshev.com',
58 ]
59 
60 
61 # Application definition
62 
63 INSTALLED_APPS = [
64     'app',
65     'users',
66     'orders',
67     'products',
68     'shipping',
69     'tinkoff',
70     'triggers',
71     'magnets',
72 
73     'corsheaders',
74     'hattori',
75     'anymail',
76     'rest_framework',
77     'rest_framework.authtoken',
78     'django_filters',
79 
80     'axes',
81     'django.contrib.admin',
82     'django.contrib.auth',
83     'django.contrib.contenttypes',
84     'django.contrib.sessions',
85     'django.contrib.messages',
86     'django.contrib.staticfiles',
87 
88     'debug_toolbar',
89 ]
90 
91 MIDDLEWARE = [
92     'django.middleware.security.SecurityMiddleware',
93 
94     'django.contrib.sessions.middleware.SessionMiddleware',
95     'corsheaders.middleware.CorsMiddleware',
96     'django.middleware.common.CommonMiddleware',
97     # 'django.middleware.csrf.CsrfViewMiddleware',
98     'django.contrib.auth.middleware.AuthenticationMiddleware',
99     'django.contrib.auth.middleware.RemoteUserMiddleware',
100     'django.contrib.messages.middleware.MessageMiddleware',
101     'django.middleware.clickjacking.XFrameOptionsMiddleware',
102     'axes.middleware.AxesMiddleware',
103     'debug_toolbar.middleware.DebugToolbarMiddleware',
104 ]
105 
106 if not DEBUG and not CI:
107     MIDDLEWARE.insert(1, 'whitenoise.middleware.WhiteNoiseMiddleware')
108 
109 
110 REST_FRAMEWORK = {
111     'DEFAULT_PERMISSION_CLASSES': (
112         'rest_framework.permissions.IsAuthenticated',
113     ),
114     'DEFAULT_AUTHENTICATION_CLASSES': (
115         'rest_framework.authentication.TokenAuthentication',
116     ),
117     'DEFAULT_RENDERER_CLASSES': [
118         'app.renderers.AppJSONRenderer',
119     ],
120     'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
121     'DEFAULT_PAGINATION_CLASS': 'app.pagination.AppPagination',
122     'PAGE_SIZE': 20,
123 }
124 
125 ROOT_URLCONF = 'app.urls'
126 
127 TEMPLATES = [
128     {
129         'BACKEND': 'django.template.backends.django.DjangoTemplates',
130         'DIRS': [],
131         'APP_DIRS': True,
132         'OPTIONS': {
133             'context_processors': [
134                 'django.template.context_processors.debug',
135                 'django.template.context_processors.request',
136                 'django.contrib.auth.context_processors.auth',
137                 'django.contrib.messages.context_processors.messages',
138             ],
139         },
140     },
141 ]
142 
143 WSGI_APPLICATION = 'app.wsgi.application'
144 
145 
146 # Database
147 # https://docs.djangoproject.com/en/2.2/ref/settings/#databases
148 DATABASES = {
149     'default': env.db(),    # Raises ImproperlyConfigured exception if DATABASE_URL not in os.environ
150 }
151 AUTH_USER_MODEL = 'users.User'
152 AUTHENTICATION_BACKENDS = [
153     'axes.backends.AxesBackend',
154     'django.contrib.auth.backends.ModelBackend',
155     'django.contrib.auth.backends.RemoteUserBackend',
156 ]
157 HEALTH_CHECKS_ERROR_CODE = 503
158 HEALTH_CHECKS = {
159     'db': 'django_healthchecks.contrib.check_database',
160 }
161 
162 MEDIA_URL = env('MEDIA_URL', default='/media/')
163 
164 STATIC_URL = env('STATIC_URL', default='/static/')
165 STATIC_ROOT = env('STATIC_ROOT')
166 
167 SENTRY_DSN = env('SENTRY_DSN', cast=str, default='')
168 
169 if not DEBUG and len(SENTRY_DSN):
170     import sentry_sdk
171     from sentry_sdk.integrations.celery import CeleryIntegration
172     from sentry_sdk.integrations.django import DjangoIntegration
173     from sentry_sdk.integrations.redis import RedisIntegration
174 
175     sentry_sdk.init(
176         dsn=SENTRY_DSN,
177         integrations=[DjangoIntegration(), CeleryIntegration(), RedisIntegration()],
178     )
179 
180 BROKER_URL = env('CELERY_BACKEND')
181 CELERY_ALWAYS_EAGER = env('CELERY_ALWAYS_EAGER', cast=bool, default=DEBUG)  # by default in debug mode we run all celery tasks in foregroud.
182 CELERY_TIMEZONE = TIME_ZONE
183 CELERY_ENABLE_UTC = False
184 CELERYBEAT_SCHEDULE = {
185     'run_started_purchase_trigger': {
186         'task': 'triggers.tasks.check_for_started_purchase_triggers',
187         'schedule': crontab(hour='*', minute=15),
188     },
189     'run_record_feedback_trigger': {
190         'task': 'triggers.tasks.check_for_record_feedback_triggers',
191         'schedule': crontab(hour='*', minute=15),
192     },
193     'ship_unshipped_orders': {
194         'task': 'orders.tasks.ship_unshipped_orders',
195         'schedule': crontab(hour='*', minute='*/2'),
196     },
197 }
198 
199 
200 AWS_ACCESS_KEY_ID = env('AWS_ACCESS_KEY_ID', default=None)
201 AWS_SECRET_ACCESS_KEY = env('AWS_SECRET_ACCESS_KEY', default=None)
202 AWS_STORAGE_BUCKET_NAME = env('AWS_STORAGE_BUCKET_NAME', default=None)
203 AWS_S3_REGION_NAME = env('AWS_S3_REGION_NAME', default=None)
204 AWS_S3_ENDPOINT_URL = env('AWS_S3_ENDPOINT_URL', default=None)
205 
206 EMAIL_ENABLED = env('EMAIL_ENABLED', cast=bool, default=False)
207 
208 EMAIL_BACKEND = env('EMAIL_BACKEND', default='django.core.mail.backends.console.EmailBackend')
209 
210 MAILJET_API_KEY = env('MAILJET_API_KEY', default='')
211 MAILJET_SECRET_KEY = env('MAILJET_SECRET_KEY', default='')
212 MAILJET_CONTACT_LIST_ID = env('MAILJET_CONTACT_LIST_ID', cast=int, default=None)
213 
214 MAILCHIMP_API_KEY = env('MAILCHIMP_API_KEY', default='')
215 MAILCHIMP_CONTACT_LIST_ID = env('MAILCHIMP_CONTACT_LIST_ID', cast=str, default=None)
216 
217 DEFAULT_FROM_EMAIL = env('EMAIL_FROM', cast=str, default='')
218 ANYMAIL = {
219     'POSTMARK_SERVER_TOKEN': env('POSTMARK_SERVER_TOKEN', cast=str, default=''),
220     'DEBUG_API_REQUESTS': env('DEBUG'),
221 }
222 
223 CLICKMEETING_API_KEY = env('CLICKMEETING_API_KEY', default=None, cast=str)
224 
225 ZOOMUS_API_KEY = env('ZOOMUS_API_KEY', default=None, cast=str)
226 ZOOMUS_API_SECRET = env('ZOOMUS_API_SECRET', default=None, cast=str)
227 
228 TINKOFF_TERMINAL_KEY = env('TINKOFF_TERMINAL_KEY', default=None)
229 TINKOFF_TERMINAL_PASSWORD = env('TINKOFF_TERMINAL_PASSWORD', default=None)
230 
231 SEND_HAPPINESS_MESSAGES = env('SEND_HAPPINESS_MESSAGES', cast=bool, default=False)
232 
233 # Uncomment this lines to catch all runtime warnings as errors
234 
235 # import warnings  # noqa
236 # warnings.filterwarnings(
237 #     'error', r".*",
238 #     RuntimeWarning, r".*"
239 # )
240
```

Path: `src/app/integrations/mailjet.py`
Content:
```
1 import mailjet_rest as mailjet
2 from django.conf import settings
3 from django.utils.functional import cached_property
4 
5 from users.models import User
6 
7 
8 class AppMailjetWrongResponseException(Exception):
9     pass
10 
11 
12 class AppMailjet:
13     def __init__(self):
14         pass
15 
16     @cached_property
17     def client(self):
18         return mailjet.Client(
19             auth=(settings.MAILJET_API_KEY, settings.MAILJET_SECRET_KEY),
20             version='v3',
21         )
22 
23     def subscribe(self, user: User):
24         response = self.client.contactslist_managecontact.create(
25             id=settings.MAILJET_CONTACT_LIST_ID,
26             data={
27                 'Action': 'addnoforce',
28                 'Email': user.email,
29                 'Properties': {
30                     'name': str(user),
31                     'firstname': user.first_name,
32                     'lastname': user.last_name,
33                 },
34             },
35         )
36 
37         if response.status_code != 201:
38             raise AppMailjetWrongResponseException(f'Wrong response from mailjet: {response.status_code}. Content: {response.content}')
39
```

Path: `src/app/tasks.py`
Content:
```
1 from typing import List, Union
2 
3 from anymail.exceptions import AnymailRequestsAPIError
4 from django.apps import apps
5 from django.conf import settings
6 from requests.exceptions import RequestException
7 
8 from app.celery import celery
9 from app.integrations import tg
10 from app.integrations.clickmeeting import ClickMeetingClient, ClickMeetingHTTPException
11 from app.integrations.mailchimp import AppMailchimp, MailchimpException
12 from app.integrations.mailjet import AppMailjet, AppMailjetWrongResponseException
13 from app.integrations.zoomus import ZoomusClient, ZoomusHTTPException
14 from app.mail.owl import TemplOwl
15 
16 
17 @celery.task(
18     autoretry_for=[AnymailRequestsAPIError],
19     retry_kwargs={
20         'max_retries': 10,
21         'countdown': 5,
22     },
23 )
24 def send_mail(to: Union[List, str], template_id, subject: str = '', ctx: dict = None, disable_antispam=False):
25     TemplOwl(
26         to=to,
27         template_id=template_id,
28         subject=subject,
29         ctx=ctx,
30         disable_antispam=disable_antispam,
31     ).send()
32 
33 
34 @celery.task(
35     autoretry_for=[RequestException, ClickMeetingHTTPException],
36     retry_kwargs={
37         'max_retries': 10,
38         'countdown': 5,
39     },
40 )
41 def invite_to_clickmeeting(room_url: str, email: str):
42     client = ClickMeetingClient()
43     client.invite(room_url, email)
44 
45 
46 @celery.task(
47     autoretry_for=[RequestException, ZoomusHTTPException],
48     retry_kwargs={
49         'max_retries': 10,
50         'countdown': 5,
51     },
52 )
53 def invite_to_zoomus(webinar_id: str, user_id: int):
54     user = apps.get_model('users.User').objects.get(pk=user_id)
55 
56     client = ZoomusClient()
57     client.invite(webinar_id, user)
58 
59 
60 @celery.task(
61     autoretry_for=[RequestException, AppMailjetWrongResponseException],
62     retry_kwargs={
63         'max_retries': 10,
64         'countdown': 5,
65     },
66 )
67 def subscribe_to_mailjet(user_id: int):
68     if not all(getattr(settings, x) for x in ['MAILJET_API_KEY', 'MAILJET_SECRET_KEY', 'MAILJET_CONTACT_LIST_ID']):
69         return
70 
71     user = apps.get_model('users.User').objects.get(pk=user_id)
72     mailjet = AppMailjet()
73 
74     mailjet.subscribe(user)
75 
76 
77 @celery.task(
78     autoretry_for=[RequestException, MailchimpException],
79     retry_kwargs={
80         'max_retries': 10,
81         'countdown': 5,
82     },
83 )
84 def subscribe_to_mailchimp(user_id: int, list_id=None, tags=None):
85     if list_id is None:
86         list_id = settings.MAILCHIMP_CONTACT_LIST_ID
87 
88     if not list_id:
89         return
90 
91     mailchimp = AppMailchimp()
92 
93     mailchimp.subscribe_django_user(
94         list_id=list_id,
95         user=apps.get_model('users.User').objects.get(pk=user_id),
96         tags=tags,
97     )
98 
99 
100 @celery.task
101 def send_happiness_message(text):
102     tg.send_happiness_message(text)
103
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/app/integrations/mailjet.py b/src/app/integrations/mailjet.py
deleted file mode 100644
--- a/src/app/integrations/mailjet.py
+++ /dev/null
@@ -1,38 +0,0 @@
-import mailjet_rest as mailjet
-from django.conf import settings
-from django.utils.functional import cached_property
-
-from users.models import User
-
-
-class AppMailjetWrongResponseException(Exception):
-    pass
-
-
-class AppMailjet:
-    def __init__(self):
-        pass
-
-    @cached_property
-    def client(self):
-        return mailjet.Client(
-            auth=(settings.MAILJET_API_KEY, settings.MAILJET_SECRET_KEY),
-            version='v3',
-        )
-
-    def subscribe(self, user: User):
-        response = self.client.contactslist_managecontact.create(
-            id=settings.MAILJET_CONTACT_LIST_ID,
-            data={
-                'Action': 'addnoforce',
-                'Email': user.email,
-                'Properties': {
-                    'name': str(user),
-                    'firstname': user.first_name,
-                    'lastname': user.last_name,
-                },
-            },
-        )
-
-        if response.status_code != 201:
-            raise AppMailjetWrongResponseException(f'Wrong response from mailjet: {response.status_code}. Content: {response.content}')
diff --git a/src/app/settings.py b/src/app/settings.py
--- a/src/app/settings.py
+++ b/src/app/settings.py
@@ -207,10 +207,6 @@
 
 EMAIL_BACKEND = env('EMAIL_BACKEND', default='django.core.mail.backends.console.EmailBackend')
 
-MAILJET_API_KEY = env('MAILJET_API_KEY', default='')
-MAILJET_SECRET_KEY = env('MAILJET_SECRET_KEY', default='')
-MAILJET_CONTACT_LIST_ID = env('MAILJET_CONTACT_LIST_ID', cast=int, default=None)
-
 MAILCHIMP_API_KEY = env('MAILCHIMP_API_KEY', default='')
 MAILCHIMP_CONTACT_LIST_ID = env('MAILCHIMP_CONTACT_LIST_ID', cast=str, default=None)
 
diff --git a/src/app/tasks.py b/src/app/tasks.py
--- a/src/app/tasks.py
+++ b/src/app/tasks.py
@@ -9,7 +9,6 @@
 from app.integrations import tg
 from app.integrations.clickmeeting import ClickMeetingClient, ClickMeetingHTTPException
 from app.integrations.mailchimp import AppMailchimp, MailchimpException
-from app.integrations.mailjet import AppMailjet, AppMailjetWrongResponseException
 from app.integrations.zoomus import ZoomusClient, ZoomusHTTPException
 from app.mail.owl import TemplOwl
 
@@ -57,23 +56,6 @@
     client.invite(webinar_id, user)
 
 
-@celery.task(
-    autoretry_for=[RequestException, AppMailjetWrongResponseException],
-    retry_kwargs={
-        'max_retries': 10,
-        'countdown': 5,
-    },
-)
-def subscribe_to_mailjet(user_id: int):
-    if not all(getattr(settings, x) for x in ['MAILJET_API_KEY', 'MAILJET_SECRET_KEY', 'MAILJET_CONTACT_LIST_ID']):
-        return
-
-    user = apps.get_model('users.User').objects.get(pk=user_id)
-    mailjet = AppMailjet()
-
-    mailjet.subscribe(user)
-
-
 @celery.task(
     autoretry_for=[RequestException, MailchimpException],
     retry_kwargs={

{"golden_diff": "diff --git a/src/app/integrations/mailjet.py b/src/app/integrations/mailjet.py\ndeleted file mode 100644\n--- a/src/app/integrations/mailjet.py\n+++ /dev/null\n@@ -1,38 +0,0 @@\n-import mailjet_rest as mailjet\n-from django.conf import settings\n-from django.utils.functional import cached_property\n-\n-from users.models import User\n-\n-\n-class AppMailjetWrongResponseException(Exception):\n-    pass\n-\n-\n-class AppMailjet:\n-    def __init__(self):\n-        pass\n-\n-    @cached_property\n-    def client(self):\n-        return mailjet.Client(\n-            auth=(settings.MAILJET_API_KEY, settings.MAILJET_SECRET_KEY),\n-            version='v3',\n-        )\n-\n-    def subscribe(self, user: User):\n-        response = self.client.contactslist_managecontact.create(\n-            id=settings.MAILJET_CONTACT_LIST_ID,\n-            data={\n-                'Action': 'addnoforce',\n-                'Email': user.email,\n-                'Properties': {\n-                    'name': str(user),\n-                    'firstname': user.first_name,\n-                    'lastname': user.last_name,\n-                },\n-            },\n-        )\n-\n-        if response.status_code != 201:\n-            raise AppMailjetWrongResponseException(f'Wrong response from mailjet: {response.status_code}. Content: {response.content}')\ndiff --git a/src/app/settings.py b/src/app/settings.py\n--- a/src/app/settings.py\n+++ b/src/app/settings.py\n@@ -207,10 +207,6 @@\n \n EMAIL_BACKEND = env('EMAIL_BACKEND', default='django.core.mail.backends.console.EmailBackend')\n \n-MAILJET_API_KEY = env('MAILJET_API_KEY', default='')\n-MAILJET_SECRET_KEY = env('MAILJET_SECRET_KEY', default='')\n-MAILJET_CONTACT_LIST_ID = env('MAILJET_CONTACT_LIST_ID', cast=int, default=None)\n-\n MAILCHIMP_API_KEY = env('MAILCHIMP_API_KEY', default='')\n MAILCHIMP_CONTACT_LIST_ID = env('MAILCHIMP_CONTACT_LIST_ID', cast=str, default=None)\n \ndiff --git a/src/app/tasks.py b/src/app/tasks.py\n--- a/src/app/tasks.py\n+++ b/src/app/tasks.py\n@@ -9,7 +9,6 @@\n from app.integrations import tg\n from app.integrations.clickmeeting import ClickMeetingClient, ClickMeetingHTTPException\n from app.integrations.mailchimp import AppMailchimp, MailchimpException\n-from app.integrations.mailjet import AppMailjet, AppMailjetWrongResponseException\n from app.integrations.zoomus import ZoomusClient, ZoomusHTTPException\n from app.mail.owl import TemplOwl\n \n@@ -57,23 +56,6 @@\n     client.invite(webinar_id, user)\n \n \n-@celery.task(\n-    autoretry_for=[RequestException, AppMailjetWrongResponseException],\n-    retry_kwargs={\n-        'max_retries': 10,\n-        'countdown': 5,\n-    },\n-)\n-def subscribe_to_mailjet(user_id: int):\n-    if not all(getattr(settings, x) for x in ['MAILJET_API_KEY', 'MAILJET_SECRET_KEY', 'MAILJET_CONTACT_LIST_ID']):\n-        return\n-\n-    user = apps.get_model('users.User').objects.get(pk=user_id)\n-    mailjet = AppMailjet()\n-\n-    mailjet.subscribe(user)\n-\n-\n @celery.task(\n     autoretry_for=[RequestException, MailchimpException],\n     retry_kwargs={\n", "issue": "\u0412\u044b\u043f\u0438\u043b\u0438\u0442\u044c \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e \u0441 mailjet (\u043e\u043d\u0430 \u0434\u0430\u0432\u043d\u043e \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f)\n\n", "before_files": [{"content": "import environ\nimport os\nfrom celery.schedules import crontab\n\nroot = environ.Path(__file__) - 2        # three folder back (/a/b/c/ - 3 = /)\nenv = environ.Env(DEBUG=(bool, False))  # set default values and casting\nenviron.Env.read_env()                   # reading .env file\nSITE_ROOT = root()\n\nUSE_L10N = True\nUSE_i18N = True\n\nLANGUAGE_CODE = 'ru'\nLOCALE_PATHS = ['locale']\n\nINTERNAL_IPS = [\n    '127.0.0.1',\n]\nFRONTEND_URL = 'https://education.borshev.com'\n\nUSE_TZ = False\nTIME_ZONE = env('TIME_ZONE', cast=str, default='Europe/Moscow')\n\n# Build paths inside the project like this: os.path.join(BASE_DIR, ...)\nBASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\nTEST_RUNNER = 'app.test.disable_test_command_runner.DisableTestCommandRunner'\n\n\n# Quick-start development settings - unsuitable for production\n# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/\n\n# SECURITY WARNING: keep the secret key used in production secret!\nSECRET_KEY = env('SECRET_KEY', cast=str, default='s3cr3t')\n\n# SECURITY WARNING: don't run with debug turned on in production!\nDEBUG = env('DEBUG', cast=bool, default=False)\nCI = env('CI', cast=bool, default=False)\nANONYMIZE_ENABLED = DEBUG\n\nABSOLUTE_HOST = env('ABSOLUTE_HOST', cast=str, default='https://edu-app.borshev.com')\nALLOWED_HOSTS = [\n    'edu-app.borshev.com',\n    'localhost',\n    'localhost:8000',\n    'education.borshev.com',\n    ABSOLUTE_HOST.replace('https://', ''),\n]\n\nCORS_ORIGIN_WHITELIST = [\n    'https://pmdaily.ru',\n    'https://education.borshev.com',\n]\n\nCSRF_TRUSTED_ORIGINS = [\n    'pmdaily.ru',\n    'education.borshev.com',\n    'borshev.com',\n]\n\n\n# Application definition\n\nINSTALLED_APPS = [\n    'app',\n    'users',\n    'orders',\n    'products',\n    'shipping',\n    'tinkoff',\n    'triggers',\n    'magnets',\n\n    'corsheaders',\n    'hattori',\n    'anymail',\n    'rest_framework',\n    'rest_framework.authtoken',\n    'django_filters',\n\n    'axes',\n    'django.contrib.admin',\n    'django.contrib.auth',\n    'django.contrib.contenttypes',\n    'django.contrib.sessions',\n    'django.contrib.messages',\n    'django.contrib.staticfiles',\n\n    'debug_toolbar',\n]\n\nMIDDLEWARE = [\n    'django.middleware.security.SecurityMiddleware',\n\n    'django.contrib.sessions.middleware.SessionMiddleware',\n    'corsheaders.middleware.CorsMiddleware',\n    'django.middleware.common.CommonMiddleware',\n    # 'django.middleware.csrf.CsrfViewMiddleware',\n    'django.contrib.auth.middleware.AuthenticationMiddleware',\n    'django.contrib.auth.middleware.RemoteUserMiddleware',\n    'django.contrib.messages.middleware.MessageMiddleware',\n    'django.middleware.clickjacking.XFrameOptionsMiddleware',\n    'axes.middleware.AxesMiddleware',\n    'debug_toolbar.middleware.DebugToolbarMiddleware',\n]\n\nif not DEBUG and not CI:\n    MIDDLEWARE.insert(1, 'whitenoise.middleware.WhiteNoiseMiddleware')\n\n\nREST_FRAMEWORK = {\n    'DEFAULT_PERMISSION_CLASSES': (\n        'rest_framework.permissions.IsAuthenticated',\n    ),\n    'DEFAULT_AUTHENTICATION_CLASSES': (\n        'rest_framework.authentication.TokenAuthentication',\n    ),\n    'DEFAULT_RENDERER_CLASSES': [\n        'app.renderers.AppJSONRenderer',\n    ],\n    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',\n    'DEFAULT_PAGINATION_CLASS': 'app.pagination.AppPagination',\n    'PAGE_SIZE': 20,\n}\n\nROOT_URLCONF = 'app.urls'\n\nTEMPLATES = [\n    {\n        'BACKEND': 'django.template.backends.django.DjangoTemplates',\n        'DIRS': [],\n        'APP_DIRS': True,\n        'OPTIONS': {\n            'context_processors': [\n                'django.template.context_processors.debug',\n                'django.template.context_processors.request',\n                'django.contrib.auth.context_processors.auth',\n                'django.contrib.messages.context_processors.messages',\n            ],\n        },\n    },\n]\n\nWSGI_APPLICATION = 'app.wsgi.application'\n\n\n# Database\n# https://docs.djangoproject.com/en/2.2/ref/settings/#databases\nDATABASES = {\n    'default': env.db(),    # Raises ImproperlyConfigured exception if DATABASE_URL not in os.environ\n}\nAUTH_USER_MODEL = 'users.User'\nAUTHENTICATION_BACKENDS = [\n    'axes.backends.AxesBackend',\n    'django.contrib.auth.backends.ModelBackend',\n    'django.contrib.auth.backends.RemoteUserBackend',\n]\nHEALTH_CHECKS_ERROR_CODE = 503\nHEALTH_CHECKS = {\n    'db': 'django_healthchecks.contrib.check_database',\n}\n\nMEDIA_URL = env('MEDIA_URL', default='/media/')\n\nSTATIC_URL = env('STATIC_URL', default='/static/')\nSTATIC_ROOT = env('STATIC_ROOT')\n\nSENTRY_DSN = env('SENTRY_DSN', cast=str, default='')\n\nif not DEBUG and len(SENTRY_DSN):\n    import sentry_sdk\n    from sentry_sdk.integrations.celery import CeleryIntegration\n    from sentry_sdk.integrations.django import DjangoIntegration\n    from sentry_sdk.integrations.redis import RedisIntegration\n\n    sentry_sdk.init(\n        dsn=SENTRY_DSN,\n        integrations=[DjangoIntegration(), CeleryIntegration(), RedisIntegration()],\n    )\n\nBROKER_URL = env('CELERY_BACKEND')\nCELERY_ALWAYS_EAGER = env('CELERY_ALWAYS_EAGER', cast=bool, default=DEBUG)  # by default in debug mode we run all celery tasks in foregroud.\nCELERY_TIMEZONE = TIME_ZONE\nCELERY_ENABLE_UTC = False\nCELERYBEAT_SCHEDULE = {\n    'run_started_purchase_trigger': {\n        'task': 'triggers.tasks.check_for_started_purchase_triggers',\n        'schedule': crontab(hour='*', minute=15),\n    },\n    'run_record_feedback_trigger': {\n        'task': 'triggers.tasks.check_for_record_feedback_triggers',\n        'schedule': crontab(hour='*', minute=15),\n    },\n    'ship_unshipped_orders': {\n        'task': 'orders.tasks.ship_unshipped_orders',\n        'schedule': crontab(hour='*', minute='*/2'),\n    },\n}\n\n\nAWS_ACCESS_KEY_ID = env('AWS_ACCESS_KEY_ID', default=None)\nAWS_SECRET_ACCESS_KEY = env('AWS_SECRET_ACCESS_KEY', default=None)\nAWS_STORAGE_BUCKET_NAME = env('AWS_STORAGE_BUCKET_NAME', default=None)\nAWS_S3_REGION_NAME = env('AWS_S3_REGION_NAME', default=None)\nAWS_S3_ENDPOINT_URL = env('AWS_S3_ENDPOINT_URL', default=None)\n\nEMAIL_ENABLED = env('EMAIL_ENABLED', cast=bool, default=False)\n\nEMAIL_BACKEND = env('EMAIL_BACKEND', default='django.core.mail.backends.console.EmailBackend')\n\nMAILJET_API_KEY = env('MAILJET_API_KEY', default='')\nMAILJET_SECRET_KEY = env('MAILJET_SECRET_KEY', default='')\nMAILJET_CONTACT_LIST_ID = env('MAILJET_CONTACT_LIST_ID', cast=int, default=None)\n\nMAILCHIMP_API_KEY = env('MAILCHIMP_API_KEY', default='')\nMAILCHIMP_CONTACT_LIST_ID = env('MAILCHIMP_CONTACT_LIST_ID', cast=str, default=None)\n\nDEFAULT_FROM_EMAIL = env('EMAIL_FROM', cast=str, default='')\nANYMAIL = {\n    'POSTMARK_SERVER_TOKEN': env('POSTMARK_SERVER_TOKEN', cast=str, default=''),\n    'DEBUG_API_REQUESTS': env('DEBUG'),\n}\n\nCLICKMEETING_API_KEY = env('CLICKMEETING_API_KEY', default=None, cast=str)\n\nZOOMUS_API_KEY = env('ZOOMUS_API_KEY', default=None, cast=str)\nZOOMUS_API_SECRET = env('ZOOMUS_API_SECRET', default=None, cast=str)\n\nTINKOFF_TERMINAL_KEY = env('TINKOFF_TERMINAL_KEY', default=None)\nTINKOFF_TERMINAL_PASSWORD = env('TINKOFF_TERMINAL_PASSWORD', default=None)\n\nSEND_HAPPINESS_MESSAGES = env('SEND_HAPPINESS_MESSAGES', cast=bool, default=False)\n\n# Uncomment this lines to catch all runtime warnings as errors\n\n# import warnings  # noqa\n# warnings.filterwarnings(\n#     'error', r\".*\",\n#     RuntimeWarning, r\".*\"\n# )\n", "path": "src/app/settings.py"}, {"content": "import mailjet_rest as mailjet\nfrom django.conf import settings\nfrom django.utils.functional import cached_property\n\nfrom users.models import User\n\n\nclass AppMailjetWrongResponseException(Exception):\n    pass\n\n\nclass AppMailjet:\n    def __init__(self):\n        pass\n\n    @cached_property\n    def client(self):\n        return mailjet.Client(\n            auth=(settings.MAILJET_API_KEY, settings.MAILJET_SECRET_KEY),\n            version='v3',\n        )\n\n    def subscribe(self, user: User):\n        response = self.client.contactslist_managecontact.create(\n            id=settings.MAILJET_CONTACT_LIST_ID,\n            data={\n                'Action': 'addnoforce',\n                'Email': user.email,\n                'Properties': {\n                    'name': str(user),\n                    'firstname': user.first_name,\n                    'lastname': user.last_name,\n                },\n            },\n        )\n\n        if response.status_code != 201:\n            raise AppMailjetWrongResponseException(f'Wrong response from mailjet: {response.status_code}. Content: {response.content}')\n", "path": "src/app/integrations/mailjet.py"}, {"content": "from typing import List, Union\n\nfrom anymail.exceptions import AnymailRequestsAPIError\nfrom django.apps import apps\nfrom django.conf import settings\nfrom requests.exceptions import RequestException\n\nfrom app.celery import celery\nfrom app.integrations import tg\nfrom app.integrations.clickmeeting import ClickMeetingClient, ClickMeetingHTTPException\nfrom app.integrations.mailchimp import AppMailchimp, MailchimpException\nfrom app.integrations.mailjet import AppMailjet, AppMailjetWrongResponseException\nfrom app.integrations.zoomus import ZoomusClient, ZoomusHTTPException\nfrom app.mail.owl import TemplOwl\n\n\n@celery.task(\n    autoretry_for=[AnymailRequestsAPIError],\n    retry_kwargs={\n        'max_retries': 10,\n        'countdown': 5,\n    },\n)\ndef send_mail(to: Union[List, str], template_id, subject: str = '', ctx: dict = None, disable_antispam=False):\n    TemplOwl(\n        to=to,\n        template_id=template_id,\n        subject=subject,\n        ctx=ctx,\n        disable_antispam=disable_antispam,\n    ).send()\n\n\n@celery.task(\n    autoretry_for=[RequestException, ClickMeetingHTTPException],\n    retry_kwargs={\n        'max_retries': 10,\n        'countdown': 5,\n    },\n)\ndef invite_to_clickmeeting(room_url: str, email: str):\n    client = ClickMeetingClient()\n    client.invite(room_url, email)\n\n\n@celery.task(\n    autoretry_for=[RequestException, ZoomusHTTPException],\n    retry_kwargs={\n        'max_retries': 10,\n        'countdown': 5,\n    },\n)\ndef invite_to_zoomus(webinar_id: str, user_id: int):\n    user = apps.get_model('users.User').objects.get(pk=user_id)\n\n    client = ZoomusClient()\n    client.invite(webinar_id, user)\n\n\n@celery.task(\n    autoretry_for=[RequestException, AppMailjetWrongResponseException],\n    retry_kwargs={\n        'max_retries': 10,\n        'countdown': 5,\n    },\n)\ndef subscribe_to_mailjet(user_id: int):\n    if not all(getattr(settings, x) for x in ['MAILJET_API_KEY', 'MAILJET_SECRET_KEY', 'MAILJET_CONTACT_LIST_ID']):\n        return\n\n    user = apps.get_model('users.User').objects.get(pk=user_id)\n    mailjet = AppMailjet()\n\n    mailjet.subscribe(user)\n\n\n@celery.task(\n    autoretry_for=[RequestException, MailchimpException],\n    retry_kwargs={\n        'max_retries': 10,\n        'countdown': 5,\n    },\n)\ndef subscribe_to_mailchimp(user_id: int, list_id=None, tags=None):\n    if list_id is None:\n        list_id = settings.MAILCHIMP_CONTACT_LIST_ID\n\n    if not list_id:\n        return\n\n    mailchimp = AppMailchimp()\n\n    mailchimp.subscribe_django_user(\n        list_id=list_id,\n        user=apps.get_model('users.User').objects.get(pk=user_id),\n        tags=tags,\n    )\n\n\n@celery.task\ndef send_happiness_message(text):\n    tg.send_happiness_message(text)\n", "path": "src/app/tasks.py"}], "after_files": [{"content": "import environ\nimport os\nfrom celery.schedules import crontab\n\nroot = environ.Path(__file__) - 2        # three folder back (/a/b/c/ - 3 = /)\nenv = environ.Env(DEBUG=(bool, False))  # set default values and casting\nenviron.Env.read_env()                   # reading .env file\nSITE_ROOT = root()\n\nUSE_L10N = True\nUSE_i18N = True\n\nLANGUAGE_CODE = 'ru'\nLOCALE_PATHS = ['locale']\n\nINTERNAL_IPS = [\n    '127.0.0.1',\n]\nFRONTEND_URL = 'https://education.borshev.com'\n\nUSE_TZ = False\nTIME_ZONE = env('TIME_ZONE', cast=str, default='Europe/Moscow')\n\n# Build paths inside the project like this: os.path.join(BASE_DIR, ...)\nBASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\nTEST_RUNNER = 'app.test.disable_test_command_runner.DisableTestCommandRunner'\n\n\n# Quick-start development settings - unsuitable for production\n# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/\n\n# SECURITY WARNING: keep the secret key used in production secret!\nSECRET_KEY = env('SECRET_KEY', cast=str, default='s3cr3t')\n\n# SECURITY WARNING: don't run with debug turned on in production!\nDEBUG = env('DEBUG', cast=bool, default=False)\nCI = env('CI', cast=bool, default=False)\nANONYMIZE_ENABLED = DEBUG\n\nABSOLUTE_HOST = env('ABSOLUTE_HOST', cast=str, default='https://edu-app.borshev.com')\nALLOWED_HOSTS = [\n    'edu-app.borshev.com',\n    'localhost',\n    'localhost:8000',\n    'education.borshev.com',\n    ABSOLUTE_HOST.replace('https://', ''),\n]\n\nCORS_ORIGIN_WHITELIST = [\n    'https://pmdaily.ru',\n    'https://education.borshev.com',\n]\n\nCSRF_TRUSTED_ORIGINS = [\n    'pmdaily.ru',\n    'education.borshev.com',\n    'borshev.com',\n]\n\n\n# Application definition\n\nINSTALLED_APPS = [\n    'app',\n    'users',\n    'orders',\n    'products',\n    'shipping',\n    'tinkoff',\n    'triggers',\n    'magnets',\n\n    'corsheaders',\n    'hattori',\n    'anymail',\n    'rest_framework',\n    'rest_framework.authtoken',\n    'django_filters',\n\n    'axes',\n    'django.contrib.admin',\n    'django.contrib.auth',\n    'django.contrib.contenttypes',\n    'django.contrib.sessions',\n    'django.contrib.messages',\n    'django.contrib.staticfiles',\n\n    'debug_toolbar',\n]\n\nMIDDLEWARE = [\n    'django.middleware.security.SecurityMiddleware',\n\n    'django.contrib.sessions.middleware.SessionMiddleware',\n    'corsheaders.middleware.CorsMiddleware',\n    'django.middleware.common.CommonMiddleware',\n    # 'django.middleware.csrf.CsrfViewMiddleware',\n    'django.contrib.auth.middleware.AuthenticationMiddleware',\n    'django.contrib.auth.middleware.RemoteUserMiddleware',\n    'django.contrib.messages.middleware.MessageMiddleware',\n    'django.middleware.clickjacking.XFrameOptionsMiddleware',\n    'axes.middleware.AxesMiddleware',\n    'debug_toolbar.middleware.DebugToolbarMiddleware',\n]\n\nif not DEBUG and not CI:\n    MIDDLEWARE.insert(1, 'whitenoise.middleware.WhiteNoiseMiddleware')\n\n\nREST_FRAMEWORK = {\n    'DEFAULT_PERMISSION_CLASSES': (\n        'rest_framework.permissions.IsAuthenticated',\n    ),\n    'DEFAULT_AUTHENTICATION_CLASSES': (\n        'rest_framework.authentication.TokenAuthentication',\n    ),\n    'DEFAULT_RENDERER_CLASSES': [\n        'app.renderers.AppJSONRenderer',\n    ],\n    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',\n    'DEFAULT_PAGINATION_CLASS': 'app.pagination.AppPagination',\n    'PAGE_SIZE': 20,\n}\n\nROOT_URLCONF = 'app.urls'\n\nTEMPLATES = [\n    {\n        'BACKEND': 'django.template.backends.django.DjangoTemplates',\n        'DIRS': [],\n        'APP_DIRS': True,\n        'OPTIONS': {\n            'context_processors': [\n                'django.template.context_processors.debug',\n                'django.template.context_processors.request',\n                'django.contrib.auth.context_processors.auth',\n                'django.contrib.messages.context_processors.messages',\n            ],\n        },\n    },\n]\n\nWSGI_APPLICATION = 'app.wsgi.application'\n\n\n# Database\n# https://docs.djangoproject.com/en/2.2/ref/settings/#databases\nDATABASES = {\n    'default': env.db(),    # Raises ImproperlyConfigured exception if DATABASE_URL not in os.environ\n}\nAUTH_USER_MODEL = 'users.User'\nAUTHENTICATION_BACKENDS = [\n    'axes.backends.AxesBackend',\n    'django.contrib.auth.backends.ModelBackend',\n    'django.contrib.auth.backends.RemoteUserBackend',\n]\nHEALTH_CHECKS_ERROR_CODE = 503\nHEALTH_CHECKS = {\n    'db': 'django_healthchecks.contrib.check_database',\n}\n\nMEDIA_URL = env('MEDIA_URL', default='/media/')\n\nSTATIC_URL = env('STATIC_URL', default='/static/')\nSTATIC_ROOT = env('STATIC_ROOT')\n\nSENTRY_DSN = env('SENTRY_DSN', cast=str, default='')\n\nif not DEBUG and len(SENTRY_DSN):\n    import sentry_sdk\n    from sentry_sdk.integrations.celery import CeleryIntegration\n    from sentry_sdk.integrations.django import DjangoIntegration\n    from sentry_sdk.integrations.redis import RedisIntegration\n\n    sentry_sdk.init(\n        dsn=SENTRY_DSN,\n        integrations=[DjangoIntegration(), CeleryIntegration(), RedisIntegration()],\n    )\n\nBROKER_URL = env('CELERY_BACKEND')\nCELERY_ALWAYS_EAGER = env('CELERY_ALWAYS_EAGER', cast=bool, default=DEBUG)  # by default in debug mode we run all celery tasks in foregroud.\nCELERY_TIMEZONE = TIME_ZONE\nCELERY_ENABLE_UTC = False\nCELERYBEAT_SCHEDULE = {\n    'run_started_purchase_trigger': {\n        'task': 'triggers.tasks.check_for_started_purchase_triggers',\n        'schedule': crontab(hour='*', minute=15),\n    },\n    'run_record_feedback_trigger': {\n        'task': 'triggers.tasks.check_for_record_feedback_triggers',\n        'schedule': crontab(hour='*', minute=15),\n    },\n    'ship_unshipped_orders': {\n        'task': 'orders.tasks.ship_unshipped_orders',\n        'schedule': crontab(hour='*', minute='*/2'),\n    },\n}\n\n\nAWS_ACCESS_KEY_ID = env('AWS_ACCESS_KEY_ID', default=None)\nAWS_SECRET_ACCESS_KEY = env('AWS_SECRET_ACCESS_KEY', default=None)\nAWS_STORAGE_BUCKET_NAME = env('AWS_STORAGE_BUCKET_NAME', default=None)\nAWS_S3_REGION_NAME = env('AWS_S3_REGION_NAME', default=None)\nAWS_S3_ENDPOINT_URL = env('AWS_S3_ENDPOINT_URL', default=None)\n\nEMAIL_ENABLED = env('EMAIL_ENABLED', cast=bool, default=False)\n\nEMAIL_BACKEND = env('EMAIL_BACKEND', default='django.core.mail.backends.console.EmailBackend')\n\nMAILCHIMP_API_KEY = env('MAILCHIMP_API_KEY', default='')\nMAILCHIMP_CONTACT_LIST_ID = env('MAILCHIMP_CONTACT_LIST_ID', cast=str, default=None)\n\nDEFAULT_FROM_EMAIL = env('EMAIL_FROM', cast=str, default='')\nANYMAIL = {\n    'POSTMARK_SERVER_TOKEN': env('POSTMARK_SERVER_TOKEN', cast=str, default=''),\n    'DEBUG_API_REQUESTS': env('DEBUG'),\n}\n\nCLICKMEETING_API_KEY = env('CLICKMEETING_API_KEY', default=None, cast=str)\n\nZOOMUS_API_KEY = env('ZOOMUS_API_KEY', default=None, cast=str)\nZOOMUS_API_SECRET = env('ZOOMUS_API_SECRET', default=None, cast=str)\n\nTINKOFF_TERMINAL_KEY = env('TINKOFF_TERMINAL_KEY', default=None)\nTINKOFF_TERMINAL_PASSWORD = env('TINKOFF_TERMINAL_PASSWORD', default=None)\n\nSEND_HAPPINESS_MESSAGES = env('SEND_HAPPINESS_MESSAGES', cast=bool, default=False)\n\n# Uncomment this lines to catch all runtime warnings as errors\n\n# import warnings  # noqa\n# warnings.filterwarnings(\n#     'error', r\".*\",\n#     RuntimeWarning, r\".*\"\n# )\n", "path": "src/app/settings.py"}, {"content": null, "path": "src/app/integrations/mailjet.py"}, {"content": "from typing import List, Union\n\nfrom anymail.exceptions import AnymailRequestsAPIError\nfrom django.apps import apps\nfrom django.conf import settings\nfrom requests.exceptions import RequestException\n\nfrom app.celery import celery\nfrom app.integrations import tg\nfrom app.integrations.clickmeeting import ClickMeetingClient, ClickMeetingHTTPException\nfrom app.integrations.mailchimp import AppMailchimp, MailchimpException\nfrom app.integrations.zoomus import ZoomusClient, ZoomusHTTPException\nfrom app.mail.owl import TemplOwl\n\n\n@celery.task(\n    autoretry_for=[AnymailRequestsAPIError],\n    retry_kwargs={\n        'max_retries': 10,\n        'countdown': 5,\n    },\n)\ndef send_mail(to: Union[List, str], template_id, subject: str = '', ctx: dict = None, disable_antispam=False):\n    TemplOwl(\n        to=to,\n        template_id=template_id,\n        subject=subject,\n        ctx=ctx,\n        disable_antispam=disable_antispam,\n    ).send()\n\n\n@celery.task(\n    autoretry_for=[RequestException, ClickMeetingHTTPException],\n    retry_kwargs={\n        'max_retries': 10,\n        'countdown': 5,\n    },\n)\ndef invite_to_clickmeeting(room_url: str, email: str):\n    client = ClickMeetingClient()\n    client.invite(room_url, email)\n\n\n@celery.task(\n    autoretry_for=[RequestException, ZoomusHTTPException],\n    retry_kwargs={\n        'max_retries': 10,\n        'countdown': 5,\n    },\n)\ndef invite_to_zoomus(webinar_id: str, user_id: int):\n    user = apps.get_model('users.User').objects.get(pk=user_id)\n\n    client = ZoomusClient()\n    client.invite(webinar_id, user)\n\n\n@celery.task(\n    autoretry_for=[RequestException, MailchimpException],\n    retry_kwargs={\n        'max_retries': 10,\n        'countdown': 5,\n    },\n)\ndef subscribe_to_mailchimp(user_id: int, list_id=None, tags=None):\n    if list_id is None:\n        list_id = settings.MAILCHIMP_CONTACT_LIST_ID\n\n    if not list_id:\n        return\n\n    mailchimp = AppMailchimp()\n\n    mailchimp.subscribe_django_user(\n        list_id=list_id,\n        user=apps.get_model('users.User').objects.get(pk=user_id),\n        tags=tags,\n    )\n\n\n@celery.task\ndef send_happiness_message(text):\n    tg.send_happiness_message(text)\n", "path": "src/app/tasks.py"}]}