Merge branch 'develop' into feature/profiling

This commit is contained in:
Iurii Tatishchev 2021-05-06 08:33:03 -07:00
commit cc4c4ffdc2
Signed by: CaZzzer
GPG Key ID: 926BE949E29DCD03
9 changed files with 322 additions and 25 deletions

View File

@ -19,10 +19,10 @@ BASE_DIR = Path(__file__).resolve().parent.parent
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.getenv('ACTRL_SECRET_KEY','empty') SECRET_KEY = os.getenv('ACTRL_SECRET_KEY', 'empty')
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = bool(int(os.getenv('ACTRL_DEBUG',1))) DEBUG = bool(int(os.getenv('ACTRL_DEBUG', 1)))
ALLOWED_HOSTS = [ ALLOWED_HOSTS = [
'127.0.0.1', '127.0.0.1',
@ -57,13 +57,13 @@ MIDDLEWARE = [
ROOT_URLCONF = 'access_controller.urls' ROOT_URLCONF = 'access_controller.urls'
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = os.getenv('ACTRL_EMAIL_HOST','smtp.gmail.com') EMAIL_HOST = os.getenv('ACTRL_EMAIL_HOST', 'smtp.gmail.com')
EMAIL_PORT = int(os.getenv('ACTRL_EMAIL_PORT',587)) EMAIL_PORT = int(os.getenv('ACTRL_EMAIL_PORT', 587))
EMAIL_USE_TLS = bool(int(os.getenv('ACTRL_EMAIL_TLS',1))) EMAIL_USE_TLS = bool(int(os.getenv('ACTRL_EMAIL_TLS', 1)))
EMAIL_HOST_USER = os.getenv('ACTRL_EMAIL_HOST_USER','group02django@gmail.com') EMAIL_HOST_USER = os.getenv('ACTRL_EMAIL_HOST_USER', 'group02django@gmail.com')
EMAIL_HOST_PASSWORD = os.getenv('ACTRL_EMAIL_HOST_PASSWORD','djangogroup02') EMAIL_HOST_PASSWORD = os.getenv('ACTRL_EMAIL_HOST_PASSWORD', 'djangogroup02')
DEFAULT_FROM_EMAIL = os.getenv('ACTRL_FROM_EMAIL',EMAIL_HOST_USER) DEFAULT_FROM_EMAIL = os.getenv('ACTRL_FROM_EMAIL', EMAIL_HOST_USER)
SERVER_EMAIL = os.getenv('ACTRL_SERVER_EMAIL',EMAIL_HOST_USER) SERVER_EMAIL = os.getenv('ACTRL_SERVER_EMAIL', EMAIL_HOST_USER)
TEMPLATES = [ TEMPLATES = [
{ {
@ -150,8 +150,8 @@ AUTHENTICATION_BACKENDS = [
ZENDESK_ROLES = { ZENDESK_ROLES = {
'engineer': int(os.getenv('ENG_CROLE_ID',0)), 'engineer': int(os.getenv('ENG_CROLE_ID', 0)),
'light_agent': int(os.getenv('LA_CROLE_ID',0)), 'light_agent': int(os.getenv('LA_CROLE_ID', 0)),
} }
ZENDESK_GROUPS = { ZENDESK_GROUPS = {
@ -161,7 +161,7 @@ ZENDESK_GROUPS = {
SOLVED_TICKETS_EMAIL = os.getenv('ST_EMAIL') SOLVED_TICKETS_EMAIL = os.getenv('ST_EMAIL')
ZENDESK_MAX_AGENTS = int(os.getenv('LICENSE_NO',0)) ZENDESK_MAX_AGENTS = int(os.getenv('LICENSE_NO', 0))
REST_FRAMEWORK = { REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions, # Use Django's standard `django.contrib.auth` permissions,
@ -171,7 +171,7 @@ REST_FRAMEWORK = {
] ]
} }
ONE_DAY = int(os.getenv('SHIFTH',0)) # Количество часов в 1 рабочем дне ONE_DAY = int(os.getenv('SHIFTH', 0)) # Количество часов в 1 рабочем дне
ACTRL_ZENDESK_SUBDOMAIN = os.getenv('ACTRL_ZENDESK_SUBDOMAIN') or os.getenv('ZD_DOMAIN') ACTRL_ZENDESK_SUBDOMAIN = os.getenv('ACTRL_ZENDESK_SUBDOMAIN') or os.getenv('ZD_DOMAIN')
ACTRL_API_EMAIL = os.getenv('ACTRL_API_EMAIL') or os.getenv('ACCESS_CONTROLLER_API_EMAIL') ACTRL_API_EMAIL = os.getenv('ACTRL_API_EMAIL') or os.getenv('ACCESS_CONTROLLER_API_EMAIL')

View File

@ -14,7 +14,6 @@ Including another URLconf
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
""" """
from django.contrib import admin from django.contrib import admin
from django.contrib.auth import views
from django.urls import path, include from django.urls import path, include
from main.views import main_page, profile_page, CustomRegistrationView, CustomLoginView, registration_error from main.views import main_page, profile_page, CustomRegistrationView, CustomLoginView, registration_error
@ -22,6 +21,7 @@ from main.views import work_page, work_hand_over, work_become_engineer, work_get
AdminPageView, statistic_page AdminPageView, statistic_page
from main.urls import router from main.urls import router
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls, name='admin'), path('admin/', admin.site.urls, name='admin'),
path('', main_page, name='index'), path('', main_page, name='index'),

57
fixtures/data.json Normal file
View File

@ -0,0 +1,57 @@
[
{
"model": "auth.user",
"pk": 1,
"fields": {
"password": "pbkdf2_sha256$216000$gHBBCr1jBELf$ZkEDW3IEd8Wij7u8vkv+0Eze32CS01bcaYWhcD9OIC4=",
"last_login": null,
"is_superuser": true,
"username": "admin@gmail.com",
"first_name": "",
"last_name": "",
"email": "admin@gmail.com",
"is_staff": true,
"is_active": true,
"date_joined": "2021-03-10T16:38:56.303Z",
"groups": [],
"user_permissions": [33]
}
},
{
"model": "main.userprofile",
"pk": 1,
"fields": {
"name": "ZendeskAdmin",
"user": 1,
"role": "admin"
}
},
{
"model": "auth.user",
"pk": 2,
"fields": {
"password": "pbkdf2_sha256$216000$5qLJgrm2Quq9$KDBNNymVZXkUx0HKBPFst2m83kLe0egPBnkW7KnkORU=",
"last_login": null,
"is_superuser": false,
"username": "123@test.ru",
"first_name": "",
"last_name": "",
"email": "123@test.ru",
"is_staff": false,
"is_active": true,
"date_joined": "2021-03-10T16:38:56.303Z",
"groups": [],
"user_permissions": []
}
},
{
"model": "main.userprofile",
"pk": 2,
"fields": {
"name": "UserForAccessTest",
"user": 2,
"role": "agent",
"custom_role_id": "360005209000"
}
}
]

View File

@ -0,0 +1,85 @@
[
{
"model": "auth.user",
"pk": 1,
"fields": {
"password": "pbkdf2_sha256$216000$gHBBCr1jBELf$ZkEDW3IEd8Wij7u8vkv+0Eze32CS01bcaYWhcD9OIC4=",
"last_login": null,
"is_superuser": true,
"username": "admin@gmail.com",
"first_name": "",
"last_name": "",
"email": "admin@gmail.com",
"is_staff": true,
"is_active": true,
"date_joined": "2021-03-10T16:38:56.303Z",
"groups": [],
"user_permissions": [33]
}
},
{
"model": "main.userprofile",
"pk": 1,
"fields": {
"name": "ZendeskAdmin",
"user": 1,
"role": "admin"
}
},
{
"model": "auth.user",
"pk": 2,
"fields": {
"password": "pbkdf2_sha256$216000$5qLJgrm2Quq9$KDBNNymVZXkUx0HKBPFst2m83kLe0egPBnkW7KnkORU=",
"last_login": null,
"is_superuser": false,
"username": "123@test.ru",
"first_name": "",
"last_name": "",
"email": "123@test.ru",
"is_staff": false,
"is_active": true,
"date_joined": "2021-03-10T16:38:56.303Z",
"groups": [],
"user_permissions": []
}
},
{
"model": "main.userprofile",
"pk": 2,
"fields": {
"name": "UserForAccessTest",
"user": 2,
"role": "agent",
"custom_role_id": "360005209000"
}
},
{
"model": "auth.user",
"pk": 3,
"fields": {
"password": "pbkdf2_sha256$216000$5qLJgrm2Quq9$KDBNNymVZXkUx0HKBPFst2m83kLe0egPBnkW7KnkORU=",
"last_login": null,
"is_superuser": false,
"username": "customer@example.com",
"first_name": "",
"last_name": "",
"email": "customer@example.com",
"is_staff": false,
"is_active": true,
"date_joined": "2021-04-15T16:38:56.303Z",
"groups": [],
"user_permissions": []
}
},
{
"model": "main.userprofile",
"pk": 3,
"fields": {
"name": "UserForAccessTest",
"user": 3,
"role": "agent",
"custom_role_id": "360005209000"
}
}
]

View File

@ -16,12 +16,13 @@ from main.requester import TicketListRequester
from main.zendesk_admin import zenpy from main.zendesk_admin import zenpy
def update_role(user_profile: UserProfile, role: int) -> None: def update_role(user_profile: UserProfile, role: int, who_changes: User) -> None:
""" """
Функция меняет роль пользователя. Функция меняет роль пользователя.
:param user_profile: Профиль пользователя :param user_profile: Профиль пользователя
:param role: Новая роль :param role: Новая роль
:param who_changes: Пользователь, меняющий роль
:return: Пользователь с обновленной ролью :return: Пользователь с обновленной ролью
""" """
zendesk = zenpy zendesk = zenpy
@ -29,7 +30,8 @@ def update_role(user_profile: UserProfile, role: int) -> None:
user.custom_role_id = role user.custom_role_id = role
user_profile.custom_role_id = role user_profile.custom_role_id = role
user_profile.save() user_profile.save()
zendesk.admin.users.update(user) log(user_profile, who_changes.userprofile)
zendesk.update_user(user)
def make_engineer(user_profile: UserProfile, who_changes: User) -> None: def make_engineer(user_profile: UserProfile, who_changes: User) -> None:
@ -39,7 +41,7 @@ def make_engineer(user_profile: UserProfile, who_changes: User) -> None:
:param user_profile: Профиль пользователя :param user_profile: Профиль пользователя
:return: Вызов функции **update_role** с параметрами: профиль пользователя, роль "engineer" :return: Вызов функции **update_role** с параметрами: профиль пользователя, роль "engineer"
""" """
update_role(user_profile, ROLES['engineer']) update_role(user_profile, ROLES['engineer'], who_changes)
def make_light_agent(user_profile: UserProfile, who_changes: User) -> None: def make_light_agent(user_profile: UserProfile, who_changes: User) -> None:
@ -67,7 +69,7 @@ def make_light_agent(user_profile: UserProfile, who_changes: User) -> None:
attempts, success = 20, False attempts, success = 20, False
while not success and attempts != 0: while not success and attempts != 0:
try: try:
update_role(user_profile, ROLES['light_agent']) update_role(user_profile, ROLES['light_agent'], who_changes)
success = True success = True
except APIException as e: except APIException as e:
attempts -= 1 attempts -= 1
@ -287,7 +289,7 @@ class CsvFormatter(logging.Formatter):
return msg return msg
def log(user, admin=0): def log(user, admin=None):
""" """
Осуществляет запись логов в базу данных и csv файл Осуществляет запись логов в базу данных и csv файл
:param admin: :param admin:

View File

@ -17,9 +17,9 @@
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="{% static 'modules/notifications/dist/notifications.js' %}"></script>
<script src="{% static 'main/js/control.js'%}" type="text/babel"></script> <script src="{% static 'main/js/control.js'%}" type="text/babel"></script>
<script src="{% static 'main/js/notifications.js' %}"></script> <script src="{% static 'main/js/notifications.js' %}"></script> {# Для #}
<script src="{% static 'modules/notifications/dist/notifications.js' %}"></script> {# Уведомлений #}
{% endblock%} {% endblock%}
{% block content %} {% block content %}
<div class="container-md"> <div class="container-md">

View File

@ -1,2 +1,150 @@
from unittest.mock import patch
from urllib.parse import urlparse
from django.contrib.auth.models import User
from django.core import mail
from django.test import TestCase, Client from django.test import TestCase, Client
from django.urls import reverse, reverse_lazy
from django.utils import translation
import access_controller.settings as sets import access_controller.settings as sets
from main.zendesk_admin import zenpy
class RegistrationTestCase(TestCase):
fixtures = ['fixtures/data.json']
def setUp(self):
self.email_backend = 'django.core.mail.backends.locmem.EmailBackend'
self.any_zendesk_user_email = 'idar.sokurov.05@mail.ru'
self.zendesk_admin_email = 'idar.sokurov.05@mail.ru'
self.client = Client()
def test_registration_complete_redirect(self):
with self.settings(EMAIL_BACKEND=self.email_backend):
resp = self.client.post(reverse('registration'), data={'email': self.any_zendesk_user_email})
self.assertRedirects(resp, reverse('password_reset_done'))
def test_registration_fail_redirect(self):
with self.settings(EMAIL_BACKEND=self.email_backend):
resp = self.client.post(reverse('registration'), data={'email': self.any_zendesk_user_email + 'asd'})
self.assertRedirects(resp, reverse('django_registration_disallowed'))
def test_registration_user_already_exist(self):
with self.settings(EMAIL_BACKEND=self.email_backend) and translation.override('ru'):
resp = self.client.post(reverse('registration'), data={'email': '123@test.ru'})
self.assertContains(resp, 'Этот адрес электронной почты уже используется', count=1, status_code=200)
def test_registration_email_sending(self):
# TODO: Найти способ лучше проверять сообщения
email_template = [
'',
'Вы получили это письмо, потому что вы (или кто-то другой) запросили восстановление пароля '
'от учётной записи на сайте testserver, которая связана с этим адресом электронной почты.',
'',
'Пожалуйста, перейдите на эту страницу и введите новый пароль:',
'',
'url',
'',
f'Ваше имя пользователя (на случай, если вы его забыли): {self.any_zendesk_user_email}',
'',
'Спасибо, что используете наш сайт!',
'',
'Команда сайта testserver',
'',
'',
'',
]
with self.settings(EMAIL_BACKEND=self.email_backend) and translation.override('ru'):
self.client.post(reverse('registration'), data={'email': self.any_zendesk_user_email})
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].to, [self.zendesk_admin_email])
self.assertEqual(mail.outbox[0].from_email, sets.DEFAULT_FROM_EMAIL)
message = mail.outbox[0].body.split('\n')
for i in range(len(message)):
if email_template[i] != 'url':
self.assertEqual(message[i], email_template[i])
else:
self.assertTrue(urlparse(message[i]).scheme)
def test_registration_user_creating(self):
with self.settings(EMAIL_BACKEND=self.email_backend):
self.client.post(reverse('registration'), data={'email': self.any_zendesk_user_email})
user = User.objects.get(email=self.any_zendesk_user_email)
zendesk_user = zenpy.get_user(self.any_zendesk_user_email)
self.assertEqual(user.userprofile.name, zendesk_user.name)
def test_permissions_applying(self):
with self.settings(EMAIL_BACKEND=self.email_backend):
self.client.post(reverse('registration'), data={'email': self.zendesk_admin_email})
user = User.objects.get(email=self.zendesk_admin_email)
self.assertEqual(user.userprofile.role, 'admin')
self.assertTrue(user.has_perm('main.has_control_access'))
class MakeEngineerTestCase(TestCase):
fixtures = ['fixtures/test_make_engineer.json']
def setUp(self):
self.light_agent = '123@test.ru'
self.admin = 'admin@gmail.com'
self.engineer = 'customer@example.com'
self.client = Client()
self.client.force_login(User.objects.get(email=self.light_agent))
self.admin_client = Client()
self.admin_client.force_login(User.objects.get(email=self.admin))
@patch('main.extra_func.zenpy')
def test_redirect(self, ZenpyMock):
user = User.objects.get(email=self.light_agent)
resp = self.client.post(reverse_lazy('work_become_engineer'))
self.assertRedirects(resp, reverse('work', args=[user.id]))
self.assertEqual(resp.status_code, 302)
@patch('main.extra_func.zenpy')
def test_light_agent_make_engineer(self, ZenpyMock):
self.client.post(reverse_lazy('work_become_engineer'))
self.assertEqual(ZenpyMock.update_user.call_args[0][0].custom_role_id, sets.ZENDESK_ROLES['engineer'])
@patch('main.extra_func.zenpy')
def test_admin_make_engineer(self, ZenpyMock):
self.admin_client.post(reverse_lazy('work_become_engineer'))
self.assertEqual(ZenpyMock.update_user.call_args[0][0].custom_role_id, sets.ZENDESK_ROLES['engineer'])
@patch('main.extra_func.zenpy')
def test_engineer_make_engineer(self, ZenpyMock):
client = Client()
client.force_login(User.objects.get(email=self.engineer))
client.post(reverse_lazy('work_become_engineer'))
self.assertEqual(ZenpyMock.update_user.call_args[0][0].custom_role_id, sets.ZENDESK_ROLES['engineer'])
@patch('main.extra_func.zenpy')
def test_control_page_make_one(self, ZenpyMock):
self.admin_client.post(
reverse_lazy('control'),
data={'users': [User.objects.get(email=self.light_agent).userprofile.id], 'engineer': 'engineer'}
)
call_list = ZenpyMock.update_user.call_args_list
mock_object = call_list[0][0][0]
self.assertEqual(len(call_list), 1)
self.assertEqual(mock_object.custom_role_id, sets.ZENDESK_ROLES['engineer'])
@patch('main.extra_func.zenpy')
def test_control_page_make_many(self, ZenpyMock):
self.admin_client.post(
reverse_lazy('control'),
data={
'users': [
User.objects.get(email=self.light_agent).userprofile.id,
User.objects.get(email=self.engineer).userprofile.id,
],
'engineer': 'engineer'
}
)
call_list = ZenpyMock.update_user.call_args_list
mock_objects = list(call_list)
self.assertEqual(len(call_list), 2)
for obj in mock_objects:
self.assertEqual(obj[0][0].custom_role_id, sets.ZENDESK_ROLES['engineer'])

View File

@ -107,7 +107,7 @@ class CustomRegistrationView(RegistrationView):
except SMTPException: except SMTPException:
self.redirect_url = 'email_sending_error' self.redirect_url = 'email_sending_error'
else: else:
raise ValueError('Непредвиденная ошибка') self.redirect_url = 'email_sending_error'
else: else:
self.redirect_url = 'invalid_zendesk_email' self.redirect_url = 'invalid_zendesk_email'
@ -295,7 +295,6 @@ class AdminPageView(LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageM
""" """
for user in users: for user in users:
make_engineer(user, self.request.user) make_engineer(user, self.request.user)
log(user, self.request.user.userprofile)
def make_light_agents(self, users): def make_light_agents(self, users):
""" """
@ -306,7 +305,6 @@ class AdminPageView(LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageM
""" """
for user in users: for user in users:
make_light_agent(user, self.request.user) make_light_agent(user, self.request.user)
log(user, self.request.user.userprofile)
class CustomLoginView(LoginView): class CustomLoginView(LoginView):

View File

@ -1,5 +1,4 @@
from typing import Optional, Dict from typing import Optional, Dict
from zenpy import Zenpy from zenpy import Zenpy
from zenpy.lib.api_objects import User as ZenpyUser, Group as ZenpyGroup from zenpy.lib.api_objects import User as ZenpyUser, Group as ZenpyGroup
from zenpy.lib.exception import APIException from zenpy.lib.exception import APIException
@ -21,6 +20,14 @@ class ZendeskAdmin:
self.buffer_group_id: int = self.get_group(ZENDESK_GROUPS['buffer']).id self.buffer_group_id: int = self.get_group(ZENDESK_GROUPS['buffer']).id
self.solved_tickets_user_id: int = self.get_user(SOLVED_TICKETS_EMAIL).id self.solved_tickets_user_id: int = self.get_user(SOLVED_TICKETS_EMAIL).id
def update_user(self, user: ZenpyUser) -> bool:
"""
Функция сохраняет изменение пользователя в Zendesk.
:param user: Пользователь с изменёнными данными
"""
self.admin.users.update(user)
def check_user(self, email: str) -> bool: def check_user(self, email: str) -> bool:
""" """
Функция осуществляет проверку существования пользователя в Zendesk по email. Функция осуществляет проверку существования пользователя в Zendesk по email.