Merge branch 'develop' into feature/periodic

# Conflicts:
#	access_controller/settings.py
#	access_controller/urls.py
#	main/extra_func.py
#	main/views.py
This commit is contained in:
Yuriy Kulakov 2021-03-12 15:37:50 +03:00
commit d300cc2f06
15 changed files with 517 additions and 44 deletions

View File

@ -136,7 +136,6 @@ ACCOUNT_ACTIVATION_DAYS = 7
LOGIN_REDIRECT_URL = '/'
LOGOUT_REDIRECT_URL = '/'
# Название_приложения.Названиеайла.Название_класса_обработчика
AUTHENTICATION_BACKENDS = [
'access_controller.auth.EmailAuthBackend',
@ -192,3 +191,5 @@ REST_FRAMEWORK = {
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
]
}
ONE_DAY = 12 # Количество часов в 1 рабочем дне

View File

@ -16,8 +16,10 @@ Including another URLconf
from django.contrib import admin
from django.contrib.auth import views as auth_views
from django.urls import path, include
from main.views import work_page, work_hand_over, work_become_engineer, AdminPageView
from main.views import main_page, profile_page, CustomRegistrationView, CustomLoginView
from main.views import work_page, work_hand_over, work_become_engineer, \
AdminPageView, statistic_page
from main.urls import router
@ -26,7 +28,7 @@ urlpatterns = [
path('', main_page, name='index'),
path('accounts/profile/', profile_page, name='profile'),
path('accounts/register/', CustomRegistrationView.as_view(), name='registration'),
path('accounts/login/', CustomLoginView.as_view(extra_context={}), name='login', ), # TODO add extra context
path('accounts/login/', CustomLoginView.as_view(), name='login'),
path('accounts/', include('django.contrib.auth.urls')),
path('accounts/', include('django_registration.backends.one_step.urls')),
path('work/<int:id>', work_page, name="work"),
@ -34,8 +36,9 @@ urlpatterns = [
path('work/become_engineer/', work_become_engineer, name="work_become_engineer"),
path('accounts/', include('django_registration.backends.activation.urls')),
path('accounts/login/', include('django.contrib.auth.urls')),
path('control/', AdminPageView.as_view(), name='control')
]
path('control/', AdminPageView.as_view(), name='control'),
path('statistic/', statistic_page, name='statistic')
]
urlpatterns += [
path(

View File

@ -1,14 +1,15 @@
import os
from datetime import timedelta, datetime, date
from django.contrib.auth.models import User
from zenpy import Zenpy
from zenpy.lib.exception import APIException
from main.models import UserProfile
from main.models import UserProfile, RoleChangeLogs
from django.core.exceptions import ObjectDoesNotExist
from access_controller.settings import ZENDESK_ROLES
from access_controller.settings import ZENDESK_ROLES as ROLES, ONE_DAY
class ZendeskAdmin:
@ -75,7 +76,7 @@ class ZendeskAdmin:
user = self.admin.users.search(email).values[0]
return user.photo['content_url'] if user.photo else None
def get_user(self, email: str) -> str:
def get_user(self, email: str):
"""
Функция **get_user** возвращает пользователя (объект) по его email
@ -89,7 +90,7 @@ class ZendeskAdmin:
Функция **get_user_org** возвращает организацию, к которой относится пользователь по его email
"""
user = self.admin.users.search(email).values[0]
return user.organization.name
return user.organization.name if user.organization else None
def create_admin(self) -> Zenpy:
"""
@ -103,7 +104,7 @@ class ZendeskAdmin:
if self.email is None:
raise ValueError('access_controller email not in env')
self.credentials['email'] = os.getenv('ACCESS_CONTROLLER_API_EMAIL')
self.credentials['email'] = self.email
if self.token:
self.credentials['token'] = self.token
@ -132,14 +133,14 @@ def make_engineer(user_profile: UserProfile) -> UserProfile:
"""
Функция **make_engineer** устанавливапет пользователю роль инженера.
"""
update_role(user_profile, ZENDESK_ROLES['engineer'])
update_role(user_profile, ROLES['engineer'])
def make_light_agent(user_profile: UserProfile) -> UserProfile:
"""
Функция **make_light_agent** устанавливапет пользователю роль легкого агента.
"""
update_role(user_profile, ZENDESK_ROLES['light_agent'])
update_role(user_profile, ROLES['light_agent'])
def get_users_list() -> list:
@ -161,6 +162,7 @@ def update_profile(user_profile: UserProfile) -> UserProfile:
user = ZendeskAdmin().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
user_profile.image = user.photo['content_url'] if user.photo else None
user_profile.save()
@ -214,9 +216,9 @@ def count_users(users) -> tuple:
"""
engineers, light_agents = 0, 0
for user in users:
if user.custom_role_id == ZENDESK_ROLES['engineer']:
if user.custom_role_id == ROLES['engineer']:
engineers += 1
elif user.custom_role_id == ZENDESK_ROLES['light_agent']:
elif user.custom_role_id == ROLES['light_agent']:
light_agents += 1
return engineers, light_agents
@ -233,3 +235,211 @@ def update_users_in_model():
except ObjectDoesNotExist:
pass
return users
def daterange(start_date, end_date) -> list:
"""
Возвращает список дней с start_date по end_date исключая правую границу
"""
dates = []
for n in range(int((end_date - start_date).days)):
dates.append(start_date + timedelta(n))
return dates
def get_timedelta(log, time=None) -> timedelta:
"""
Возвращает объект класса timedelta, который хранит промежуток времени от начала суток до момента,
который находится в log (объект класса RoleChangeLogs) или в time(datetime.time), если введён
"""
if time is None:
time = log.change_time.time()
time = timedelta(hours=time.hour, minutes=time.minute, seconds=time.second)
return time
def last_day_of_month(day):
"""
Возвращает последний день любого месяца
"""
next_month = day.replace(day=28) + timedelta(days=4)
return next_month - timedelta(days=next_month.day)
class StatisticData:
def __init__(self, start_date, end_date, user_email, stat=None):
self.display = None
self.interval = None
self.start_date = start_date
self.end_date = end_date
self.email = user_email
self.errors = list()
self.warnings = list()
self.data = dict()
self.statistic = dict()
self._set_data()
if stat is None:
self._set_statistic()
else:
self.statistic = stat
def get_statistic(self):
"""
Вернуть словарь statistic с применением формата отображения и интеравала работы(если они есть)
None, если были ошибки при создании
"""
if self.is_valid_statistic():
stat = self.statistic
stat = self._use_display(stat)
stat = self._use_interval(stat)
return stat
else:
return None
def is_valid_statistic(self):
"""
Были ли ошибки при создании статистики
"""
return not self.errors and self.statistic
def set_interval(self, interval):
"""
Устанавливает интервал работы
"""
if interval not in ['months', 'days']:
self.errors += ['Интервал работы должен быть в днях или месяцах']
return False
self.interval = interval
return True
def set_display(self, display_format):
"""
Устанавливает формат отображения
"""
if display_format not in ['days', 'hours']:
self.errors += ['Формат отображения должен быть в часах или днях']
return False
self.display = display_format
return True
def get_data(self):
"""
Вернуть данные
data - массив объектов RoleChangeLogs, является списком логов пользователя
data может быть пустым списком
"""
if self.is_valid_data():
return self.data
else:
return None
def is_valid_data(self):
"""
Были ли ошибки при получении логов
"""
return not self.errors
def _use_display(self, stat):
"""
Приводит данные к формату отображения
"""
if not self.is_valid_statistic() or not self.display:
return stat
new_stat = {}
for key, item in stat.items():
if self.display == 'hours':
new_stat[key] = item / 3600
elif self.display == 'days':
new_stat[key] = item / (ONE_DAY * 3600)
return new_stat
def _use_interval(self, stat):
"""
Объединяет ключи и значения в соответствии с интервалом работы
"""
if not self.is_valid_statistic() or not self.interval:
return stat
new_stat = {}
if self.interval == 'months':
# Переделываем ключи под формат('началоесяца - конец_месяца')
for key, value in stat.items():
current_month_start = max(self.start_date, date(year=key.year, month=key.month, day=1))
current_month_end = min(self.end_date, last_day_of_month(date(year=key.year, month=key.month, day=1)))
index = ' - '.join([str(current_month_start), str(current_month_end)])
if new_stat.get(index):
new_stat[index] += value
else:
new_stat[index] = value
elif self.interval == 'days':
new_stat = stat # статистика изначально в днях
return new_stat
def check_time(self):
"""
Проверка на правильность введенного времени
"""
if self.end_date < self.start_date or self.end_date > datetime.now().date():
return False
return True
def _set_data(self):
"""
Получение логов в диапазоне дат start_date-end_date для пользователя с почтой email
"""
if not self.check_time():
self.errors += ['Конец диапазона должен быть позже начала диапазона и раньше текущего времени']
return
try:
self.data = RoleChangeLogs.objects.filter(
change_time__range=[self.start_date, self.end_date + timedelta(days=1)],
user=User.objects.get(email=self.email),
).order_by('change_time')
except User.DoesNotExist:
self.errors += ['Пользователь не найден']
def _set_statistic(self):
"""
Функция заполняет словарь, в котором ключ - дата, значение - кол-во проработанных в этот день секунд
"""
self.clear_statistic()
if not self.get_data():
self.warnings += ['Не обнаружены изменения роли в данном промежутке']
return None
first_log, last_log = self.data[0], self.data[len(self.data) - 1]
if first_log.old_role == ROLES['engineer']:
self.fill_daterange(self.start_date, first_log.change_time.date())
self.statistic[first_log.change_time.date()] += get_timedelta(first_log).total_seconds()
if last_log.new_role == ROLES['engineer']:
self.fill_daterange(last_log.change_time.date() + timedelta(days=1), self.end_date + timedelta(days=1))
self.statistic[last_log.change_time.date()] += (timedelta(days=1) - get_timedelta(last_log)).total_seconds()
if self.end_date == datetime.now().date():
self.statistic[self.end_date] = get_timedelta(None, datetime.now().time()).total_seconds()
for log_index in range(len(self.data) - 1):
if self.data[log_index].new_role == ROLES['engineer']:
current_log, next_log = self.data[log_index], self.data[log_index + 1]
if current_log.change_time.date() != next_log.change_time.date():
self.statistic[current_log.change_time.date()] += (
timedelta(days=1) - get_timedelta(current_log)).total_seconds()
self.statistic[next_log.change_time.date()] += get_timedelta(next_log).total_seconds()
self.fill_daterange(current_log.change_time.date() + timedelta(days=1), next_log.change_time.date())
else:
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):
"""
Заполение диапазона дат значением val
по умолчанию val = кол-во секунд в 1 дне
"""
for day in daterange(first, last):
self.statistic[day] = val
def clear_statistic(self):
"""
Обнуление всех дней
"""
self.statistic.clear()
self.fill_daterange(self.start_date, self.end_date + timedelta(days=1), 0)

View File

@ -2,6 +2,7 @@ from django import forms
from django.contrib.auth.forms import AuthenticationForm
from django_registration.forms import RegistrationFormUniqueEmail
from access_controller.settings import ZENDESK_ROLES
from main.models import UserProfile
@ -14,7 +15,6 @@ class CustomRegistrationForm(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)
for visible in self.visible_fields():
@ -23,7 +23,7 @@ class CustomRegistrationForm(RegistrationFormUniqueEmail):
visible.field.widget.attrs['class'] += 'form-control'
else:
visible.field.widget.attrs['class'] = 'form-control'
if visible.html_name !='email':
if visible.html_name != 'email':
visible.field.required = False
class Meta(RegistrationFormUniqueEmail.Meta):
@ -42,7 +42,8 @@ class AdminPageUsers(forms.Form):
queryset=UserProfile.objects.filter(role='agent'),
widget=forms.CheckboxSelectMultiple(
attrs={
'class': 'form-check-input'
'class': 'form-check-input',
}
),
label=''
@ -65,3 +66,31 @@ class CustomAuthenticationForm(AuthenticationForm):
,
'inactive': "Аккаунт не активен.",
}
class StatisticForm(forms.Form):
email = forms.EmailField(
label='Электроная почта',
)
interval = forms.CharField( # TODO: Переделать под html страницу
label='Интервал работы',
)
display_format = forms.CharField( # TODO: Переделать под html страницу
label='Формат отображения',
)
range_start = forms.DateField( # TODO: Переделать под html страницу
label='Начало диапазона',
widget=forms.DateInput(
attrs={
'type': 'date',
}
),
)
range_end = forms.DateField( # TODO: Переделать под html страницу
label='Конец диапазона',
widget=forms.DateInput(
attrs={
'type': 'date',
}
),
)

View File

@ -0,0 +1,61 @@
# Generated by Django 3.1.6 on 2021-03-11 08:00
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('main', '0008_auto_20210303_2305'),
]
operations = [
migrations.AlterField(
model_name='rolechangelogs',
name='change_time',
field=models.DateTimeField(help_text='Дата и время изменения роли'),
),
migrations.AlterField(
model_name='rolechangelogs',
name='changed_by',
field=models.ForeignKey(help_text='Кем была изменена роль', on_delete=django.db.models.deletion.CASCADE, related_name='changed_by', to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='rolechangelogs',
name='name',
field=models.TextField(help_text='Имя пользователя'),
),
migrations.AlterField(
model_name='rolechangelogs',
name='new_role',
field=models.TextField(help_text='Присвоенная роль'),
),
migrations.AlterField(
model_name='rolechangelogs',
name='user',
field=models.ForeignKey(help_text='Пользователь, которому присвоили другую роль', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='userprofile',
name='image',
field=models.URLField(blank=True, help_text='Аватарка', null=True),
),
migrations.AlterField(
model_name='userprofile',
name='name',
field=models.CharField(default='None', help_text='Имя пользователя на нашем сайте', max_length=100),
),
migrations.AlterField(
model_name='userprofile',
name='role',
field=models.CharField(default='None', help_text='Код роли пользователя', max_length=100),
),
migrations.AlterField(
model_name='userprofile',
name='user',
field=models.OneToOneField(help_text='Пользователь', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 3.1.6 on 2021-03-11 08:04
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('main', '0009_models_help_text'),
]
operations = [
migrations.AlterModelOptions(
name='userprofile',
options={'permissions': (('has_control_access', 'Can view admin page'),)},
),
]

View File

@ -0,0 +1,28 @@
# Generated by Django 3.1.6 on 2021-03-11 14:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('main', '0010_userprofile_meta'),
]
operations = [
migrations.AddField(
model_name='rolechangelogs',
name='old_role',
field=models.IntegerField(default=0, help_text='Старая роль'),
),
migrations.AlterField(
model_name='rolechangelogs',
name='new_role',
field=models.IntegerField(default=0, help_text='Присвоенная роль'),
),
migrations.AlterField(
model_name='userprofile',
name='role',
field=models.IntegerField(default=0, help_text='Код роли пользователя'),
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 3.1.6 on 2021-03-12 09:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('main', '0011_auto_20210311_1734'),
]
operations = [
migrations.AddField(
model_name='userprofile',
name='custom_role_id',
field=models.IntegerField(default=0, help_text='Код роли пользователя'),
),
migrations.AlterField(
model_name='userprofile',
name='role',
field=models.CharField(default='None', help_text='Глобальное имя роли пользователя', max_length=100),
),
]

View File

@ -7,8 +7,14 @@ from django.dispatch import receiver
class UserProfile(models.Model):
"""Модель профиля пользователя"""
class Meta:
permissions = (
('has_control_access', 'Can view admin page'),
)
user = models.OneToOneField(to=User, on_delete=models.CASCADE, help_text='Пользователь')
role = models.CharField(default='None', max_length=100, help_text='Код роли пользователя')
role = models.CharField(default='None', max_length=100, help_text='Глобальное имя роли пользователя')
custom_role_id = models.IntegerField(default=0, help_text='Код роли пользователя')
image = models.URLField(null=True, blank=True, help_text='Аватарка')
name = models.CharField(default='None', max_length=100, help_text='Имя пользователя на нашем сайте')
@ -26,8 +32,11 @@ 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='Пользователь, которому присвоили другую роль')
name = models.TextField(help_text='Имя пользователя')
new_role = models.TextField(help_text='Присвоенная роль')
old_role = models.IntegerField(default=0, help_text='Старая роль')
new_role = models.IntegerField(default=0, help_text='Присвоенная роль')
change_time = models.DateTimeField(help_text='Дата и время изменения роли')
changed_by = models.ForeignKey(to=User, on_delete=models.CASCADE, related_name='changed_by', help_text='Кем была изменена роль')
changed_by = models.ForeignKey(to=User, on_delete=models.CASCADE, related_name='changed_by',
help_text='Кем была изменена роль')

View File

@ -3,16 +3,18 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<nav class="navbar navbar-light" style="background-color: #00FF00;">
<nav class="navbar navbar-light" style="background-color: #113A60;">
<a class="navbar-brand" href="{% url 'index' %}">
<img src="{% static 'main/img/logo.png' %}" width="30" height="30" class="d-inline-block align-top" alt="" loading="lazy">
Access Controller
<img src="{% static 'main/img/logo_real.png' %}" width="107" height="22" class="d-inline-block align-top" alt="" loading="lazy">
<t style="color:#FFFFFF">Access Controller</t>
</a>
{% if request.user.is_authenticated %}
<div class="btn-group" role="group" aria-label="Basic example">
<a class="btn btn-secondary" href="{% url 'profile' %}">Профиль</a>
{% if perms.main.has_control_access %}
<a class="btn btn-secondary" href="{% url 'control' %}">Управление</a>
{% else %}
<a class="btn btn-secondary" href="{% url 'work' request.user.id %}">Запрос прав</a>
{% endif %}
<a class="btn btn-secondary" href="{% url 'logout' %}">Выйти</a>
</div>

View File

@ -10,5 +10,5 @@
{% block content %}
<br>
<h4> Регистрация прошла успешно. <a href="/login/">Войти сейчас</a></h4>
<h4> Регистрация прошла успешно. <a href="{% url 'login'%}">Войти сейчас</a></h4>
{% endblock %}

View File

@ -0,0 +1,49 @@
{% extends 'base/base.html' %}
{% load static %}
{% block title %}{{ pagename }}{% endblock %}
{% block heading %} Пример страницы статистики(палками не бейти плиз){% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="{% static 'main/css/work.css' %}">
{% endblock %}
{% block content %}
<div>
<form action="" method="post">
{% csrf_token %}
{% for field in form %}
{{field.label}}
{{field}}
<br>
{% endfor %}
<input type="submit">
</form>
<ul>
{% for error in errors %}
<li><span class="badge bg-danger">{{error}}</span></li>
{% endfor %}
</ul>
{%if form.errors%}
<ul>
{% for field, errors in form.errors.items %}
{% for error in errors %}
<li><span class="badge bg-danger">{{error}}</span></li>
{% endfor %}
{% endfor %}
</ul>
{%endif%}
<ul>
{% for warning in warnings %}
<li><span class="badge bg-warning">{{warning}}</span></li>
{% endfor %}
</ul>
{% for key,val in log_stats.items %}
<h3>{{key}} <b>|</b> {{val}}</h3>
<br>
{% endfor %}
</div>
{% endblock %}

View File

@ -22,13 +22,13 @@
<h6 class="table-title">Список сотрудников с правами инженера</h6>
<table class="light-table">
<thead>
<th>ID</th>
<th>Email</th>
<th>Name</th>
</thead>
<tbody>
{% for engineer in engineers %}
<tr>
<td>{{ engineer.id }}</td>
<td>{{ engineer.email }}</td>
<td>{{ engineer.name }}</td>
</tr>
{% endfor %}

View File

@ -1,8 +1,10 @@
import logging
import os
from datetime import datetime
from django.contrib.auth.tokens import default_token_generator
from django.contrib.auth.forms import PasswordResetForm
from django.contrib.auth.models import User, Permission
from django.contrib.auth.tokens import default_token_generator
from django.contrib.auth.views import LoginView
from django.contrib.contenttypes.models import ContentType
from django.core.handlers.wsgi import WSGIRequest
@ -10,21 +12,21 @@ from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import render, get_list_or_404, redirect
from django.urls import reverse_lazy, reverse
from django.views.generic import FormView
from zenpy import Zenpy
from access_controller.settings import EMAIL_HOST_USER
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
from django.contrib.auth.models import User, Permission
from main.models import UserProfile
from main.forms import CustomRegistrationForm, AdminPageUsers, CustomAuthenticationForm
from django_registration.views import RegistrationView
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from zenpy import Zenpy
from zenpy.lib.api_objects import User as ZenpyUser
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
from main.models import UserProfile
from main.forms import AdminPageUsers, CustomRegistrationForm, CustomAuthenticationForm, StatisticForm
from access_controller.settings import EMAIL_HOST_USER, ZENDESK_ROLES
# Django REST
from rest_framework import viewsets, status
from main.serializers import ProfileSerializer
@ -130,10 +132,20 @@ def auth_user(request):
@login_required()
def work_page(request, id):
users = get_users_list()
if request.user.id == id:
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']:
light_agents.append(user)
context = {
'engineers': UserProfile.objects.filter(role="admin"),
'agents': UserProfile.objects.filter(role="agent"),
'engineers': engineers,
'agents': light_agents,
'pagename': 'Управление правами'
}
return render(request, 'pages/work.html', context)
@ -143,8 +155,9 @@ def work_page(request, id):
@login_required()
def work_hand_over(request):
zenpy_user, admin = auth_user(request)
if zenpy_user.role == "admin" or zenpy_user.role == "end-user":
zenpy_user.role = "agent"
if zenpy_user.custom_role_id == ZENDESK_ROLES['engineer']:
zenpy_user.custom_role_id = ZENDESK_ROLES['light_agent']
admin.users.update(zenpy_user)
request.user.userprofile.role = "agent"
request.user.userprofile.save()
@ -154,10 +167,10 @@ def work_hand_over(request):
@login_required()
def work_become_engineer(request):
zenpy_user, admin = auth_user(request)
if zenpy_user.role == "agent" or zenpy_user.role == "end-user":
zenpy_user.role = "admin"
if zenpy_user.custom_role_id == ZENDESK_ROLES['light_agent']:
zenpy_user.custom_role_id = ZENDESK_ROLES['engineer']
admin.users.update(zenpy_user)
request.user.userprofile.role = "admin"
request.user.userprofile.role = "agent"
request.user.userprofile.save()
return HttpResponseRedirect(reverse('work', args=(request.user.id,)))
@ -233,3 +246,31 @@ class UsersViewSet(viewsets.ReadOnlyModelViewSet):
'light_agents': count[1]
})
@login_required()
def statistic_page(request):
if not request.user.is_superuser:
return redirect('index')
context = {
'pagename': 'страница статистики',
'errors': list(),
}
if request.method == "POST":
form = StatisticForm(request.POST)
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
context['log_stats'] = stats if not context['errors'] else None
if request.method == 'GET':
form = StatisticForm()
context['form'] = form
return render(request, 'pages/stat.html', context)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB