From 750371e595b3007d5c87bc5d8ceadeb02ecc4c3e Mon Sep 17 00:00:00 2001 From: Iurii Tatishchev Date: Tue, 20 Apr 2021 13:29:46 -0700 Subject: [PATCH 1/7] move ZendeskAdmin to separate file --- main/extra_func.py | 154 ++++-------------------------------------- main/views.py | 3 +- main/zendesk_admin.py | 138 +++++++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+), 142 deletions(-) create mode 100644 main/zendesk_admin.py diff --git a/main/extra_func.py b/main/extra_func.py index 3e54ae4..4aaee13 100644 --- a/main/extra_func.py +++ b/main/extra_func.py @@ -1,5 +1,4 @@ import logging -import os from datetime import timedelta, datetime, date from django.contrib.auth.models import User @@ -7,139 +6,12 @@ from django.core.exceptions import ObjectDoesNotExist from django.utils import timezone from zenpy import Zenpy from zenpy.lib.exception import APIException - +from zenpy.lib.api_objects import User as ZenpyUser from access_controller.settings import ZENDESK_ROLES as ROLES, ONE_DAY, ZENDESK_GROUPS, SOLVED_TICKETS_EMAIL, \ - ACTRL_API_EMAIL, ACTRL_API_TOKEN, ACTRL_API_PASSWORD, ACTRL_ZENDESK_SUBDOMAIN + ACTRL_ZENDESK_SUBDOMAIN from main.models import UserProfile, RoleChangeLogs, UnassignedTicket, UnassignedTicketStatus - - -class ZendeskAdmin: - """ - Класс **ZendeskAdmin** существует, чтобы в каждой функции отдельно не проверять аккаунт администратора. - - :param credentials: Полномочия (первым указывается учетная запись организации в Zendesk) - :type credentials: :class:`dict` - :param email: Email администратора, указанный в env - :type email: :class:`str` - :param token: Токен администратора (формируется в Zendesk, указывается в env) - :type token: :class:`str` - :param password: Пароль администратора, указанный в env - :type password: :class:`str` - """ - - credentials: dict = { - 'subdomain': ACTRL_ZENDESK_SUBDOMAIN - } - email: str = ACTRL_API_EMAIL - token: str = ACTRL_API_TOKEN - password: str = ACTRL_API_PASSWORD - - def __init__(self): - self.create_admin() - - def check_user(self, email: str) -> bool: - """ - Функция осуществляет проверку существования пользователя в Zendesk по email. - - :param email: Email пользователя - :return: Является ли зарегистрированным - """ - return True if self.admin.search(email, type='user') else False - - def get_user_name(self, email: str) -> str: - """ - Функция **get_user_name** возвращает имя пользователя по его email - """ - user = self.admin.users.search(email).values[0] - return user.name - - def get_user_role(self, email: str) -> str: - """ - Функция возвращает роль пользователя по его email. - - :param email: Email пользователя - :return: Роль пользователя - """ - user = self.admin.users.search(email).values[0] - return user.role - - def get_user_id(self, email: str) -> str: - """ - Функция возвращает id пользователя по его email - - :param email: Email пользователя - :return: ID пользователя - """ - user = self.admin.users.search(email).values[0] - return user.id - - def get_user_image(self, email: str) -> str: - """ - Функция возвращает url-ссылку на аватар пользователя по его email. - - :param email: Email пользователя - :return: Аватар пользователя - """ - user = self.admin.users.search(email).values[0] - return user.photo['content_url'] if user.photo else None - - def get_user(self, email: str): - """ - Функция возвращает пользователя (объект) по его email. - - :param email: Email пользователя - :return: Объект пользователя, найденного в БД - """ - return self.admin.users.search(email).values[0] - - def get_group(self, name: str) -> str: - """ - Функция возвращает группу по названию - - :param name: Имя пользователя - :return: Группы пользователя (в случае отсутствия None) - """ - groups = self.admin.search(name, type='group') - for group in groups: - return group - return None - - def get_user_org(self, email: str) -> str: - """ - Функция возвращает организацию, к которой относится пользователь по его email. - - :param email: Email пользователя - :return: Организация пользователя - """ - user = self.admin.users.search(email).values[0] - return user.organization.name if user.organization else None - - def create_admin(self) -> None: - """ - Функция создает администратора, проверяя наличие вводимых данных в env. - - :param credentials: В список полномочий администратора вносятся email, token, password из env - :type credentials: :class:`dict` - :raise: :class:`ValueError`: исключение, вызываемое если email не введен в env - :raise: :class:`APIException`: исключение, вызываемое если пользователя с таким email не существует в Zendesk - """ - - if self.email is None: - raise ValueError('access_controller email not in env') - self.credentials['email'] = self.email - - if self.token: - self.credentials['token'] = self.token - elif self.password: - self.credentials['password'] = self.password - else: - raise ValueError('access_controller token or password not in env') - self.admin = Zenpy(**self.credentials) - try: - self.admin.search(self.email, type='user') - except APIException: - raise ValueError('invalid access_controller`s login data') +from main.zendesk_admin import zenpy def update_role(user_profile: UserProfile, role: int) -> None: @@ -150,7 +22,7 @@ def update_role(user_profile: UserProfile, role: int) -> None: :param role: Новая роль :return: Пользователь с обновленной ролью """ - zendesk = ZendeskAdmin() + zendesk = zenpy user = zendesk.get_user(user_profile.user.email) user.custom_role_id = role user_profile.custom_role_id = role @@ -183,11 +55,11 @@ def make_light_agent(user_profile: UserProfile, who_changes: User) -> None: status=UnassignedTicketStatus.SOLVED if ticket.status == 'solved' else UnassignedTicketStatus.UNASSIGNED ) if ticket.status == 'solved': - ticket.assignee = ZendeskAdmin().get_user(SOLVED_TICKETS_EMAIL) + ticket.assignee = zenpy.get_user(SOLVED_TICKETS_EMAIL) else: ticket.assignee = None - ticket.group = ZendeskAdmin().get_group(ZENDESK_GROUPS['buffer']) - ZendeskAdmin().admin.tickets.update(ticket) + ticket.group = zenpy.get_group(ZENDESK_GROUPS['buffer']) + zenpy.admin.tickets.update(ticket) update_role(user_profile, ROLES['light_agent']) @@ -195,7 +67,7 @@ def get_users_list() -> list: """ Функция **get_users_list** возвращает список пользователей Zendesk, относящихся к организации SYSTEM. """ - zendesk = ZendeskAdmin() + zendesk = zenpy # У пользователей должна быть организация SYSTEM org = next(zendesk.admin.search(type='organization', name='SYSTEM')) @@ -207,7 +79,7 @@ def get_tickets_list(email): """ Функция возвращает список тикетов пользователя Zendesk """ - return ZendeskAdmin().admin.search(assignee=email, type='ticket') + return zenpy.admin.search(assignee=email, type='ticket') def update_profile(user_profile: UserProfile) -> UserProfile: @@ -217,7 +89,7 @@ def update_profile(user_profile: UserProfile) -> UserProfile: :param user_profile: Профиль пользователя :return: Обновленный, в соответствие с текущими данными в Zendesk, профиль пользователя """ - user = ZendeskAdmin().get_user(user_profile.user.email) + user = zenpy.get_user(user_profile.user.email) user_profile.name = user.name user_profile.role = user.role user_profile.custom_role_id = user.custom_role_id if user.custom_role_id else 0 @@ -232,7 +104,7 @@ def check_user_exist(email: str) -> bool: :param email: Email пользователя :return: Зарегистрирован ли пользователь в Zendesk """ - return ZendeskAdmin().check_user(email) + return zenpy.check_user(email) def get_user_organization(email: str) -> str: @@ -242,7 +114,7 @@ def get_user_organization(email: str) -> str: :param email: Email пользователя :return: Организация пользователя """ - return ZendeskAdmin().get_user_org(email) + return zenpy.get_user_org(email) def check_user_auth(email: str, password: str) -> bool: @@ -264,7 +136,7 @@ def check_user_auth(email: str, password: str) -> bool: return True -def update_user_in_model(profile: UserProfile, zendesk_user: User): +def update_user_in_model(profile: UserProfile, zendesk_user: ZenpyUser): """ Функция обновляет профиль пользователя при изменении данных пользователя на Zendesk. diff --git a/main/views.py b/main/views.py index 0c89f11..c7eec58 100644 --- a/main/views.py +++ b/main/views.py @@ -23,7 +23,8 @@ from zenpy.lib.api_objects import User as ZenpyUser from access_controller.settings import DEFAULT_FROM_EMAIL, ZENDESK_ROLES, ZENDESK_MAX_AGENTS from main.extra_func import check_user_exist, update_profile, get_user_organization, \ make_engineer, make_light_agent, get_users_list, update_users_in_model, count_users, \ - StatisticData, log, ZendeskAdmin + StatisticData, log +from main.zendesk_admin import ZendeskAdmin from main.forms import AdminPageUsers, CustomRegistrationForm, CustomAuthenticationForm, StatisticForm from main.serializers import ProfileSerializer, ZendeskUserSerializer from .models import UserProfile diff --git a/main/zendesk_admin.py b/main/zendesk_admin.py new file mode 100644 index 0000000..e0d1674 --- /dev/null +++ b/main/zendesk_admin.py @@ -0,0 +1,138 @@ +from typing import Optional + +from zenpy import Zenpy +from zenpy.lib.api_objects import User as ZenpyUser +from zenpy.lib.exception import APIException + +from access_controller.settings import ACTRL_ZENDESK_SUBDOMAIN, ACTRL_API_EMAIL, ACTRL_API_TOKEN, ACTRL_API_PASSWORD + + +class ZendeskAdmin: + """ + Класс **ZendeskAdmin** существует, чтобы в каждой функции отдельно не проверять аккаунт администратора. + + :param credentials: Полномочия (первым указывается учетная запись организации в Zendesk) + :type credentials: :class:`dict` + :param email: Email администратора, указанный в env + :type email: :class:`str` + :param token: Токен администратора (формируется в Zendesk, указывается в env) + :type token: :class:`str` + :param password: Пароль администратора, указанный в env + :type password: :class:`str` + """ + + credentials: dict = { + 'subdomain': ACTRL_ZENDESK_SUBDOMAIN + } + email: str = ACTRL_API_EMAIL + token: str = ACTRL_API_TOKEN + password: str = ACTRL_API_PASSWORD + + def __init__(self): + self.create_admin() + + def check_user(self, email: str) -> bool: + """ + Функция осуществляет проверку существования пользователя в Zendesk по email. + + :param email: Email пользователя + :return: Является ли зарегистрированным + """ + return True if self.admin.search(email, type='user') else False + + def get_user_name(self, email: str) -> str: + """ + Функция **get_user_name** возвращает имя пользователя по его email + """ + user = self.admin.users.search(email).values[0] + return user.name + + def get_user_role(self, email: str) -> str: + """ + Функция возвращает роль пользователя по его email. + + :param email: Email пользователя + :return: Роль пользователя + """ + user = self.admin.users.search(email).values[0] + return user.role + + def get_user_id(self, email: str) -> str: + """ + Функция возвращает id пользователя по его email + + :param email: Email пользователя + :return: ID пользователя + """ + user = self.admin.users.search(email).values[0] + return user.id + + def get_user_image(self, email: str) -> str: + """ + Функция возвращает url-ссылку на аватар пользователя по его email. + + :param email: Email пользователя + :return: Аватар пользователя + """ + user = self.admin.users.search(email).values[0] + return user.photo['content_url'] if user.photo else None + + def get_user(self, email: str) -> ZenpyUser: + """ + Функция возвращает пользователя (объект) по его email. + + :param email: Email пользователя + :return: Объект пользователя, найденного в БД + """ + return self.admin.users.search(email).values[0] + + def get_group(self, name: str) -> Optional[str]: + """ + Функция возвращает группу по названию + + :param name: Имя пользователя + :return: Группы пользователя (в случае отсутствия None) + """ + groups = self.admin.search(name, type='group') + for group in groups: + return group + return None + + def get_user_org(self, email: str) -> str: + """ + Функция возвращает организацию, к которой относится пользователь по его email. + + :param email: Email пользователя + :return: Организация пользователя + """ + user = self.admin.users.search(email).values[0] + return user.organization.name if user.organization else None + + def create_admin(self) -> None: + """ + Функция создает администратора, проверяя наличие вводимых данных в env. + + :param credentials: В список полномочий администратора вносятся email, token, password из env + :type credentials: :class:`dict` + :raise: :class:`ValueError`: исключение, вызываемое если email не введен в env + :raise: :class:`APIException`: исключение, вызываемое если пользователя с таким email не существует в Zendesk + """ + + if self.email is None: + raise ValueError('access_controller email not in env') + self.credentials['email'] = self.email + + if self.token: + self.credentials['token'] = self.token + elif self.password: + self.credentials['password'] = self.password + else: + raise ValueError('access_controller token or password not in env') + self.admin = Zenpy(**self.credentials) + try: + self.admin.search(self.email, type='user') + except APIException: + raise ValueError('invalid access_controller`s login data') + + +zenpy = ZendeskAdmin() From 045d2cfb2b7b3cbeb87fbe77a8b400798cc50c68 Mon Sep 17 00:00:00 2001 From: Iurii Tatishchev Date: Tue, 20 Apr 2021 13:30:15 -0700 Subject: [PATCH 2/7] bulk request for tickets to buffer group --- main/extra_func.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/main/extra_func.py b/main/extra_func.py index 4aaee13..f8d95b8 100644 --- a/main/extra_func.py +++ b/main/extra_func.py @@ -48,6 +48,8 @@ def make_light_agent(user_profile: UserProfile, who_changes: User) -> None: :return: Вызов функции **update_role** с параметрами: профиль пользователя, роль "light_agent" """ tickets = get_tickets_list(user_profile.user.email) + tickets_to_update = [] + buffer_group = zenpy.get_group(ZENDESK_GROUPS['buffer']) for ticket in tickets: UnassignedTicket.objects.create( assignee=user_profile.user, @@ -58,8 +60,11 @@ def make_light_agent(user_profile: UserProfile, who_changes: User) -> None: ticket.assignee = zenpy.get_user(SOLVED_TICKETS_EMAIL) else: ticket.assignee = None - ticket.group = zenpy.get_group(ZENDESK_GROUPS['buffer']) - zenpy.admin.tickets.update(ticket) + ticket.group = buffer_group + tickets_to_update.append(ticket) + + print(tickets_to_update) + zenpy.admin.tickets.update(tickets.values) update_role(user_profile, ROLES['light_agent']) From a5d0c222bd0a5c4b19bb693d6b2eec8ce752c4c9 Mon Sep 17 00:00:00 2001 From: Iurii Tatishchev Date: Tue, 20 Apr 2021 14:39:27 -0700 Subject: [PATCH 3/7] Multiple attempts to change role to compensate Zendesk API delay --- main/extra_func.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/main/extra_func.py b/main/extra_func.py index f8d95b8..162adaf 100644 --- a/main/extra_func.py +++ b/main/extra_func.py @@ -48,8 +48,8 @@ def make_light_agent(user_profile: UserProfile, who_changes: User) -> None: :return: Вызов функции **update_role** с параметрами: профиль пользователя, роль "light_agent" """ tickets = get_tickets_list(user_profile.user.email) - tickets_to_update = [] buffer_group = zenpy.get_group(ZENDESK_GROUPS['buffer']) + solved_tickets_user = zenpy.get_user(SOLVED_TICKETS_EMAIL) for ticket in tickets: UnassignedTicket.objects.create( assignee=user_profile.user, @@ -57,15 +57,22 @@ def make_light_agent(user_profile: UserProfile, who_changes: User) -> None: status=UnassignedTicketStatus.SOLVED if ticket.status == 'solved' else UnassignedTicketStatus.UNASSIGNED ) if ticket.status == 'solved': - ticket.assignee = zenpy.get_user(SOLVED_TICKETS_EMAIL) + ticket.assignee = solved_tickets_user else: ticket.assignee = None ticket.group = buffer_group - tickets_to_update.append(ticket) - print(tickets_to_update) zenpy.admin.tickets.update(tickets.values) - update_role(user_profile, ROLES['light_agent']) + + attempts, success = 5, False + while not success and attempts != 0: + try: + update_role(user_profile, ROLES['light_agent']) + success = True + except APIException as e: + attempts -= 1 + if attempts == 0: + raise e def get_users_list() -> list: From 95d0db24b6b2d6dee97d5805db87c08679cb957a Mon Sep 17 00:00:00 2001 From: Iurii Tatishchev Date: Tue, 20 Apr 2021 15:02:17 -0700 Subject: [PATCH 4/7] Find buffer group id and solved tickets user id in ZendeskAdmin constructor --- main/extra_func.py | 12 +++++------- main/zendesk_admin.py | 5 ++++- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/main/extra_func.py b/main/extra_func.py index 162adaf..fd59968 100644 --- a/main/extra_func.py +++ b/main/extra_func.py @@ -6,10 +6,9 @@ from django.core.exceptions import ObjectDoesNotExist from django.utils import timezone from zenpy import Zenpy from zenpy.lib.exception import APIException -from zenpy.lib.api_objects import User as ZenpyUser +from zenpy.lib.api_objects import User as ZenpyUser, Ticket as ZenpyTicket -from access_controller.settings import ZENDESK_ROLES as ROLES, ONE_DAY, ZENDESK_GROUPS, SOLVED_TICKETS_EMAIL, \ - ACTRL_ZENDESK_SUBDOMAIN +from access_controller.settings import ZENDESK_ROLES as ROLES, ONE_DAY, ACTRL_ZENDESK_SUBDOMAIN from main.models import UserProfile, RoleChangeLogs, UnassignedTicket, UnassignedTicketStatus from main.zendesk_admin import zenpy @@ -48,8 +47,7 @@ def make_light_agent(user_profile: UserProfile, who_changes: User) -> None: :return: Вызов функции **update_role** с параметрами: профиль пользователя, роль "light_agent" """ tickets = get_tickets_list(user_profile.user.email) - buffer_group = zenpy.get_group(ZENDESK_GROUPS['buffer']) - solved_tickets_user = zenpy.get_user(SOLVED_TICKETS_EMAIL) + ticket: ZenpyTicket for ticket in tickets: UnassignedTicket.objects.create( assignee=user_profile.user, @@ -57,10 +55,10 @@ def make_light_agent(user_profile: UserProfile, who_changes: User) -> None: status=UnassignedTicketStatus.SOLVED if ticket.status == 'solved' else UnassignedTicketStatus.UNASSIGNED ) if ticket.status == 'solved': - ticket.assignee = solved_tickets_user + ticket.assignee_id = zenpy.solved_tickets_user_id else: ticket.assignee = None - ticket.group = buffer_group + ticket.group_id = zenpy.buffer_group_id zenpy.admin.tickets.update(tickets.values) diff --git a/main/zendesk_admin.py b/main/zendesk_admin.py index e0d1674..70a461f 100644 --- a/main/zendesk_admin.py +++ b/main/zendesk_admin.py @@ -4,7 +4,8 @@ from zenpy import Zenpy from zenpy.lib.api_objects import User as ZenpyUser from zenpy.lib.exception import APIException -from access_controller.settings import ACTRL_ZENDESK_SUBDOMAIN, ACTRL_API_EMAIL, ACTRL_API_TOKEN, ACTRL_API_PASSWORD +from access_controller.settings import ACTRL_ZENDESK_SUBDOMAIN, ACTRL_API_EMAIL, ACTRL_API_TOKEN, ACTRL_API_PASSWORD, \ + ZENDESK_GROUPS, SOLVED_TICKETS_EMAIL class ZendeskAdmin: @@ -30,6 +31,8 @@ class ZendeskAdmin: def __init__(self): self.create_admin() + 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 def check_user(self, email: str) -> bool: """ From 8d14f2e8b49b7d612fb31eccbe2fd2d09d2ced3e Mon Sep 17 00:00:00 2001 From: Iurii Tatishchev Date: Tue, 20 Apr 2021 15:13:55 -0700 Subject: [PATCH 5/7] Fix some type hints, small ZendeskAdmin refactor --- main/extra_func.py | 5 ++-- main/views.py | 4 ++-- main/zendesk_admin.py | 53 +++++++++++++++++-------------------------- 3 files changed, 26 insertions(+), 36 deletions(-) diff --git a/main/extra_func.py b/main/extra_func.py index fd59968..2ebb384 100644 --- a/main/extra_func.py +++ b/main/extra_func.py @@ -1,5 +1,6 @@ import logging from datetime import timedelta, datetime, date +from typing import Optional from django.contrib.auth.models import User from django.core.exceptions import ObjectDoesNotExist @@ -92,7 +93,7 @@ def get_tickets_list(email): return zenpy.admin.search(assignee=email, type='ticket') -def update_profile(user_profile: UserProfile) -> UserProfile: +def update_profile(user_profile: UserProfile): """ Функция обновляет профиль пользователя в соответствии с текущим в Zendesk. @@ -318,7 +319,7 @@ class StatisticData: self.display = display_format return True - def get_data(self) -> list: + def get_data(self) -> Optional[dict]: """ Функция возвращает данные - список объектов RoleChangeLogs. """ diff --git a/main/views.py b/main/views.py index c7eec58..818de06 100644 --- a/main/views.py +++ b/main/views.py @@ -24,7 +24,7 @@ from access_controller.settings import DEFAULT_FROM_EMAIL, ZENDESK_ROLES, ZENDES from main.extra_func import check_user_exist, update_profile, get_user_organization, \ make_engineer, make_light_agent, get_users_list, update_users_in_model, count_users, \ StatisticData, log -from main.zendesk_admin import ZendeskAdmin +from main.zendesk_admin import zenpy from main.forms import AdminPageUsers, CustomRegistrationForm, CustomAuthenticationForm, StatisticForm from main.serializers import ProfileSerializer, ZendeskUserSerializer from .models import UserProfile @@ -167,7 +167,7 @@ def auth_user(request: WSGIRequest) -> ZenpyUser: :param request: email, subdomain и token пользователя :return: объект пользователя Zendesk """ - admin = ZendeskAdmin().admin + admin = zenpy.admin zenpy_user: ZenpyUser = admin.users.search(request.user.email).values[0] return zenpy_user, admin diff --git a/main/zendesk_admin.py b/main/zendesk_admin.py index 70a461f..7692e0e 100644 --- a/main/zendesk_admin.py +++ b/main/zendesk_admin.py @@ -1,7 +1,7 @@ -from typing import Optional +from typing import Optional, Dict from zenpy import Zenpy -from zenpy.lib.api_objects import User as ZenpyUser +from zenpy.lib.api_objects import User as ZenpyUser, Group as ZenpyGroup from zenpy.lib.exception import APIException from access_controller.settings import ACTRL_ZENDESK_SUBDOMAIN, ACTRL_API_EMAIL, ACTRL_API_TOKEN, ACTRL_API_PASSWORD, \ @@ -13,24 +13,12 @@ class ZendeskAdmin: Класс **ZendeskAdmin** существует, чтобы в каждой функции отдельно не проверять аккаунт администратора. :param credentials: Полномочия (первым указывается учетная запись организации в Zendesk) - :type credentials: :class:`dict` - :param email: Email администратора, указанный в env - :type email: :class:`str` - :param token: Токен администратора (формируется в Zendesk, указывается в env) - :type token: :class:`str` - :param password: Пароль администратора, указанный в env - :type password: :class:`str` + :type credentials: :class:`Dict[str, str]` """ - credentials: dict = { - 'subdomain': ACTRL_ZENDESK_SUBDOMAIN - } - email: str = ACTRL_API_EMAIL - token: str = ACTRL_API_TOKEN - password: str = ACTRL_API_PASSWORD - - def __init__(self): - self.create_admin() + def __init__(self, credentials: Dict[str, str]): + self.credentials = credentials + self.admin = self.create_admin() 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 @@ -89,7 +77,7 @@ class ZendeskAdmin: """ return self.admin.users.search(email).values[0] - def get_group(self, name: str) -> Optional[str]: + def get_group(self, name: str) -> Optional[ZenpyGroup]: """ Функция возвращает группу по названию @@ -111,31 +99,32 @@ class ZendeskAdmin: user = self.admin.users.search(email).values[0] return user.organization.name if user.organization else None - def create_admin(self) -> None: + def create_admin(self) -> Zenpy: """ Функция создает администратора, проверяя наличие вводимых данных в env. - :param credentials: В список полномочий администратора вносятся email, token, password из env - :type credentials: :class:`dict` :raise: :class:`ValueError`: исключение, вызываемое если email не введен в env :raise: :class:`APIException`: исключение, вызываемое если пользователя с таким email не существует в Zendesk """ - if self.email is None: + if self.credentials.get('email') is None: raise ValueError('access_controller email not in env') - self.credentials['email'] = self.email - if self.token: - self.credentials['token'] = self.token - elif self.password: - self.credentials['password'] = self.password - else: + if self.credentials.get('token') is None and self.credentials.get('password') is None: raise ValueError('access_controller token or password not in env') - self.admin = Zenpy(**self.credentials) + + admin = Zenpy(**self.credentials) try: - self.admin.search(self.email, type='user') + admin.search(self.credentials['email'], type='user') except APIException: raise ValueError('invalid access_controller`s login data') + return admin -zenpy = ZendeskAdmin() + +zenpy = ZendeskAdmin({ + 'subdomain': ACTRL_ZENDESK_SUBDOMAIN, + 'email': ACTRL_API_EMAIL, + 'token': ACTRL_API_TOKEN, + 'password': ACTRL_API_PASSWORD, +}) From 7f837d99441dd1c2bfc7e65626fc13e986ac0bc8 Mon Sep 17 00:00:00 2001 From: Iurii Tatishchev Date: Tue, 20 Apr 2021 15:34:10 -0700 Subject: [PATCH 6/7] Remove unnecessary ZendeskAdmin methods --- main/zendesk_admin.py | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/main/zendesk_admin.py b/main/zendesk_admin.py index 7692e0e..8ecb877 100644 --- a/main/zendesk_admin.py +++ b/main/zendesk_admin.py @@ -31,43 +31,6 @@ class ZendeskAdmin: """ return True if self.admin.search(email, type='user') else False - def get_user_name(self, email: str) -> str: - """ - Функция **get_user_name** возвращает имя пользователя по его email - """ - user = self.admin.users.search(email).values[0] - return user.name - - def get_user_role(self, email: str) -> str: - """ - Функция возвращает роль пользователя по его email. - - :param email: Email пользователя - :return: Роль пользователя - """ - user = self.admin.users.search(email).values[0] - return user.role - - def get_user_id(self, email: str) -> str: - """ - Функция возвращает id пользователя по его email - - :param email: Email пользователя - :return: ID пользователя - """ - user = self.admin.users.search(email).values[0] - return user.id - - def get_user_image(self, email: str) -> str: - """ - Функция возвращает url-ссылку на аватар пользователя по его email. - - :param email: Email пользователя - :return: Аватар пользователя - """ - user = self.admin.users.search(email).values[0] - return user.photo['content_url'] if user.photo else None - def get_user(self, email: str) -> ZenpyUser: """ Функция возвращает пользователя (объект) по его email. From f65eb546524d9fe04b25c4cf2e2952a5b6f789d5 Mon Sep 17 00:00:00 2001 From: Iurii Tatishchev Date: Tue, 20 Apr 2021 15:39:55 -0700 Subject: [PATCH 7/7] Small views.py refactor --- main/views.py | 58 +++++++++++++++++++-------------------------------- 1 file changed, 21 insertions(+), 37 deletions(-) diff --git a/main/views.py b/main/views.py index 818de06..2dc6636 100644 --- a/main/views.py +++ b/main/views.py @@ -1,4 +1,5 @@ from smtplib import SMTPException +from typing import Dict, Any from django.contrib import messages from django.contrib.auth.decorators import login_required @@ -11,14 +12,13 @@ from django.contrib.contenttypes.models import ContentType from django.contrib.messages.views import SuccessMessageMixin from django.core.handlers.wsgi import WSGIRequest from django.http import HttpResponseRedirect, HttpResponse -from django.shortcuts import render, redirect, get_list_or_404 +from django.shortcuts import render, redirect from django.urls import reverse_lazy, reverse from django.views.generic import FormView from django_registration.views import RegistrationView # Django REST from rest_framework import viewsets from rest_framework.response import Response -from zenpy.lib.api_objects import User as ZenpyUser from access_controller.settings import DEFAULT_FROM_EMAIL, ZENDESK_ROLES, ZENDESK_MAX_AGENTS from main.extra_func import check_user_exist, update_profile, get_user_organization, \ @@ -31,9 +31,7 @@ from .models import UserProfile def setup_context(profile_lit: bool = False, control_lit: bool = False, work_lit: bool = False, - registration_lit: bool = False, login_lit: bool = False, stats_lit: bool = False): - - print(profile_lit, control_lit, work_lit, registration_lit, login_lit) + registration_lit: bool = False, login_lit: bool = False, stats_lit: bool = False) -> Dict[str, Any]: context = { 'profile_lit': profile_lit, @@ -160,18 +158,6 @@ def profile_page(request: WSGIRequest) -> HttpResponse: return render(request, 'pages/profile.html', context) -def auth_user(request: WSGIRequest) -> ZenpyUser: - """ - Функция возвращает профиль пользователя на Zendesk. - - :param request: email, subdomain и token пользователя - :return: объект пользователя Zendesk - """ - admin = zenpy.admin - zenpy_user: ZenpyUser = admin.users.search(request.user.email).values[0] - return zenpy_user, admin - - @login_required() def work_page(request: WSGIRequest, id: int) -> HttpResponse: """ @@ -186,7 +172,6 @@ def work_page(request: WSGIRequest, id: int) -> HttpResponse: engineers = [] light_agents = [] for user in users: - if user.custom_role_id == ZENDESK_ROLES['engineer']: engineers.append(user) elif user.custom_role_id == ZENDESK_ROLES['light_agent']: @@ -207,39 +192,38 @@ def work_page(request: WSGIRequest, id: int) -> HttpResponse: @login_required() def work_hand_over(request: WSGIRequest) -> HttpResponseRedirect: """ - Функция позволяет текущему пользователю (login_required) сдать права, а именно сменить в Zendesk роль с "engineer" на "light agent" - и установить роль "agent" в БД. Действия выполняются, если исходная роль пользователя "engineer". + Функция позволяет текущему пользователю сдать права, а именно сменить в Zendesk роль с "engineer" на "light_agent" :param request: данные текущего пользователя (login_required) :return: перезагрузка текущей страницы после выполнения смены роли """ - make_light_agent(request.user.userprofile,request.user) + make_light_agent(request.user.userprofile, request.user) return HttpResponseRedirect(reverse('work', args=(request.user.id,))) @login_required() def work_become_engineer(request: WSGIRequest) -> HttpResponseRedirect: """ - Функция меняет роль пользователя в Zendesk на "engineer" и присваивает роль "agent" в БД (в случае, если исходная роль пользователя была "light_agent"). + Функция позволяет текущему пользователю получить права, а именно сменить в Zendesk роль с "light_agent" на "engineer" :param request: данные текущего пользователя (login_required) :return: перезагрузка текущей страницы после выполнения смены роли """ - zenpy_user, admin = auth_user(request) - make_engineer(request.user.userprofile,request.user) + make_engineer(request.user.userprofile, request.user) return HttpResponseRedirect(reverse('work', args=(request.user.id,))) + @login_required() def work_get_tickets(request): - zenpy_user, admin = auth_user(request) count_tickets = int(request.GET["count_tickets"]) - tickets = [ticket for ticket in admin.search(type="ticket") if ticket.group.name == 'Сменная группа' and ticket.assignee is None] + tickets = [ticket for ticket in zenpy.admin.search(type="ticket") if + ticket.group.name == 'Сменная группа' and ticket.assignee is None] for i in range(len(tickets)): if i == count_tickets: return HttpResponseRedirect(reverse('work', args=(request.user.id,))) - tickets[i].assignee = zenpy_user - admin.tickets.update(tickets[i]) + tickets[i].assignee = zenpy.get_user(request.user.email) + zenpy.admin.tickets.update(tickets[i]) return HttpResponseRedirect(reverse('work', args=(request.user.id,))) @@ -378,16 +362,16 @@ def statistic_page(request: WSGIRequest) -> HttpResponse: if form.is_valid(): start_date, end_date = form.cleaned_data['range_start'], form.cleaned_data['range_end'] interval, show = form.cleaned_data['interval'], form.cleaned_data['display_format'] - Data = StatisticData(start_date, end_date, form.cleaned_data['email']) - Data.set_display(show) - Data.set_interval(interval) - stats = Data.get_statistic() - if Data.errors: - context['errors'] = Data.errors - if Data.warnings: - context['warnings'] = Data.warnings + data = StatisticData(start_date, end_date, form.cleaned_data['email']) + data.set_display(show) + data.set_interval(interval) + stats = data.get_statistic() + if data.errors: + context['errors'] = data.errors + if data.warnings: + context['warnings'] = data.warnings context['log_stats'] = stats if not context['errors'] else None - if request.method == 'GET': + elif request.method == 'GET': form = StatisticForm() context['form'] = form return render(request, 'pages/statistic.html', context)