Merge branch 'feature/documentation' into 'develop'
Feature/documentation See merge request 2020-2021/online/s101/group-02/access_controller!41
@ -3,14 +3,17 @@
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
SOURCEDIR = source
|
||||
BUILDDIR = build
|
||||
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
@echo " spelling to check for typos in documentation"
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
@ -19,5 +22,10 @@ help:
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
|
||||
spelling:
|
||||
$(SPHINXBUILD) -b spelling -W $(ALLSPHINXOPTS) $(BUILDDIR)/spelling
|
||||
$(SPHINXBUILD) -b spelling -W $(SOURCEDIR) $(BUILDDIR)/spelling
|
||||
@echo
|
||||
@echo "Check finished. Wrong words can be found in " \
|
||||
"build/spelling/output.txt."
|
||||
|
||||
|
BIN
docs/source/_static/login.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
docs/source/_static/main.png
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
docs/source/_static/main_logined.png
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
docs/source/_static/main_logined_agent.png
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
docs/source/_static/permission_management.png
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
docs/source/_static/permission_request.png
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
docs/source/_static/profile.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
docs/source/_static/registration.png
Normal file
After Width: | Height: | Size: 25 KiB |
@ -1,10 +1,9 @@
|
||||
Документация разработчика
|
||||
=========================
|
||||
|
||||
|
||||
******
|
||||
*******
|
||||
Models
|
||||
******
|
||||
*******
|
||||
|
||||
.. automodule:: main.models
|
||||
:members:
|
||||
@ -26,9 +25,27 @@ Extra Functions
|
||||
:members:
|
||||
|
||||
|
||||
***************
|
||||
Serializers
|
||||
***************
|
||||
|
||||
.. automodule:: main.serializers
|
||||
:members:
|
||||
|
||||
|
||||
***************
|
||||
API functions
|
||||
***************
|
||||
|
||||
.. automodule:: main.apiauth
|
||||
:members:
|
||||
|
||||
|
||||
*****
|
||||
Views
|
||||
*****
|
||||
|
||||
.. automodule:: main.views
|
||||
:members:
|
||||
|
||||
|
||||
|
@ -14,6 +14,9 @@ import os
|
||||
import sys
|
||||
import importlib
|
||||
import inspect
|
||||
import enchant
|
||||
from enchant import checker
|
||||
|
||||
|
||||
sys.path.insert(0, os.path.abspath('../../'))
|
||||
|
||||
@ -35,10 +38,7 @@ ManagerDescriptor.__get__ = lambda self, *args, **kwargs: self.manager
|
||||
from django.db.models.query import QuerySet
|
||||
|
||||
QuerySet.__repr__ = lambda self: self.__class__.__name__
|
||||
try:
|
||||
import enchant # NoQA
|
||||
except ImportError:
|
||||
enchant = None
|
||||
|
||||
|
||||
django.setup()
|
||||
|
||||
@ -52,8 +52,6 @@ author = 'SHP S101, group 2'
|
||||
release = 'v0.01'
|
||||
|
||||
|
||||
# Django sphinx setup by https://gist.github.com/codingjoe/314bda5a07ff3b41f247
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
def process_django_models(app, what, name, obj, options, lines):
|
||||
@ -91,7 +89,6 @@ def process_django_models(app, what, name, obj, options, lines):
|
||||
lines.append(':type %s: %s.%s' % (field.attname, module, field_type.__name__))
|
||||
if enchant is not None:
|
||||
lines += spelling_white_list
|
||||
print('ok')
|
||||
return lines
|
||||
|
||||
|
||||
@ -119,18 +116,18 @@ def skip_queryset(app, what, name, obj, skip, options):
|
||||
return skip
|
||||
|
||||
|
||||
def setup(app):
|
||||
# Register the docstring processor with sphinx
|
||||
app.connect('autodoc-process-docstring', process_django_models)
|
||||
app.connect('autodoc-skip-member', skip_queryset)
|
||||
if enchant is not None:
|
||||
app.connect('autodoc-process-docstring', process_modules)
|
||||
# def setup(app):
|
||||
# # Register the docstring processor with sphinx
|
||||
# app.connect('autodoc-process-docstring', process_django_models)
|
||||
# app.connect('autodoc-skip-member', skip_queryset)
|
||||
# app.connect('autodoc-process-docstring', process_modules)
|
||||
|
||||
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
extensions = {
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.intersphinx',
|
||||
@ -138,12 +135,11 @@ extensions = [
|
||||
'sphinx_rtd_theme',
|
||||
'sphinx.ext.graphviz',
|
||||
'sphinx.ext.inheritance_diagram',
|
||||
'sphinx_autodoc_typehints'
|
||||
'sphinx_autodoc_typehints',
|
||||
'sphinxcontrib.spelling'
|
||||
|
||||
]
|
||||
}
|
||||
|
||||
if enchant is not None:
|
||||
extensions.append('sphinxcontrib.spelling')
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
@ -188,16 +184,23 @@ intersphinx_mapping = {
|
||||
autodoc_default_flags = ['members']
|
||||
|
||||
# spell checking
|
||||
spelling_lang = 'en_US'
|
||||
spelling_word_list_filename = 'spelling_wordlist.txt'
|
||||
spelling_lang = 'ru_RU'
|
||||
tokenizer_lang = 'ru_RU'
|
||||
spelling_exclude_patterns=['ignored_*']
|
||||
spelling_show_suggestions = True
|
||||
spelling_show_whole_line=True
|
||||
spelling_warning=True
|
||||
spelling_ignore_pypi_package_names = True
|
||||
spelling_ignore_wiki_words=True
|
||||
spelling_ignore_acronyms=True
|
||||
spelling_ignore_python_builtins=True
|
||||
spelling_ignore_importable_modules=True
|
||||
spelling_ignore_contributor_names=True
|
||||
|
||||
# -- Options for todo extension ----------------------------------------------
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = True
|
||||
|
||||
set_type_checking_flag = True
|
||||
typehints_fully_qualified = True
|
||||
always_document_param_types = True
|
||||
|
@ -3,7 +3,7 @@
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to ZenDesk Access Controller's documentation!
|
||||
Документация контроллера прав доступа
|
||||
=====================================================
|
||||
|
||||
.. toctree::
|
||||
@ -15,7 +15,6 @@ Welcome to ZenDesk Access Controller's documentation!
|
||||
todo
|
||||
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
|
@ -2,17 +2,88 @@
|
||||
Документация пользователя
|
||||
=========================
|
||||
|
||||
******************************
|
||||
**Управление правами доступа**
|
||||
******************************
|
||||
|
||||
|
||||
**ZenDesk Access Controller** - Web-приложение, для выдачи прав пользователям системы по запросу самого пользователя.
|
||||
**ZenDesk Access Controller** - web-приложение, для выдачи прав пользователям системы по запросу самого пользователя.
|
||||
|
||||
Например, из 12 человек 3 сейчас работают с правами администратора, по окончании рабочей смены они сдают свои права (освобождают места) и другие пользователи могут запросить эти права в свое пользование.
|
||||
|
||||
Оставшиеся 9 человек получают права легкого агента - без прав редактирования, а только чтение.
|
||||
|
||||
**Интерфейс пользователя:**
|
||||
*****************
|
||||
Главная страница
|
||||
*****************
|
||||
|
||||
Меню главной страницы предоставляет Вам выбор:
|
||||
|
||||
* **"Войти"** - если Вы уже являетесь зарегистрированным пользователем
|
||||
* **"Зарегистрироваться"** - при первом входе
|
||||
|
||||
.. image:: _static/main.png
|
||||
|
||||
Внимание! Для регистрации используется email с сайта Zendesk. Регистрация по каждому email
|
||||
возможна один раз
|
||||
|
||||
**После авторизации пользователь может выбрать из следующих разделов меню:**
|
||||
|
||||
* **"Профиль"** - просмотреть свои данные и запросить права доступа
|
||||
* **"Запрос прав"** - получение прав для работы с тикетами или **"Управление"** - доступно для администратора и предоставляет возможность группового назначения ролей пользователям
|
||||
|
||||
.. image:: _static/main_logined_agent.png
|
||||
|
||||
*************
|
||||
Регистрация
|
||||
*************
|
||||
|
||||
Для регистрации необходимо ввести email, который указан Вами в Zendesk.
|
||||
|
||||
.. image:: _static/registration.png
|
||||
|
||||
На электронную почту придет ссылка, пройдя по которой, Вам необходимо задать пароль.
|
||||
|
||||
***********
|
||||
Авторизация
|
||||
***********
|
||||
|
||||
Для входа необходимо ввести email и пароль
|
||||
|
||||
.. image:: _static/login.png
|
||||
|
||||
Если Вы не помните пароль необходимо пройти по ссылке "Забыли пароль" и указать email.
|
||||
На Вашу почту придет ссылка для установки нового пароля.
|
||||
|
||||
********
|
||||
Профиль
|
||||
********
|
||||
|
||||
Профиль пользователя - это Ваша рабочая страница.
|
||||
|
||||
Здесь Вы можете просмотреть информацию пользователя (Ваши данные с Zendesk) и запросить права доступа для работы с тикетами.
|
||||
|
||||
.. image:: _static/profile.png
|
||||
|
||||
********************
|
||||
Запрос прав доступа
|
||||
********************
|
||||
|
||||
На странице запроса прав Вам доступна информация о количестве и списке работающих над тикетами сотрудников,
|
||||
а также возможность сдать и запросить права.
|
||||
|
||||
.. image:: _static/permission_request.png
|
||||
|
||||
******************************************
|
||||
Управление правами доступа администратором
|
||||
******************************************
|
||||
|
||||
Для администратора существует удобный интерфейс страницы управления, в котором представлены:
|
||||
|
||||
* Количество свободных инженерных мест
|
||||
* Количество и список инженеров и легких агентов
|
||||
* Возможность группового назначения прав с использованием чек-боксов
|
||||
|
||||
.. image:: _static/permission_management.png
|
||||
|
||||
.. |copy| unicode:: 0xA9 .. Школа программистов S101, группа 2. 2021гю
|
||||
|
86
docs/source/spelling_wordlist.txt
Normal file
@ -0,0 +1,86 @@
|
||||
тикетами
|
||||
тикета
|
||||
тикетов
|
||||
тикет
|
||||
web
|
||||
Indices
|
||||
and
|
||||
tables
|
||||
Models
|
||||
логирования
|
||||
User
|
||||
user
|
||||
superuser
|
||||
light
|
||||
light_agent
|
||||
admin
|
||||
agent
|
||||
bootstrap
|
||||
form
|
||||
control
|
||||
Zendesk
|
||||
email
|
||||
Extra
|
||||
Functions
|
||||
env
|
||||
ID
|
||||
url
|
||||
None
|
||||
token
|
||||
password
|
||||
engineer
|
||||
SYSTEM
|
||||
start_date
|
||||
end_date
|
||||
timedelta
|
||||
log
|
||||
RoleChangeLogs
|
||||
time(datetime.time)
|
||||
stat
|
||||
statistic
|
||||
True
|
||||
False
|
||||
val
|
||||
start
|
||||
end
|
||||
date
|
||||
Токен
|
||||
токеном
|
||||
аутентифицирован
|
||||
(datetime.time)
|
||||
datetime
|
||||
time
|
||||
serializer
|
||||
валидны
|
||||
html
|
||||
subdomain
|
||||
логгирования
|
||||
логгирование
|
||||
forms
|
||||
StatisticForm
|
||||
Userprofile
|
||||
login
|
||||
login_required
|
||||
required
|
||||
id
|
||||
prom
|
||||
home
|
||||
PycharmProjects
|
||||
Access
|
||||
access
|
||||
controler
|
||||
controller
|
||||
main
|
||||
views
|
||||
py
|
||||
docstring
|
||||
of
|
||||
page
|
||||
API
|
||||
functions
|
||||
Serializer
|
||||
Serializers
|
||||
|
||||
|
||||
|
||||
|
@ -4,7 +4,16 @@ from zenpy import Zenpy
|
||||
from zenpy.lib.api_objects import User as ZenpyUser
|
||||
|
||||
|
||||
def api_auth():
|
||||
def api_auth() -> dict:
|
||||
"""
|
||||
Функция создания пользователя с использованием Zendesk API.
|
||||
|
||||
Получает из env Zendesk - email, token, password пользователя.
|
||||
Если данные валидны и пользователь Zendesk с указанным email и токеном или паролем существует,
|
||||
создается словарь данных пользователя, полученных через API c Zendesk.
|
||||
|
||||
:return: данные пользователя
|
||||
"""
|
||||
credentials = {
|
||||
'subdomain': 'ngenix1612197338'
|
||||
}
|
||||
|
@ -13,16 +13,16 @@ from main.models import UserProfile, RoleChangeLogs, UnassignedTicket, Unassigne
|
||||
|
||||
class ZendeskAdmin:
|
||||
"""
|
||||
Класс **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`
|
||||
: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 = {
|
||||
@ -37,7 +37,10 @@ class ZendeskAdmin:
|
||||
|
||||
def check_user(self, email: str) -> bool:
|
||||
"""
|
||||
Функция **check_user** осуществляет проверку существования пользователя в Zendesk по email
|
||||
Функция осуществляет проверку существования пользователя в Zendesk по email.
|
||||
|
||||
:param email: Email пользователя
|
||||
:return: Является ли зарегистрированным
|
||||
"""
|
||||
return True if self.admin.search(email, type='user') else False
|
||||
|
||||
@ -50,35 +53,50 @@ class ZendeskAdmin:
|
||||
|
||||
def get_user_role(self, email: str) -> str:
|
||||
"""
|
||||
Функция **get_user_role** возвращает роль пользователя по его email
|
||||
Функция возвращает роль пользователя по его email.
|
||||
|
||||
:param email: Email пользователя
|
||||
:return: Роль пользователя
|
||||
"""
|
||||
user = self.admin.users.search(email).values[0]
|
||||
return user.role
|
||||
|
||||
def get_user_id(self, email: str) -> str:
|
||||
"""
|
||||
Функция **get_user_id** возвращает id пользователя по его email
|
||||
Функция возвращает 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:
|
||||
"""
|
||||
Функция **get_user_image** возвращает url-ссылку на аватар пользователя по его email
|
||||
Функция возвращает 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):
|
||||
"""
|
||||
Функция **get_user** возвращает пользователя (объект) по его email
|
||||
Функция возвращает пользователя (объект) по его email.
|
||||
|
||||
:param email: email пользователя
|
||||
:return: email пользователя, найденного в БД
|
||||
:param email: Email пользователя
|
||||
:return: Объект пользователя, найденного в БД
|
||||
"""
|
||||
return self.admin.users.search(email).values[0]
|
||||
|
||||
def get_group(self, name):
|
||||
def get_group(self, name: str) -> str:
|
||||
"""
|
||||
Функция возвращает группы, к которым принадлежит пользователь.
|
||||
|
||||
:param name: Имя пользователя
|
||||
:return: Группы пользователя (в случае отсутствия None)
|
||||
"""
|
||||
groups = self.admin.search(name)
|
||||
for group in groups:
|
||||
return group
|
||||
@ -86,19 +104,22 @@ class ZendeskAdmin:
|
||||
|
||||
def get_user_org(self, email: str) -> str:
|
||||
"""
|
||||
Функция **get_user_org** возвращает организацию, к которой относится пользователь по его email
|
||||
Функция возвращает организацию, к которой относится пользователь по его 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) -> Zenpy:
|
||||
"""
|
||||
Функция **Create_admin()** создает администратора, проверяя наличие вводимых данных в env.
|
||||
Функция создает администратора, проверяя наличие вводимых данных в env.
|
||||
|
||||
:param credentials: В список полномочий администратора вносятся email, token, password из env
|
||||
:type credentials: :class:`dict`
|
||||
:raise: :class:`ValueError`: исключение, вызываемое если email не введен в env
|
||||
:raise: :class:`APIException`: исключение, вызываемое если пользователя с таким email не существует в Zendesk
|
||||
: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:
|
||||
@ -120,7 +141,11 @@ class ZendeskAdmin:
|
||||
|
||||
def update_role(user_profile: UserProfile, role: str) -> UserProfile:
|
||||
"""
|
||||
Функция **update_role** меняет роль пользователя.
|
||||
Функция меняет роль пользователя.
|
||||
|
||||
:param user_profile: Профиль пользователя
|
||||
:param role: Новая роль
|
||||
:return: Пользователь с обновленной ролью
|
||||
"""
|
||||
zendesk = ZendeskAdmin()
|
||||
user = zendesk.get_user(user_profile.user.email)
|
||||
@ -130,7 +155,10 @@ def update_role(user_profile: UserProfile, role: str) -> UserProfile:
|
||||
|
||||
def make_engineer(user_profile: UserProfile, who_changes: User) -> UserProfile:
|
||||
"""
|
||||
Функция **make_engineer** устанавливает пользователю роль инженера.
|
||||
Функция устанавливает пользователю роль инженера.
|
||||
|
||||
:param user_profile: Профиль пользователя
|
||||
:return: Вызов функции **update_role** с параметрами: профиль пользователя, роль "engineer"
|
||||
"""
|
||||
RoleChangeLogs.objects.create(
|
||||
user=user_profile.user,
|
||||
@ -143,7 +171,10 @@ def make_engineer(user_profile: UserProfile, who_changes: User) -> UserProfile:
|
||||
|
||||
def make_light_agent(user_profile: UserProfile, who_changes: User) -> UserProfile:
|
||||
"""
|
||||
Функция **make_light_agent** устанавливапет пользователю роль легкого агента.
|
||||
Функция устанавливает пользователю роль легкого агента.
|
||||
|
||||
:param user_profile: Профиль пользователя
|
||||
:return: Вызов функции **update_role** с параметрами: профиль пользователя, роль "light_agent"
|
||||
"""
|
||||
tickets = get_tickets_list(user_profile.user.email)
|
||||
for ticket in tickets:
|
||||
@ -170,7 +201,7 @@ def make_light_agent(user_profile: UserProfile, who_changes: User) -> UserProfil
|
||||
|
||||
def get_users_list() -> list:
|
||||
"""
|
||||
Функция **get_users_list** возвращает список пользователей Zendesk, относящихся к организации.
|
||||
Функция **get_users_list** возвращает список пользователей Zendesk, относящихся к организации SYSTEM.
|
||||
"""
|
||||
zendesk = ZendeskAdmin()
|
||||
|
||||
@ -189,7 +220,10 @@ def get_tickets_list(email):
|
||||
|
||||
def update_profile(user_profile: UserProfile) -> UserProfile:
|
||||
"""
|
||||
Функция обновляет профиль пользователя в соотвтетствии с текущим в Zendesk
|
||||
Функция обновляет профиль пользователя в соответствии с текущим в Zendesk.
|
||||
|
||||
:param user_profile: Профиль пользователя
|
||||
:return: Обновленный, в соответствие с текущими данными в Zendesk, профиль пользователя
|
||||
"""
|
||||
user = ZendeskAdmin().get_user(user_profile.user.email)
|
||||
user_profile.name = user.name
|
||||
@ -201,21 +235,27 @@ def update_profile(user_profile: UserProfile) -> UserProfile:
|
||||
|
||||
def check_user_exist(email: str) -> bool:
|
||||
"""
|
||||
Функция проверяет, существует ли пользователь
|
||||
Функция проверяет, существует ли пользователь.
|
||||
|
||||
:param email: Email пользователя
|
||||
:return: Зарегистрирован ли пользователь в Zendesk
|
||||
"""
|
||||
return ZendeskAdmin().check_user(email)
|
||||
|
||||
|
||||
def get_user_organization(email: str) -> str:
|
||||
"""
|
||||
Функция возвращает организацию пользователя
|
||||
Функция возвращает организацию пользователя.
|
||||
|
||||
:param email: Email пользователя
|
||||
:return: Организация пользователя
|
||||
"""
|
||||
return ZendeskAdmin().get_user_org(email)
|
||||
|
||||
|
||||
def check_user_auth(email: str, password: str) -> bool:
|
||||
"""
|
||||
Функция проверяет, верны ли входные данные
|
||||
Функция проверяет, верны ли входные данные.
|
||||
|
||||
:raise: :class:`APIException`: исключение, вызываемое если пользователь не аутентифицирован
|
||||
"""
|
||||
@ -232,7 +272,14 @@ def check_user_auth(email: str, password: str) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def update_user_in_model(profile, zendesk_user):
|
||||
def update_user_in_model(profile: UserProfile, zendesk_user: User) -> UserProfile:
|
||||
"""
|
||||
Функция обновляет профиль пользователя при изменении данных пользователя на Zendesk.
|
||||
|
||||
:param profile: Профиль пользователя
|
||||
:param zendesk_user: Данные пользователя в Zendesk
|
||||
:return: Обновленный профиль пользователя
|
||||
"""
|
||||
profile.name = zendesk_user.name
|
||||
profile.role = zendesk_user.role
|
||||
profile.image = zendesk_user.photo['content_url'] if zendesk_user.photo else None
|
||||
@ -243,7 +290,7 @@ def update_user_in_model(profile, zendesk_user):
|
||||
|
||||
def count_users(users) -> tuple:
|
||||
"""
|
||||
Функция подсчета количества сотрудников с ролями engineer и light_a
|
||||
Функция подсчета количества сотрудников с ролями engineer и light_agent
|
||||
"""
|
||||
engineers, light_agents = 0, 0
|
||||
for user in users:
|
||||
@ -270,7 +317,11 @@ def update_users_in_model():
|
||||
|
||||
def daterange(start_date, end_date) -> list:
|
||||
"""
|
||||
Возвращает список дней с start_date по end_date исключая правую границу
|
||||
Функция возвращает список дней с start_date по end_date, исключая правую границу.
|
||||
|
||||
:param start_date: Начальная дата
|
||||
:param end_date: Конечная дата
|
||||
:return: Список дней, не включая конечную дату
|
||||
"""
|
||||
dates = []
|
||||
for n in range(int((end_date - start_date).days)):
|
||||
@ -280,8 +331,12 @@ def daterange(start_date, end_date) -> list:
|
||||
|
||||
def get_timedelta(log, time=None) -> timedelta:
|
||||
"""
|
||||
Возвращает объект класса timedelta, который хранит промежуток времени от начала суток до момента,
|
||||
который находится в log (объект класса RoleChangeLogs) или в time(datetime.time), если введён
|
||||
Функция возвращает объект класса timedelta, который хранит промежуток времени от начала суток до момента,
|
||||
который находится в log (объект класса RoleChangeLogs) или в time(datetime.time), если введён.
|
||||
|
||||
:param log: Лог
|
||||
:param time: Время
|
||||
:return: Сколько времени прошло от начала суток до события
|
||||
"""
|
||||
if time is None:
|
||||
time = log.change_time.time()
|
||||
@ -289,15 +344,41 @@ def get_timedelta(log, time=None) -> timedelta:
|
||||
return time
|
||||
|
||||
|
||||
def last_day_of_month(day):
|
||||
def last_day_of_month(day: int) -> int:
|
||||
"""
|
||||
Возвращает последний день любого месяца
|
||||
Функция возвращает последний день текущего месяца.
|
||||
|
||||
:param day: Текущий день
|
||||
:return: Последний день месяца
|
||||
"""
|
||||
next_month = day.replace(day=28) + timedelta(days=4)
|
||||
return next_month - timedelta(days=next_month.day)
|
||||
|
||||
|
||||
class StatisticData:
|
||||
"""
|
||||
Класс для учета статистики интервалов работы пользователей.
|
||||
Передаваемые параметры: start_date, end_date, email, stat.
|
||||
|
||||
:param display: Формат отображения времени (часы, минуты)
|
||||
:type display: :class:`list`
|
||||
:param interval: Интервал времени в часах и минутах
|
||||
:type interval: :class:`list`
|
||||
:param start_date: Дата начала работы
|
||||
:type start_date: :class:`date`
|
||||
:param end_date: Дата окончания работы
|
||||
:type end_date: :class:`date`
|
||||
:param email: Email пользователя
|
||||
:type email: :class:`str`
|
||||
:param errors: Список ошибок
|
||||
:type errors: :class:`list`
|
||||
:param warnings: Список предупреждений
|
||||
:type warnings: :class:`list`
|
||||
:param data: Ретроспектива смены ролей пользователя
|
||||
:type data: :class:`dict`
|
||||
:param statistic: Интервалы работы пользователя
|
||||
:type statistic: :class:`dict`
|
||||
"""
|
||||
def __init__(self, start_date, end_date, user_email, stat=None):
|
||||
self.display = None
|
||||
self.interval = None
|
||||
@ -314,10 +395,11 @@ class StatisticData:
|
||||
else:
|
||||
self.statistic = stat
|
||||
|
||||
def get_statistic(self):
|
||||
def get_statistic(self) -> dict:
|
||||
"""
|
||||
Вернуть словарь statistic с применением формата отображения и интеравала работы(если они есть)
|
||||
None, если были ошибки при создании
|
||||
Функция возвращает статистику работы пользователя.
|
||||
|
||||
:return: Словарь statistic с применением формата отображения и интервала работы(если они есть). None, если были ошибки при создании.
|
||||
"""
|
||||
if self.is_valid_statistic():
|
||||
stat = self.statistic
|
||||
@ -327,15 +409,20 @@ class StatisticData:
|
||||
else:
|
||||
return None
|
||||
|
||||
def is_valid_statistic(self):
|
||||
def is_valid_statistic(self) -> bool:
|
||||
"""
|
||||
Были ли ошибки при создании статистики
|
||||
Функция проверяет были ли ошибки при создании статистики.
|
||||
|
||||
:return: True, при отсутствии ошибок
|
||||
"""
|
||||
return not self.errors and self.statistic
|
||||
|
||||
def set_interval(self, interval):
|
||||
def set_interval(self, interval: list) -> bool:
|
||||
"""
|
||||
Устанавливает интервал работы
|
||||
Функция проверяет корректность представления интервала работы.
|
||||
|
||||
:param interval: Интервал должен быть указан в днях или месяцах.
|
||||
:return: True, если указан верно
|
||||
"""
|
||||
if interval not in ['months', 'days']:
|
||||
self.errors += ['Интервал работы должен быть в днях или месяцах']
|
||||
@ -343,9 +430,12 @@ class StatisticData:
|
||||
self.interval = interval
|
||||
return True
|
||||
|
||||
def set_display(self, display_format):
|
||||
def set_display(self, display_format: list) -> bool:
|
||||
"""
|
||||
Устанавливает формат отображения
|
||||
Функция проверяет корректность формата отображения интервала.
|
||||
|
||||
:param display_format: Формат отображения должен быть указан в днях или месяцах.
|
||||
:return: True, если указан верно
|
||||
"""
|
||||
if display_format not in ['days', 'hours']:
|
||||
self.errors += ['Формат отображения должен быть в часах или днях']
|
||||
@ -353,26 +443,29 @@ class StatisticData:
|
||||
self.display = display_format
|
||||
return True
|
||||
|
||||
def get_data(self):
|
||||
def get_data(self) -> list:
|
||||
"""
|
||||
Вернуть данные
|
||||
data - массив объектов RoleChangeLogs, является списком логов пользователя
|
||||
data может быть пустым списком
|
||||
Функция возвращает данные - список объектов RoleChangeLogs.
|
||||
"""
|
||||
if self.is_valid_data():
|
||||
return self.data
|
||||
else:
|
||||
return None
|
||||
|
||||
def is_valid_data(self):
|
||||
def is_valid_data(self) -> bool:
|
||||
"""
|
||||
Были ли ошибки при получении логов
|
||||
Функция определяет были ли ошибки при получении логов.
|
||||
|
||||
:return: True, если ошибок нет
|
||||
"""
|
||||
return not self.errors
|
||||
|
||||
def _use_display(self, stat):
|
||||
def _use_display(self, stat: list) -> list:
|
||||
"""
|
||||
Приводит данные к формату отображения
|
||||
Функция приводит данные к формату отображения.
|
||||
|
||||
:param stat: Список данных статистики пользователя
|
||||
:return: Обновленный список
|
||||
"""
|
||||
if not self.is_valid_statistic() or not self.display:
|
||||
return stat
|
||||
@ -384,9 +477,12 @@ class StatisticData:
|
||||
new_stat[key] = item / (ONE_DAY * 3600)
|
||||
return new_stat
|
||||
|
||||
def _use_interval(self, stat):
|
||||
def _use_interval(self, stat: dict) -> dict:
|
||||
"""
|
||||
Объединяет ключи и значения в соответствии с интервалом работы
|
||||
Функция объединяет ключи и значения в соответствии с интервалом работы.
|
||||
|
||||
:param stat: Статистика работы пользователя
|
||||
:return: Обновленная статистика
|
||||
"""
|
||||
if not self.is_valid_statistic() or not self.interval:
|
||||
return stat
|
||||
@ -405,9 +501,11 @@ class StatisticData:
|
||||
new_stat = stat # статистика изначально в днях
|
||||
return new_stat
|
||||
|
||||
def check_time(self):
|
||||
def check_time(self) -> bool:
|
||||
"""
|
||||
Проверка на правильность введенного времени
|
||||
Функция проверяет корректность введенного времени.
|
||||
|
||||
:return: True, если время указано корректно. Иначе, False
|
||||
"""
|
||||
if self.end_date < self.start_date or self.end_date > datetime.now().date():
|
||||
return False
|
||||
@ -415,7 +513,9 @@ class StatisticData:
|
||||
|
||||
def _init_data(self):
|
||||
"""
|
||||
Получение логов в диапазоне дат start_date-end_date для пользователя с почтой email
|
||||
Функция возвращает логи в диапазоне дат start_date - end_date для пользователя с указанным email.
|
||||
|
||||
:return: Данные о смене статусов пользователя. Если пользователь не найден или интервал времени некорректен - ошибку.
|
||||
"""
|
||||
if not self.check_time():
|
||||
self.errors += ['Конец диапазона должен быть позже начала диапазона и раньше текущего времени']
|
||||
@ -428,9 +528,11 @@ class StatisticData:
|
||||
except User.DoesNotExist:
|
||||
self.errors += ['Пользователь не найден']
|
||||
|
||||
def _init_statistic(self):
|
||||
def _init_statistic(self) -> dict:
|
||||
"""
|
||||
Функция заполняет словарь, в котором ключ - дата, значение - кол-во проработанных в этот день секунд
|
||||
Функция заполняет словарь, в котором ключ - дата, значение - кол-во проработанных в этот день секунд.
|
||||
|
||||
:return: Статистика работы пользователя (statistic)
|
||||
"""
|
||||
self.clear_statistic()
|
||||
if not self.get_data():
|
||||
@ -465,17 +567,23 @@ class StatisticData:
|
||||
elapsed_time = next_log.change_time - current_log.change_time
|
||||
self.statistic[current_log.change_time.date()] += elapsed_time.total_seconds()
|
||||
|
||||
def fill_daterange(self, first, last, val=24 * 3600):
|
||||
def fill_daterange(self, first: date, last: date, val: int = 24 * 3600) -> dict:
|
||||
"""
|
||||
Заполение диапазона дат значением val
|
||||
по умолчанию val = кол-во секунд в 1 дне
|
||||
Функция заполняет диапазон дат значением val (по умолчанию val = кол-во секунд в 1 дне).
|
||||
|
||||
:param first: Начальная дата интервала
|
||||
:param last: Последняя дата интервала
|
||||
:param val: Количество секунд в одном дне
|
||||
:return: Статистику пользователя с указанным количеством секунд в заданных днях
|
||||
"""
|
||||
for day in daterange(first, last):
|
||||
self.statistic[day] = val
|
||||
|
||||
def clear_statistic(self):
|
||||
def clear_statistic(self) -> dict:
|
||||
"""
|
||||
Обнуление всех дней
|
||||
Функция осуществляет обновление всех дней.
|
||||
|
||||
:return: Статистику пользователя с количеством рабочих секунд = 0
|
||||
"""
|
||||
self.statistic.clear()
|
||||
self.fill_daterange(self.start_date, self.end_date + timedelta(days=1), 0)
|
||||
|
@ -8,12 +8,11 @@ from main.models import UserProfile
|
||||
|
||||
class CustomRegistrationForm(RegistrationFormUniqueEmail):
|
||||
"""
|
||||
Форма для регистрации :class:`django_registration.forms.RegistrationFormUniqueEmail`
|
||||
Форма для регистрации :class:`django_registration.forms.RegistrationFormUniqueEmail`
|
||||
с добавлением bootstrap-класса "form-control".
|
||||
|
||||
с добавлением bootstrap-класса "form-control"
|
||||
|
||||
:param visible_fields.email: Поле для ввода email, зарегистирированного на Zendesk
|
||||
:type visible_fields.email: :class:`django_registration.forms.RegistrationFormUniqueEmail`
|
||||
:param visible_fields.email: Поле для ввода email, зарегистрированного на Zendesk
|
||||
:type visible_fields.email: :class:`django_registration.forms.RegistrationFormUniqueEmail`
|
||||
"""
|
||||
def __init__(self, *args, **kwargs) -> RegistrationFormUniqueEmail:
|
||||
super().__init__(*args, **kwargs)
|
||||
@ -32,10 +31,10 @@ class CustomRegistrationForm(RegistrationFormUniqueEmail):
|
||||
|
||||
class AdminPageUsers(forms.Form):
|
||||
"""
|
||||
Форма для установки статусов engineer или light_agent пользователям
|
||||
Форма для установки статусов engineer или light_agent пользователям.
|
||||
|
||||
:param users: Поле для установки статуса
|
||||
:type users: :class:`ModelMultipleChoiceField`
|
||||
:param users: Поле для установки статуса
|
||||
:type users: :class:`ModelMultipleChoiceField`
|
||||
"""
|
||||
|
||||
users = forms.ModelMultipleChoiceField(
|
||||
@ -52,8 +51,11 @@ class AdminPageUsers(forms.Form):
|
||||
|
||||
class CustomAuthenticationForm(AuthenticationForm):
|
||||
"""
|
||||
Форма для авторизации :class:`django.contrib.auth.forms.AuthenticationForm`
|
||||
с изменением поля username на email
|
||||
Форма для авторизации :class:`django.contrib.auth.forms.AuthenticationForm`
|
||||
с изменением поля username на email.
|
||||
|
||||
:param username: Поле для ввода email пользователя
|
||||
:type username: :class:`django.forms.fields.CharField`
|
||||
"""
|
||||
username = forms.CharField(
|
||||
label="Электронная почта",
|
||||
@ -69,6 +71,20 @@ class CustomAuthenticationForm(AuthenticationForm):
|
||||
|
||||
|
||||
class StatisticForm(forms.Form):
|
||||
"""
|
||||
Форма отображения интервалов работы пользователя.
|
||||
|
||||
:param email: Поле для ввода email пользователя
|
||||
:type email: :class:`django.forms.fields.EmailField`
|
||||
:param interval: Расчет интервала рабочего времени
|
||||
:type interval: :class:`django.forms.fields.CharField`
|
||||
:param display_format: Формат отображения данных
|
||||
:type display_format: :class:`django.forms.fields.CharField`
|
||||
:param range_start: Дата и время начала работы
|
||||
:type range_start: :class:`django.forms.fields.DateField`
|
||||
:param range_end: Дата и время окончания работы
|
||||
:type range_end: :class:`django.forms.fields.DateField`
|
||||
"""
|
||||
email = forms.EmailField(
|
||||
label='Электроная почта',
|
||||
)
|
||||
|
@ -6,7 +6,11 @@ from django.utils import timezone
|
||||
|
||||
|
||||
class UserProfile(models.Model):
|
||||
"""Модель профиля пользователя"""
|
||||
"""
|
||||
Модель профиля пользователя.
|
||||
|
||||
Профиль создается и изменяется при создании и изменении модель User.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
@ -32,17 +36,27 @@ def save_user_profile(sender, instance, **kwargs):
|
||||
|
||||
|
||||
class RoleChangeLogs(models.Model):
|
||||
"""Модель для логирования изменений ролей пользователя"""
|
||||
user = models.ForeignKey(to=User, on_delete=models.CASCADE,
|
||||
help_text='Пользователь, которому присвоили другую роль')
|
||||
"""
|
||||
Модель для логирования изменений ролей пользователя.
|
||||
"""
|
||||
|
||||
user = models.ForeignKey(to=User, on_delete=models.CASCADE, help_text='Пользователь, которому присвоили другую роль')
|
||||
old_role = models.IntegerField(default=0, help_text='Старая роль')
|
||||
new_role = models.IntegerField(default=0, help_text='Присвоенная роль')
|
||||
change_time = models.DateTimeField(help_text='Дата и время изменения роли', default=timezone.now)
|
||||
changed_by = models.ForeignKey(to=User, on_delete=models.CASCADE, related_name='changed_by',
|
||||
help_text='Кем была изменена роль')
|
||||
change_time = models.DateTimeField(default=timezone.now, help_text='Дата и время изменения роли')
|
||||
changed_by = models.ForeignKey(to=User, on_delete=models.CASCADE, related_name='changed_by', help_text='Кем была изменена роль')
|
||||
|
||||
|
||||
class UnassignedTicketStatus(models.IntegerChoices):
|
||||
"""
|
||||
Класс статусов не распределенных тикетов.
|
||||
|
||||
:param UNASSIGNED: Снят с пользователя, перенесён в буферную группу
|
||||
:param RESTORED: Авторство восстановлено
|
||||
:param NOT_FOUND: Пока нас не было, тикет испарился из буферной группы. Дополнительные действия не требуются
|
||||
:param CLOSED: Тикет уже был закрыт. Дополнительные действия не требуются
|
||||
:param SOLVED: Тикет решён. Записан на пользователя с почтой SOLVED_TICKETS_EMAIL
|
||||
"""
|
||||
UNASSIGNED = 0, 'Снят с пользователя, перенесён в буферную группу'
|
||||
RESTORED = 1, 'Авторство восстановлено'
|
||||
NOT_FOUND = 2, 'Пока нас не было, тикет испарился из буферной группы. Дополнительные действия не требуются'
|
||||
@ -51,6 +65,9 @@ class UnassignedTicketStatus(models.IntegerChoices):
|
||||
|
||||
|
||||
class UnassignedTicket(models.Model):
|
||||
assignee = models.ForeignKey(to=User, on_delete=models.CASCADE, related_name='tickets')
|
||||
"""
|
||||
Модель не распределенного тикета.
|
||||
"""
|
||||
assignee = models.ForeignKey(to=User, on_delete=models.CASCADE, related_name='tickets', help_text='Пользователь, с которого снят тикет')
|
||||
ticket_id = models.IntegerField(help_text='Номер тикера, для которого сняли ответственного')
|
||||
status = models.IntegerField(choices=UnassignedTicketStatus.choices, default=UnassignedTicketStatus.UNASSIGNED)
|
||||
status = models.IntegerField(choices=UnassignedTicketStatus.choices, default=UnassignedTicketStatus.UNASSIGNED, help_text='Статус тикета')
|
||||
|
@ -4,12 +4,18 @@ from main.models import UserProfile
|
||||
|
||||
|
||||
class UserSerializer(serializers.HyperlinkedModelSerializer):
|
||||
"""
|
||||
Класс serializer для модели User.
|
||||
"""
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['email']
|
||||
|
||||
|
||||
class ProfileSerializer(serializers.HyperlinkedModelSerializer):
|
||||
"""
|
||||
Класс serializer для модель профиля пользователя.
|
||||
"""
|
||||
user = UserSerializer()
|
||||
|
||||
class Meta:
|
||||
|
135
main/views.py
@ -1,4 +1,6 @@
|
||||
import logging
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.forms import PasswordResetForm
|
||||
@ -35,11 +37,16 @@ from .models import UserProfile
|
||||
|
||||
class CustomRegistrationView(RegistrationView):
|
||||
"""
|
||||
Отображение и логика работы страницы регистрации пользователя
|
||||
Отображение и логика работы страницы регистрации пользователя.
|
||||
|
||||
1. Ввод email пользователя, указанный на Zendesk
|
||||
2. В случае если пользователь с данным паролем зарегистрирован на Zendesk и относится к определенной организации, происходит сброс ссылки с установлением пароля на указанный email
|
||||
3. Создается пользователь class User, а также его профиль
|
||||
:param form_class: Форма, которую необходимо заполнить для регистрации
|
||||
:type form_class: :class:`forms.CustomRegistrationForm`
|
||||
:param template_name: Указание пути к html-странице django регистрации
|
||||
:type template_name: :class:`str`
|
||||
:param success_url: Указание пути к html-странице завершения регистрации
|
||||
:type success_url: :class:`django.utils.functional.lazy.<locals>.__proxy__`
|
||||
:param is_allowed: Определение зарегистрирован ли пользователь с введенным email на Zendesk и принадлежит ли он к организации SYSTEM
|
||||
:type is_allowed: :class:`bool`
|
||||
"""
|
||||
form_class = CustomRegistrationForm
|
||||
template_name = 'django_registration/registration_form.html'
|
||||
@ -47,6 +54,16 @@ class CustomRegistrationView(RegistrationView):
|
||||
is_allowed = True
|
||||
|
||||
def register(self, form: CustomRegistrationForm) -> User:
|
||||
"""
|
||||
Функция регистрации пользователя.
|
||||
1. Ввод email пользователя, указанный на Zendesk
|
||||
2. В случае если пользователь с данным паролем зарегистрирован на Zendesk и относится к организации SYSTEM,
|
||||
происходит сброс ссылки с установлением пароля на указанный email
|
||||
3. Создается пользователь class User, а также его профиль.
|
||||
|
||||
:param form: Email пользователя на Zendesk
|
||||
:return: user
|
||||
"""
|
||||
self.is_allowed = True
|
||||
if check_user_exist(form.data['email']) and get_user_organization(form.data['email']) == 'SYSTEM':
|
||||
forms = PasswordResetForm(self.request.POST)
|
||||
@ -76,9 +93,11 @@ class CustomRegistrationView(RegistrationView):
|
||||
self.is_allowed = False
|
||||
|
||||
@staticmethod
|
||||
def set_permission(user) -> None:
|
||||
def set_permission(user: User) -> None:
|
||||
"""
|
||||
Дает разрешение на просмотр страница администратора, если пользователь имеет роль admin
|
||||
Функция дает разрешение на просмотр страница администратора, если пользователь имеет роль admin.
|
||||
|
||||
:param user: авторизованный пользователь (получает разрешение, имея роль "admin")
|
||||
"""
|
||||
if user.userprofile.role == 'admin':
|
||||
content_type = ContentType.objects.get_for_model(UserProfile)
|
||||
@ -90,8 +109,11 @@ class CustomRegistrationView(RegistrationView):
|
||||
|
||||
def get_success_url(self, user: User = None) -> success_url:
|
||||
"""
|
||||
Возвращает url-адрес страницы, куда нужно перейти после успешной/неуспешной регистрации
|
||||
Используется самой django-registration
|
||||
Функция возвращает url-адрес страницы, куда нужно перейти после успешной/не успешной регистрации.
|
||||
Используется самой django-registration.
|
||||
|
||||
:param user: пользователь, пытающийся зарегистрироваться
|
||||
:return: адресация на страницу успешной регистрации
|
||||
"""
|
||||
if self.is_allowed:
|
||||
return reverse_lazy('password_reset_done')
|
||||
@ -102,7 +124,10 @@ class CustomRegistrationView(RegistrationView):
|
||||
@login_required()
|
||||
def profile_page(request: WSGIRequest) -> HttpResponse:
|
||||
"""
|
||||
Отображение страницы профиля
|
||||
Функция отображения страницы профиля.
|
||||
|
||||
:param request: данные пользователя из БД
|
||||
:return: адресация на страницу пользователя
|
||||
"""
|
||||
user_profile: UserProfile = request.user.userprofile
|
||||
update_profile(user_profile)
|
||||
@ -113,14 +138,27 @@ def profile_page(request: WSGIRequest) -> HttpResponse:
|
||||
return render(request, 'pages/profile.html', context)
|
||||
|
||||
|
||||
def auth_user(request):
|
||||
def auth_user(request: WSGIRequest) -> ZenpyUser:
|
||||
"""
|
||||
Функция возвращает профиль пользователя на Zendesk.
|
||||
|
||||
:param request: email, subdomain и token пользователя
|
||||
:return: объект пользователя Zendesk
|
||||
"""
|
||||
admin = ZendeskAdmin().admin
|
||||
zenpy_user: ZenpyUser = admin.users.search(request.user.email).values[0]
|
||||
return zenpy_user, admin
|
||||
|
||||
|
||||
@login_required()
|
||||
def work_page(request, id):
|
||||
def work_page(request: WSGIRequest, id: int) -> HttpResponse:
|
||||
"""
|
||||
Функция отображения страницы "Управления правами" для текущего пользователя (login_required).
|
||||
|
||||
:param request: объект пользователя
|
||||
:param id: id пользователя, используется для динамической адресации
|
||||
:return: адресация на страницу "Управления правами" (либо на страницу "Авторизации", если id и user.id не совпадают
|
||||
"""
|
||||
users = get_users_list()
|
||||
if request.user.id == id:
|
||||
engineers = []
|
||||
@ -143,7 +181,16 @@ def work_page(request, id):
|
||||
return redirect("login")
|
||||
|
||||
|
||||
def user_update(zenpy_user, admin, request):
|
||||
def user_update(zenpy_user: User, admin: User, request: WSGIRequest) -> UserProfile:
|
||||
"""
|
||||
Функция устанавливает пользователю роль "agent" (изменяет профиль).
|
||||
|
||||
:param zenpy_user: Пользователь Zendesk
|
||||
:param admin: Пользователь
|
||||
:param request: Запрос установки роли "agent" в Userprofile
|
||||
:return: Обновленный профиль пользователя
|
||||
"""
|
||||
|
||||
admin.users.update(zenpy_user)
|
||||
request.user.userprofile.role = "agent"
|
||||
request.user.userprofile.save()
|
||||
@ -151,7 +198,14 @@ def user_update(zenpy_user, admin, request):
|
||||
|
||||
|
||||
@login_required()
|
||||
def work_hand_over(request):
|
||||
def work_hand_over(request: WSGIRequest) -> HttpResponseRedirect:
|
||||
"""
|
||||
Функция позволяет текущему пользователю (login_required) сдать права, а именно сменить в Zendesk роль с "engineer" на "light agent"
|
||||
и установить роль "agent" в БД. Действия выполняются, если исходная роль пользователя "engineer".
|
||||
|
||||
:param request: данные текущего пользователя (login_required)
|
||||
:return: перезагрузка текущей страницы после выполнения смены роли
|
||||
"""
|
||||
zenpy_user, admin = auth_user(request)
|
||||
if zenpy_user.custom_role_id == ZENDESK_ROLES['engineer']:
|
||||
zenpy_user.custom_role_id = ZENDESK_ROLES['light_agent']
|
||||
@ -160,7 +214,13 @@ def work_hand_over(request):
|
||||
|
||||
|
||||
@login_required()
|
||||
def work_become_engineer(request):
|
||||
def work_become_engineer(request: WSGIRequest) -> HttpResponseRedirect:
|
||||
"""
|
||||
Функция меняет роль пользователя в Zendesk на "engineer" и присваивает роль "agent" в БД (в случае, если исходная роль пользователя была "light_agent").
|
||||
|
||||
:param request: данные текущего пользователя (login_required)
|
||||
:return: перезагрузка текущей страницы после выполнения смены роли
|
||||
"""
|
||||
zenpy_user, admin = auth_user(request)
|
||||
if zenpy_user.custom_role_id == ZENDESK_ROLES['light_agent']:
|
||||
zenpy_user.custom_role_id = ZENDESK_ROLES['engineer']
|
||||
@ -168,16 +228,34 @@ def work_become_engineer(request):
|
||||
return HttpResponseRedirect(reverse('work', args=(request.user.id,)))
|
||||
|
||||
|
||||
def main_page(request):
|
||||
def main_page(request: WSGIRequest) -> HttpResponse:
|
||||
"""
|
||||
Отображение логгирования на главной странице
|
||||
Функция отображения логгирования на главной странице.
|
||||
|
||||
.. todo::
|
||||
Дописать параметры в документацию:
|
||||
|
||||
:param request:
|
||||
:return:
|
||||
"""
|
||||
logger = logging.getLogger('main.index')
|
||||
logger.info('Index page opened')
|
||||
return render(request, 'pages/index.html')
|
||||
|
||||
|
||||
class AdminPageView(LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, FormView):
|
||||
class AdminPageView(LoginRequiredMixin, PermissionRequiredMixin, FormView):
|
||||
"""
|
||||
Класс отображения страницы администратора.
|
||||
|
||||
:param permission_required: Права доступа к странице администратора
|
||||
:type permission_required: :class:`str`
|
||||
:param template_name: HTML-шаблон страницы администратора
|
||||
:type template_name: :class:`str`
|
||||
:param form_class: Форма страницы администратора
|
||||
:type form_class: :class:`forms.AdminPageUsersForm`
|
||||
:param success_url: Адрес страницы администратора
|
||||
:type success_url: :class:`HttpResponseRedirect`
|
||||
"""
|
||||
permission_required = 'main.has_control_access'
|
||||
template_name = 'pages/adm_ruleset.html'
|
||||
form_class = AdminPageUsers
|
||||
@ -186,7 +264,10 @@ class AdminPageView(LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageM
|
||||
|
||||
def form_valid(self, form: AdminPageUsers) -> AdminPageUsers:
|
||||
"""
|
||||
Функция установки ролей пользователям
|
||||
Функция обновления страницы AdminPageUsers.
|
||||
|
||||
:param form: Форма страницы администратора
|
||||
:return: Обновленная страница (пользователям проставлены новые статусы)
|
||||
"""
|
||||
users = form.cleaned_data['users']
|
||||
if 'engineer' in self.request.POST:
|
||||
@ -196,6 +277,12 @@ class AdminPageView(LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageM
|
||||
return super().form_valid(form)
|
||||
|
||||
def make_engineers(self, users):
|
||||
"""
|
||||
Функция проходит по списку пользователей, проставляя статус "engineer".
|
||||
|
||||
:param users: Список пользователей
|
||||
:return: Обновленный список пользователей
|
||||
"""
|
||||
for user in users:
|
||||
make_engineer(user, self.request.user)
|
||||
|
||||
@ -244,9 +331,15 @@ class UsersViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
|
||||
|
||||
@login_required()
|
||||
def statistic_page(request):
|
||||
if not request.user.has_perm('main.has_control_access'):
|
||||
raise PermissionDenied
|
||||
def statistic_page(request: WSGIRequest) -> HttpResponse:
|
||||
"""
|
||||
Функция отображения страницы статистики (для "superuser").
|
||||
|
||||
:param request: данные о пользователе: email, время и интервал работы. Данные получаем через forms.StatisticForm
|
||||
:return: адресация на страницу статистики
|
||||
"""
|
||||
if not request.user.is_superuser:
|
||||
return redirect('index')
|
||||
context = {
|
||||
'pagename': 'страница статистики',
|
||||
'errors': list(),
|
||||
|